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