1 /*
2 This file is part of SLRN.
3
4 Copyright (c) 1994, 1999 John E. Davis <davis@space.mit.edu>
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc., 675
18 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 #include "config.h"
21 #include <stdio.h>
22 #include <string.h>
23
24 #ifdef HAVE_STDLIB_H
25 # include <stdlib.h>
26 #endif
27
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31
32 #include <stdarg.h>
33 #include <slang.h>
34 #include "jdmacros.h"
35
36 #ifdef KANJI
37 #include "chmap.h"
38 #endif
39 #include "sltcp.h"
40
41 #include "nntpcodes.h"
42 #include "nntplib.h"
43 #include "util.h"
44 #include "ttymsg.h"
45
46 void (*NNTP_Connection_Lost_Hook) (NNTP_Type *);
47 int (*NNTP_Authorization_Hook) (char *, char **, char **);
48
49 FILE *NNTP_Debug_Fp;
50
51 static int _nntp_connect_server (NNTP_Type *);
52
nntp_allocate_nntp(void)53 static NNTP_Type *nntp_allocate_nntp (void)
54 {
55 static NNTP_Type nn;
56 NNTP_Type *s;
57
58 s = &nn;
59
60 memset ((char *) s, 0, sizeof (NNTP_Type));
61 s->can_xover = -1;
62 s->can_xhdr = -1;
63 s->can_xpat = -1;
64 s->can_xgtitle = -1;
65
66 return s;
67 }
68
_nntp_deallocate_nntp(NNTP_Type * s)69 static void _nntp_deallocate_nntp (NNTP_Type *s)
70 {
71 if (s == NULL) return;
72
73 memset ((char *) s, 0, sizeof (NNTP_Type));
74 }
75
76 /* Call connection-lost hook, unless it is ok to reconnect */
check_connect_lost_hook(NNTP_Type * s)77 static int check_connect_lost_hook (NNTP_Type *s)
78 {
79 if (s->flags & NNTP_RECONNECT_OK)
80 return 0;
81
82 if (NNTP_Connection_Lost_Hook != NULL)
83 (*NNTP_Connection_Lost_Hook) (s);
84
85 return -1;
86 }
87
88
nntp_fgets_server(NNTP_Type * s,char * buf,unsigned int len)89 int nntp_fgets_server (NNTP_Type *s, char *buf, unsigned int len)
90 {
91 if ((s == NULL) || (s->init_state <= 0))
92 return -1;
93
94 *buf = 0;
95
96 if (-1 == sltcp_fgets (s->tcp, buf, len))
97 {
98 (void) nntp_disconnect_server (s);
99 (void) check_connect_lost_hook (s);
100 return -1;
101 }
102
103 if (NNTP_Debug_Fp != NULL)
104 fprintf (NNTP_Debug_Fp, "<%s", buf);
105
106 return 0;
107 }
108
nntp_fputs_server(NNTP_Type * s,char * buf)109 int nntp_fputs_server (NNTP_Type *s, char *buf)
110 {
111 if ((s == NULL) || (s->init_state == 0))
112 return -1;
113
114 if (NNTP_Debug_Fp != NULL)
115 fprintf (NNTP_Debug_Fp, ">%s\n", buf);
116
117 if (-1 == sltcp_fputs (s->tcp, buf))
118 {
119 (void) nntp_disconnect_server (s);
120 (void) check_connect_lost_hook (s);
121 return -1;
122 }
123
124 return 0;
125 }
126
nntp_write_server(NNTP_Type * s,char * buf,unsigned int n)127 int nntp_write_server (NNTP_Type *s, char *buf, unsigned int n)
128 {
129 if ((s == NULL) || (s->init_state == 0))
130 return -1;
131
132 if (NNTP_Debug_Fp != NULL)
133 {
134 unsigned int i;
135 fputc ('>', NNTP_Debug_Fp);
136 for (i = 0; i < n; i++)
137 fputc (buf[i], NNTP_Debug_Fp);
138 fputc ('\n', NNTP_Debug_Fp);
139 }
140
141 if (n != sltcp_write (s->tcp, buf, n))
142 {
143 (void) nntp_disconnect_server (s);
144 (void) check_connect_lost_hook (s);
145 return -1;
146 }
147
148 return 0;
149 }
150
nntp_gets_server(NNTP_Type * s,char * buf,unsigned int len)151 int nntp_gets_server (NNTP_Type *s, char *buf, unsigned int len)
152 {
153 if (-1 == nntp_fgets_server (s, buf, len))
154 return -1;
155
156 len = strlen (buf);
157 if (len && (buf[len - 1] == '\n'))
158 {
159 len--;
160 buf[len] = 0;
161 if (len && (buf[len - 1] == '\r'))
162 buf[len - 1] = 0;
163 }
164
165 return 0;
166 }
167
nntp_puts_server(NNTP_Type * s,char * buf)168 int nntp_puts_server (NNTP_Type *s, char *buf)
169 {
170 if ((-1 == nntp_fputs_server (s, buf))
171 || (-1 == nntp_fputs_server (s, "\r\n"))
172 || (-1 == sltcp_flush_output (s->tcp)))
173 {
174 (void) check_connect_lost_hook (s);
175 nntp_disconnect_server (s);
176 return -1;
177 }
178
179 return 0;
180 }
181
_nntp_error_response(NNTP_Type * s,char * fmt)182 static void _nntp_error_response (NNTP_Type *s, char *fmt)
183 {
184 slrn_error ("%s", fmt);
185 slrn_error ("Reason: %s", s->rspbuf);
186 }
187
188
_nntp_try_parse_timeout(char * str)189 static int _nntp_try_parse_timeout (char *str)
190 {
191 /* I know of only two timeout responses:
192 * 503 Timeout
193 * 503 connection timed out
194 *
195 * Here the idea is to look for 'time' and then 'out'.
196 */
197 static SLRegexp_Type re;
198 unsigned char compiled_pattern_buf[256];
199
200 re.pat = (unsigned char *) "time.*out";
201 re.buf = compiled_pattern_buf;
202 re.case_sensitive = 0;
203 re.buf_len = sizeof (compiled_pattern_buf);
204
205 (void) SLang_regexp_compile (&re);
206 if (NULL == SLang_regexp_match ((unsigned char *) str, strlen (str), &re))
207 return -1;
208
209 return 0;
210 }
211
nntp_get_server_response(NNTP_Type * s)212 int nntp_get_server_response (NNTP_Type *s)
213 {
214 int status;
215
216 if ((s == NULL) || (s->init_state <= 0))
217 return -1;
218
219 if (-1 == nntp_gets_server (s, s->rspbuf, NNTP_RSPBUF_SIZE))
220 return -1;
221
222 status = atoi (s->rspbuf);
223
224 switch (status)
225 {
226 case ERR_FAULT:
227 if (-1 == _nntp_try_parse_timeout (s->rspbuf))
228 break;
229 /* Drop */
230
231 case 0: /* invalid code */
232 case ERR_GOODBYE:
233 nntp_disconnect_server (s);
234 (void) check_connect_lost_hook (s);
235 status = -1;
236 break;
237 }
238
239 s->code = status;
240 return status;
241 }
242
_nntp_reconnect(NNTP_Type * s)243 static int _nntp_reconnect (NNTP_Type *s)
244 {
245 nntp_disconnect_server (s);
246
247 if (-1 == check_connect_lost_hook (s))
248 return -1;
249
250 s->flags &= ~NNTP_RECONNECT_OK;
251
252 slrn_message_now ("Server %s dropped connection. Reconnecting...", s->host);
253
254 if (-1 == _nntp_connect_server (s))
255 {
256 (void) check_connect_lost_hook (s);
257
258 s->flags |= NNTP_RECONNECT_OK;
259 return -1;
260 }
261
262 if ((s->group_name[0] != 0)
263 && (-1 == nntp_server_vcmd (s, "GROUP %s", s->group_name)))
264 {
265 (void) check_connect_lost_hook (s);
266
267 s->flags |= NNTP_RECONNECT_OK;
268 return -1;
269 }
270
271 s->flags |= NNTP_RECONNECT_OK;
272 return 0;
273 }
274
nntp_start_server_cmd(NNTP_Type * s,char * cmd)275 int nntp_start_server_cmd (NNTP_Type *s, char *cmd)
276 {
277 int max_tries = 3;
278
279 if (s == NULL)
280 return -1;
281
282 while ((s->init_state != 0) && max_tries
283 && (SLKeyBoard_Quit == 0))
284 {
285 if (-1 != nntp_puts_server (s, cmd))
286 return 0;
287
288 if (-1 == _nntp_reconnect (s))
289 return -1;
290
291 max_tries--;
292 }
293
294 return -1;
295 }
296
nntp_server_cmd(NNTP_Type * s,char * cmd)297 int nntp_server_cmd (NNTP_Type *s, char *cmd)
298 {
299 int max_tries = 3;
300
301 do
302 {
303 int code;
304
305 if (-1 == nntp_start_server_cmd (s, cmd))
306 return -1;
307
308 if (-1 != (code = nntp_get_server_response (s)))
309 return code;
310
311 max_tries--;
312 }
313 while (max_tries && (s->flags & NNTP_RECONNECT_OK));
314
315 return -1;
316 }
317
nntp_server_vcmd(NNTP_Type * s,char * fmt,...)318 int nntp_server_vcmd (NNTP_Type *s, char *fmt, ...)
319 {
320 char buf [NNTP_MAX_CMD_LEN];
321 va_list ap;
322
323 va_start (ap, fmt);
324 vsprintf (buf, fmt, ap);
325 va_end (ap);
326
327 return nntp_server_cmd (s, buf);
328 }
329
nntp_start_server_vcmd(NNTP_Type * s,char * fmt,...)330 int nntp_start_server_vcmd (NNTP_Type *s, char *fmt, ...)
331 {
332 char buf [NNTP_MAX_CMD_LEN];
333 va_list ap;
334
335 va_start (ap, fmt);
336 vsprintf (buf, fmt, ap);
337 va_end (ap);
338
339 return nntp_start_server_cmd (s, buf);
340 }
341
nntp_close_server(NNTP_Type * s)342 int nntp_close_server (NNTP_Type *s)
343 {
344 if (s == NULL)
345 return -1;
346
347 if (s->init_state <= 0)
348 return 0;
349
350 s->init_state = -1; /* closing */
351
352 /* This might be called from a connect_lost hook. We also do not
353 * want to reconnect to send the QUIT command so do not call any
354 * of the *server_cmd functions.
355 */
356 (void) nntp_puts_server (s, "QUIT");
357 (void) sltcp_close (s->tcp);
358
359 s->tcp = NULL;
360 s->init_state = 0;
361
362 _nntp_deallocate_nntp (s);
363
364 return 0;
365 }
366
_nntp_authorization(NNTP_Type * s)367 static int _nntp_authorization (NNTP_Type *s)
368 {
369 char *name, *pass;
370
371 if (NNTP_Authorization_Hook == NULL)
372 return 0;
373
374 if (-1 == (*NNTP_Authorization_Hook) (s->host, &name, &pass))
375 return -1;
376
377 if ((name == NULL) || (pass == NULL))
378 return 0;
379
380 slrn_message_now ("Authenticating %s ...", name);
381
382 if (-1 == nntp_server_vcmd (s, "AUTHINFO USER %s", name))
383 return -1;
384
385 if (s->code != NEED_AUTHDATA)
386 return 0;
387
388 switch (nntp_server_vcmd (s, "AUTHINFO PASS %s", pass))
389 {
390 case -1:
391 return -1;
392 case ERR_ACCESS:
393 _nntp_error_response (s, "Authorization failed.");
394 return -1;
395 case OK_AUTH:
396 s->can_post = 1;
397 break;
398 }
399 return 0;
400 }
401
_nntp_connect_server(NNTP_Type * s)402 static int _nntp_connect_server (NNTP_Type *s)
403 {
404 if (s->tcp != NULL)
405 sltcp_close (s->tcp);
406
407 #ifdef KANJI
408 slrn_message_now (Slrn_Japanese_Messages
409 ? "�ۥ��� %s ����³�� ..."
410 : "Connecting to host %s ...", s->host);
411 #else
412 slrn_message_now ("Connecting to host %s ...", s->host);
413 #endif
414
415 if (NULL == (s->tcp = sltcp_open_connection (s->host, s->port)))
416 return -1;
417
418 s->init_state = 1;
419
420 /* Read logon message. */
421 switch (nntp_get_server_response (s))
422 {
423 case OK_CANPOST:
424 s->can_post = 1;
425 break;
426
427 case OK_NOPOST:
428 s->can_post = 0;
429 break;
430
431 default:
432 goto failed;
433 }
434
435 if ((-1 == nntp_server_cmd (s, "MODE READER"))
436 || (ERR_ACCESS == s->code)
437 || (-1 == _nntp_authorization (s)))
438 goto failed;
439
440 #ifdef KANJI
441 if (Slrn_Japanese_Messages)
442 slrn_message_now ("�ۥ��Ȥ���³���ޤ��������%s��ǽ�Ǥ���",
443 (s->can_post ? "" : "��"));
444 else
445 #endif
446 slrn_message_now ("Connected to host. Posting %sOk.",
447 (s->can_post ? "" : "NOT "));
448 return 0;
449
450 failed:
451
452 _nntp_error_response (s, "Failed to initialize server");
453 (void) sltcp_close (s->tcp);
454 s->tcp = NULL;
455 return -1;
456 }
457
458 #ifndef NNTPSERVER_FILE
459 # define NNTPSERVER_FILE NULL
460 #endif
461
_nntp_getserverbyfile(char * file)462 static char *_nntp_getserverbyfile (char *file)
463 {
464 FILE *fp;
465 char *host;
466 static char buf[256];
467
468 host = getenv("NNTPSERVER");
469
470 if (host != NULL)
471 {
472 strcpy (buf, host);
473 return buf;
474 }
475
476 if (file == NULL)
477 {
478 #ifdef NNTPSERVER_NAME
479 strcpy (buf, NNTPSERVER_NAME);
480 return buf;
481 #else
482 return NULL;
483 #endif
484 }
485
486 if (NULL == (fp = fopen(file, "r")))
487 return NULL;
488
489 while (NULL != fgets(buf, sizeof (buf), fp))
490 {
491 char *b;
492
493 b = slrn_skip_whitespace (buf);
494 if ((*b == 0) || (*b == '#'))
495 continue;
496
497 slrn_trim_string (b);
498 (void) fclose(fp);
499 return b;
500 }
501
502 (void) fclose(fp);
503 return NULL; /* No entry */
504 }
505
nntp_get_server_name(void)506 char *nntp_get_server_name (void)
507 {
508 char *host;
509
510 if (NULL != (host = _nntp_getserverbyfile(NNTPSERVER_FILE)))
511 return host;
512
513 fprintf (stderr, "You need to set the NNTPSERVER environment variable to your server name.\r\n");
514 #ifdef VMS
515 fprintf (stderr, "Example: $ define/job NNTPSERVER my.news.server\r\n");
516 #else
517 # if defined(IBMPC_SYSTEM)
518 fprintf (stderr, "Example: set NNTPSERVER=my.news.server\r\n");
519 # else
520 fprintf (stderr, "Example (csh): setenv NNTPSERVER my.news.server\r\n");
521 # endif
522 #endif
523 return NULL;
524 }
525
526
527 /* In general, host has the form: "address:port". If port is non-negative
528 * use its value despite the value coded in the hostname.
529 */
_nntp_setup_host_and_port(char * host,int port,NNTP_Type * s)530 static int _nntp_setup_host_and_port (char *host, int port, NNTP_Type *s)
531 {
532 char *a, *b, *bmax;
533
534 a = host;
535 b = s->host;
536 bmax = b + NNTP_MAX_HOST_LEN;
537 while ((*a != 0) && (*a != ':') && (b < bmax))
538 *b++ = *a++;
539 *b = 0;
540
541 if (port <= 0)
542 {
543 if (*a == ':')
544 port = atoi (a + 1);
545 else if (-1 == (port = sltcp_map_service_to_port ("nntp")))
546 port = 119;
547 }
548
549 s->port = port;
550
551 return 0;
552 }
553
554
nntp_open_server(char * host,int port)555 NNTP_Type *nntp_open_server (char *host, int port)
556 {
557 NNTP_Type *s;
558
559 #if 0
560 if (NNTP_Debug_Fp == NULL)
561 NNTP_Debug_Fp = fopen ("nntp.log", "w");
562 #endif
563
564 if (host == NULL)
565 {
566 host = nntp_get_server_name ();
567 if (host == NULL)
568 return NULL;
569 }
570
571 if (NULL == (s = nntp_allocate_nntp ()))
572 return NULL;
573
574 (void) _nntp_setup_host_and_port (host, port, s);
575
576 if (-1 == _nntp_connect_server (s))
577 {
578 _nntp_deallocate_nntp (s);
579 return NULL;
580 }
581
582 return s;
583 }
584
nntp_read_line(NNTP_Type * s,char * buf,unsigned int len)585 int nntp_read_line (NNTP_Type *s, char *buf, unsigned int len)
586 {
587 if (-1 == nntp_gets_server (s, buf, len))
588 return -1;
589
590 if ((buf[0] == '.') && (buf[1] == 0))
591 return 0;
592
593 return 1;
594 }
595
nntp_discard_output(NNTP_Type * s)596 int nntp_discard_output (NNTP_Type *s)
597 {
598 char buf [NNTP_BUFFER_SIZE];
599 int status;
600
601 while (1 == (status = nntp_read_line (s, buf, sizeof (buf))))
602 continue;
603
604 return status;
605 }
606
nntp_reconnect_server(NNTP_Type * s)607 int nntp_reconnect_server (NNTP_Type *s)
608 {
609 unsigned int flag;
610 int status;
611
612 if (s == NULL)
613 return -1;
614
615 nntp_disconnect_server (s);
616
617 flag = s->flags & NNTP_RECONNECT_OK;
618 s->flags |= NNTP_RECONNECT_OK;
619
620 status = _nntp_reconnect (s);
621
622 if (flag == 0) s->flags &= ~NNTP_RECONNECT_OK;
623
624 return status;
625 }
626
nntp_check_connection(NNTP_Type * s)627 int nntp_check_connection (NNTP_Type *s)
628 {
629 if ((s == NULL) || (s->tcp == NULL) || (s->tcp->tcp_fd == -1))
630 return -1;
631
632 return 0;
633 }
634
nntp_disconnect_server(NNTP_Type * s)635 void nntp_disconnect_server (NNTP_Type *s)
636 {
637 if (s == NULL) return;
638
639 sltcp_close_socket (s->tcp);
640 }
641
_nntp_probe_server(NNTP_Type * s,char * cmd)642 static int _nntp_probe_server (NNTP_Type *s, char *cmd)
643 {
644 if (-1 == nntp_server_cmd (s, cmd))
645 return -1;
646
647 if (ERR_COMMAND == s->code)
648 return 0;
649
650 return 1;
651 }
652
653 #define PROBE_XCMD(s, var,cmd) (((var) != -1) ? (var) : ((var) = _nntp_probe_server ((s),(cmd))))
654
nntp_has_cmd(NNTP_Type * s,char * cmd)655 int nntp_has_cmd (NNTP_Type *s, char *cmd)
656 {
657 /* DNEWS, in my opinion does not handle XHDR probing properly.
658 * Sigh.
659 */
660 if (!strcmp (cmd, "XHDR"))
661 {
662 int ret;
663
664 if (s->can_xhdr != -1)
665 return s->can_xhdr;
666
667 if (-1 == (ret = nntp_server_cmd (s, cmd)))
668 return -1;
669
670 if (ret == ERR_COMMAND)
671 {
672 s->can_xhdr = 0;
673 return 0;
674 }
675
676 s->can_xhdr = 1;
677
678 if (ret == OK_HEAD) /* DNEWS bug?? */
679 (void) nntp_discard_output (s);
680
681 return 1;
682 }
683
684 if (!strcmp (cmd, "XPAT"))
685 return PROBE_XCMD(s, s->can_xpat, cmd);
686
687 if (!strcmp (cmd, "XGTITLE"))
688 return PROBE_XCMD(s, s->can_xgtitle, cmd);
689
690 if (!strcmp (cmd, "XOVER"))
691 return PROBE_XCMD(s, s->can_xover, cmd);
692
693 return _nntp_probe_server (s, cmd);
694 }
695
_nntp_num_or_msgid_cmd(NNTP_Type * s,char * cmd,int n,char * msgid)696 static int _nntp_num_or_msgid_cmd (NNTP_Type *s, char *cmd, int n, char *msgid)
697 {
698 if (n != -1)
699 return nntp_server_vcmd (s, "%s %d", cmd, n);
700 else
701 {
702 if (msgid == NULL) msgid = "";
703 return nntp_server_vcmd (s, "%s %s", cmd, msgid);
704 }
705 }
706
nntp_head_cmd(NNTP_Type * s,int n,char * msgid,int * real_id)707 int nntp_head_cmd (NNTP_Type *s, int n, char *msgid, int *real_id)
708 {
709 int status;
710
711 status = _nntp_num_or_msgid_cmd (s, "HEAD", n, msgid);
712 if ((status == OK_HEAD) && (real_id != NULL))
713 *real_id = atoi(s->rspbuf + 4);
714 return status;
715 }
716
nntp_xover_cmd(NNTP_Type * s,int min,int max)717 int nntp_xover_cmd (NNTP_Type *s, int min, int max)
718 {
719 return nntp_server_vcmd (s, "XOVER %d-%d", min, max);
720 }
721
nntp_article_cmd(NNTP_Type * s,int n,char * msgid)722 int nntp_article_cmd (NNTP_Type *s, int n, char *msgid)
723 {
724 return _nntp_num_or_msgid_cmd (s, "ARTICLE", n, msgid);
725 }
726
nntp_next_cmd(NNTP_Type * s,int * n)727 int nntp_next_cmd (NNTP_Type *s, int *n)
728 {
729 int status;
730
731 if ((OK_NEXT == (status = nntp_server_cmd (s, "NEXT")))
732 && (n != NULL))
733 *n = atoi (s->rspbuf + 4);
734
735 return status;
736 }
737
nntp_select_group(NNTP_Type * s,char * name,int * minp,int * maxp)738 int nntp_select_group (NNTP_Type *s, char *name, int *minp, int *maxp)
739 {
740 int estim;
741 int min, max;
742
743 switch (nntp_server_vcmd (s, "GROUP %s", name))
744 {
745 case -1:
746 return -1;
747
748 case OK_GROUP:
749
750 if (3 != sscanf(s->rspbuf + 4, "%d %d %d", &estim, &min, &max))
751 return -1;
752
753 if (minp != NULL) *minp = min;
754 if (maxp != NULL) *maxp = max;
755
756 strcpy (s->group_name, name);
757 break;
758
759 case ERR_ACCESS:
760 default:
761 break;
762 }
763
764 return s->code;
765 }
766
nntp_post_cmd(NNTP_Type * s)767 int nntp_post_cmd (NNTP_Type *s)
768 {
769 return _nntp_num_or_msgid_cmd (s, "POST", -1, NULL);
770 }
771
nntp_end_post(NNTP_Type * s)772 int nntp_end_post (NNTP_Type *s)
773 {
774 if (-1 == nntp_puts_server (s, "."))
775 return -1;
776
777 return nntp_get_server_response (s);
778 }
779
nntp_xpat_cmd(NNTP_Type * s,char * hdr,int rmin,int rmax,char * pat)780 int nntp_xpat_cmd (NNTP_Type *s, char *hdr, int rmin, int rmax, char *pat)
781 {
782 if (0 == PROBE_XCMD(s, s->can_xpat, "XPAT"))
783 return ERR_COMMAND;
784
785 return nntp_server_vcmd (s, "XPAT %s %d-%d *%s*", hdr, rmin, rmax, pat);
786 }
787
nntp_xgtitle_cmd(NNTP_Type * s,char * pattern)788 int nntp_xgtitle_cmd (NNTP_Type *s, char *pattern)
789 {
790 if (s->can_xgtitle == 0)
791 return -1;
792
793 /* XGTITLE appears broken on some servers. So, do not probe for it. */
794 slrn_message_now ("Sending Query to Server ...");
795
796 if (-1 == nntp_server_vcmd (s, "XGTITLE %s", pattern))
797 return -1;
798
799 if (s->code != OK_XGTITLE)
800 {
801 slrn_message ("Server does not support XGTITLE command.");
802 s->can_xgtitle = 0;
803 return ERR_COMMAND;
804 }
805
806 s->can_xgtitle = 1;
807 return OK_XGTITLE;
808 }
809
810 /* hdr should not include ':' */
nntp_xhdr_cmd(NNTP_Type * s,char * hdr,int num,char * buf,unsigned int buflen)811 int nntp_xhdr_cmd (NNTP_Type *s, char *hdr, int num, char *buf, unsigned int buflen)
812 {
813 char tmpbuf[1024];
814 int found;
815 unsigned int colon;
816 int status;
817
818 status = PROBE_XCMD(s, s->can_xhdr, "XHDR");
819 if (status == -1)
820 return -1;
821
822 if (status == 1)
823 {
824 char *b, ch;
825
826 if (-1 == nntp_server_vcmd (s, "XHDR %s %d", hdr, num))
827 return -1;
828
829 if (OK_HEAD != s->code)
830 {
831 /* It comes as no surprise that Micro$oft apparantly makes
832 * buggy servers too. Sigh.
833 */
834 if (s->code != 224) return -1;
835 s->code = OK_HEAD;
836 }
837
838 status = nntp_read_line (s, tmpbuf, sizeof(tmpbuf));
839 if (status != 1)
840 return -1;
841
842 /* skip past article number */
843 b = tmpbuf;
844 while (((ch = *b++) >= '0') && (ch <= '9'))
845 ;
846 strncpy (buf, b, buflen - 1);
847 buf[buflen - 1] = 0;
848
849 /* I should handle multi-line returns but I doubt that there will be
850 * any for our use of xhdr
851 */
852 (void) nntp_discard_output (s);
853 return 0;
854 }
855
856 /* Server does not have XHDR so, use HEAD */
857
858 if (-1 == nntp_head_cmd (s, num, NULL, NULL))
859 return -1;
860
861 if (s->code != OK_HEAD)
862 return -1;
863
864 found = 0;
865 colon = strlen (hdr);
866
867 while (1 == (status = nntp_read_line (s, tmpbuf, sizeof (tmpbuf))))
868 {
869 char *b;
870
871 if (found
872 || slrn_case_strncmp ((unsigned char *) tmpbuf, (unsigned char *) hdr, colon)
873 || (tmpbuf[colon] != ':'))
874 continue;
875
876 found = 1;
877
878 b = tmpbuf + (colon + 1); /* skip past colon */
879 if (*b == ' ') b++;
880 strncpy (buf, b, buflen - 1);
881 buf[buflen - 1] = 0;
882 }
883
884 return status;
885 }
886
nntp_list_newsgroups(NNTP_Type * s)887 int nntp_list_newsgroups (NNTP_Type *s)
888 {
889 return _nntp_num_or_msgid_cmd (s, "LIST", -1, "NEWSGROUPS");
890 }
891
nntp_list_active_cmd(NNTP_Type * s)892 int nntp_list_active_cmd (NNTP_Type *s)
893 {
894 return _nntp_num_or_msgid_cmd (s, "LIST", -1, NULL);
895 }
896
nntp_listgroup(NNTP_Type * s,char * group)897 int nntp_listgroup (NNTP_Type *s, char *group)
898 {
899 return nntp_server_vcmd (s, "LISTGROUP %s", group);
900 /* OK_GROUP desired */
901 }
902
nntp_body_cmd(NNTP_Type * s,int n,char * msgid)903 int nntp_body_cmd (NNTP_Type *s, int n, char *msgid)
904 {
905 return _nntp_num_or_msgid_cmd (s, "BODY", n, msgid);
906 }
907
nntp_read_and_malloc(NNTP_Type * s)908 char *nntp_read_and_malloc (NNTP_Type *s)
909 {
910 char line [NNTP_BUFFER_SIZE];
911 char *mbuf;
912 unsigned int buffer_len, buffer_len_max;
913 int status;
914
915 mbuf = NULL;
916 buffer_len_max = buffer_len = 0;
917
918 while (1 == (status = nntp_read_line (s, line, sizeof(line))))
919 {
920 unsigned int len;
921
922 len = strlen (line);
923
924 if (len + buffer_len + 4 > buffer_len_max)
925 {
926 char *new_mbuf;
927
928 buffer_len_max += 4096 + len;
929 new_mbuf = slrn_realloc (mbuf, buffer_len_max, 0);
930
931 if (new_mbuf == NULL)
932 {
933 slrn_free (mbuf);
934 nntp_discard_output (s);
935 return NULL;
936 }
937 mbuf = new_mbuf;
938 }
939
940 strcpy (mbuf + buffer_len, line);
941 buffer_len += len;
942 mbuf [buffer_len++] = '\n';
943 mbuf [buffer_len] = 0;
944 }
945
946 if (status == 0)
947 {
948 if (mbuf == NULL)
949 mbuf = slrn_strmalloc ("", 0);
950
951 return mbuf;
952 }
953
954 slrn_free (mbuf);
955 return NULL;
956 }
957
nntp_map_code_to_string(int code)958 char *nntp_map_code_to_string (int code)
959 {
960 switch (code)
961 {
962 case INF_HELP: /* 100 */
963 return "Help text on way";
964 case INF_AUTH: /* 180 */
965 return "Authorization capabilities";
966 case INF_DEBUG: /* 199 */
967 return "Debug output";
968 case OK_CANPOST: /* 200 */
969 return "Hello; you can post";
970 case OK_NOPOST: /* 201 */
971 return "Hello; you can't post";
972 case OK_SLAVE: /* 202 */
973 return "Slave status noted";
974 case OK_GOODBYE: /* 205 */
975 return "Closing connection";
976 case OK_GROUP: /* 211 */
977 return "Group selected";
978 case OK_GROUPS: /* 215 */
979 return "Newsgroups follow";
980 case OK_ARTICLE: /* 220 */
981 return "Article (head & body) follows";
982 case OK_HEAD: /* 221 */
983 return "Head follows";
984 case OK_BODY: /* 222 */
985 return "Body follows";
986 case OK_NOTEXT: /* 223 */
987 return "No text sent -- stat, next, last";
988 case OK_NEWNEWS: /* 230 */
989 return "New articles by message-id follow";
990 case OK_NEWGROUPS:/* 231 */
991 return "New newsgroups follow";
992 case OK_XFERED: /* 235 */
993 return "Article transferred successfully";
994 case OK_POSTED: /* 240 */
995 return "Article posted successfully";
996 case OK_AUTHSYS: /* 280 */
997 return "Authorization system ok";
998 case OK_AUTH: /* 281 */
999 return "Authorization (user/pass) ok";
1000 case OK_XGTITLE: /* 282 */
1001 return "Ok, XGTITLE info follows";
1002 case OK_XOVER: /* 224 */
1003 return "ok -- overview data follows";
1004 case CONT_XFER: /* 335 */
1005 return "Continue to send article";
1006 case CONT_POST: /* 340 */
1007 return "Continue to post article";
1008 case NEED_AUTHINFO:/* 380 */
1009 return "authorization is required";
1010 case NEED_AUTHDATA:/* 381 */
1011 return "<type> authorization data required";
1012 case ERR_GOODBYE: /* 400 */
1013 return "Have to hang up for some reason";
1014 case ERR_NOGROUP: /* 411 */
1015 return "No such newsgroup";
1016 case ERR_NCING: /* 412 */
1017 return "Not currently in newsgroup";
1018 case ERR_NOCRNT: /* 420 */
1019 return "No current article selected";
1020 case ERR_NONEXT: /* 421 */
1021 return "No next article in this group";
1022 case ERR_NOPREV: /* 422 */
1023 return "No previous article in this group";
1024 case ERR_NOARTIG: /* 423 */
1025 return "No such article in this group";
1026 case ERR_NOART: /* 430 */
1027 return "No such article at all";
1028 case ERR_GOTIT: /* 435 */
1029 return "Already got that article, don't send";
1030 case ERR_XFERFAIL:/* 436 */
1031 return "Transfer failed";
1032 case ERR_XFERRJCT:/* 437 */
1033 return "Article rejected, don't resend";
1034 case ERR_NOPOST: /* 440 */
1035 return "Posting not allowed";
1036 case ERR_POSTFAIL:/* 441 */
1037 return "Posting failed";
1038 case ERR_NOAUTH: /* 480 */
1039 return "authorization required for command";
1040 case ERR_AUTHSYS: /* 481 */ /* ERR_XGTITLE has same code */
1041 return "Authorization system invalid";
1042 case ERR_AUTHREJ: /* 482 */
1043 return "Authorization data rejected";
1044 case ERR_COMMAND: /* 500 */
1045 return "Command not recognized";
1046 case ERR_CMDSYN: /* 501 */
1047 return "Command syntax error";
1048 case ERR_ACCESS: /* 502 */
1049 return "Access to server denied";
1050 case ERR_FAULT: /* 503 */
1051 return "Program fault, command not performed";
1052 case ERR_AUTHBAD: /* 580 */
1053 return "Authorization Failed";
1054 }
1055
1056 return "Unknown NNTP code";
1057 }
1058
1059