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