Line 0
Link Here
|
|
|
1 |
/* |
2 |
* Copyright (C) 1998 Brandon Long <blong@fiction.net> |
3 |
* Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net> |
4 |
* Copyright (C) 2000-2007 Vsevolod Volkov <vvv@mutt.org.ua> |
5 |
* |
6 |
* This program is free software; you can redistribute it and/or modify |
7 |
* it under the terms of the GNU General Public License as published by |
8 |
* the Free Software Foundation; either version 2 of the License, or |
9 |
* (at your option) any later version. |
10 |
* |
11 |
* This program is distributed in the hope that it will be useful, |
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU General Public License |
17 |
* along with this program; if not, write to the Free Software |
18 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 |
*/ |
20 |
|
21 |
#if HAVE_CONFIG_H |
22 |
# include "config.h" |
23 |
#endif |
24 |
|
25 |
#include "mutt.h" |
26 |
#include "mutt_curses.h" |
27 |
#include "sort.h" |
28 |
#include "mx.h" |
29 |
#include "mime.h" |
30 |
#include "rfc1524.h" |
31 |
#include "rfc2047.h" |
32 |
#include "mailbox.h" |
33 |
#include "nntp.h" |
34 |
|
35 |
#ifdef HAVE_PGP |
36 |
#include "pgp.h" |
37 |
#endif |
38 |
|
39 |
#ifdef HAVE_SMIME |
40 |
#include "smime.h" |
41 |
#endif |
42 |
|
43 |
#include <unistd.h> |
44 |
#include <string.h> |
45 |
#include <ctype.h> |
46 |
#include <stdlib.h> |
47 |
|
48 |
static unsigned int _checked = 0; |
49 |
|
50 |
#ifdef DEBUG |
51 |
static void nntp_error (const char *where, const char *msg) |
52 |
{ |
53 |
dprint (1, (debugfile, "nntp_error(): unexpected response in %s: %s\n", where, msg)); |
54 |
} |
55 |
#endif /* DEBUG */ |
56 |
|
57 |
static int nntp_auth (NNTP_SERVER *serv) |
58 |
{ |
59 |
CONNECTION *conn = serv->conn; |
60 |
char buf[STRING]; |
61 |
unsigned char flags = conn->account.flags; |
62 |
|
63 |
if (mutt_account_getuser (&conn->account) || !conn->account.user[0] || |
64 |
mutt_account_getpass (&conn->account) || !conn->account.pass[0]) |
65 |
{ |
66 |
conn->account.flags = flags; |
67 |
return -2; |
68 |
} |
69 |
|
70 |
mutt_message _("Logging in..."); |
71 |
|
72 |
snprintf (buf, sizeof (buf), "AUTHINFO USER %s\r\n", conn->account.user); |
73 |
mutt_socket_write (conn, buf); |
74 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
75 |
{ |
76 |
conn->account.flags = flags; |
77 |
return -1; |
78 |
} |
79 |
|
80 |
#ifdef DEBUG |
81 |
/* don't print the password unless we're at the ungodly debugging level */ |
82 |
if (debuglevel < M_SOCK_LOG_FULL) |
83 |
dprint (M_SOCK_LOG_CMD, (debugfile, "> AUTHINFO PASS *\n")); |
84 |
#endif |
85 |
snprintf (buf, sizeof (buf), "AUTHINFO PASS %s\r\n", conn->account.pass); |
86 |
mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL); |
87 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
88 |
{ |
89 |
conn->account.flags = flags; |
90 |
return -1; |
91 |
} |
92 |
|
93 |
if (mutt_strncmp ("281", buf, 3)) |
94 |
{ |
95 |
conn->account.flags = flags; |
96 |
mutt_error _("Login failed."); |
97 |
sleep (2); |
98 |
return -3; |
99 |
} |
100 |
|
101 |
return 0; |
102 |
} |
103 |
|
104 |
static int nntp_connect_error (NNTP_SERVER *serv) |
105 |
{ |
106 |
serv->status = NNTP_NONE; |
107 |
mutt_socket_close (serv->conn); |
108 |
mutt_error _("Server closed connection!"); |
109 |
sleep (2); |
110 |
return -1; |
111 |
} |
112 |
|
113 |
static int nntp_connect_and_auth (NNTP_SERVER *serv) |
114 |
{ |
115 |
CONNECTION *conn = serv->conn; |
116 |
char buf[STRING]; |
117 |
int rc; |
118 |
|
119 |
serv->status = NNTP_NONE; |
120 |
|
121 |
if (mutt_socket_open (conn) < 0) |
122 |
return -1; |
123 |
|
124 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
125 |
return nntp_connect_error (serv); |
126 |
|
127 |
if (!mutt_strncmp ("200", buf, 3)) |
128 |
mutt_message (_("Connected to %s. Posting ok."), conn->account.host); |
129 |
else if (!mutt_strncmp ("201", buf, 3)) |
130 |
mutt_message (_("Connected to %s. Posting NOT ok."), conn->account.host); |
131 |
else |
132 |
{ |
133 |
mutt_socket_close (conn); |
134 |
mutt_remove_trailing_ws (buf); |
135 |
mutt_error ("%s", buf); |
136 |
sleep (2); |
137 |
return -1; |
138 |
} |
139 |
|
140 |
sleep (1); |
141 |
|
142 |
/* Tell INN to switch to mode reader if it isn't so. Ignore all |
143 |
returned codes and messages. */ |
144 |
mutt_socket_write (conn, "MODE READER\r\n"); |
145 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
146 |
return nntp_connect_error (serv); |
147 |
|
148 |
mutt_socket_write (conn, "STAT\r\n"); |
149 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
150 |
return nntp_connect_error (serv); |
151 |
|
152 |
if (!(conn->account.flags & M_ACCT_USER) && mutt_strncmp ("480", buf, 3)) |
153 |
{ |
154 |
serv->status = NNTP_OK; |
155 |
return 0; |
156 |
} |
157 |
|
158 |
rc = nntp_auth (serv); |
159 |
if (rc == -1) |
160 |
return nntp_connect_error (serv); |
161 |
if (rc == -2) |
162 |
{ |
163 |
mutt_socket_close (conn); |
164 |
serv->status = NNTP_BYE; |
165 |
return -1; |
166 |
} |
167 |
if (rc < 0) |
168 |
{ |
169 |
mutt_socket_close (conn); |
170 |
mutt_error _("Login failed."); |
171 |
sleep (2); |
172 |
return -1; |
173 |
} |
174 |
serv->status = NNTP_OK; |
175 |
return 0; |
176 |
} |
177 |
|
178 |
static int nntp_attempt_features (NNTP_SERVER *serv) |
179 |
{ |
180 |
char buf[LONG_STRING]; |
181 |
CONNECTION *conn = serv->conn; |
182 |
|
183 |
mutt_socket_write (conn, "XOVER\r\n"); |
184 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
185 |
return nntp_connect_error (serv); |
186 |
if (mutt_strncmp ("500", buf, 3)) |
187 |
serv->hasXOVER = 1; |
188 |
|
189 |
mutt_socket_write (conn, "XPAT\r\n"); |
190 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
191 |
return nntp_connect_error (serv); |
192 |
if (mutt_strncmp ("500", buf, 3)) |
193 |
serv->hasXPAT = 1; |
194 |
|
195 |
mutt_socket_write (conn, "LISTGROUP\r\n"); |
196 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
197 |
return nntp_connect_error (serv); |
198 |
if (mutt_strncmp ("500", buf, 3)) |
199 |
serv->hasLISTGROUP = 1; |
200 |
|
201 |
mutt_socket_write (conn, "XGTITLE +\r\n"); |
202 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
203 |
return nntp_connect_error (serv); |
204 |
if (mutt_strncmp ("500", buf, 3)) |
205 |
serv->hasXGTITLE = 1; |
206 |
|
207 |
if (!mutt_strncmp ("282", buf, 3)) |
208 |
{ |
209 |
do |
210 |
{ |
211 |
if (mutt_socket_readln (buf, sizeof (buf), conn) < 0) |
212 |
return nntp_connect_error (serv); |
213 |
} while (!(buf[0] == '.' && buf[1] == '\0')); |
214 |
} |
215 |
|
216 |
return 0; |
217 |
} |
218 |
|
219 |
static int nntp_open_connection (NNTP_SERVER *serv) |
220 |
{ |
221 |
if (serv->status == NNTP_OK) |
222 |
return 0; |
223 |
if (serv->status == NNTP_BYE) |
224 |
return -1; |
225 |
if (nntp_connect_and_auth (serv) < 0) |
226 |
return -1; |
227 |
if (nntp_attempt_features (serv) < 0) |
228 |
return -1; |
229 |
return 0; |
230 |
} |
231 |
|
232 |
static int nntp_reconnect (NNTP_SERVER *serv) |
233 |
{ |
234 |
char buf[SHORT_STRING]; |
235 |
|
236 |
mutt_socket_close (serv->conn); |
237 |
|
238 |
FOREVER |
239 |
{ |
240 |
if (nntp_connect_and_auth (serv) == 0) |
241 |
return 0; |
242 |
|
243 |
snprintf (buf, sizeof (buf), _("Connection to %s lost. Reconnect?"), |
244 |
serv->conn->account.host); |
245 |
if (query_quadoption (OPT_NNTPRECONNECT, buf) != M_YES) |
246 |
{ |
247 |
serv->status = NNTP_BYE; |
248 |
return -1; |
249 |
} |
250 |
} |
251 |
} |
252 |
|
253 |
/* Send data from line[LONG_STRING] and receive answer to same line */ |
254 |
static int mutt_nntp_query (NNTP_DATA *data, char *line, size_t linelen) |
255 |
{ |
256 |
char buf[LONG_STRING]; |
257 |
int done = TRUE; |
258 |
|
259 |
if (data->nserv->status == NNTP_BYE) |
260 |
return -1; |
261 |
|
262 |
do |
263 |
{ |
264 |
if (*line) |
265 |
{ |
266 |
mutt_socket_write (data->nserv->conn, line); |
267 |
} |
268 |
else if (data->group) |
269 |
{ |
270 |
snprintf (buf, sizeof (buf), "GROUP %s\r\n", data->group); |
271 |
mutt_socket_write (data->nserv->conn, buf); |
272 |
} |
273 |
|
274 |
done = TRUE; |
275 |
if (mutt_socket_readln (buf, sizeof (buf), data->nserv->conn) < 0) |
276 |
{ |
277 |
if (nntp_reconnect (data->nserv) < 0) |
278 |
return -1; |
279 |
|
280 |
if (data->group) |
281 |
{ |
282 |
snprintf (buf, sizeof (buf), "GROUP %s\r\n", data->group); |
283 |
mutt_socket_write (data->nserv->conn, buf); |
284 |
if (mutt_socket_readln (buf, sizeof (buf), data->nserv->conn) < 0) |
285 |
return -1; |
286 |
} |
287 |
if (*line) |
288 |
done = FALSE; |
289 |
} |
290 |
else if ((!mutt_strncmp ("480", buf, 3)) && nntp_auth (data->nserv) < 0) |
291 |
return -1; |
292 |
} while (!done); |
293 |
|
294 |
strfcpy (line, buf, linelen); |
295 |
return 0; |
296 |
} |
297 |
|
298 |
/* |
299 |
* This function calls funct(*line, *data) for each received line, |
300 |
* funct(NULL, *data) if rewind(*data) needs, exits when fail or done. |
301 |
* Returned codes: |
302 |
* 0 - successful, |
303 |
* 1 - correct but not performed (may be, have to be continued), |
304 |
* -1 - conection lost, |
305 |
* -2 - invalid command or execution error, |
306 |
* -3 - error in funct(*line, *data). |
307 |
*/ |
308 |
static int mutt_nntp_fetch (NNTP_DATA *nntp_data, char *query, char *msg, |
309 |
int (*funct) (char *, void *), void *data, int tagged) |
310 |
{ |
311 |
char buf[LONG_STRING]; |
312 |
char *inbuf, *p; |
313 |
int done = FALSE; |
314 |
int chunk, line; |
315 |
size_t lenbuf = 0; |
316 |
int ret; |
317 |
|
318 |
do |
319 |
{ |
320 |
strfcpy (buf, query, sizeof (buf)); |
321 |
if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) |
322 |
return -1; |
323 |
if (buf[0] == '5') |
324 |
return -2; |
325 |
if (buf[0] != '2') |
326 |
return 1; |
327 |
|
328 |
ret = 0; |
329 |
line = 0; |
330 |
inbuf = safe_malloc (sizeof (buf)); |
331 |
|
332 |
FOREVER |
333 |
{ |
334 |
chunk = mutt_socket_readln_d (buf, sizeof (buf), nntp_data->nserv->conn, |
335 |
M_SOCK_LOG_HDR); |
336 |
if (chunk < 0) |
337 |
break; |
338 |
|
339 |
p = buf; |
340 |
if (!lenbuf && buf[0] == '.') |
341 |
{ |
342 |
if (buf[1] == '\0') |
343 |
{ |
344 |
done = TRUE; |
345 |
break; |
346 |
} |
347 |
if (buf[1] == '.') |
348 |
p++; |
349 |
} |
350 |
|
351 |
strfcpy (inbuf + lenbuf, p, sizeof (buf)); |
352 |
|
353 |
if (chunk >= sizeof (buf)) |
354 |
{ |
355 |
lenbuf += strlen (p); |
356 |
} |
357 |
else |
358 |
{ |
359 |
line++; |
360 |
if (msg && ReadInc && (line % ReadInc == 0)) { |
361 |
if (tagged) |
362 |
mutt_message (_("%s (tagged: %d) %d"), msg, tagged, line); |
363 |
else |
364 |
mutt_message ("%s %d", msg, line); |
365 |
} |
366 |
|
367 |
if (ret == 0 && funct (inbuf, data) < 0) |
368 |
ret = -3; |
369 |
lenbuf = 0; |
370 |
} |
371 |
|
372 |
safe_realloc (&inbuf, lenbuf + sizeof (buf)); |
373 |
} |
374 |
FREE (&inbuf); |
375 |
funct (NULL, data); |
376 |
} |
377 |
while (!done); |
378 |
return ret; |
379 |
} |
380 |
|
381 |
static int nntp_read_tempfile (char *line, void *file) |
382 |
{ |
383 |
FILE *f = (FILE *)file; |
384 |
|
385 |
if (!line) |
386 |
rewind (f); |
387 |
else |
388 |
{ |
389 |
fputs (line, f); |
390 |
if (fputc ('\n', f) == EOF) |
391 |
return -1; |
392 |
} |
393 |
return 0; |
394 |
} |
395 |
|
396 |
static void nntp_parse_xref (CONTEXT *ctx, char *group, char *xref, HEADER *h) |
397 |
{ |
398 |
register char *p, *b; |
399 |
register char *colon = NULL; |
400 |
|
401 |
b = p = xref; |
402 |
while (*p) |
403 |
{ |
404 |
/* skip to next word */ |
405 |
b = p; |
406 |
while (*b && ((*b == ' ') || (*b == '\t'))) b++; |
407 |
p = b; |
408 |
colon = NULL; |
409 |
/* skip to end of word */ |
410 |
while (*p && (*p != ' ') && (*p != '\t')) |
411 |
{ |
412 |
if (*p == ':') |
413 |
colon = p; |
414 |
p++; |
415 |
} |
416 |
if (*p) |
417 |
{ |
418 |
*p = '\0'; |
419 |
p++; |
420 |
} |
421 |
if (colon) |
422 |
{ |
423 |
*colon = '\0'; |
424 |
colon++; |
425 |
nntp_get_status (ctx, h, b, atoi(colon)); |
426 |
if (h && h->article_num == 0 && mutt_strcmp (group, b) == 0) |
427 |
h->article_num = atoi(colon); |
428 |
} |
429 |
} |
430 |
} |
431 |
|
432 |
/* |
433 |
* returns: |
434 |
* 0 on success |
435 |
* 1 if article not found |
436 |
* -1 if read or write error on tempfile or socket |
437 |
*/ |
438 |
static int nntp_read_header (CONTEXT *ctx, const char *msgid, int article_num) |
439 |
{ |
440 |
NNTP_DATA *nntp_data = ((NNTP_DATA *)ctx->data); |
441 |
FILE *f; |
442 |
char buf[LONG_STRING]; |
443 |
char tempfile[_POSIX_PATH_MAX]; |
444 |
int ret; |
445 |
HEADER *h = ctx->hdrs[ctx->msgcount]; |
446 |
|
447 |
mutt_mktemp (tempfile); |
448 |
if (!(f = safe_fopen (tempfile, "w+"))) |
449 |
return -1; |
450 |
|
451 |
if (!msgid) |
452 |
snprintf (buf, sizeof (buf), "HEAD %d\r\n", article_num); |
453 |
else |
454 |
snprintf (buf, sizeof (buf), "HEAD %s\r\n", msgid); |
455 |
|
456 |
ret = mutt_nntp_fetch (nntp_data, buf, NULL, nntp_read_tempfile, f, 0); |
457 |
if (ret) |
458 |
{ |
459 |
#ifdef DEBUG |
460 |
if (ret != -1) |
461 |
dprint(1, (debugfile, "nntp_read_header: %s\n", buf)); |
462 |
#endif |
463 |
fclose (f); |
464 |
unlink (tempfile); |
465 |
return (ret == -1 ? -1 : 1); |
466 |
} |
467 |
|
468 |
h->article_num = article_num; |
469 |
h->env = mutt_read_rfc822_header (f, h, 0, 0); |
470 |
fclose (f); |
471 |
unlink (tempfile); |
472 |
|
473 |
if (h->env->xref != NULL) |
474 |
nntp_parse_xref (ctx, nntp_data->group, h->env->xref, h); |
475 |
else if (h->article_num == 0 && msgid) |
476 |
{ |
477 |
snprintf (buf, sizeof (buf), "STAT %s\r\n", msgid); |
478 |
if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) == 0) |
479 |
h->article_num = atoi (buf + 4); |
480 |
} |
481 |
|
482 |
return 0; |
483 |
} |
484 |
|
485 |
static int parse_description (char *line, void *n) |
486 |
{ |
487 |
#define news ((NNTP_SERVER *) n) |
488 |
register char *d = line; |
489 |
NNTP_DATA *data; |
490 |
|
491 |
if (!line) |
492 |
return 0; |
493 |
while (*d && *d != '\t' && *d != ' ') d++; |
494 |
*d = 0; |
495 |
d++; |
496 |
while (*d && (*d == '\t' || *d == ' ')) d++; |
497 |
dprint (2, (debugfile, "group: %s, desc: %s\n", line, d)); |
498 |
if ((data = (NNTP_DATA *) hash_find (news->newsgroups, line)) != NULL && |
499 |
mutt_strcmp (d, data->desc)) |
500 |
{ |
501 |
FREE (&data->desc); |
502 |
data->desc = safe_strdup (d); |
503 |
} |
504 |
return 0; |
505 |
#undef news |
506 |
} |
507 |
|
508 |
static void nntp_get_desc (NNTP_DATA *data, char *mask, char *msg) |
509 |
{ |
510 |
char buf[STRING]; |
511 |
|
512 |
if (!option (OPTLOADDESC) || !data || !data->nserv) |
513 |
return; |
514 |
|
515 |
/* Get newsgroup description, if we can */ |
516 |
if (data->nserv->hasXGTITLE) |
517 |
snprintf (buf, sizeof (buf), "XGTITLE %s\r\n", mask); |
518 |
else |
519 |
snprintf (buf, sizeof (buf), "LIST NEWSGROUPS %s\r\n", mask); |
520 |
if (mutt_nntp_fetch (data, buf, msg, parse_description, data->nserv, 0) != 0) |
521 |
{ |
522 |
#ifdef DEBUG |
523 |
nntp_error ("nntp_get_desc()", buf); |
524 |
#endif |
525 |
} |
526 |
} |
527 |
|
528 |
/* |
529 |
* XOVER returns a tab separated list of: |
530 |
* id|subject|from|date|Msgid|references|bytes|lines|xref |
531 |
* |
532 |
* This has to duplicate some of the functionality of |
533 |
* mutt_read_rfc822_header(), since it replaces the call to that (albeit with |
534 |
* a limited number of headers which are "parsed" by placement in the list) |
535 |
*/ |
536 |
static int nntp_parse_xover (CONTEXT *ctx, char *buf, HEADER *hdr) |
537 |
{ |
538 |
NNTP_DATA *nntp_data = (NNTP_DATA *) ctx->data; |
539 |
char *p, *b; |
540 |
int x, done = 0; |
541 |
|
542 |
hdr->env = mutt_new_envelope(); |
543 |
hdr->env->newsgroups = safe_strdup (nntp_data->group); |
544 |
hdr->content = mutt_new_body(); |
545 |
hdr->content->type = TYPETEXT; |
546 |
hdr->content->subtype = safe_strdup ("plain"); |
547 |
hdr->content->encoding = ENC7BIT; |
548 |
hdr->content->disposition = DISPINLINE; |
549 |
hdr->content->length = -1; |
550 |
b = p = buf; |
551 |
|
552 |
for (x = 0; !done && x < 9; x++) |
553 |
{ |
554 |
/* if from file, need to skip newline character */ |
555 |
while (*p && *p != '\n' && *p != '\t') p++; |
556 |
if (!*p) done++; |
557 |
*p = '\0'; |
558 |
p++; |
559 |
switch (x) |
560 |
{ |
561 |
case 0: |
562 |
|
563 |
hdr->article_num = atoi (b); |
564 |
nntp_get_status (ctx, hdr, NULL, hdr->article_num); |
565 |
break; |
566 |
case 1: |
567 |
hdr->env->subject = safe_strdup (b); |
568 |
/* Now we need to do the things which would normally be done in |
569 |
* mutt_read_rfc822_header() */ |
570 |
if (hdr->env->subject) |
571 |
{ |
572 |
regmatch_t pmatch[1]; |
573 |
|
574 |
rfc2047_decode (&hdr->env->subject); |
575 |
|
576 |
if (regexec (ReplyRegexp.rx, hdr->env->subject, 1, pmatch, 0) == 0) |
577 |
hdr->env->real_subj = hdr->env->subject + pmatch[0].rm_eo; |
578 |
else |
579 |
hdr->env->real_subj = hdr->env->subject; |
580 |
} |
581 |
break; |
582 |
case 2: |
583 |
rfc822_free_address (&hdr->env->from); |
584 |
hdr->env->from = rfc822_parse_adrlist (hdr->env->from, b); |
585 |
rfc2047_decode_adrlist (hdr->env->from); |
586 |
break; |
587 |
case 3: |
588 |
hdr->date_sent = mutt_parse_date (b, hdr); |
589 |
hdr->received = hdr->date_sent; |
590 |
break; |
591 |
case 4: |
592 |
FREE (&hdr->env->message_id); |
593 |
hdr->env->message_id = safe_strdup (b); |
594 |
break; |
595 |
case 5: |
596 |
mutt_free_list (&hdr->env->references); |
597 |
hdr->env->references = mutt_parse_references (b, 0); |
598 |
break; |
599 |
case 6: |
600 |
hdr->content->length = atoi (b); |
601 |
break; |
602 |
case 7: |
603 |
hdr->lines = atoi (b); |
604 |
break; |
605 |
case 8: |
606 |
if (!hdr->read) |
607 |
FREE (&hdr->env->xref); |
608 |
b = b + 6; /* skips the "Xref: " */ |
609 |
hdr->env->xref = safe_strdup (b); |
610 |
nntp_parse_xref (ctx, nntp_data->group, b, hdr); |
611 |
} |
612 |
if (!*p) |
613 |
return -1; |
614 |
b = p; |
615 |
} |
616 |
return 0; |
617 |
} |
618 |
|
619 |
typedef struct |
620 |
{ |
621 |
CONTEXT *ctx; |
622 |
unsigned int base; |
623 |
unsigned int first; |
624 |
unsigned int last; |
625 |
unsigned short *messages; |
626 |
char* msg; |
627 |
} FETCH_CONTEXT; |
628 |
|
629 |
#define fc ((FETCH_CONTEXT *) c) |
630 |
static int nntp_fetch_numbers (char *line, void *c) |
631 |
{ |
632 |
unsigned int num; |
633 |
|
634 |
if (!line) |
635 |
return 0; |
636 |
num = atoi (line); |
637 |
if (num < fc->base || num > fc->last) |
638 |
return 0; |
639 |
fc->messages[num - fc->base] = 1; |
640 |
return 0; |
641 |
} |
642 |
|
643 |
static int add_xover_line (char *line, void *c) |
644 |
{ |
645 |
unsigned int num, total; |
646 |
CONTEXT *ctx = fc->ctx; |
647 |
NNTP_DATA *data = (NNTP_DATA *)ctx->data; |
648 |
|
649 |
if (!line) |
650 |
return 0; |
651 |
|
652 |
if (ctx->msgcount >= ctx->hdrmax) |
653 |
mx_alloc_memory (ctx); |
654 |
ctx->hdrs[ctx->msgcount] = mutt_new_header (); |
655 |
ctx->hdrs[ctx->msgcount]->index = ctx->msgcount; |
656 |
|
657 |
nntp_parse_xover (ctx, line, ctx->hdrs[ctx->msgcount]); |
658 |
num = ctx->hdrs[ctx->msgcount]->article_num; |
659 |
|
660 |
if (num >= fc->first && num <= fc->last && fc->messages[num - fc->base]) |
661 |
{ |
662 |
ctx->msgcount++; |
663 |
if (num > data->lastLoaded) |
664 |
data->lastLoaded = num; |
665 |
num = num - fc->first + 1; |
666 |
total = fc->last - fc->first + 1; |
667 |
if (!ctx->quiet && fc->msg && ReadInc && (num % ReadInc == 0)) |
668 |
mutt_message ("%s %d/%d", fc->msg, num, total); |
669 |
} |
670 |
else |
671 |
mutt_free_header (&ctx->hdrs[ctx->msgcount]); /* skip it */ |
672 |
|
673 |
return 0; |
674 |
} |
675 |
#undef fc |
676 |
|
677 |
static int nntp_fetch_headers (CONTEXT *ctx, unsigned int first, |
678 |
unsigned int last) |
679 |
{ |
680 |
char buf[HUGE_STRING]; |
681 |
char *msg = _("Fetching message headers..."); |
682 |
NNTP_DATA *nntp_data = ((NNTP_DATA *)ctx->data); |
683 |
int ret; |
684 |
int num; |
685 |
int oldmsgcount; |
686 |
unsigned int current; |
687 |
FILE *f; |
688 |
FETCH_CONTEXT fc; |
689 |
|
690 |
/* if empty group or nothing to do */ |
691 |
if (!last || first > last) |
692 |
return 0; |
693 |
|
694 |
/* fetch list of articles */ |
695 |
fc.ctx = ctx; |
696 |
fc.base = first; |
697 |
fc.last = last; |
698 |
fc.messages = safe_calloc (last - first + 1, sizeof (unsigned short)); |
699 |
if (nntp_data->nserv->hasLISTGROUP) |
700 |
{ |
701 |
mutt_message _("Fetching list of articles..."); |
702 |
snprintf (buf, sizeof (buf), "LISTGROUP %s\r\n", nntp_data->group); |
703 |
if (mutt_nntp_fetch (nntp_data, buf, NULL, nntp_fetch_numbers, &fc, 0) != 0) |
704 |
{ |
705 |
mutt_error (_("LISTGROUP command failed: %s"), buf); |
706 |
#ifdef DEBUG |
707 |
nntp_error ("nntp_fetch_headers()", buf); |
708 |
#endif |
709 |
FREE (&fc.messages); |
710 |
return -1; |
711 |
} |
712 |
} |
713 |
else |
714 |
{ |
715 |
for (num = 0; num < last - first + 1; num++) |
716 |
fc.messages[num] = 1; |
717 |
} |
718 |
|
719 |
/* CACHE: must be loaded xover cache here */ |
720 |
num = nntp_data->lastCached - first + 1; |
721 |
if (option (OPTNEWSCACHE) && nntp_data->cache && num > 0) |
722 |
{ |
723 |
nntp_cache_expand (buf, nntp_data->cache); |
724 |
mutt_message _("Fetching headers from cache..."); |
725 |
if ((f = safe_fopen (buf, "r"))) |
726 |
{ |
727 |
int r = 0; |
728 |
|
729 |
/* counting number of lines */ |
730 |
while (fgets (buf, sizeof (buf), f) != NULL) |
731 |
r++; |
732 |
rewind (f); |
733 |
while (r > num && fgets (buf, sizeof (buf), f) != NULL) |
734 |
r--; |
735 |
oldmsgcount = ctx->msgcount; |
736 |
fc.first = first; |
737 |
fc.last = first + num - 1; |
738 |
fc.msg = NULL; |
739 |
while (fgets (buf, sizeof (buf), f) != NULL) |
740 |
add_xover_line (buf, &fc); |
741 |
fclose (f); |
742 |
nntp_data->lastLoaded = fc.last; |
743 |
first = fc.last + 1; |
744 |
if (ctx->msgcount > oldmsgcount) |
745 |
mx_update_context (ctx, ctx->msgcount - oldmsgcount); |
746 |
} |
747 |
else |
748 |
nntp_delete_cache (nntp_data); |
749 |
} |
750 |
num = last - first + 1; |
751 |
if (num <= 0) |
752 |
{ |
753 |
FREE (&fc.messages); |
754 |
return 0; |
755 |
} |
756 |
|
757 |
/* |
758 |
* Without XOVER, we have to fetch each article header and parse |
759 |
* it. With XOVER, we ask for all of them |
760 |
*/ |
761 |
mutt_message (msg); |
762 |
if (nntp_data->nserv->hasXOVER) |
763 |
{ |
764 |
oldmsgcount = ctx->msgcount; |
765 |
fc.first = first; |
766 |
fc.last = last; |
767 |
fc.msg = msg; |
768 |
snprintf (buf, sizeof (buf), "XOVER %d-%d\r\n", first, last); |
769 |
ret = mutt_nntp_fetch (nntp_data, buf, NULL, add_xover_line, &fc, 0); |
770 |
if (ctx->msgcount > oldmsgcount) |
771 |
mx_update_context (ctx, ctx->msgcount - oldmsgcount); |
772 |
if (ret != 0) |
773 |
{ |
774 |
mutt_error (_("XOVER command failed: %s"), buf); |
775 |
#ifdef DEBUG |
776 |
nntp_error ("nntp_fetch_headers()", buf); |
777 |
#endif |
778 |
FREE (&fc.messages); |
779 |
return -1; |
780 |
} |
781 |
/* fetched OK */ |
782 |
} |
783 |
else |
784 |
for (current = first; current <= last; current++) |
785 |
{ |
786 |
HEADER *h; |
787 |
|
788 |
ret = current - first + 1; |
789 |
mutt_message ("%s %d/%d", msg, ret, num); |
790 |
|
791 |
if (!fc.messages[current - fc.base]) |
792 |
continue; |
793 |
|
794 |
if (ctx->msgcount >= ctx->hdrmax) |
795 |
mx_alloc_memory (ctx); |
796 |
h = ctx->hdrs[ctx->msgcount] = mutt_new_header (); |
797 |
h->index = ctx->msgcount; |
798 |
|
799 |
ret = nntp_read_header (ctx, NULL, current); |
800 |
if (ret == 0) /* Got article. Fetch next header */ |
801 |
{ |
802 |
nntp_get_status (ctx, h, NULL, h->article_num); |
803 |
ctx->msgcount++; |
804 |
mx_update_context (ctx, 1); |
805 |
} |
806 |
else |
807 |
mutt_free_header (&h); /* skip it */ |
808 |
if (ret == -1) |
809 |
{ |
810 |
FREE (&fc.messages); |
811 |
return -1; |
812 |
} |
813 |
|
814 |
if (current > nntp_data->lastLoaded) |
815 |
nntp_data->lastLoaded = current; |
816 |
} |
817 |
FREE (&fc.messages); |
818 |
nntp_data->lastLoaded = last; |
819 |
mutt_clear_error (); |
820 |
return 0; |
821 |
} |
822 |
|
823 |
/* |
824 |
* currently, nntp "mailbox" is "newsgroup" |
825 |
*/ |
826 |
int nntp_open_mailbox (CONTEXT *ctx) |
827 |
{ |
828 |
NNTP_DATA *nntp_data; |
829 |
NNTP_SERVER *serv; |
830 |
char buf[HUGE_STRING]; |
831 |
char server[LONG_STRING]; |
832 |
int count = 0; |
833 |
unsigned int first; |
834 |
ACCOUNT acct; |
835 |
|
836 |
if (nntp_parse_url (ctx->path, &acct, buf, sizeof (buf)) < 0 || !*buf) |
837 |
{ |
838 |
mutt_error (_("%s is an invalid newsgroup specification!"), ctx->path); |
839 |
mutt_sleep (2); |
840 |
return -1; |
841 |
} |
842 |
|
843 |
server[0] = '\0'; |
844 |
nntp_expand_path (server, sizeof (server), &acct); |
845 |
if (!(serv = mutt_select_newsserver (server)) || serv->status != NNTP_OK) |
846 |
return -1; |
847 |
|
848 |
CurrentNewsSrv = serv; |
849 |
|
850 |
/* create NNTP-specific state struct if nof found in list */ |
851 |
if ((nntp_data = (NNTP_DATA *) hash_find (serv->newsgroups, buf)) == NULL) |
852 |
{ |
853 |
nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (buf) + 1); |
854 |
nntp_data->group = (char *) nntp_data + sizeof (NNTP_DATA); |
855 |
strcpy (nntp_data->group, buf); |
856 |
hash_insert (serv->newsgroups, nntp_data->group, nntp_data, 0); |
857 |
nntp_add_to_list (serv, nntp_data); |
858 |
} |
859 |
ctx->data = nntp_data; |
860 |
ctx->mx_close = nntp_fastclose_mailbox; |
861 |
nntp_data->nserv = serv; |
862 |
|
863 |
mutt_message (_("Selecting %s..."), nntp_data->group); |
864 |
|
865 |
if (!nntp_data->desc) |
866 |
{ |
867 |
nntp_get_desc (nntp_data, nntp_data->group, NULL); |
868 |
if (nntp_data->desc) |
869 |
nntp_save_cache_index (serv); |
870 |
} |
871 |
|
872 |
buf[0] = 0; |
873 |
if (mutt_nntp_query (nntp_data, buf, sizeof(buf)) < 0) |
874 |
{ |
875 |
#ifdef DEBUG |
876 |
nntp_error ("nntp_open_mailbox()", buf); |
877 |
#endif |
878 |
return -1; |
879 |
} |
880 |
|
881 |
if (mutt_strncmp ("211", buf, 3)) |
882 |
{ |
883 |
LIST *l = serv->list; |
884 |
|
885 |
/* GROUP command failed */ |
886 |
if (!mutt_strncmp ("411", buf, 3)) |
887 |
{ |
888 |
mutt_error (_("Newsgroup %s not found on server %s"), |
889 |
nntp_data->group, serv->conn->account.host); |
890 |
|
891 |
/* CACHE: delete cache and line from .index */ |
892 |
nntp_delete_cache (nntp_data); |
893 |
hash_delete (serv->newsgroups, nntp_data->group, NULL, nntp_delete_data); |
894 |
while (l && l->data != (void *) nntp_data) l = l->next; |
895 |
if (l) |
896 |
l->data = NULL; |
897 |
|
898 |
sleep (2); |
899 |
} |
900 |
|
901 |
return -1; |
902 |
} |
903 |
|
904 |
sscanf (buf + 4, "%d %u %u %s", &count, &nntp_data->firstMessage, |
905 |
&nntp_data->lastMessage, buf); |
906 |
|
907 |
nntp_data->deleted = 0; |
908 |
|
909 |
time (&serv->check_time); |
910 |
|
911 |
/* |
912 |
* Check for max adding context. If it is greater than $nntp_context, |
913 |
* strip off extra articles |
914 |
*/ |
915 |
first = nntp_data->firstMessage; |
916 |
if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext) |
917 |
first = nntp_data->lastMessage - NntpContext + 1; |
918 |
if (first) |
919 |
nntp_data->lastLoaded = first - 1; |
920 |
return nntp_fetch_headers (ctx, first, nntp_data->lastMessage); |
921 |
} |
922 |
|
923 |
int nntp_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno) |
924 |
{ |
925 |
char buf[LONG_STRING]; |
926 |
char path[_POSIX_PATH_MAX]; |
927 |
NNTP_CACHE *cache; |
928 |
char *m = _("Fetching message..."); |
929 |
int ret; |
930 |
|
931 |
/* see if we already have the message in our cache */ |
932 |
cache = &((NNTP_DATA *) ctx->data)->acache[ctx->hdrs[msgno]->index % NNTP_CACHE_LEN]; |
933 |
|
934 |
/* if everything is fine, assign msg->fp and return */ |
935 |
if (cache->path && cache->index == ctx->hdrs[msgno]->index && |
936 |
(msg->fp = fopen (cache->path, "r"))) |
937 |
return 0; |
938 |
|
939 |
/* clear the previous entry */ |
940 |
unlink (cache->path); |
941 |
free (cache->path); |
942 |
|
943 |
mutt_message (m); |
944 |
|
945 |
cache->index = ctx->hdrs[msgno]->index; |
946 |
mutt_mktemp (path); |
947 |
cache->path = safe_strdup (path); |
948 |
if (!(msg->fp = safe_fopen (path, "w+"))) |
949 |
{ |
950 |
FREE (&cache->path); |
951 |
return -1; |
952 |
} |
953 |
|
954 |
if (ctx->hdrs[msgno]->article_num == 0) |
955 |
snprintf (buf, sizeof (buf), "ARTICLE %s\r\n", |
956 |
ctx->hdrs[msgno]->env->message_id); |
957 |
else |
958 |
snprintf (buf, sizeof (buf), "ARTICLE %d\r\n", |
959 |
ctx->hdrs[msgno]->article_num); |
960 |
|
961 |
ret = mutt_nntp_fetch ((NNTP_DATA *)ctx->data, buf, m, nntp_read_tempfile, |
962 |
msg->fp, ctx->tagged); |
963 |
if (ret == 1) |
964 |
{ |
965 |
mutt_error (_("Article %d not found on server"), |
966 |
ctx->hdrs[msgno]->article_num); |
967 |
dprint (1, (debugfile, "nntp_fetch_message: %s\n", buf)); |
968 |
} |
969 |
|
970 |
if (ret) |
971 |
{ |
972 |
fclose (msg->fp); |
973 |
unlink (path); |
974 |
FREE (&cache->path); |
975 |
return -1; |
976 |
} |
977 |
|
978 |
mutt_free_envelope (&ctx->hdrs[msgno]->env); |
979 |
ctx->hdrs[msgno]->env = mutt_read_rfc822_header (msg->fp, ctx->hdrs[msgno], 0, 0); |
980 |
/* fix content length */ |
981 |
fseek(msg->fp, 0, SEEK_END); |
982 |
ctx->hdrs[msgno]->content->length = ftell (msg->fp) - |
983 |
ctx->hdrs[msgno]->content->offset; |
984 |
|
985 |
/* this is called in mutt before the open which fetches the message, |
986 |
* which is probably wrong, but we just call it again here to handle |
987 |
* the problem instead of fixing it. |
988 |
*/ |
989 |
mutt_parse_mime_message (ctx, ctx->hdrs[msgno]); |
990 |
|
991 |
/* These would normally be updated in mx_update_context(), but the |
992 |
* full headers aren't parsed with XOVER, so the information wasn't |
993 |
* available then. |
994 |
*/ |
995 |
#if defined(HAVE_PGP) || defined(HAVE_SMIME) |
996 |
ctx->hdrs[msgno]->security = crypt_query (ctx->hdrs[msgno]->content); |
997 |
#endif /* HAVE_PGP || HAVE_SMIME */ |
998 |
|
999 |
mutt_clear_error(); |
1000 |
rewind (msg->fp); |
1001 |
|
1002 |
return 0; |
1003 |
} |
1004 |
|
1005 |
/* Post article */ |
1006 |
int nntp_post (const char *msg) { |
1007 |
char buf[LONG_STRING]; |
1008 |
size_t len; |
1009 |
FILE *f; |
1010 |
NNTP_DATA *nntp_data; |
1011 |
|
1012 |
if (Context && Context->magic == M_NNTP) |
1013 |
nntp_data = (NNTP_DATA *)Context->data; |
1014 |
else |
1015 |
{ |
1016 |
if (!(CurrentNewsSrv = mutt_select_newsserver (NewsServer)) || |
1017 |
!CurrentNewsSrv->list || !CurrentNewsSrv->list->data) |
1018 |
{ |
1019 |
mutt_error (_("Can't post article. No connection to news server.")); |
1020 |
return -1; |
1021 |
} |
1022 |
nntp_data = (NNTP_DATA *)CurrentNewsSrv->list->data; |
1023 |
} |
1024 |
|
1025 |
if (!(f = safe_fopen (msg, "r"))) |
1026 |
{ |
1027 |
mutt_error (_("Can't post article. Unable to open %s"), msg); |
1028 |
return -1; |
1029 |
} |
1030 |
|
1031 |
strfcpy (buf, "POST\r\n", sizeof (buf)); |
1032 |
if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) |
1033 |
{ |
1034 |
mutt_error (_("Can't post article. Connection to %s lost."), |
1035 |
nntp_data->nserv->conn->account.host); |
1036 |
return -1; |
1037 |
} |
1038 |
if (buf[0] != '3') |
1039 |
{ |
1040 |
mutt_error (_("Can't post article: %s"), buf); |
1041 |
return -1; |
1042 |
} |
1043 |
|
1044 |
buf[0] = '.'; |
1045 |
buf[1] = '\0'; |
1046 |
while (fgets (buf + 1, sizeof (buf) - 2, f) != NULL) |
1047 |
{ |
1048 |
len = strlen (buf); |
1049 |
if (buf[len - 1] == '\n') |
1050 |
{ |
1051 |
buf[len - 1] = '\r'; |
1052 |
buf[len] = '\n'; |
1053 |
len++; |
1054 |
buf[len] = '\0'; |
1055 |
} |
1056 |
if (buf[1] == '.') |
1057 |
mutt_socket_write_d (nntp_data->nserv->conn, buf, -1, M_SOCK_LOG_HDR); |
1058 |
else |
1059 |
mutt_socket_write_d (nntp_data->nserv->conn, buf + 1, -1, M_SOCK_LOG_HDR); |
1060 |
} |
1061 |
fclose (f); |
1062 |
|
1063 |
if (buf[strlen (buf) - 1] != '\n') |
1064 |
mutt_socket_write_d (nntp_data->nserv->conn, "\r\n", -1, M_SOCK_LOG_HDR); |
1065 |
mutt_socket_write_d (nntp_data->nserv->conn, ".\r\n", -1, M_SOCK_LOG_HDR); |
1066 |
if (mutt_socket_readln (buf, sizeof (buf), nntp_data->nserv->conn) < 0) |
1067 |
{ |
1068 |
mutt_error (_("Can't post article. Connection to %s lost."), |
1069 |
nntp_data->nserv->conn->account.host); |
1070 |
return -1; |
1071 |
} |
1072 |
if (buf[0] != '2') |
1073 |
{ |
1074 |
mutt_error (_("Can't post article: %s"), buf); |
1075 |
return -1; |
1076 |
} |
1077 |
|
1078 |
return 0; |
1079 |
} |
1080 |
|
1081 |
/* nntp_logout_all: close all open connections. */ |
1082 |
void nntp_logout_all (void) |
1083 |
{ |
1084 |
char buf[LONG_STRING]; |
1085 |
CONNECTION* conn; |
1086 |
|
1087 |
conn = mutt_socket_head (); |
1088 |
|
1089 |
while (conn) |
1090 |
{ |
1091 |
CONNECTION *next = conn->next; |
1092 |
|
1093 |
if (conn->account.type == M_ACCT_TYPE_NNTP) |
1094 |
{ |
1095 |
mutt_message (_("Closing connection to %s..."), conn->account.host); |
1096 |
mutt_socket_write (conn, "QUIT\r\n"); |
1097 |
mutt_socket_readln (buf, sizeof (buf), conn); |
1098 |
mutt_clear_error (); |
1099 |
mutt_socket_close (conn); |
1100 |
mutt_socket_free (conn); |
1101 |
} |
1102 |
|
1103 |
conn = next; |
1104 |
} |
1105 |
} |
1106 |
|
1107 |
static void nntp_free_acache (NNTP_DATA *data) |
1108 |
{ |
1109 |
int i; |
1110 |
|
1111 |
for (i = 0; i < NNTP_CACHE_LEN; i++) |
1112 |
{ |
1113 |
if (data->acache[i].path) |
1114 |
{ |
1115 |
unlink (data->acache[i].path); |
1116 |
FREE (&data->acache[i].path); |
1117 |
} |
1118 |
} |
1119 |
} |
1120 |
|
1121 |
void nntp_delete_data (void *p) |
1122 |
{ |
1123 |
NNTP_DATA *data = (NNTP_DATA *)p; |
1124 |
|
1125 |
if (!p) |
1126 |
return; |
1127 |
FREE (&data->entries); |
1128 |
FREE (&data->desc); |
1129 |
FREE (&data->cache); |
1130 |
nntp_free_acache (data); |
1131 |
FREE (p); |
1132 |
} |
1133 |
|
1134 |
int nntp_sync_mailbox (CONTEXT *ctx) |
1135 |
{ |
1136 |
NNTP_DATA *data = ctx->data; |
1137 |
|
1138 |
/* CACHE: update cache and .index files */ |
1139 |
if ((option (OPTSAVEUNSUB) || data->subscribed)) |
1140 |
nntp_save_cache_group (ctx); |
1141 |
nntp_free_acache (data); |
1142 |
|
1143 |
data->nserv->check_time = 0; /* next nntp_check_mailbox() will really check */ |
1144 |
return 0; |
1145 |
} |
1146 |
|
1147 |
int nntp_fastclose_mailbox (CONTEXT *ctx) |
1148 |
{ |
1149 |
NNTP_DATA *data = (NNTP_DATA *) ctx->data, *tmp; |
1150 |
|
1151 |
if (!data) |
1152 |
return 0; |
1153 |
nntp_free_acache (data); |
1154 |
if (!data->nserv || !data->nserv->newsgroups || !data->group) |
1155 |
return 0; |
1156 |
nntp_save_cache_index (data->nserv); |
1157 |
if ((tmp = hash_find (data->nserv->newsgroups, data->group)) == NULL |
1158 |
|| tmp != data) |
1159 |
nntp_delete_data (data); |
1160 |
return 0; |
1161 |
} |
1162 |
|
1163 |
/* commit changes and terminate connection */ |
1164 |
int nntp_close_mailbox (CONTEXT *ctx) |
1165 |
{ |
1166 |
if (!ctx) |
1167 |
return -1; |
1168 |
mutt_message _("Quitting newsgroup..."); |
1169 |
if (ctx->data) |
1170 |
{ |
1171 |
NNTP_DATA *data = (NNTP_DATA *) ctx->data; |
1172 |
int ret; |
1173 |
|
1174 |
if (data->nserv && data->nserv->conn && ctx->unread) |
1175 |
{ |
1176 |
ret = query_quadoption (OPT_CATCHUP, _("Mark all articles read?")); |
1177 |
if (ret == M_YES) |
1178 |
mutt_newsgroup_catchup (data->nserv, data->group); |
1179 |
else if (ret < 0) |
1180 |
return -1; |
1181 |
} |
1182 |
} |
1183 |
nntp_sync_mailbox (ctx); |
1184 |
if (ctx->data && ((NNTP_DATA *)ctx->data)->nserv) |
1185 |
{ |
1186 |
NNTP_SERVER *news; |
1187 |
|
1188 |
news = ((NNTP_DATA *)ctx->data)->nserv; |
1189 |
newsrc_gen_entries (ctx); |
1190 |
((NNTP_DATA *)ctx->data)->unread = ctx->unread; |
1191 |
mutt_newsrc_update (news); |
1192 |
} |
1193 |
mutt_clear_error(); |
1194 |
return 0; |
1195 |
} |
1196 |
|
1197 |
/* use the GROUP command to poll for new mail */ |
1198 |
static int _nntp_check_mailbox (CONTEXT *ctx, NNTP_DATA *nntp_data) |
1199 |
{ |
1200 |
char buf[LONG_STRING]; |
1201 |
int count = 0; |
1202 |
|
1203 |
if (nntp_data->nserv->check_time + NewsPollTimeout > time (NULL)) |
1204 |
return 0; |
1205 |
|
1206 |
buf[0] = 0; |
1207 |
if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) |
1208 |
{ |
1209 |
#ifdef DEBUG |
1210 |
nntp_error ("nntp_check_mailbox()", buf); |
1211 |
#endif |
1212 |
return -1; |
1213 |
} |
1214 |
if (mutt_strncmp ("211", buf, 3)) |
1215 |
{ |
1216 |
buf[0] = 0; |
1217 |
if (mutt_nntp_query (nntp_data, buf, sizeof (buf)) < 0) |
1218 |
{ |
1219 |
#ifdef DEBUG |
1220 |
nntp_error ("nntp_check_mailbox()", buf); |
1221 |
#endif |
1222 |
return -1; |
1223 |
} |
1224 |
} |
1225 |
if (!mutt_strncmp ("211", buf, 3)) |
1226 |
{ |
1227 |
int first; |
1228 |
int last; |
1229 |
|
1230 |
sscanf (buf + 4, "%d %d %d", &count, &first, &last); |
1231 |
nntp_data->firstMessage = first; |
1232 |
nntp_data->lastMessage = last; |
1233 |
if (ctx && last > nntp_data->lastLoaded) |
1234 |
{ |
1235 |
nntp_fetch_headers (ctx, nntp_data->lastLoaded + 1, last); |
1236 |
time (&nntp_data->nserv->check_time); |
1237 |
return 1; |
1238 |
} |
1239 |
if (!last || (!nntp_data->rc && !nntp_data->lastCached)) |
1240 |
nntp_data->unread = count; |
1241 |
else |
1242 |
mutt_newsgroup_stat (nntp_data); |
1243 |
/* active was renumbered? */ |
1244 |
if (last < nntp_data->lastLoaded) |
1245 |
{ |
1246 |
if (!nntp_data->max) |
1247 |
{ |
1248 |
nntp_data->entries = safe_calloc (5, sizeof (NEWSRC_ENTRY)); |
1249 |
nntp_data->max = 5; |
1250 |
} |
1251 |
nntp_data->lastCached = 0; |
1252 |
nntp_data->num = 1; |
1253 |
nntp_data->entries[0].first = 1; |
1254 |
nntp_data->entries[0].last = 0; |
1255 |
} |
1256 |
} |
1257 |
|
1258 |
time (&nntp_data->nserv->check_time); |
1259 |
return 0; |
1260 |
} |
1261 |
|
1262 |
int nntp_check_mailbox (CONTEXT *ctx) |
1263 |
{ |
1264 |
return _nntp_check_mailbox (ctx, (NNTP_DATA *)ctx->data); |
1265 |
} |
1266 |
|
1267 |
static int add_group (char *buf, void *serv) |
1268 |
{ |
1269 |
#define s ((NNTP_SERVER *) serv) |
1270 |
char group[LONG_STRING], mod, desc[HUGE_STRING]; |
1271 |
int first, last; |
1272 |
NNTP_DATA *nntp_data; |
1273 |
static int n = 0; |
1274 |
|
1275 |
_checked = n; /* _checked have N, where N = number of groups */ |
1276 |
if (!buf) /* at EOF must be zerouth */ |
1277 |
n = 0; |
1278 |
|
1279 |
if (!s || !buf) |
1280 |
return 0; |
1281 |
|
1282 |
*desc = 0; |
1283 |
sscanf (buf, "%s %d %d %c %[^\n]", group, &last, &first, &mod, desc); |
1284 |
if (!group) |
1285 |
return 0; |
1286 |
if ((nntp_data = (NNTP_DATA *) hash_find (s->newsgroups, group)) == NULL) |
1287 |
{ |
1288 |
n++; |
1289 |
nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1); |
1290 |
nntp_data->group = (char *) nntp_data + sizeof (NNTP_DATA); |
1291 |
strcpy (nntp_data->group, group); |
1292 |
nntp_data->nserv = s; |
1293 |
if (s->newsgroups->nelem < s->newsgroups->curnelem * 2) |
1294 |
s->newsgroups = hash_resize (s->newsgroups, s->newsgroups->nelem * 2); |
1295 |
hash_insert (s->newsgroups, nntp_data->group, nntp_data, 0); |
1296 |
nntp_add_to_list (s, nntp_data); |
1297 |
} |
1298 |
nntp_data->deleted = 0; |
1299 |
nntp_data->firstMessage = first; |
1300 |
nntp_data->lastMessage = last; |
1301 |
if (mod == 'y') |
1302 |
nntp_data->allowed = 1; |
1303 |
else |
1304 |
nntp_data->allowed = 0; |
1305 |
if (nntp_data->desc) |
1306 |
FREE (&nntp_data->desc); |
1307 |
if (*desc) |
1308 |
nntp_data->desc = safe_strdup (desc); |
1309 |
if (nntp_data->rc || nntp_data->lastCached) |
1310 |
mutt_newsgroup_stat (nntp_data); |
1311 |
else if (nntp_data->lastMessage && |
1312 |
nntp_data->firstMessage <= nntp_data->lastMessage) |
1313 |
nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1; |
1314 |
else |
1315 |
nntp_data->unread = 0; |
1316 |
|
1317 |
return 0; |
1318 |
#undef s |
1319 |
} |
1320 |
|
1321 |
int nntp_check_newgroups (NNTP_SERVER *serv, int force) |
1322 |
{ |
1323 |
char buf[LONG_STRING]; |
1324 |
char msg[SHORT_STRING]; |
1325 |
NNTP_DATA nntp_data; |
1326 |
LIST *l; |
1327 |
LIST emp; |
1328 |
time_t now; |
1329 |
struct tm *t; |
1330 |
unsigned int count = 0; |
1331 |
unsigned int total = 0; |
1332 |
|
1333 |
if (!serv || !serv->newgroups_time) |
1334 |
return -1; |
1335 |
|
1336 |
if (nntp_open_connection (serv) < 0) |
1337 |
return -1; |
1338 |
|
1339 |
/* check subscribed groups for new news */ |
1340 |
if (option (OPTSHOWNEWNEWS)) |
1341 |
{ |
1342 |
mutt_message _("Checking for new messages..."); |
1343 |
for (l = serv->list; l; l = l->next) |
1344 |
{ |
1345 |
serv->check_time = 0; /* really check! */ |
1346 |
if (l->data && ((NNTP_DATA *) l->data)->subscribed) |
1347 |
_nntp_check_mailbox (NULL, (NNTP_DATA *) l->data); |
1348 |
} |
1349 |
} |
1350 |
else if (!force) |
1351 |
return 0; |
1352 |
|
1353 |
mutt_message _("Checking for new newsgroups..."); |
1354 |
now = serv->newgroups_time; |
1355 |
time (&serv->newgroups_time); |
1356 |
t = gmtime (&now); |
1357 |
snprintf (buf, sizeof (buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n", |
1358 |
(t->tm_year % 100), t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, |
1359 |
t->tm_sec); |
1360 |
nntp_data.nserv = serv; |
1361 |
if (Context && Context->magic == M_NNTP) |
1362 |
nntp_data.group = ((NNTP_DATA *)Context->data)->group; |
1363 |
else |
1364 |
nntp_data.group = NULL; |
1365 |
l = serv->tail; |
1366 |
if (mutt_nntp_fetch (&nntp_data, buf, _("Adding new newsgroups..."), |
1367 |
add_group, serv, 0) != 0) |
1368 |
{ |
1369 |
#ifdef DEBUG |
1370 |
nntp_error ("nntp_check_newgroups()", buf); |
1371 |
#endif |
1372 |
return -1; |
1373 |
} |
1374 |
|
1375 |
strfcpy (msg, _("Loading descriptions..."), sizeof (msg)); |
1376 |
mutt_message (msg); |
1377 |
if (l) |
1378 |
emp.next = l->next; |
1379 |
else |
1380 |
emp.next = serv->list; |
1381 |
l = &emp; |
1382 |
while (l->next) |
1383 |
{ |
1384 |
l = l->next; |
1385 |
((NNTP_DATA *) l->data)->new = 1; |
1386 |
total++; |
1387 |
} |
1388 |
l = &emp; |
1389 |
while (l->next) |
1390 |
{ |
1391 |
l = l->next; |
1392 |
nntp_get_desc ((NNTP_DATA *) l->data, ((NNTP_DATA *) l->data)->group, NULL); |
1393 |
count++; |
1394 |
if (ReadInc && (count % ReadInc == 0)) |
1395 |
mutt_message ("%s %d/%d", msg, count, total); |
1396 |
} |
1397 |
if (emp.next) |
1398 |
nntp_save_cache_index (serv); |
1399 |
mutt_clear_error (); |
1400 |
return _checked; |
1401 |
} |
1402 |
|
1403 |
/* Load list of all newsgroups from cache ALL */ |
1404 |
int nntp_get_cache_all (NNTP_SERVER *serv) |
1405 |
{ |
1406 |
char buf[HUGE_STRING]; |
1407 |
FILE *f; |
1408 |
|
1409 |
nntp_cache_expand (buf, serv->cache); |
1410 |
if ((f = safe_fopen (buf, "r"))) |
1411 |
{ |
1412 |
int i = 0; |
1413 |
|
1414 |
while (fgets (buf, sizeof(buf), f) != NULL) |
1415 |
{ |
1416 |
if (ReadInc && (i % ReadInc == 0)) |
1417 |
mutt_message (_("Loading list from cache... %d"), i); |
1418 |
add_group (buf, serv); |
1419 |
i++; |
1420 |
} |
1421 |
add_group (NULL, NULL); |
1422 |
fclose (f); |
1423 |
mutt_clear_error (); |
1424 |
return 0; |
1425 |
} |
1426 |
else |
1427 |
{ |
1428 |
FREE (&serv->cache); |
1429 |
return -1; |
1430 |
} |
1431 |
} |
1432 |
|
1433 |
/* Load list of all newsgroups from active */ |
1434 |
int nntp_get_active (NNTP_SERVER *serv) |
1435 |
{ |
1436 |
char msg[SHORT_STRING]; |
1437 |
NNTP_DATA nntp_data; |
1438 |
LIST *tmp; |
1439 |
|
1440 |
if (nntp_open_connection (serv) < 0) |
1441 |
return -1; |
1442 |
|
1443 |
snprintf (msg, sizeof(msg), _("Loading list of all newsgroups on server %s..."), |
1444 |
serv->conn->account.host); |
1445 |
mutt_message (msg); |
1446 |
time (&serv->newgroups_time); |
1447 |
nntp_data.nserv = serv; |
1448 |
nntp_data.group = NULL; |
1449 |
|
1450 |
if (mutt_nntp_fetch (&nntp_data, "LIST\r\n", msg, add_group, serv, 0) < 0) |
1451 |
{ |
1452 |
#ifdef DEBUG |
1453 |
nntp_error ("nntp_get_active()", "LIST\r\n"); |
1454 |
#endif |
1455 |
return -1; |
1456 |
} |
1457 |
|
1458 |
strfcpy (msg, _("Loading descriptions..."), sizeof (msg)); |
1459 |
mutt_message (msg); |
1460 |
nntp_get_desc (&nntp_data, "*", msg); |
1461 |
|
1462 |
for (tmp = serv->list; tmp; tmp = tmp->next) |
1463 |
{ |
1464 |
NNTP_DATA *data = (NNTP_DATA *)tmp->data; |
1465 |
|
1466 |
if (data && data->deleted && !data->rc) |
1467 |
{ |
1468 |
nntp_delete_cache (data); |
1469 |
hash_delete (serv->newsgroups, data->group, NULL, nntp_delete_data); |
1470 |
tmp->data = NULL; |
1471 |
} |
1472 |
} |
1473 |
nntp_save_cache_index (serv); |
1474 |
|
1475 |
mutt_clear_error (); |
1476 |
return _checked; |
1477 |
} |
1478 |
|
1479 |
/* |
1480 |
* returns -1 if error ocurred while retrieving header, |
1481 |
* number of articles which ones exist in context on success. |
1482 |
*/ |
1483 |
int nntp_check_msgid (CONTEXT *ctx, const char *msgid) |
1484 |
{ |
1485 |
int ret; |
1486 |
|
1487 |
/* if msgid is already in context, don't reload them */ |
1488 |
if (hash_find (ctx->id_hash, msgid)) |
1489 |
return 1; |
1490 |
if (ctx->msgcount == ctx->hdrmax) |
1491 |
mx_alloc_memory (ctx); |
1492 |
ctx->hdrs[ctx->msgcount] = mutt_new_header (); |
1493 |
ctx->hdrs[ctx->msgcount]->index = ctx->msgcount; |
1494 |
|
1495 |
mutt_message (_("Fetching %s from server..."), msgid); |
1496 |
ret = nntp_read_header (ctx, msgid, 0); |
1497 |
/* since nntp_read_header() may set read flag, we must reset it */ |
1498 |
ctx->hdrs[ctx->msgcount]->read = 0; |
1499 |
if (ret != 0) |
1500 |
mutt_free_header (&ctx->hdrs[ctx->msgcount]); |
1501 |
else |
1502 |
{ |
1503 |
ctx->msgcount++; |
1504 |
mx_update_context (ctx, 1); |
1505 |
ctx->changed = 1; |
1506 |
} |
1507 |
return ret; |
1508 |
} |
1509 |
|
1510 |
typedef struct |
1511 |
{ |
1512 |
CONTEXT *ctx; |
1513 |
unsigned int num; |
1514 |
unsigned int max; |
1515 |
unsigned int *child; |
1516 |
} CHILD_CONTEXT; |
1517 |
|
1518 |
static int check_children (char *s, void *c) |
1519 |
{ |
1520 |
#define cc ((CHILD_CONTEXT *) c) |
1521 |
unsigned int i, n; |
1522 |
|
1523 |
if (!s || (n = atoi (s)) == 0) |
1524 |
return 0; |
1525 |
for (i = 0; i < cc->ctx->msgcount; i++) |
1526 |
if (cc->ctx->hdrs[i]->article_num == n) |
1527 |
return 0; |
1528 |
if (cc->num >= cc->max) |
1529 |
safe_realloc (&cc->child, sizeof (unsigned int) * (cc->max += 25)); |
1530 |
cc->child[cc->num++] = n; |
1531 |
|
1532 |
return 0; |
1533 |
#undef cc |
1534 |
} |
1535 |
|
1536 |
int nntp_check_children (CONTEXT *ctx, const char *msgid) |
1537 |
{ |
1538 |
NNTP_DATA *nntp_data = (NNTP_DATA *)ctx->data; |
1539 |
char buf[STRING]; |
1540 |
int i, ret = 0, tmp = 0; |
1541 |
CHILD_CONTEXT cc; |
1542 |
|
1543 |
if (!nntp_data || !nntp_data->nserv || !nntp_data->nserv->conn || |
1544 |
!nntp_data->nserv->conn->account.host) |
1545 |
return -1; |
1546 |
if (nntp_data->firstMessage > nntp_data->lastLoaded) |
1547 |
return 0; |
1548 |
if (!nntp_data->nserv->hasXPAT) |
1549 |
{ |
1550 |
mutt_error (_("Server %s does not support this operation!"), |
1551 |
nntp_data->nserv->conn->account.host); |
1552 |
return -1; |
1553 |
} |
1554 |
|
1555 |
snprintf (buf, sizeof (buf), "XPAT References %d-%d *%s*\r\n", |
1556 |
nntp_data->firstMessage, nntp_data->lastLoaded, msgid); |
1557 |
|
1558 |
cc.ctx = ctx; |
1559 |
cc.num = 0; |
1560 |
cc.max = 25; |
1561 |
cc.child = safe_malloc (sizeof (unsigned int) * 25); |
1562 |
if (mutt_nntp_fetch (nntp_data, buf, NULL, check_children, &cc, 0)) |
1563 |
{ |
1564 |
FREE (&cc.child); |
1565 |
return -1; |
1566 |
} |
1567 |
/* dont try to read the xover cache. check_children() already |
1568 |
* made sure that we dont have the article, so we need to visit |
1569 |
* the server. Reading the cache at this point is also bad |
1570 |
* because it would duplicate messages */ |
1571 |
if (option (OPTNEWSCACHE)) |
1572 |
{ |
1573 |
tmp++; |
1574 |
unset_option (OPTNEWSCACHE); |
1575 |
} |
1576 |
for (i = 0; i < cc.num; i++) |
1577 |
{ |
1578 |
if ((ret = nntp_fetch_headers (ctx, cc.child[i], cc.child[i]))) |
1579 |
break; |
1580 |
if (ctx->msgcount && |
1581 |
ctx->hdrs[ctx->msgcount - 1]->article_num == cc.child[i]) |
1582 |
ctx->hdrs[ctx->msgcount - 1]->read = 0; |
1583 |
} |
1584 |
if (tmp) |
1585 |
set_option (OPTNEWSCACHE); |
1586 |
FREE (&cc.child); |
1587 |
return ret; |
1588 |
} |