revise 'translatable string' policy
[claws.git] / src / common / nntp.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <glib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "intl.h"
29 #include "nntp.h"
30 #include "socket.h"
31 #include "utils.h"
32 #include "log.h"
33 #if USE_OPENSSL
34 #  include "ssl.h"
35 #endif
36
37 static gint verbose = 1;
38
39 static void nntp_session_destroy(Session        *session);
40
41 static gint nntp_ok             (SockInfo       *sock,
42                                  gchar          *argbuf);
43
44 static gint nntp_gen_send       (SockInfo       *sock,
45                                  const gchar    *format,
46                                  ...);
47 static gint nntp_gen_recv       (SockInfo       *sock,
48                                  gchar          *buf,
49                                  gint            size);
50 static gint nntp_gen_command    (NNTPSession    *session,
51                                  gchar          *argbuf,
52                                  const gchar    *format,
53                                  ...);
54
55 #if USE_OPENSSL
56 Session *nntp_session_new(const gchar *server, gushort port, gchar *buf,
57                           const gchar *userid, const gchar *passwd,
58                           SSLType ssl_type)
59 #else
60 Session *nntp_session_new(const gchar *server, gushort port, gchar *buf,
61                           const gchar *userid, const gchar *passwd)
62 #endif
63 {
64         NNTPSession *session;
65         SockInfo *sock;
66
67         if ((sock = sock_connect(server, port)) == NULL) {
68                 log_warning(_("Can't connect to NNTP server: %s:%d\n"),
69                             server, port);
70                 return NULL;
71         }
72
73 #if USE_OPENSSL
74         if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
75                 sock_close(sock);
76                 return NULL;
77         }
78 #endif
79
80         if (nntp_ok(sock, buf) != NN_SUCCESS) {
81                 sock_close(sock);
82                 return NULL;
83         }
84
85         session = g_new0(NNTPSession, 1);
86
87         session_init(SESSION(session));
88
89         SESSION(session)->type                  = SESSION_NEWS;
90         SESSION(session)->server                = g_strdup(server);
91         SESSION(session)->sock                  = sock;
92         SESSION(session)->last_access_time      = time(NULL);
93         SESSION(session)->data                  = NULL;
94
95         SESSION(session)->destroy               = nntp_session_destroy;
96
97         session->group = NULL;
98
99         if (userid && passwd) {
100                 gint ok;
101
102                 session->userid = g_strdup(userid);
103                 session->passwd = g_strdup(passwd);
104
105                 ok = nntp_gen_send(sock, "AUTHINFO USER %s", session->userid);
106                 if (ok != NN_SUCCESS) {
107                         session_destroy(SESSION(session));
108                         return NULL;
109                 }
110                 ok = nntp_ok(sock, NULL);
111                 if (ok == NN_AUTHCONT) {
112                         ok = nntp_gen_send(sock, "AUTHINFO PASS %s",
113                                            session->passwd);
114                         if (ok != NN_SUCCESS) {
115                                 session_destroy(SESSION(session));
116                                 return NULL;
117                         }
118                         ok = nntp_ok(sock, NULL);
119                         if (ok != NN_SUCCESS)
120                                 session->auth_failed = TRUE;
121                 }
122                 if (ok == NN_SOCKET) {
123                         session_destroy(SESSION(session));
124                         return NULL;
125                 }
126         }
127
128         return SESSION(session);
129 }
130
131 void nntp_forceauth(NNTPSession *session, gchar *buf, const gchar *userid, const gchar *passwd)
132
133 {
134         if (!session) return;
135                 
136         nntp_gen_command(session, buf , "AUTHINFO USER %s", userid);
137
138
139 }
140
141 static void nntp_session_destroy(Session *session)
142 {
143         NNTPSession *nntp_session = NNTP_SESSION(session);
144
145         g_return_if_fail(session != NULL);
146
147         g_free(nntp_session->group);
148         g_free(nntp_session->userid);
149         g_free(nntp_session->passwd);
150 }
151
152 gint nntp_group(NNTPSession *session, const gchar *group,
153                 gint *num, gint *first, gint *last)
154 {
155         gint ok;
156         gint resp;
157         gchar buf[NNTPBUFSIZE];
158
159         ok = nntp_gen_command(session, buf, "GROUP %s", group);
160
161         if (ok != NN_SUCCESS && ok != NN_SOCKET && ok != NN_AUTHREQ) {
162                 ok = nntp_mode(session, FALSE);
163                 if (ok == NN_SUCCESS)
164                         ok = nntp_gen_command(session, buf, "GROUP %s", group);
165         }
166
167         if (ok != NN_SUCCESS)
168                 return ok;
169
170         if (sscanf(buf, "%d %d %d %d", &resp, num, first, last)
171             != 4) {
172                 log_warning(_("protocol error: %s\n"), buf);
173                 return NN_PROTOCOL;
174         }
175
176         return NN_SUCCESS;
177 }
178
179 gint nntp_get_article(NNTPSession *session, const gchar *cmd, gint num,
180                       gchar **msgid)
181 {
182         gint ok;
183         gchar buf[NNTPBUFSIZE];
184
185         if (num > 0)
186                 ok = nntp_gen_command(session, buf, "%s %d", cmd, num);
187         else
188                 ok = nntp_gen_command(session, buf, cmd);
189
190         if (ok != NN_SUCCESS)
191                 return ok;
192
193         extract_parenthesis(buf, '<', '>');
194         if (buf[0] == '\0') {
195                 log_warning(_("protocol error\n"));
196                 return NN_PROTOCOL;
197         }
198         *msgid = g_strdup(buf);
199
200         return NN_SUCCESS;
201 }
202
203 gint nntp_article(NNTPSession *session, gint num, gchar **msgid)
204 {
205         return nntp_get_article(session, "ARTICLE", num, msgid);
206 }
207
208 gint nntp_body(NNTPSession *session, gint num, gchar **msgid)
209 {
210         return nntp_get_article(session, "BODY", num, msgid);
211 }
212
213 gint nntp_head(NNTPSession *session, gint num, gchar **msgid)
214 {
215         return nntp_get_article(session, "HEAD", num, msgid);
216 }
217
218 gint nntp_stat(NNTPSession *session, gint num, gchar **msgid)
219 {
220         return nntp_get_article(session, "STAT", num, msgid);
221 }
222
223 gint nntp_next(NNTPSession *session, gint *num, gchar **msgid)
224 {
225         gint ok;
226         gint resp;
227         gchar buf[NNTPBUFSIZE];
228
229         ok = nntp_gen_command(session, buf, "NEXT");
230
231         if (ok != NN_SUCCESS)
232                 return ok;
233
234         if (sscanf(buf, "%d %d", &resp, num) != 2) {
235                 log_warning(_("protocol error: %s\n"), buf);
236                 return NN_PROTOCOL;
237         }
238
239         extract_parenthesis(buf, '<', '>');
240         if (buf[0] == '\0') {
241                 log_warning(_("protocol error\n"));
242                 return NN_PROTOCOL;
243         }
244         *msgid = g_strdup(buf);
245
246         return NN_SUCCESS;
247 }
248
249 gint nntp_xover(NNTPSession *session, gint first, gint last)
250 {
251         gint ok;
252         gchar buf[NNTPBUFSIZE];
253
254         ok = nntp_gen_command(session, buf, "XOVER %d-%d", first, last);
255         if (ok != NN_SUCCESS)
256                 return ok;
257
258         return NN_SUCCESS;
259 }
260
261 gint nntp_xhdr(NNTPSession *session, const gchar *header, gint first, gint last)
262 {
263         gint ok;
264         gchar buf[NNTPBUFSIZE];
265
266         ok = nntp_gen_command(session, buf, "XHDR %s %d-%d",
267                               header, first, last);
268         if (ok != NN_SUCCESS)
269                 return ok;
270
271         return NN_SUCCESS;
272 }
273
274 gint nntp_list(NNTPSession *session)
275 {
276         return nntp_gen_command(session, NULL, "LIST");
277 }
278
279 gint nntp_post(NNTPSession *session, FILE *fp)
280 {
281         gint ok;
282         gchar buf[NNTPBUFSIZE];
283         gchar *msg;
284
285         ok = nntp_gen_command(session, buf, "POST");
286         if (ok != NN_SUCCESS)
287                 return ok;
288
289         msg = get_outgoing_rfc2822_str(fp);
290         if (sock_write_all(SESSION(session)->sock, msg, strlen(msg)) < 0) {
291                 log_warning(_("Error occurred while posting\n"));
292                 g_free(msg);
293                 return NN_SOCKET;
294         }
295         g_free(msg);
296
297         sock_write_all(SESSION(session)->sock, ".\r\n", 3);
298         if ((ok = nntp_ok(SESSION(session)->sock, buf)) != NN_SUCCESS)
299                 return ok;
300
301         return NN_SUCCESS;
302 }
303
304 gint nntp_newgroups(NNTPSession *session)
305 {
306         return NN_SUCCESS;
307 }
308
309 gint nntp_newnews(NNTPSession *session)
310 {
311         return NN_SUCCESS;
312 }
313
314 gint nntp_mode(NNTPSession *session, gboolean stream)
315 {
316         gint ok;
317
318         ok = nntp_gen_command(session, NULL, "MODE %s",
319                               stream ? "STREAM" : "READER");
320
321         return ok;
322 }
323
324 static gint nntp_ok(SockInfo *sock, gchar *argbuf)
325 {
326         gint ok;
327         gchar buf[NNTPBUFSIZE];
328
329         if ((ok = nntp_gen_recv(sock, buf, sizeof(buf))) == NN_SUCCESS) {
330                 if (strlen(buf) < 3)
331                         return NN_ERROR;
332
333                 if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
334                     (buf[3] == ' ' || buf[3] == '\0')) {
335                         if (argbuf)
336                                 strcpy(argbuf, buf);
337
338                         if (!strncmp(buf, "381", 3))
339                                 return NN_AUTHCONT;
340
341                         return NN_SUCCESS;
342                 } else if (!strncmp(buf, "480", 3))
343                         return NN_AUTHREQ;
344                 else
345                         return NN_ERROR;
346         }
347
348         return ok;
349 }
350
351 static gint nntp_gen_send(SockInfo *sock, const gchar *format, ...)
352 {
353         gchar buf[NNTPBUFSIZE];
354         va_list args;
355
356         va_start(args, format);
357         g_vsnprintf(buf, sizeof(buf), format, args);
358         va_end(args);
359
360         if (verbose) {
361                 if (!g_strncasecmp(buf, "AUTHINFO PASS", 13))
362                         log_print("NNTP> AUTHINFO PASS ********\n");
363                 else
364                         log_print("NNTP> %s\n", buf);
365         }
366
367         strcat(buf, "\r\n");
368         if (sock_write_all(sock, buf, strlen(buf)) < 0) {
369                 log_warning(_("Error occurred while sending command\n"));
370                 return NN_SOCKET;
371         }
372
373         return NN_SUCCESS;
374 }
375
376 static gint nntp_gen_recv(SockInfo *sock, gchar *buf, gint size)
377 {
378         if (sock_gets(sock, buf, size) == -1)
379                 return NN_SOCKET;
380
381         strretchomp(buf);
382
383         if (verbose)
384                 log_print("NNTP< %s\n", buf);
385
386         return NN_SUCCESS;
387 }
388
389 static gint nntp_gen_command(NNTPSession *session, gchar *argbuf,
390                              const gchar *format, ...)
391 {
392         gchar buf[NNTPBUFSIZE];
393         va_list args;
394         gint ok;
395         SockInfo *sock;
396
397         va_start(args, format);
398         g_vsnprintf(buf, sizeof(buf), format, args);
399         va_end(args);
400
401         sock = SESSION(session)->sock;
402         ok = nntp_gen_send(sock, "%s", buf);
403         if (ok != NN_SUCCESS)
404                 return ok;
405         ok = nntp_ok(sock, argbuf);
406         if (ok == NN_AUTHREQ) {
407                 if (!session->userid || !session->passwd) {
408                         session->auth_failed = TRUE;
409                         return ok;
410                 }
411
412                 ok = nntp_gen_send(sock, "AUTHINFO USER %s", session->userid);
413                 if (ok != NN_SUCCESS)
414                         return ok;
415                 ok = nntp_ok(sock, NULL);
416                 if (ok == NN_AUTHCONT) {
417                         ok = nntp_gen_send(sock, "AUTHINFO PASS %s",
418                                            session->passwd);
419                         if (ok != NN_SUCCESS)
420                                 return ok;
421                         ok = nntp_ok(sock, NULL);
422                 }
423                 if (ok != NN_SUCCESS) {
424                         session->auth_failed = TRUE;
425                         return ok;
426                 }
427
428                 ok = nntp_gen_send(sock, "%s", buf);
429                 if (ok != NN_SUCCESS)
430                         return ok;
431                 ok = nntp_ok(sock, argbuf);
432
433         } else if (ok == NN_AUTHCONT) {
434                 ok = nntp_gen_send(sock, "AUTHINFO PASS %s",
435                                    session->passwd);
436                 if (ok != NN_SUCCESS)  {
437                         session->auth_failed = TRUE;
438                         return ok;
439                 }
440                 ok = nntp_ok(sock, NULL);
441        }
442
443         return ok;
444 }