Added NNTP authentication support
[claws.git] / src / nntp.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999,2000 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
33 static gint verbose = 1;
34
35 static void nntp_gen_send(gint sock, const gchar *format, ...);
36 static gint nntp_gen_recv(gint sock, gchar *buf, gint size);
37
38 gint nntp_open(const gchar *server, gushort port, gchar *buf)
39 {
40         SockInfo *sockinfo;
41         gint sock;
42
43         if ((sockinfo = sock_connect(server, port)) == NULL) {
44                 log_warning(_("Can't connect to NNTP server: %s:%d\n"),
45                             server, port);
46                 return -1;
47         }
48         sock = sockinfo->sock;
49         sock_sockinfo_free(sockinfo);
50
51         if (nntp_ok(sock, buf) == NN_SUCCESS)
52                 return sock;
53         else {
54                 sock_close(sock);
55                 return -1;
56         }
57 }
58
59 gint nntp_group(gint sock, const gchar *group,
60                 gint *num, gint *first, gint *last)
61 {
62         gint ok;
63         gint resp;
64         gchar buf[NNTPBUFSIZE];
65
66         nntp_gen_send(sock, "GROUP %s", group);
67
68         if ((ok = nntp_ok(sock, buf)) != NN_SUCCESS)
69                 return ok;
70
71         if (sscanf(buf, "%d %d %d %d", &resp, num, first, last)
72             != 4) {
73                 log_warning(_("protocol error: %s\n"), buf);
74                 return NN_PROTOCOL;
75         }
76
77         return NN_SUCCESS;
78 }
79
80 gint nntp_get_article(gint sock, const gchar *cmd, gint num, gchar **msgid)
81 {
82         gint ok;
83         gchar buf[NNTPBUFSIZE];
84
85         if (num > 0)
86                 nntp_gen_send(sock, "%s %d", cmd, num);
87         else
88                 nntp_gen_send(sock, cmd);
89
90         if ((ok = nntp_ok(sock, buf)) != NN_SUCCESS)
91                 return ok;
92
93         extract_parenthesis(buf, '<', '>');
94         if (buf[0] == '\0') {
95                 log_warning(_("protocol error\n"));
96                 return NN_PROTOCOL;
97         }
98         *msgid = g_strdup(buf);
99
100         return NN_SUCCESS;
101 }
102
103 gint nntp_article(gint sock, gint num, gchar **msgid)
104 {
105         return nntp_get_article(sock, "ARTICLE", num, msgid);
106 }
107
108 gint nntp_body(gint sock, gint num, gchar **msgid)
109 {
110         return nntp_get_article(sock, "BODY", num, msgid);
111 }
112
113 gint nntp_head(gint sock, gint num, gchar **msgid)
114 {
115         return nntp_get_article(sock, "HEAD", num, msgid);
116 }
117
118 gint nntp_stat(gint sock, gint num, gchar **msgid)
119 {
120         return nntp_get_article(sock, "STAT", num, msgid);
121 }
122
123 gint nntp_next(gint sock, gint *num, gchar **msgid)
124 {
125         gint ok;
126         gint resp;
127         gchar buf[NNTPBUFSIZE];
128
129         nntp_gen_send(sock, "NEXT");
130
131         if ((ok = nntp_ok(sock, buf)) != NN_SUCCESS)
132                 return ok;
133
134         if (sscanf(buf, "%d %d", &resp, num) != 2) {
135                 log_warning(_("protocol error: %s\n"), buf);
136                 return NN_PROTOCOL;
137         }
138
139         extract_parenthesis(buf, '<', '>');
140         if (buf[0] == '\0') {
141                 log_warning(_("protocol error\n"));
142                 return NN_PROTOCOL;
143         }
144         *msgid = g_strdup(buf);
145
146         return NN_SUCCESS;
147 }
148
149 gint nntp_xover(gint sock, gint first, gint last)
150 {
151         gint ok;
152         gchar buf[NNTPBUFSIZE];
153
154         nntp_gen_send(sock, "XOVER %d-%d", first, last);
155         if ((ok = nntp_ok(sock, buf)) != NN_SUCCESS)
156                 return ok;
157
158         return NN_SUCCESS;
159 }
160
161 gint nntp_post(gint sock, FILE *fp)
162 {
163         gint ok;
164         gchar buf[NNTPBUFSIZE];
165
166         nntp_gen_send(sock, "POST");
167         if ((ok = nntp_ok(sock, buf)) != NN_SUCCESS)
168                 return ok;
169
170         while (fgets(buf, sizeof(buf), fp) != NULL) {
171                 strretchomp(buf);
172                 if (buf[0] == '.') {
173                         if (sock_write(sock, ".", 1) < 0) {
174                                 log_warning(_("Error occurred while posting\n"));
175                                 return NN_SOCKET;
176                         }
177                 }
178
179                 if (sock_puts(sock, buf) < 0) {
180                         log_warning(_("Error occurred while posting\n"));
181                         return NN_SOCKET;
182                 }
183         }
184
185         sock_write(sock, ".\r\n", 3);
186         if ((ok = nntp_ok(sock, buf)) != NN_SUCCESS)
187                 return ok;
188
189         return NN_SUCCESS;
190 }
191
192 gint nntp_newgroups(gint sock)
193 {
194         return NN_SUCCESS;
195 }
196
197 gint nntp_newnews(gint sock)
198 {
199         return NN_SUCCESS;
200 }
201
202 gint nntp_mode(gint sock, gboolean stream)
203 {
204         gint ok;
205
206         nntp_gen_send(sock, "MODE %s", stream ? "STREAM" : "READER");
207         ok = nntp_ok(sock, NULL);
208
209         return ok;
210 }
211
212 gint nntp_authinfo_user(gint sock, const gchar *user)
213 {
214         gint ok;
215         gchar buf[NNTPBUFSIZE];
216
217         nntp_gen_send(sock, "AUTHINFO USER %s", user);
218         ok = nntp_ok(sock, buf);
219
220         return ok;
221 }
222
223 gint nntp_authinfo_pass(gint sock, const gchar *pass)
224 {
225         gint ok;
226         gchar buf[NNTPBUFSIZE];
227
228         nntp_gen_send(sock, "AUTHINFO PASS %s", pass);
229         ok = nntp_ok(sock, buf);
230
231         return ok;
232 }
233
234 gint nntp_ok(gint sock, gchar *argbuf)
235 {
236         gint ok;
237         gchar buf[NNTPBUFSIZE];
238
239         if ((ok = nntp_gen_recv(sock, buf, sizeof(buf))) == NN_SUCCESS) {
240                 if (strlen(buf) < 4)
241                         return NN_ERROR;
242
243                 if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') &&
244                     buf[3] == ' ') {
245                         if (argbuf)
246                                 strcpy(argbuf, buf);
247
248                         if (!strncmp(buf, "381 ", 4))
249                                 return NN_AUTHCONT;
250                         return NN_SUCCESS;
251                 } else if (!strncmp(buf, "480 ", 4))
252                         return NN_AUTHREQ;
253                 else
254                         return NN_ERROR;
255         }
256
257         return ok;
258 }
259
260 static void nntp_gen_send(gint sock, const gchar *format, ...)
261 {
262         gchar buf[NNTPBUFSIZE];
263         va_list args;
264
265         va_start(args, format);
266         g_vsnprintf(buf, sizeof(buf), format, args);
267         va_end(args);
268
269         if (verbose) {
270                 if (!g_strncasecmp(buf, "AUTHINFO PASS", 13))
271                         log_print("NNTP> AUTHINFO PASS ***\n");
272                 else
273                         log_print("NNTP> %s\n", buf);
274         }
275
276         strcat(buf, "\r\n");
277         sock_write(sock, buf, strlen(buf));
278 }
279
280 static gint nntp_gen_recv(gint sock, gchar *buf, gint size)
281 {
282         if (sock_read(sock, buf, size) == -1)
283                 return NN_SOCKET;
284
285         strretchomp(buf);
286
287         if (verbose)
288                 log_print("NNTP< %s\n", buf);
289
290         return NN_SUCCESS;
291 }
292
293 /*
294   nntp_list sends the command "LIST" to the news server,
295   a function is needed to read the newsgroups list.
296  */
297
298 gint nntp_list(gint sock)
299 {
300         GList * result = NULL;
301
302         gint ok;
303         gint resp;
304         gchar buf[NNTPBUFSIZE];
305
306         nntp_gen_send(sock, "LIST");
307
308         if ((ok = nntp_ok(sock, buf)) != NN_SUCCESS)
309                 return NN_ERROR;
310
311         if (verbose)
312                 log_print("NNTP< %s\n", buf);
313
314         return NN_SUCCESS;
315 }