tin  2.4.4
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.4.4.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

nntplib.c
Go to the documentation of this file.
1 /*
2  * Project : tin - a Usenet reader
3  * Module : nntplib.c
4  * Author : S. Barber & I. Lea
5  * Created : 1991-01-12
6  * Updated : 2019-07-16
7  * Notes : NNTP client routines taken from clientlib.c 1.5.11 (1991-02-10)
8  * Copyright : (c) Copyright 1991-99 by Stan Barber & Iain Lea
9  * Permission is hereby granted to copy, reproduce, redistribute
10  * or otherwise use this software as long as: there is no
11  * monetary profit gained specifically from the use or
12  * reproduction or this software, it is not sold, rented,
13  * traded or otherwise marketed, and this copyright notice
14  * is included prominently in any copy made.
15  */
16 
17 
18 #ifndef TIN_H
19 # include "tin.h"
20 #endif /* !TIN_H */
21 #ifndef TCURSES_H
22 # include "tcurses.h"
23 #endif /* !TCURSES_H */
24 #ifndef TNNTP_H
25 # include "tnntp.h"
26 #endif /* !TNNTP_H */
27 
28 char *nntp_server = NULL;
29 #ifdef NO_POSTING
31 #else
33 #endif /* NO_POSTING */
34 
35 #ifdef NNTP_ABLE
36  /* Flag to show whether tin did reconnect in last get_server() */
37  t_bool reconnected_in_last_get_server = FALSE;
38  /* Flag used in LIST ACVTIVE loop */
39  t_bool did_reconnect = FALSE;
40 #endif /* NNTP_ABLE */
41 
42 static TCP *nntp_rd_fp = NULL;
43 
44 #ifdef NNTP_ABLE
45  static TCP *nntp_wr_fp = NULL;
46  /* Copy of last NNTP command sent, so we can retry it if needed */
47  static char last_put[NNTP_STRLEN];
48  static constext *xover_cmds = "XOVER";
49  static constext *xhdr_cmds = "XHDR";
50  /* Set so we don't reconnect just to QUIT */
51  static t_bool quitting = FALSE;
52 #endif /* NNTP_ABLE */
53 
54 /*
55  * local prototypes
56  */
57 #ifdef NNTP_ABLE
58  static int mode_reader(t_bool *sec);
59  static int reconnect(int retry);
60  static int server_init(char *machine, const char *cservice, unsigned short port, char *text, size_t mlen);
61  static void close_server(t_bool send_no_quit);
62  static void list_motd(void);
63 # ifdef INET6
64  static int get_tcp6_socket(char *machine, unsigned short port);
65 # else
66  static int get_tcp_socket(char *machine, char *service, unsigned short port);
67 # endif /* INET6 */
68 # ifdef DECNET
69  static int get_dnet_socket(char *machine, char *service);
70 # endif /* DECNET */
71 #endif /* NNTP_ABLE */
72 
73 
74 /*
75  * Return the actual fd in use for the nntp read-side socket
76  * This is a leak of internal state and should go away if possible
77  */
78 FILE *
80  FILE *fp)
81 {
82  return (fp == FAKE_NNTP_FP ? nntp_rd_fp : fp);
83 }
84 
85 
86 #if 0 /* unused */
87 FILE *
88 get_nntp_wr_fp(
89  FILE *fp)
90 {
91  return (fp == FAKE_NNTP_FP ? nntp_wr_fp : fp);
92 }
93 #endif /* 0 */
94 
95 /*
96  * getserverbyfile(file)
97  *
98  * Get the name of a server from a named file.
99  * Handle white space and comments.
100  * Use NNTPSERVER environment variable if set.
101  *
102  * Parameters: "file" is the name of the file to read.
103  *
104  * Returns: Pointer to static data area containing the
105  * first non-ws/comment line in the file.
106  * NULL on error (or lack of entry in file).
107  *
108  * Side effects: None.
109  */
110 char *
112  const char *file)
113 {
114  static char buf[256];
115 #ifdef NNTP_ABLE
116  FILE *fp;
117  char *cp;
118 # if !defined(HAVE_SETENV) && defined(HAVE_PUTENV)
119  char tmpbuf[256];
120  char *new_env;
121  static char *old_env = NULL;
122 # endif /* !HAVE_SETENV && HAVE_PUTENV */
123 #endif /* NNTP_ABLE */
124 
125  if (read_saved_news) {
126  STRCPY(buf, "reading saved news");
127  return buf;
128  }
129 
130  if (!read_news_via_nntp) {
131  STRCPY(buf, "local"); /* what if a server is named "local"? */
132  return buf;
133  }
134 
135 #ifdef NNTP_ABLE
137  get_nntpserver(buf, sizeof(buf), cmdline.nntpserver);
138 # ifdef HAVE_SETENV
139  setenv("NNTPSERVER", buf, 1);
140 # else
141 # ifdef HAVE_PUTENV
142  snprintf(tmpbuf, sizeof(tmpbuf), "NNTPSERVER=%s", buf);
143  new_env = my_strdup(tmpbuf);
144  putenv(new_env);
145  FreeIfNeeded(old_env);
146  old_env = new_env; /* we are 'leaking' the last malloced mem at exit here */
147 # endif /* HAVE_PUTENV */
148 # endif /* HAVE_SETENV */
149  return buf;
150  }
151 
152  if ((cp = getenv("NNTPSERVER")) != NULL) {
153  get_nntpserver(buf, sizeof(buf), cp);
154  return buf;
155  }
156 
157  if (file == NULL)
158  return NULL;
159 
160  if ((fp = fopen(file, "r")) != NULL) {
161 
162  while (fgets(buf, (int) sizeof(buf), fp) != NULL) {
163  if (*buf == '\n' || *buf == '#')
164  continue;
165 
166  if ((cp = strrchr(buf, '\n')) != NULL)
167  *cp = '\0';
168 
169  (void) fclose(fp);
170  return buf;
171  }
172  (void) fclose(fp);
173  }
174 
175 # ifdef NNTP_DEFAULT_SERVER
176  if (*(NNTP_DEFAULT_SERVER))
177  return strcpy(buf, NNTP_DEFAULT_SERVER);
178 # endif /* NNTP_DEFAULT_SERVER */
179 
180 #endif /* NNTP_ABLE */
181  return NULL; /* No entry */
182 }
183 
184 
185 /*
186  * server_init Get a connection to the remote server.
187  *
188  * Parameters: "machine" is the machine to connect to.
189  * "service" is the service to connect to on the machine.
190  * "port" is the service port to connect to.
191  *
192  * Returns: server's initial response code
193  * or -errno
194  *
195  * Side effects: Connects to server.
196  * "nntp_rd_fp" and "nntp_wr_fp" are fp's
197  * for reading and writing to server.
198  * "text" is updated to contain the rest of response string from server
199  */
200 #ifdef NNTP_ABLE
201 static int
202 server_init(
203  char *machine,
204  const char *cservice, /* usually a literal */
205  unsigned short port,
206  char *text,
207  size_t mlen)
208 {
209 # ifndef INET6
210  char temp[256];
211  char *service = strncpy(temp, cservice, sizeof(temp) - 1); /* ...calls non-const funcs */
212 # endif /* !INET6 */
213  int sockt_rd, sockt_wr;
214 
215 # ifdef DECNET
216  char *cp;
217 
218  cp = strchr(machine, ':');
219 
220  if (cp && cp[1] == ':') {
221  *cp = '\0';
222  sockt_rd = get_dnet_socket(machine, service);
223  } else
224  sockt_rd = get_tcp_socket(machine, service, port);
225 # else
226 # ifdef INET6
227  sockt_rd = get_tcp6_socket(machine, port);
228 # else
229  sockt_rd = get_tcp_socket(machine, service, port);
230 # endif /* INET6 */
231 # endif /* DECNET */
232 
233  if (sockt_rd < 0)
234  return sockt_rd;
235 
236  /*
237  * Now we'll make file pointers (i.e., buffered I/O) out of
238  * the socket file descriptor. Note that we can't just
239  * open a fp for reading and writing -- we have to open
240  * up two separate fp's, one for reading, one for writing.
241  */
242 
243  if ((nntp_rd_fp = (TCP *) s_fdopen(sockt_rd, "r")) == NULL) {
244  perror("server_init: fdopen() #1");
245  s_close(sockt_rd);
246  return -errno;
247  }
248 
249  if ((sockt_wr = s_dup(sockt_rd)) < 0) {
250  perror("server_init: dup()");
252  nntp_rd_fp = NULL;
253  return -errno;
254  }
255 
256 # ifdef TLI /* Transport Level Interface */
257  if (t_sync(sockt_rd) < 0) { /* Sync up new fd with TLI */
258  t_error("server_init: t_sync()");
260  nntp_rd_fp = NULL;
261  return -EPROTO;
262  }
263 # else
264  if ((nntp_wr_fp = (TCP *) s_fdopen(sockt_wr, "w")) == NULL) {
265  perror("server_init: fdopen() #2");
266  s_close(sockt_wr);
268  nntp_rd_fp = NULL;
269  return -errno;
270  }
271 # endif /* TLI */
272 
273  last_put[0] = '\0'; /* no retries in get_respcode */
274  /*
275  * Now get the server's signon message
276  */
277  return (get_respcode(text, mlen));
278 }
279 #endif /* NNTP_ABLE */
280 
281 
282 /*
283  * get_tcp_socket -- get us a socket connected to the specified server.
284  *
285  * Parameters: "machine" is the machine the server is running on.
286  * "service" is the service to connect to on the server.
287  * "port" is the port to connect to on the server.
288  *
289  * Returns: Socket connected to the server if all if ok
290  * EPROTO for internal socket errors
291  * EHOSTUNREACH if specified NNTP port/server can't be located
292  * errno any valid errno value on connection
293  *
294  * Side effects: Connects to server.
295  *
296  * Errors: Returned & printed via perror.
297  */
298 #if defined(NNTP_ABLE) && !defined(INET6)
299 static int
300 get_tcp_socket(
301  char *machine, /* remote host */
302  char *service, /* nttp/smtp etc. */
303  unsigned short port) /* tcp port number */
304 {
305  int s = -1;
306  int save_errno = 0;
307  struct sockaddr_in sock_in;
308 # ifdef TLI /* Transport Level Interface */
309  char device[20];
310  char *env_device;
311  extern int t_errno;
312  extern struct hostent *gethostbyname();
313  struct hostent *hp;
314  struct t_call *callptr;
315 
316  /*
317  * Create a TCP transport endpoint.
318  */
319  if ((env_device = getenv("DEV_TCP")) != NULL) /* SCO uses DEV_TCP, most other OS use /dev/tcp */
320  STRCPY(device, env_device);
321  else
322  strcpy(device, "/dev/tcp");
323 
324  if ((s = t_open(device, O_RDWR, (struct t_info *) 0)) < 0) {
325  t_error(txt_error_topen);
326  return -EPROTO;
327  }
328  if (t_bind(s, (struct t_bind *) 0, (struct t_bind *) 0) < 0) {
329  t_error("t_bind");
330  t_close(s);
331  return -EPROTO;
332  }
333  memset((char *) &sock_in, '\0', sizeof(sock_in));
334  sock_in.sin_family = AF_INET;
335  sock_in.sin_port = htons(port);
336 
337  if (!isdigit((unsigned char) *machine)
338 # ifdef HAVE_INET_ATON
339  || !inet_aton(machine, &sock_in)
340 # else
341 # ifdef HAVE_INET_ADDR
342  || (long) (sock_in.sin_addr.s_addr = inet_addr(machine)) == INADDR_NONE
343 # endif /* HAVE_INET_ADDR */
344 # endif /* HAVE_INET_ATON */
345  ) {
346  if ((hp = gethostbyname(machine)) == NULL) {
347  my_fprintf(stderr, _(txt_gethostbyname), "gethostbyname() ", machine);
348  t_close(s);
349  return -EHOSTUNREACH;
350  }
351  memcpy((char *) &sock_in.sin_addr, hp->h_addr_list[0], hp->h_length);
352  }
353 
354  /*
355  * Allocate a t_call structure and initialize it.
356  * Let t_alloc() initialize the addr structure of the t_call structure.
357  */
358  if ((callptr = (struct t_call *) t_alloc(s, T_CALL, T_ADDR)) == NULL) {
359  t_error("t_alloc");
360  t_close(s);
361  return -EPROTO;
362  }
363 
364  callptr->addr.maxlen = sizeof(sock_in);
365  callptr->addr.len = sizeof(sock_in);
366  callptr->addr.buf = (char *) &sock_in;
367  callptr->opt.len = 0; /* no options */
368  callptr->udata.len = 0; /* no user data with connect */
369 
370  /*
371  * Connect to the server.
372  */
373  if (t_connect(s, callptr, (struct t_call *) 0) < 0) {
374  save_errno = t_errno;
375  if (save_errno == TLOOK)
376  fprintf(stderr, "%s", _(txt_error_server_unavailable));
377  else
378  t_error("t_connect");
379  t_free((char *) callptr, T_CALL);
380  t_close(s);
381  return -save_errno;
382  }
383 
384  /*
385  * Now replace the timod module with the tirdwr module so that
386  * standard read() and write() system calls can be used on the
387  * descriptor.
388  */
389 
390  t_free((char *) callptr, T_CALL);
391 
392  if (ioctl(s, I_POP, NULL) < 0) {
393  perror("I_POP(timod)");
394  t_close(s);
395  return -EPROTO;
396  }
397 
398  if (ioctl(s, I_PUSH, "tirdwr") < 0) {
399  perror("I_PUSH(tirdwr)");
400  t_close(s);
401  return -EPROTO;
402  }
403 
404 # else
405 # ifndef EXCELAN
406  struct servent *sp;
407  struct hostent *hp;
408 # ifdef h_addr
409  int x = 0;
410  char **cp;
411 # endif /* h_addr */
412 # ifdef HAVE_HOSTENT_H_ADDR_LIST
413  static char *alist[2] = {0, 0};
414 # endif /* HAVE_HOSTENT_H_ADDR_LIST */
415  static struct hostent def;
416  static struct in_addr defaddr;
417  static char namebuf[256];
418 
419 # ifdef HAVE_GETSERVBYNAME
420  if ((sp = (struct servent *) getservbyname(service, "tcp")) == NULL) {
421  my_fprintf(stderr, _(txt_error_unknown_service), service);
422  return -EHOSTUNREACH;
423  }
424 # else
425  sp = my_malloc(sizeof(struct servent));
426  sp->s_port = htons(IPPORT_NNTP);
427 # endif /* HAVE_GETSERVBYNAME */
428 
429  /* If not a raw ip address, try nameserver */
430  if (!isdigit((unsigned char) *machine)
431 # ifdef HAVE_INET_ATON
432  || !inet_aton(machine, &defaddr)
433 # else
434 # ifdef HAVE_INET_ADDR
435  || (long) (defaddr.s_addr = (long) inet_addr(machine)) == -1
436 # endif /* HAVE_INET_ADDR */
437 # endif /* HAVE_INET_ATON */
438  )
439  {
440  hp = gethostbyname(machine);
441  } else {
442  /* Raw ip address, fake */
443  STRCPY(namebuf, machine);
444  def.h_name = (char *) namebuf;
445 # ifdef HAVE_HOSTENT_H_ADDR_LIST
446  def.h_addr_list = alist;
447  def.h_addr_list[0] = (char *) &defaddr;
448 # else
449  def.h_addr = (char *) &defaddr;
450 # endif /* HAVE_HOSTENT_H_ADDR_LIST */
451  def.h_length = sizeof(struct in_addr);
452  def.h_addrtype = AF_INET;
453  def.h_aliases = 0;
454  hp = &def;
455  }
456 
457  if (hp == NULL) {
458  my_fprintf(stderr, _(txt_gethostbyname), "\n", machine);
459  return -EHOSTUNREACH;
460  }
461 
462  memset((char *) &sock_in, '\0', sizeof(sock_in));
463  sock_in.sin_family = hp->h_addrtype;
464  sock_in.sin_port = htons(port);
465 /* sock_in.sin_port = sp->s_port; */
466 # else
467  memset((char *) &sock_in, '\0', sizeof(sock_in));
468  sock_in.sin_family = AF_INET;
469 # endif /* !EXCELAN */
470 
471  /*
472  * The following is kinda gross. The name server under 4.3
473  * returns a list of addresses, each of which should be tried
474  * in turn if the previous one fails. However, 4.2 hostent
475  * structure doesn't have this list of addresses.
476  * Under 4.3, h_addr is a #define to h_addr_list[0].
477  * We use this to figure out whether to include the NS specific
478  * code...
479  */
480 
481 # ifdef h_addr
482  /*
483  * Get a socket and initiate connection -- use multiple addresses
484  */
485  for (cp = hp->h_addr_list; cp && *cp; cp++) {
486 # if defined(__hpux) && defined(SVR4)
487  unsigned long socksize, socksizelen;
488 # endif /* __hpux && SVR4 */
489 
490  if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
491  perror("socket");
492  return -errno;
493  }
494 
495  memcpy((char *) &sock_in.sin_addr, *cp, hp->h_length);
496 
497 # ifdef HAVE_INET_NTOA
498  if (x < 0)
499  my_fprintf(stderr, _(txt_trying), (char *) inet_ntoa(sock_in.sin_addr));
500 # endif /* HAVE_INET_NTOA */
501 
502 # if defined(__hpux) && defined(SVR4) /* recommended by raj@cup.hp.com */
503 # define HPSOCKSIZE 0x8000
504  getsockopt(s, SOL_SOCKET, SO_SNDBUF, /* (caddr_t) */ &socksize, /* (caddr_t) */ &socksizelen);
505  if (socksize < HPSOCKSIZE) {
506  socksize = HPSOCKSIZE;
507  setsockopt(s, SOL_SOCKET, SO_SNDBUF, /* (caddr_t) */ &socksize, sizeof(socksize));
508  }
509  socksize = 0;
510  socksizelen = sizeof(socksize);
511  getsockopt(s, SOL_SOCKET, SO_RCVBUF, /* (caddr_t) */ &socksize, /* (caddr_t) */ &socksizelen);
512  if (socksize < HPSOCKSIZE) {
513  socksize = HPSOCKSIZE;
514  setsockopt(s, SOL_SOCKET, SO_RCVBUF, /* (caddr_t) */ &socksize, sizeof(socksize));
515  }
516 # endif /* __hpux && SVR4 */
517 
518  if ((x = connect(s, (struct sockaddr *) &sock_in, sizeof(sock_in))) == 0)
519  break;
520 
521  save_errno = errno; /* Keep for later */
522 # ifdef HAVE_INET_NTOA
523  my_fprintf(stderr, _(txt_connection_to), (char *) inet_ntoa(sock_in.sin_addr));
524  perror("");
525 # endif /* HAVE_INET_NTOA */
526  (void) s_close(s);
527  }
528 
529  if (x < 0) {
530  my_fprintf(stderr, "%s", _(txt_giving_up));
531  return -save_errno; /* Return the last errno we got */
532  }
533 # else
534 
535 # ifdef EXCELAN
536  if ((s = socket(SOCK_STREAM, (struct sockproto *) NULL, &sock_in, SO_KEEPALIVE)) < 0) {
537  perror("socket");
538  return -errno;
539  }
540 
541  /* set up addr for the connect */
542  memset((char *) &sock_in, '\0', sizeof(sock_in));
543  sock_in.sin_family = AF_INET;
544  sock_in.sin_port = htons(IPPORT_NNTP);
545 
546  if ((sock_in.sin_addr.s_addr = rhost(&machine)) == -1) {
547  my_fprintf(stderr, _(txt_gethostbyname), "\n", machine);
548  return -1;
549  }
550 
551  /* And connect */
552  if (connect(s, (struct sockaddr *) &sock_in) < 0) {
553  save_errno = errno;
554  perror("connect");
555  (void) s_close(s);
556  return -save_errno;
557  }
558 
559 # else
560  if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
561  perror("socket");
562  return -errno;
563  }
564 
565  /* And then connect */
566 # ifdef HAVE_HOSTENT_H_ADDR_LIST
567  memcpy((char *) &sock_in.sin_addr, hp->h_addr_list[0], hp->h_length);
568 # else
569  memcpy((char *) &sock_in.sin_addr, hp->h_addr, hp->h_length);
570 # endif /* HAVE_HOSTENT_H_ADDR_LIST */
571  if (connect(s, (struct sockaddr *) &sock_in, sizeof(sock_in)) < 0) {
572  save_errno = errno;
573  perror("connect");
574  (void) s_close(s);
575  return -save_errno;
576  }
577 
578 # endif /* EXCELAN */
579 # endif /* h_addr */
580 # endif /* TLI */
581  return s;
582 }
583 #endif /* NNTP_ABLE && !INET6 */
584 
585 
586 #if defined(NNTP_ABLE) && defined(INET6)
587 /*
588  * get_tcp6_socket -- get us a socket connected to the server.
589  *
590  * Parameters: "machine" is the machine the server is running on.
591  * "port" is the portnumber to connect to.
592  *
593  * Returns: Socket connected to the news server if
594  * all is ok, else -1 on error.
595  *
596  * Side effects: Connects to server via IPv4 or IPv6.
597  *
598  * Errors: Printed via my_fprintf.
599  */
600 static int
601 get_tcp6_socket(
602  char *machine,
603  unsigned short port)
604 {
605  char mymachine[MAXHOSTNAMELEN + 1];
606  char myport[12];
607  int s = -1, err, ec = 0, es = 0;
608  struct addrinfo hints, *res, *res0;
609 
610  snprintf(mymachine, sizeof(mymachine), "%s", machine);
611  snprintf(myport, sizeof(myport), "%u", port);
612 
613 /* just in case */
614 # ifdef AF_UNSPEC
615 # define ADDRFAM AF_UNSPEC
616 # else
617 # ifdef PF_UNSPEC
618 # define ADDRFAM PF_UNSPEC
619 # else
620 # define ADDRFAM AF_INET
621 # endif /* PF_UNSPEC */
622 # endif /* AF_UNSPEC */
623  memset(&hints, 0, sizeof(hints));
624 /* hints.ai_flags = AI_CANONNAME; */
625  hints.ai_family = (force_ipv4 ? AF_INET : (force_ipv6 ? AF_INET6 : ADDRFAM));
626  hints.ai_socktype = SOCK_STREAM;
627  res0 = (struct addrinfo *) 0;
628  if ((err = getaddrinfo(mymachine, myport, &hints, &res0))) {
629  my_fprintf(stderr, "\ngetaddrinfo: %s\n", gai_strerror(err));
630  return -1;
631  }
632  err = -1;
633  for (res = res0; res; res = res->ai_next) {
634  if ((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
635  es = errno;
636  continue;
637  }
638  if (connect(s, res->ai_addr, res->ai_addrlen) != 0) {
639  ec = errno;
640  s_close(s);
641  } else {
642  es = ec = err = 0;
643  break;
644  }
645  }
646  if (res0 != NULL)
647  freeaddrinfo(res0);
648  if (err < 0) {
649  my_fprintf(stderr, "%s", _(txt_error_socket_or_connect_problem));
650  if (es)
651  my_fprintf(stderr, "\tsocket(2): %s\n", strerror(es));
652  if (ec)
653  my_fprintf(stderr, "\tconnect(2): %s\n", strerror(ec));
654  sleep(3);
655  return -1;
656  }
657  return s;
658 }
659 #endif /* NNTP_ABLE && INET6 */
660 
661 
662 #ifdef DECNET
663 /*
664  * get_dnet_socket -- get us a socket connected to the server.
665  *
666  * Parameters: "machine" is the machine the server is running on.
667  * "service" is the name of the service to connect to.
668  *
669  * Returns: Socket connected to the news server if
670  * all is ok, else -1 on error.
671  *
672  * Side effects: Connects to server.
673  *
674  * Errors: Printed via nerror.
675  */
676 static int
677 get_dnet_socket(
678  char *machine,
679  char *service)
680 {
681 # ifdef NNTP_ABLE
682  int s, area, node;
683  struct sockaddr_dn sdn;
684  struct nodeent *getnodebyname(), *np;
685 
686  memset((char *) &sdn, '\0', sizeof(sdn));
687 
688  switch (s = sscanf(machine, "%d%*[.]%d", &area, &node)) {
689  case 1:
690  node = area;
691  area = 0;
692  /* FALLTHROUGH */
693  case 2:
694  node += area * 1024;
695  sdn.sdn_add.a_len = 2;
696  sdn.sdn_family = AF_DECnet;
697  sdn.sdn_add.a_addr[0] = node % 256;
698  sdn.sdn_add.a_addr[1] = node / 256;
699  break;
700 
701  default:
702  if ((np = getnodebyname(machine)) == NULL) {
703  my_fprintf(stderr, _(txt_gethostbyname), "", machine);
704  return -1;
705  } else {
706  memcpy((char *) sdn.sdn_add.a_addr, np->n_addr, np->n_length);
707  sdn.sdn_add.a_len = np->n_length;
708  sdn.sdn_family = np->n_addrtype;
709  }
710  break;
711  }
712  sdn.sdn_objnum = 0;
713  sdn.sdn_flags = 0;
714  sdn.sdn_objnamel = strlen("NNTP");
715  memcpy(&sdn.sdn_objname[0], "NNTP", sdn.sdn_objnamel);
716 
717  if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) {
718  nerror("socket");
719  return -1;
720  }
721 
722  /* And then connect */
723 
724  if (connect(s, (struct sockaddr *) &sdn, sizeof(sdn)) < 0) {
725  nerror("connect");
726  s_close(s);
727  return -1;
728  }
729 
730  return s;
731 # else
732  return -1;
733 # endif /* NNTP_ABLE */
734 }
735 #endif /* DECNET */
736 
737 
738 /*----------------------------------------------------------------------
739  * Ideally the code after this point should be the only interface to the
740  * NNTP internals...
741  */
742 
743 /*
744  * u_put_server -- send data to the server. Do not flush output.
745  */
746 #ifdef NNTP_ABLE
747 void
748 u_put_server(
749  const char *string)
750 {
751  s_puts(string, nntp_wr_fp);
752 # ifdef DEBUG
753  if (debug & DEBUG_NNTP) {
754  if (strcmp(string, "\r\n"))
755  debug_print_file("NNTP", ">>>%s%s", logtime(), string);
756  }
757 # endif /* DEBUG */
758 }
759 
760 
761 /*
762  * Send 'string' to the NNTP server, terminating it with CR and LF, as per
763  * ARPA standard.
764  *
765  * Returns: Nothing.
766  *
767  * Side effects: Talks to the server.
768  * Closes connection if things are not right.
769  *
770  * Note: This routine flushes the buffer each time it is called. For large
771  * transmissions (i.e., posting news) don't use it. Instead, do the
772  * fprintf's yourself, and then a final fflush.
773  * Only cache commands, don't cache data transmissions.
774  */
775 void
776 put_server(
777  const char *string)
778 {
779  if (*string && strlen(string)) {
780  DEBUG_IO((stderr, "put_server(%s)\n", string));
781  s_puts(string, nntp_wr_fp);
782  s_puts("\r\n", nntp_wr_fp);
783 # ifdef DEBUG
784  if (debug & DEBUG_NNTP)
785  debug_print_file("NNTP", ">>>%s%s", logtime(), string);
786 # endif /* DEBUG */
787  /*
788  * remember the last command we wrote to be able to resend it after a
789  * reconnect. reconnection is handled by get_server()
790  *
791  * don't cache "LIST [ACTIVE|COUNTS|NEWSGROUPS] something" as we
792  * would need to resend all of them but we remember just the last
793  * one. we cache "LIST cmd." instead, this will slow down things, but
794  * that's ok on reconnect.
795  */
796  if (strcmp(last_put, string))
797  STRCPY(last_put, string);
798  if (!strncmp(string, "LIST ACTIVE ", 12))
799  last_put[11] = '\0'; /* "LIST ACTIVE" */
800  else if (!strncmp(string, "LIST COUNTS ", 12))
801  last_put[11] = '\0'; /* "LIST COUNTS" */
802  else if (!strncmp(string, "LIST NEWSGROUPS ", 16))
803  last_put[15] = '\0'; /* "LIST NEWSGROUPS" */
804  }
805  (void) s_flush(nntp_wr_fp);
806 }
807 
808 
809 /*
810  * Reconnect to server after a timeout, reissue last command to
811  * get us back into the pre-timeout state
812  */
813 static int
814 reconnect(
815  int retry)
816 {
817  char buf[NNTP_STRLEN];
818  int save_signal_context = signal_context;
819 
820  /*
821  * Tear down current connection
822  * Close the NNTP connection with prejudice
823  */
824  if (nntp_wr_fp)
825  s_fclose(nntp_wr_fp);
826  if (nntp_rd_fp)
828  nntp_rd_fp = nntp_wr_fp = NULL;
829 
830  if (!tinrc.auto_reconnect)
831  ring_bell();
832 
833  DEBUG_IO((stderr, _("\nServer timed out, trying reconnect # %d\n"), retry));
834 
835  /*
836  * set signal_context temporary to cReconnect to avoid trouble when receiving
837  * SIGWINCH while being in prompt_yn()
838  */
840 
841  /*
842  * Exit tin if there are no more tries or if the user says no to reconnect.
843  * The exit code stops tin from trying to disconnect again - the connection
844  * is already dead
845  */
846  if (retry > NNTP_TRY_RECONNECT || (!tinrc.auto_reconnect && prompt_yn(_(txt_reconnect_to_news_server), TRUE) != 1)) {
847  if (!strcmp("POST", last_put)) {
852  }
853  if (retry > NNTP_TRY_RECONNECT) {
854 # ifdef DEBUG
855  /* TODO: -> lang.c */
856  if ((debug & DEBUG_NNTP) && verbose > 1)
857  debug_print_file("NNTP", "reconnect(%d) limit %d reached, giving up.", retry, NNTP_TRY_RECONNECT);
858 # endif /* DEBUG */
859  }
860  tin_done(NNTP_ERROR_EXIT, _("NNTP connection error. Exiting...")); /* user said no to reconnect or no more retries */
861  }
862 
863  /* reset signal_context */
864  signal_context = save_signal_context;
865 
866  clear_message();
867  strcpy(buf, last_put); /* Keep copy here, it will be clobbered a lot otherwise */
868 
869  if (!nntp_open()) {
870  /* Re-establish our current group and resend last command */
871  if (curr_group != NULL) {
872  DEBUG_IO((stderr, _("Rejoin current group\n")));
873  snprintf(last_put, sizeof(last_put), "GROUP %s", curr_group->name);
874  put_server(last_put);
875  s_gets(last_put, NNTP_STRLEN, nntp_rd_fp);
876 # ifdef DEBUG
877  if (debug & DEBUG_NNTP)
878  debug_print_file("NNTP", "<<<%s%s", logtime(), last_put);
879 # endif /* DEBUG */
880  DEBUG_IO((stderr, _("Read (%s)\n"), last_put));
881  }
882  DEBUG_IO((stderr, _("Resend last command (%s)\n"), buf));
883  put_server(buf);
884  did_reconnect = TRUE;
885  retry = NNTP_TRY_RECONNECT;
886  }
887 
888  return retry;
889 }
890 
891 
892 /*
893  * Read a line of data from the NNTP socket. If something gives, do reconnect
894  *
895  * Parameters: "string" has the buffer space for the line received.
896  * "size" is maximum size of the buffer to read.
897  *
898  * Returns: NULL on end of stream, or a line of data.
899  * Basically, we try to act like fgets() on an NNTP stream.
900  *
901  * Side effects: Talks to server, changes contents of "string".
902  * Reopens connection when necessary and requested.
903  * Exits via tin_done() if fatal error occurs.
904  */
905 char *
906 get_server(
907  char *string,
908  int size)
909 {
910  static int retry_cnt = 0;
911 
912  reconnected_in_last_get_server = FALSE;
913  errno = 0;
914 
915  /*
916  * NULL socket reads indicates socket has closed. Try a few times more
917  *
918  * Leave the s_gets() after a timeout for these cases:
919  * -some servers do not close the connection but simply do not send any
920  * response data
921  * -the network connection went down
922  */
923 # if defined(HAVE_ALARM) && defined(SIGALRM)
924  alarm(tinrc.nntp_read_timeout_secs);
925 # endif /* HAVE_ALARM && SIGALRM */
926  while (nntp_rd_fp == NULL || s_gets(string, size, nntp_rd_fp) == NULL) {
927  if (errno == EINTR) {
928  errno = 0;
929 # if defined(HAVE_ALARM) && defined(SIGALRM)
930  alarm(tinrc.nntp_read_timeout_secs); /* Restart the timer */
931 # endif /* HAVE_ALARM && SIGALRM */
932  continue;
933  }
934 # if defined(HAVE_ALARM) && defined(SIGALRM)
935  alarm(0);
936 # endif /* HAVE_ALARM && SIGALRM */
937  if (quitting) /* Don't bother to reconnect */
938  tin_done(NNTP_ERROR_EXIT, NULL); /* And don't try to disconnect again! */
939 
940 # ifdef DEBUG
941  if (errno != 0 && errno != EINTR) /* Will only confuse end users */
942  perror_message("get_server()");
943 # endif /* DEBUG */
944 
945  /*
946  * Reconnect only if command was not "QUIT" anyway (in which case a
947  * reconnection would be useless because the connection will be
948  * closed immediately). Also prevents tin from asking to reconnect
949  * when user is quitting tin if tinrc.auto_reconnect is false.
950  */
951  if (strcmp(last_put, "QUIT")) {
952  /*
953  * Typhoon v2.1.1.363 colses the connection right after an unknown
954  * command, (i.e. CAPABILITIES) so we avoid the reissue it on a
955  * reconnect if it was the last command.
956  */
957  if (!strcmp(last_put, "CAPABILITIES")) {
958  strcpy(last_put, "MODE READER");
960  }
961  retry_cnt = reconnect(++retry_cnt); /* Will abort when out of tries */
962  reconnected_in_last_get_server = TRUE;
963  } else {
964  /*
965  * Use standard NNTP closing message and response code if user is
966  * quitting tin and leave loop.
967  */
968  strncpy(string, _(txt_nntp_ok_goodbye), size - 3);
969  strcat(string, "\r\n"); /* tin_fgets() needs CRLF */
970  break;
971  }
972  }
973 # if defined(HAVE_ALARM) && defined(SIGALRM)
974  alarm(0);
975 # endif /* HAVE_ALARM && SIGALRM */
976  retry_cnt = 0;
977  return string;
978 }
979 
980 
981 /*
982  * Send "QUIT" command and close the connection to the server
983  *
984  * Side effects: Closes the connection to the server.
985  * You can't use "put_server" or "get_server" after this
986  * routine is called.
987  *
988  * TODO: remember servers response string and if it contains anything else
989  * than just "." (i.e. transfer statistics) present it to the user?
990  *
991  */
992 static void
993 close_server(
994  t_bool send_no_quit)
995 {
996  if (!send_no_quit && nntp_wr_fp && nntp_rd_fp) {
997 
998  if (!batch_mode || verbose) {
999  char *msg;
1000 
1001  msg = strunc(_(txt_disconnecting), cCOLS - 1);
1002  my_fputs(msg, stdout);
1003  my_fputc('\n', stdout);
1004  free(msg);
1005  }
1006  nntp_command("QUIT", OK_GOODBYE, NULL, 0);
1007  quitting = TRUE; /* Don't reconnect just for this */
1008  }
1009  if (nntp_wr_fp)
1010  (void) s_fclose(nntp_wr_fp);
1011  if (nntp_rd_fp)
1012  (void) s_fclose(nntp_rd_fp);
1013  s_end();
1014  nntp_wr_fp = nntp_rd_fp = NULL;
1015 }
1016 
1017 
1018 /*
1019  * Try and use CAPABILITIES here. Get this list before issuing other NNTP
1020  * commands because the correct methods may be mentioned in the list of
1021  * extensions.
1022  *
1023  * Sets up: t_capabilities nntp_caps
1024  */
1025 int
1026 check_extensions(
1027  int rvl)
1028 {
1029  char *d;
1030  char *ptr;
1031  char buf[NNTP_STRLEN];
1032  int i;
1033  int ret = 0;
1034 
1035  buf[0] = '\0';
1036 
1037  /* rvl > 0 = manually send "CAPABILITIES" to avoid endless AUTH loop */
1038  i = rvl ? rvl : new_nntp_command("CAPABILITIES", INF_CAPABILITIES, buf, sizeof(buf));
1039  switch (i) {
1040  case INF_CAPABILITIES:
1041  /* clear capabilities */
1043  nntp_caps.version = 0;
1046  nntp_caps.post = FALSE;
1060  nntp_caps.xpat = TRUE; /* toggles to false if fails, INN > 2.7.0 announces it */
1061  nntp_caps.hdr = FALSE;
1062  nntp_caps.hdr_cmd = NULL;
1063  nntp_caps.over = FALSE;
1065  nntp_caps.over_cmd = NULL;
1075 # if 0
1076  nntp_caps.streaming = FALSE;
1077  nntp_caps.ihave = FALSE;
1078 # endif /* 0 */
1079 # ifndef BROKEN_LISTGROUP
1081 # else
1083 # endif /* !BROKEN_LISTGROUP */
1084 
1085  while ((ptr = tin_fgets(FAKE_NNTP_FP, FALSE)) != NULL) {
1086 # ifdef DEBUG
1087  if (debug & DEBUG_NNTP)
1088  debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1089 # endif /* DEBUG */
1090  /* look for version number(s) */
1092  if (!strncasecmp(ptr, "VERSION", 7)) {
1093  d = ptr + 7;
1094  d = strpbrk(d, " \t");
1095  while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1096  d++;
1097  nntp_caps.version = (unsigned int) atoi(d);
1098  d = strpbrk(d, " \t");
1099  }
1100  }
1101  }
1102  /* we currently only support CAPABILITIES VERSION 2 */
1103  if (nntp_caps.version == 2) {
1104  /*
1105  * check for LIST variants
1106  */
1107  if (!strncasecmp(ptr, "LIST", 4)) {
1108  d = ptr + 4;
1109  d = strpbrk(d, " \t");
1110  while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1111  d++;
1112  if (!strncasecmp(d, "ACTIVE.TIMES", 12))
1114  else if (!strncasecmp(d, "ACTIVE", 6))
1116  else if (!strncasecmp(d, "DISTRIB.PATS", 12))
1118  else if (!strncasecmp(d, "DISTRIBUTIONS", 13)) /* RFC 6048 */
1120  else if (!strncasecmp(d, "HEADERS", 7))
1121  nntp_caps.list_headers = TRUE; /* HDR requires LIST HEADERS, but not vice versa */
1122  else if (!strncasecmp(d, "NEWSGROUPS", 10))
1124  else if (!strncasecmp(d, "OVERVIEW.FMT", 12)) /* OVER requires OVERVIEW.FMT, but not vice versa */
1126  else if (!strncasecmp(d, "MOTD", 4)) /* RFC 6048 */
1128  else if (!strncasecmp(d, "SUBSCRIPTIONS", 13)) /* RFC 6048 */
1130  else if (!strncasecmp(d, "MODERATORS", 10)) /* RFC 6048 */
1132  else if (!strncasecmp(d, "COUNTS", 6)) /* RFC 6048 */
1134  d = strpbrk(d, " \t");
1135  }
1136  } else if (!strncasecmp(ptr, "IMPLEMENTATION", 14)) {
1138  nntp_caps.implementation = my_strdup(ptr + 14);
1140  } else if (!strcasecmp(ptr, "MODE-READER")) {
1141  if (!nntp_caps.reader)
1143  } else if (!strcasecmp(ptr, "READER")) { /* if we saw READER, "LIST ACTIVE" and "LIST NEWSGROUPS" must be implemented */
1144  nntp_caps.reader = TRUE;
1148  } else if (!strcasecmp(ptr, "POST"))
1149  nntp_caps.post = TRUE;
1150  else if (!strcasecmp(ptr, "NEWNEWS"))
1152  else if (!strcasecmp(ptr, "XPAT")) /* extension, RFC 2980 */
1153  nntp_caps.xpat = TRUE;
1154  else if (!strcasecmp(ptr, "STARTTLS"))
1156  /*
1157  * NOTE: if we saw OVER, LIST OVERVIEW.FMT _must_ be implemented
1158  */
1159  else if (!strncasecmp(ptr, &xover_cmds[1], strlen(&xover_cmds[1]))) {
1161  nntp_caps.over_cmd = &xover_cmds[1];
1162  d = ptr + strlen(&xover_cmds[1]);
1163  d = strpbrk(d, " \t");
1164  while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1165  d++;
1166  if (!strcasecmp(d, "MSGID"))
1168  d = strpbrk(d, " \t");
1169  }
1170  }
1171  /*
1172  * NOTE: if we saw HDR, LIST HEADERS _must_ be implemented
1173  */
1174  else if (!strncasecmp(ptr, &xhdr_cmds[1], strlen(&xhdr_cmds[1]))) {
1175  nntp_caps.hdr_cmd = &xhdr_cmds[1];
1179  } else if (!strncasecmp(ptr, "AUTHINFO", 8)) {
1180  d = ptr + 8;
1181  d = strpbrk(d, " \t");
1182  if (d == NULL) /* AUTHINFO without args */
1184  while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1185  d++;
1186  if (!strncasecmp(d, "USER", 4))
1188  if (!strncasecmp(d, "SASL", 4))
1190  d = strpbrk(d, " \t");
1191  }
1192  } else if (!strncasecmp(ptr, "SASL", 4)) {
1193  d = ptr + 4;
1194  d = strpbrk(d, " \t");
1195  while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1196  d++;
1197  if (!strncasecmp(d, "CRAM-MD5", 8)) { /* RFC 2195 */
1200  }
1201  if (!strncasecmp(d, "DIGEST-MD5", 10)) { /* RFC 2831 */
1204  }
1205  if (!strncasecmp(d, "PLAIN", 5)) { /* RFC 4616 */
1208  }
1209  if (!strncasecmp(d, "GSSAPI", 6)) { /* RFC 4752 */
1212  }
1213  if (!strncasecmp(d, "EXTERNAL", 8)) { /* RFC 4422 */
1216  }
1217  /* inn also can do these */
1218  if (!strncasecmp(d, "OTP", 3)) { /* RFC 2444 */
1220  nntp_caps.sasl |= SASL_OTP;
1221  }
1222  if (!strncasecmp(d, "NTLM", 4)) { /* Microsoft */
1225  }
1226  if (!strncasecmp(d, "LOGIN", 5)) { /* Microsoft */
1229  }
1230  }
1231  } else if (!strncasecmp(ptr, "COMPRESS", 8)) { /* RFC 8054 */
1232  d = ptr + 8;
1233  d = strpbrk(d, " \t");
1234  while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1235  d++;
1236  if (!strncasecmp(d, "DEFLATE", 7)) {
1239  }
1240  }
1241  }
1242 # if 0 /* we don't need these */
1243  else if (!strcasecmp(ptr, "IHAVE"))
1244  nntp_caps.ihave = TRUE;
1245  else if (!strcasecmp(ptr, "STREAMING"))
1246  nntp_caps.streaming = TRUE;
1247 # endif /* 0 */
1248  /* XZVER, XZHDR, ... */
1249  } else
1250  nntp_caps.type = NONE;
1251  }
1252  break;
1253 
1254  /*
1255  * XanaNewz 2 Server Version 2.0.0.3 doesn't know CAPABILITIES
1256  * but responds with 400 _without_ closing the connection. If
1257  * you use tin on a XanaNewz 2 Server comment out the following
1258  * case.
1259  */
1260 # if 1
1261  case ERR_GOODBYE:
1262  ret = i;
1263  error_message(2, "%s", buf);
1264  break;
1265 # endif /* 1 */
1266 
1267  default:
1268  break;
1269  }
1270 
1271  return ret;
1272 }
1273 
1274 
1275 /*
1276  * Switch INN into NNRP mode with 'mode reader'
1277  */
1278 static int
1279 mode_reader(
1280  t_bool *sec)
1281 {
1282  int ret = 0;
1283 
1284  if (!nntp_caps.reader) {
1285  char line[NNTP_STRLEN];
1286 # ifdef DEBUG
1287  if ((debug & DEBUG_NNTP) && verbose > 1)
1288  debug_print_file("NNTP", "mode_reader(MODE READER)");
1289 # endif /* DEBUG */
1290  DEBUG_IO((stderr, "nntp_command(MODE READER)\n"));
1291  put_server("MODE READER");
1292 
1293  /*
1294  * According to RFC 3977 (5.3), MODE READER may only return the
1295  * following response codes:
1296  *
1297  * 200 (OK_CANPOST) Hello, you can post
1298  * 201 (OK_NOPOST) Hello, you can't post
1299  * 502 (ERR_ACCESS) Service unavailable
1300  *
1301  * However, there are servers out there (e.g. Delegate 9.8.x) that do
1302  * not implement this command and therefore return ERR_COMMAND (500).
1303  * Unfortunately there are some new servers out there (i.e. INN 2.4.0
1304  * (20020220 prerelease) which do return ERR_COMMAND if they are feed
1305  * only servers.
1306  */
1307 
1308  switch ((ret = get_respcode(line, sizeof(line)))) {
1309  case OK_CANPOST:
1310  can_post = TRUE && !force_no_post;
1311  *sec = TRUE;
1312  ret = 0;
1313  break;
1314 
1315  case OK_NOPOST:
1316  can_post = FALSE;
1317  *sec = TRUE;
1318  ret = 0;
1319  break;
1320 
1321  case ERR_GOODBYE:
1322  case ERR_ACCESS:
1323  error_message(2, "%s", line);
1324  return ret;
1325 
1326  case ERR_COMMAND:
1327 # if 1
1328  ret = 0;
1329  break;
1330 # endif /* 1 */
1331 
1332  default:
1333  break;
1334  }
1335  }
1336  return ret;
1337 }
1338 #endif /* NNTP_ABLE */
1339 
1340 
1341 /*
1342  * Open a connection to the NNTP server. Authenticate if necessary or
1343  * desired, and test if the server supports XOVER.
1344  * Returns: 0 success
1345  * > 0 NNTP error response code
1346  * < 0 -errno from system call or similar error
1347  */
1348 int
1350  void)
1351 {
1352 #ifdef NNTP_ABLE
1353  char *linep;
1354  char line[NNTP_STRLEN]= { '\0' };
1355  int ret;
1356  t_bool sec = FALSE;
1357  /* It appears that is_reconnect guards code that should be run only once */
1358  static t_bool is_reconnect = FALSE;
1359 
1360  if (!read_news_via_nntp)
1361  return 0;
1362 
1363 # ifdef DEBUG
1364  if ((debug & DEBUG_NNTP) && verbose > 1)
1365  debug_print_file("NNTP", "nntp_open() BEGIN");
1366 # endif /* DEBUG */
1367 
1368  if (nntp_server == NULL) {
1369  error_message(2, _(txt_cannot_get_nntp_server_name));
1370  error_message(2, _(txt_server_name_in_file_env_var), NNTP_SERVER_FILE);
1371  return -EHOSTUNREACH;
1372  }
1373 
1374  if (!batch_mode || verbose) {
1375  if (nntp_tcp_port != IPPORT_NNTP)
1376  wait_message(0, _(txt_connecting_port), nntp_server, nntp_tcp_port);
1377  else
1378  wait_message(0, _(txt_connecting), nntp_server);
1379  }
1380 
1381 # ifdef DEBUG
1382  if ((debug & DEBUG_NNTP) && verbose > 1)
1383  debug_print_file("NNTP", "nntp_open() %s:%d", nntp_server, nntp_tcp_port);
1384 # endif /* DEBUG */
1385 
1386  ret = server_init(nntp_server, NNTP_TCP_NAME, nntp_tcp_port, line, sizeof(line));
1387  DEBUG_IO((stderr, "server_init returns %d,%s\n", ret, line));
1388 
1389  if ((!batch_mode || verbose) && ret >= 0)
1390  my_fputc('\n', stdout);
1391 
1392 # ifdef DEBUG
1393  if ((debug & DEBUG_NNTP) && verbose > 1)
1394  debug_print_file("NNTP", "nntp_open() %s", line);
1395 # endif /* DEBUG */
1396 
1397  switch (ret) {
1398  /*
1399  * ret < 0 : some error from system call
1400  * ret > 0 : NNTP response code
1401  *
1402  * According to the ietf-nntp mailinglist:
1403  * 200 you may (try to) do anything
1404  * 201 you may not POST
1405  * All unrecognised 200 series codes should be assumed as success.
1406  * All unrecognised 300 series codes should be assumed as notice to continue.
1407  * All unrecognised 400 series codes should be assumed as temporary error.
1408  * All unrecognised 500 series codes should be assumed as error.
1409  */
1410 
1411  case OK_CANPOST:
1412  can_post = TRUE && !force_no_post;
1413  break;
1414 
1415  case OK_NOPOST:
1416  can_post = FALSE;
1417  break;
1418 
1419  default:
1420  if (ret >= 200 && ret <= 299) {
1421  can_post = TRUE && !force_no_post;
1422  break;
1423  }
1424  if (ret < 0)
1425  error_message(2, _(txt_failed_to_connect_to_server), nntp_server);
1426  else
1427  error_message(2, "%s", line);
1428 
1429  return ret;
1430  }
1431  if (!is_reconnect && *line) {
1432  /* remove leading whitespace and save server's initial response */
1433  linep = line;
1434  while (isspace((int) *linep))
1435  linep++;
1436 
1437  STRCPY(bug_nntpserver1, linep);
1438  }
1439 
1440  /*
1441  * Find out which NNTP extensions are available
1442  * - Typhoon v2.1.1.363 closes the connection after an unknown command
1443  * (i.e. CAPABILITIES) but as we are not allowed to cache CAPABILITIES
1444  * we reissue the command on reconnect. To prevent a loop we catch this
1445  * case.
1446  *
1447  * TODO: The authentication method required may be mentioned in the list
1448  * of extensions. (For details about authentication methods, see
1449  * RFC 4643).
1450  */
1451  if (nntp_caps.type != BROKEN)
1452  check_extensions(0);
1453 
1454  /*
1455  * If the user wants us to authenticate on connection startup, do it now.
1456  * Some news servers return "201 no posting" first, but after successful
1457  * authentication you get a "200 posting allowed". To find out if we are
1458  * allowed to post after authentication issue a "MODE READER" again and
1459  * interpret the response code.
1460  */
1461 
1463  if (nntp_caps.mode_reader) {
1464  char buf[NNTP_STRLEN];
1465 
1466 # ifdef DEBUG
1467  if ((debug & DEBUG_NNTP) && verbose > 1)
1468  debug_print_file("NNTP", "nntp_open(MODE READER)");
1469 # endif /* DEBUG */
1470  put_server("MODE READER");
1471  switch (get_only_respcode(buf, sizeof(buf))) {
1472  /* just honor ciritical errors */
1473  case ERR_GOODBYE:
1474  case ERR_ACCESS:
1475  error_message(2, "%s", buf);
1476  return -1;
1477 
1478  default:
1479  break;
1480  }
1481  check_extensions(0);
1482  }
1483  }
1484 
1485  if (force_auth_on_conn_open) {
1486 # ifdef DEBUG
1487  if ((debug & DEBUG_NNTP) && verbose > 1)
1488  debug_print_file("NNTP", "nntp_open(authenticate(force_auth_on_conn_open))");
1489 # endif /* DEBUG */
1490 
1491  if (!authenticate(nntp_server, userid, FALSE)) /* 3rd parameter is FALSE as we need to get prompted for username password here */
1492  return -1;
1493  }
1494 
1496  if ((ret = mode_reader(&sec))) {
1497  if (nntp_caps.type == CAPABILITIES)
1499 
1500  return ret;
1501  }
1502  if (nntp_caps.type == CAPABILITIES)
1503  check_extensions(0);
1504  }
1505 
1506  if (nntp_caps.type == CAPABILITIES) {
1507  if (!nntp_caps.reader) {
1508 # ifdef DEBUG
1509  if ((debug & DEBUG_NNTP) && verbose > 1)
1510  debug_print_file("NNTP", "CAPABILITIES did not announce READER");
1511 # endif /* DEBUG */
1512  error_message(2, _("CAPABILITIES did not announce READER")); /* TODO: -> lang.c */
1513  return -1; /* give up */
1514  }
1516  }
1517 
1518  if (!is_reconnect && *line) {
1519 # if 0
1520  /*
1521  * gives wrong results if RFC 3977 server requests auth after
1522  * CAPABILITIES is parsed (with no posting allowed) and after auth
1523  * posting is allowed. as we will inform the user later on when he
1524  * actually tries to post it should do no harm to skip this message
1525  */
1526  /* Inform user if he cannot post */
1527  if (!can_post && !batch_mode)
1528  wait_message(0, "%s\n", _(txt_cannot_post));
1529 # endif /* 0 */
1530 
1531  /* Remove leading white space and save server's second response */
1532  linep = line;
1533  while (isspace((int) *linep))
1534  linep++;
1535 
1536  STRCPY(bug_nntpserver2, linep);
1537 
1538  /*
1539  * Show user last server response line, do some nice formatting if
1540  * response is longer than a screen wide.
1541  *
1542  * TODO: This only breaks the line once, but the response could be
1543  * longer than two lines ...
1544  */
1545  if (!batch_mode || verbose) {
1546  char *chr1, *chr2;
1547  int j;
1548 
1549  j = atoi(get_val("COLUMNS", "80"));
1550  chr1 = my_strdup((sec ? bug_nntpserver2 : bug_nntpserver1));
1551 
1552  if (((int) strlen(chr1)) >= j) {
1553  chr2 = chr1 + strlen(chr1) - 1;
1554  while (chr2 - chr1 >= j)
1555  chr2--;
1556  while (chr2 > chr1 && *chr2 != ' ')
1557  chr2--;
1558  if (chr2 != chr1)
1559  *chr2 = '\n';
1560  }
1561 
1562  wait_message(0, "%s\n", chr1);
1563  free(chr1);
1564  }
1565  }
1566 
1567  /*
1568  * If CAPABILITIES failed, check if NNTP supports XOVER or OVER command
1569  * We have to check that we _don't_ get an ERR_COMMAND
1570  */
1571  if (nntp_caps.type != CAPABILITIES) {
1572  int i, j = 0;
1573 
1574  for (i = 0; i < 2 && j >= 0; i++) {
1575  j = new_nntp_command(&xover_cmds[i], ERR_NCING, line, sizeof(line));
1576  switch (j) {
1577  case ERR_COMMAND:
1578  break;
1579 
1580  case OK_XOVER: /* unexpected multiline ok, e.g.: Synchronet 3.13 NNTP Service 1.92 or on reconnect if last cmd was GROUP */
1581  nntp_caps.over_cmd = &xover_cmds[i];
1582 # ifdef DEBUG
1583  if ((debug & DEBUG_NNTP) && verbose > 1)
1584  debug_print_file("NNTP", "nntp_open() %s skipping data", &xover_cmds[i]);
1585 # endif /* DEBUG */
1586  while (tin_fgets(FAKE_NNTP_FP, FALSE))
1587  ;
1588  j = -1;
1589  break;
1590 
1591  default:
1592  nntp_caps.over_cmd = &xover_cmds[i];
1593  j = -1;
1594  break;
1595  }
1596  }
1597  for (i = 0, j = 0; i < 2 && j >= 0; i++) {
1598  j = new_nntp_command(&xhdr_cmds[i], ERR_CMDSYN, line, sizeof(line));
1599  switch (j) {
1600  case ERR_COMMAND:
1601  break;
1602 
1603  case 221: /* unexpected multiline ok, e.g.: SoftVelocity Discussions 2.5q */
1604  nntp_caps.hdr_cmd = &xhdr_cmds[i];
1605 # ifdef DEBUG
1606  if ((debug & DEBUG_NNTP) && verbose > 1)
1607  debug_print_file("NNTP", "nntp_open() %s skipping data", &xhdr_cmds[i]);
1608 # endif /* DEBUG */
1609  while (tin_fgets(FAKE_NNTP_FP, FALSE))
1610  ;
1611  j = -1;
1612  break;
1613 
1614  default: /* usually ERR_CMDSYN (args missing), Typhoon/Twister sends ERR_NCING */
1615  nntp_caps.hdr_cmd = &xhdr_cmds[i];
1616  j = -1;
1617  break;
1618  }
1619  }
1620  /* no XPAT probing here, we do when it's needed */
1621  nntp_caps.xpat = TRUE;
1622 # if 0
1623  switch (new_nntp_command("XPAT Newsgroups <0> *", ERR_NOART, line, sizeof(line))) {
1624  case ERR_NOART:
1625  nntp_caps.xpat = TRUE;
1626  break;
1627 
1628  default:
1629  break;
1630  }
1631 # endif /* 0 */
1632  } else {
1633  if (!nntp_caps.over_cmd) {
1634  /*
1635  * CAPABILITIES didn't mention OVER or XOVER, try XOVER
1636  */
1637  switch (new_nntp_command(xover_cmds, ERR_NCING, line, sizeof(line))) {
1638  case ERR_COMMAND:
1639  break;
1640 
1641  case OK_XOVER: /* unexpected multiline ok, e.g.: Synchronet 3.13 NNTP Service 1.92 or on reconnect if last cmd was GROUP */
1642  nntp_caps.over_cmd = xover_cmds;
1643 # ifdef DEBUG
1644  if ((debug & DEBUG_NNTP) && verbose > 1)
1645  debug_print_file("NNTP", "nntp_open() %s skipping data", xover_cmds);
1646 # endif /* DEBUG */
1647  while (tin_fgets(FAKE_NNTP_FP, FALSE))
1648  ;
1649  break;
1650 
1651  default:
1652  nntp_caps.over_cmd = xover_cmds;
1653  break;
1654  }
1655  }
1656  if (!nntp_caps.hdr_cmd) {
1657  /*
1658  * CAPABILITIES didn't mention HDR or XHDR, try XHDR
1659  */
1660  switch (new_nntp_command(xhdr_cmds, ERR_NCING, line, sizeof(line))) {
1661  case ERR_COMMAND:
1662  break;
1663 
1664  case 221: /* unexpected multiline ok, e.g.: SoftVelocity Discussions 2.5q */
1665  nntp_caps.hdr_cmd = xhdr_cmds;
1666 # ifdef DEBUG
1667  if ((debug & DEBUG_NNTP) && verbose > 1)
1668  debug_print_file("NNTP", "nntp_open() %s skipping data", xhdr_cmds);
1669 # endif /* DEBUG */
1670  while (tin_fgets(FAKE_NNTP_FP, FALSE))
1671  ;
1672  break;
1673 
1674  default: /* ERR_NCING or ERR_CMDSYN */
1675  nntp_caps.hdr_cmd = xhdr_cmds;
1676  break;
1677  }
1678  }
1679  }
1680 
1681  if (!nntp_caps.over_cmd) {
1682  if (!is_reconnect && !batch_mode) {
1683  wait_message(2, _(txt_no_xover_support));
1684 
1686  wait_message(2, _(txt_caching_on));
1687  else
1688  wait_message(2, _(txt_caching_off));
1689  }
1690  }
1691 # if 0
1692  else {
1693  /*
1694  * TODO: issue warning if old index files found?
1695  * in index_newsdir?
1696  */
1697  }
1698 # endif /* 0 */
1699 
1700  if (!is_reconnect && !batch_mode && show_description && check_for_new_newsgroups) {
1701  /*
1702  * TODO:
1703  * - add a tinrc var to turn LIST MOTD on/off?
1704  * (currently done automatically for -d, -q and -Q)
1705  */
1706  if (nntp_caps.list_motd)
1707  list_motd();
1708  }
1709 
1710  is_reconnect = TRUE;
1711 
1712 #endif /* NNTP_ABLE */
1713 
1714  DEBUG_IO((stderr, "nntp_open okay\n"));
1715  return 0;
1716 }
1717 
1718 
1719 /*
1720  * 'Public' function to shutdown the NNTP connection
1721  */
1722 void
1724  t_bool send_no_quit)
1725 {
1726 #ifdef NNTP_ABLE
1728 # ifdef DEBUG
1729  if ((debug & DEBUG_NNTP) && verbose > 1)
1730  debug_print_file("NNTP", "nntp_close(%s) END", bool_unparse(send_no_quit));
1731 # endif /* DEBUG */
1732  close_server(send_no_quit);
1733  }
1734 #endif /* NNTP_ABLE */
1735 }
1736 
1737 
1738 #ifdef NNTP_ABLE
1739 /*
1740  * Get a response code from the server.
1741  * Returns:
1742  * +ve NNTP return code
1743  * -1 on an error or user abort. We don't differentiate.
1744  * If 'message' is not NULL, then any trailing text after the response
1745  * code is copied into it.
1746  * Does not perform authentication if required; use get_respcode()
1747  * instead.
1748  */
1749 int
1750 get_only_respcode(
1751  char *message,
1752  size_t mlen)
1753 {
1754  int respcode;
1755  char *end, *ptr;
1756 
1757  ptr = tin_fgets(FAKE_NNTP_FP, FALSE);
1758 
1759  if (tin_errno || ptr == NULL) {
1760 # ifdef DEBUG
1761  if ((debug & DEBUG_NNTP) && verbose > 1)
1762  debug_print_file("NNTP", "Error: tin_error<>0 or ptr==NULL in get_only_respcode()");
1763 # endif /* DEBUG */
1764  return -1;
1765  }
1766 
1767 # ifdef DEBUG
1768  if (debug & DEBUG_NNTP)
1769  debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1770 # endif /* DEBUG */
1771  respcode = (int) strtol(ptr, &end, 10);
1772  if (end == ptr) /* no leading numbers in response */
1773  respcode = -1;
1774  DEBUG_IO((stderr, "get_only_respcode(%d)\n", respcode));
1775 
1776  /*
1777  * we also reconnect on ERR_FAULT if last_put was ARTICLE or LIST or POST
1778  * as inn (2.2.3) sends ERR_FAULT on timeout
1779  *
1780  * what about other LIST cmds? (ACTIVE|COUNTS|OVERVIEW.FMT|...)
1781  */
1782  if (last_put[0] != '\0' && ((respcode == ERR_FAULT && (!strncmp(last_put, "ARTICLE", 7) || !strcmp(last_put, "POST") || !strcmp(last_put, "LIST"))) || respcode == ERR_GOODBYE || respcode == OK_GOODBYE) && strcmp(last_put, "QUIT")) {
1783  /*
1784  * Maybe server timed out.
1785  * If so, retrying will force a reconnect.
1786  */
1787 # ifdef DEBUG
1788  if ((debug & DEBUG_NNTP) && verbose > 1)
1789  debug_print_file("NNTP", "get_only_respcode() timeout");
1790 # endif /* DEBUG */
1791  put_server(last_put);
1792  ptr = tin_fgets(FAKE_NNTP_FP, FALSE);
1793 
1794  if (tin_errno || ptr == NULL) {
1795 # ifdef DEBUG
1796  if ((debug & DEBUG_NNTP) && verbose > 1)
1797  debug_print_file("NNTP", "Error: tin_errno<>0 or ptr==NULL in get_only_respcode(retry)");
1798 # endif /* DEBUG */
1799  return -1;
1800  }
1801 
1802 # ifdef DEBUG
1803  if (debug & DEBUG_NNTP)
1804  debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1805 # endif /* DEBUG */
1806  respcode = (int) strtol(ptr, &end, 10);
1807  if (end == ptr) /* no leading numbers in response */
1808  respcode = -1;
1809  DEBUG_IO((stderr, "get_only_respcode(%d)\n", respcode));
1810  }
1811  if (message != NULL && mlen > 1 && *end != '\0') /* Pass out the rest of the text */
1812  my_strncpy(message, ++end, mlen - 1);
1813 
1814  return respcode;
1815 }
1816 
1817 
1818 /*
1819  * Get a response code from the server.
1820  * Returns:
1821  * +ve NNTP return code
1822  * -1 on an error
1823  * If 'message' is not NULL, then any trailing text after the response
1824  * code is copied into it.
1825  * Performs authentication if required and repeats the last command if
1826  * necessary after a timeout.
1827  *
1828  * TODO: make this handle 401 and 483 (RFC 3977) return codes.
1829  * as 401 requires the examination of the returned text besides the
1830  * return value, we have to "fix" all nntp_command(..., NULL, 0) and
1831  * get_only_respcode(NULL, 0) calls to do this properly.
1832  */
1833 int
1834 get_respcode(
1835  char *message,
1836  size_t mlen)
1837 {
1838  int respcode;
1839  char savebuf[NNTP_STRLEN];
1840  char *ptr, *end;
1841 
1842  respcode = get_only_respcode(message, mlen);
1843  if ((respcode == ERR_NOAUTH) || (respcode == NEED_AUTHINFO)) {
1844  /*
1845  * Server requires authentication.
1846  */
1847 # ifdef DEBUG
1848  if ((debug & DEBUG_NNTP) && verbose > 1)
1849  debug_print_file("NNTP", "get_respcode() authentication");
1850 # endif /* DEBUG */
1851  STRCPY(savebuf, last_put);
1852 
1853  if (!authenticate(nntp_server, userid, FALSE))
1855 
1856  if (nntp_caps.type == CAPABILITIES)
1858  else {
1859  put_server("MODE READER");
1860  if (get_only_respcode(message, mlen) == OK_CANPOST)
1861  can_post = TRUE && !force_no_post;
1862  }
1863  if (curr_group != NULL) {
1864  DEBUG_IO((stderr, _("Rejoin current group\n")));
1865  snprintf(last_put, sizeof(last_put), "GROUP %s", curr_group->name);
1866  put_server(last_put);
1867  s_gets(last_put, NNTP_STRLEN, nntp_rd_fp);
1868 # ifdef DEBUG
1869  if (debug & DEBUG_NNTP)
1870  debug_print_file("NNTP", "<<<%s%s", logtime(), last_put);
1871 # endif /* DEBUG */
1872  DEBUG_IO((stderr, _("Read (%s)\n"), last_put));
1873  }
1874  STRCPY(last_put, savebuf);
1875 
1876  put_server(last_put);
1877  ptr = tin_fgets(FAKE_NNTP_FP, FALSE);
1878 
1879  if (tin_errno) {
1880 # ifdef DEBUG
1881  if ((debug & DEBUG_NNTP) && verbose > 1)
1882  debug_print_file("NNTP", "Error: tin_errno <> 0");
1883 # endif /* DEBUG */
1884  return -1;
1885  }
1886 
1887 # ifdef DEBUG
1888  if (debug & DEBUG_NNTP)
1889  debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1890 # endif /* DEBUG */
1891  if (ptr == NULL)
1892  return -1;
1893 
1894  respcode = (int) strtol(ptr, &end, 10);
1895  if (end == ptr) /* no leading numbers in response */
1896  return -1;
1897 
1898  if (message != NULL && mlen > 1) /* Pass out the rest of the text */
1899  strncpy(message, end, mlen - 1);
1900  }
1901  return respcode;
1902 }
1903 
1904 
1905 /*
1906  * Do an NNTP command. Send command to server, and read the reply.
1907  * If the reply code matches success, then return an open file stream
1908  * Return NULL if we did not see the response we wanted.
1909  * If message is not NULL, then the trailing text of the reply string is
1910  * copied into it for the caller to process.
1911  */
1912 FILE *
1913 nntp_command(
1914  const char *command,
1915  int success,
1916  char *message,
1917  size_t mlen)
1918 {
1919 DEBUG_IO((stderr, "nntp_command(%s)\n", command));
1920 # ifdef DEBUG
1921  if ((debug & DEBUG_NNTP) && verbose > 1)
1922  debug_print_file("NNTP", "nntp_command(%s)", command);
1923 # endif /* DEBUG */
1924  put_server(command);
1925 
1927  if (get_respcode(message, mlen) != success) {
1928 # ifdef DEBUG
1929  if ((debug & DEBUG_NNTP) && verbose > 1)
1930  debug_print_file("NNTP", "nntp_command(%s) NOT_OK", command);
1931 # endif /* DEBUG */
1932  /* error_message(2, "%s", message); */
1933  return (FILE *) 0;
1934  }
1935  }
1936 # ifdef DEBUG
1937  if ((debug & DEBUG_NNTP) && verbose > 1)
1938  debug_print_file("NNTP", "nntp_command(%s) OK", command);
1939 # endif /* DEBUG */
1940  return FAKE_NNTP_FP;
1941 }
1942 
1943 
1944 /*
1945  * same as above, but with a slightly more useful return code.
1946  * TODO: use it instead of nntp_command in the rest of the code
1947  * (wherever it is more useful).
1948  */
1949 int
1950 new_nntp_command(
1951  const char *command,
1952  int success,
1953  char *message,
1954  size_t mlen)
1955 {
1956  int respcode = 0;
1957 
1958 DEBUG_IO((stderr, "new_nntp_command(%s)\n", command));
1959 # ifdef DEBUG
1960  if ((debug & DEBUG_NNTP) && verbose > 1)
1961  debug_print_file("NNTP", "new_nntp_command(%s)", command);
1962 # endif /* DEBUG */
1963  put_server(command);
1964 
1966  if ((respcode = get_respcode(message, mlen)) != success) {
1967 # ifdef DEBUG
1968  if ((debug & DEBUG_NNTP) && verbose > 1)
1969  debug_print_file("NNTP", "new_nntp_command(%s) NOT_OK - Expected: %d, got: %d", command, success, respcode);
1970 # endif /* DEBUG */
1971  return respcode;
1972  }
1973  }
1974 # ifdef DEBUG
1975  if ((debug & DEBUG_NNTP) && verbose > 1)
1976  debug_print_file("NNTP", "new_nntp_command(%s) OK", command);
1977 # endif /* DEBUG */
1978  return respcode;
1979 }
1980 
1981 
1982 static void
1983 list_motd(
1984  void)
1985 {
1986  char *ptr;
1987  char *p;
1988  char buf[NNTP_STRLEN];
1989  int i;
1990  size_t len;
1991  unsigned int l = 0;
1992 
1993  buf[0] = '\0';
1994  i = new_nntp_command("LIST MOTD", OK_MOTD, buf, sizeof(buf));
1995 
1996  switch (i) {
1997  case OK_MOTD:
1998 # ifdef HAVE_COLOR
1999  fcol(tinrc.col_message);
2000 # endif /* HAVE_COLOR */
2001  while ((ptr = tin_fgets(FAKE_NNTP_FP, FALSE)) != NULL) {
2002 # ifdef DEBUG
2003  if (debug & DEBUG_NNTP)
2004  debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
2005 # endif /* DEBUG */
2006  /*
2007  * RFC 6048 2.5.2 "The information MUST be in UTF-8"
2008  *
2009  * TODO: - store a hash value of the entire motd in the server-rc
2010  * and only if it differs from the old value display the
2011  * motd?
2012  * - use some sort of pager?
2013  * - -> lang.c
2014  */
2015  p = my_strdup(ptr);
2016  len = strlen(p);
2017  process_charsets(&p, &len, "UTF-8", tinrc.mm_local_charset, FALSE);
2018  my_printf("%s%s\n", _("MOTD: "), p);
2019  free(p);
2020  l++;
2021  }
2022 # ifdef HAVE_COLOR
2023  fcol(tinrc.col_normal);
2024 # endif /* HAVE_COLOR */
2025  if (l) {
2026  my_flush();
2027  sleep((l >> 1) | 0x01);
2028  }
2029  break;
2030 
2031  default: /* common response codes are 500, 501, 503 */
2032  break;
2033  }
2034 }
2035 #endif /* NNTP_ABLE */
DEBUG_NNTP
#define DEBUG_NNTP
Definition: debug.h:47
s_flush
#define s_flush
Definition: tnntp.h:45
NNTP_STRLEN
#define NNTP_STRLEN
Definition: nntplib.h:155
can_post
t_bool can_post
Definition: nntplib.c:32
SASL_LOGIN
@ SASL_LOGIN
Definition: nntplib.h:182
t_capabilities::compress
t_bool compress
Definition: nntplib.h:218
t_capabilities::headers_range
char * headers_range
Definition: nntplib.h:196
CAPABILITIES
@ CAPABILITIES
Definition: nntplib.h:171
strcasecmp
int strcasecmp(const char *p, const char *q)
Definition: string.c:468
BROKEN
@ BROKEN
Definition: nntplib.h:171
_
#define _(Text)
Definition: tin.h:94
append_file
void append_file(char *old_filename, char *new_filename)
Definition: misc.c:120
t_capabilities::authinfo_state
t_bool authinfo_state
Definition: nntplib.h:216
ERR_FAULT
#define ERR_FAULT
Definition: nntplib.h:147
nntp_close
void nntp_close(t_bool send_no_quit)
Definition: nntplib.c:1723
t_capabilities::over_cmd
const char * over_cmd
Definition: nntplib.h:210
my_strdup
char * my_strdup(const char *str)
Definition: string.c:133
txt_cannot_post
constext txt_cannot_post[]
Definition: lang.c:130
bool_unparse
#define bool_unparse(b)
Definition: bool.h:83
t_capabilities::list_active
t_bool list_active
Definition: nntplib.h:192
s_end
#define s_end()
Definition: tnntp.h:51
str_trim
char * str_trim(char *string)
Definition: string.c:532
ERR_AUTHFAIL
#define ERR_AUTHFAIL
Definition: nntplib.h:140
t_config::auto_reconnect
t_bool auto_reconnect
Definition: tinrc.h:205
t_capabilities::type
enum extension_type type
Definition: nntplib.h:187
t_capabilities::list_motd
t_bool list_motd
Definition: nntplib.h:200
read_news_via_nntp
t_bool read_news_via_nntp
Definition: init.c:150
check_for_new_newsgroups
t_bool check_for_new_newsgroups
Definition: init.c:127
bool_equal
#define bool_equal(a, b)
Definition: bool.h:82
my_flush
#define my_flush()
Definition: tcurses.h:171
cReconnect
@ cReconnect
Definition: tin.h:107
bug_nntpserver2
char bug_nntpserver2[PATH_LEN]
Definition: init.c:69
t_capabilities::newnews
t_bool newnews
Definition: nntplib.h:211
t_capabilities::headers_id
char * headers_id
Definition: nntplib.h:197
show_description
t_bool show_description
Definition: init.c:152
tinrc
struct t_config tinrc
Definition: init.c:191
perror_message
void perror_message(const char *fmt,...)
Definition: screen.c:220
wait_message
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
FreeAndNull
#define FreeAndNull(p)
Definition: tin.h:2204
INF_CAPABILITIES
#define INF_CAPABILITIES
Definition: nntplib.h:87
curr_group
struct t_group * curr_group
Definition: group.c:55
OK_NOPOST
#define OK_NOPOST
Definition: nntplib.h:91
read_saved_news
t_bool read_saved_news
Definition: init.c:151
signal_context
int signal_context
Definition: signal.c:105
t_capabilities::list_distributions
t_bool list_distributions
Definition: nntplib.h:202
s_dup
#define s_dup
Definition: tnntp.h:50
nntp_server
char * nntp_server
Definition: nntplib.c:28
OK_GOODBYE
#define OK_GOODBYE
Definition: nntplib.h:93
my_fputc
#define my_fputc(ch, stream)
Definition: tcurses.h:152
t_capabilities::list_counts
t_bool list_counts
Definition: nntplib.h:204
TCP
FILE TCP
Definition: tin.h:2263
OK_MOTD
#define OK_MOTD
Definition: nntplib.h:97
t_capabilities::broken_listgroup
t_bool broken_listgroup
Definition: nntplib.h:224
dead_articles
char dead_articles[PATH_LEN]
Definition: init.c:72
tcurses.h
end
static char * end
Definition: plp_snprintf.c:205
SASL_OTP
@ SASL_OTP
Definition: nntplib.h:180
tin.h
t_capabilities::over
t_bool over
Definition: nntplib.h:208
tin_done
void tin_done(int ret, const char *fmt,...)
Definition: misc.c:557
cmdline
struct t_cmdlineopts cmdline
Definition: init.c:189
COMPRESS_DEFLATE
@ COMPRESS_DEFLATE
Definition: nntplib.h:184
ERR_CMDSYN
#define ERR_CMDSYN
Definition: nntplib.h:145
dead_article
char dead_article[PATH_LEN]
Definition: init.c:71
t_cmdlineopts::nntpserver
char nntpserver[255]
Definition: tin.h:1469
t_capabilities::sasl
enum sasl_types sasl
Definition: nntplib.h:217
strunc
char * strunc(const char *message, int len)
Definition: string.c:1069
NNTP_TRY_RECONNECT
#define NNTP_TRY_RECONNECT
Definition: nntplib.h:54
t_capabilities::hdr_cmd
const char * hdr_cmd
Definition: nntplib.h:207
tin_fgets
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:320
my_fprintf
#define my_fprintf
Definition: tcurses.h:170
dangerous_signal_exit
t_bool dangerous_signal_exit
Definition: init.c:130
COMPRESS_NONE
@ COMPRESS_NONE
Definition: nntplib.h:184
my_strncpy
void my_strncpy(char *p, const char *q, size_t n)
Definition: string.c:190
OK_CANPOST
#define OK_CANPOST
Definition: nntplib.h:90
MAXHOSTNAMELEN
#define MAXHOSTNAMELEN
Definition: tnntp.h:114
s_puts
#define s_puts
Definition: tnntp.h:49
nntp_caps
struct t_capabilities nntp_caps
Definition: init.c:516
DEBUG_IO
#define DEBUG_IO(x)
Definition: debug.h:62
SASL_EXTERNAL
@ SASL_EXTERNAL
Definition: nntplib.h:179
t_config::mm_local_charset
char mm_local_charset[LEN]
Definition: tinrc.h:103
SASL_DIGEST_MD5
@ SASL_DIGEST_MD5
Definition: nntplib.h:177
SASL_PLAIN
@ SASL_PLAIN
Definition: nntplib.h:175
get_nntp_fp
FILE * get_nntp_fp(FILE *fp)
Definition: nntplib.c:79
NONE
@ NONE
Definition: nntplib.h:171
NNTP_SERVER_FILE
#define NNTP_SERVER_FILE
Definition: nntplib.h:45
t_capabilities::version
unsigned int version
Definition: nntplib.h:188
buf
static char buf[16]
Definition: langinfo.c:50
ERR_COMMAND
#define ERR_COMMAND
Definition: nntplib.h:144
ERR_NCING
#define ERR_NCING
Definition: nntplib.h:127
FreeIfNeeded
#define FreeIfNeeded(p)
Definition: tin.h:2203
t_capabilities::authinfo_user
t_bool authinfo_user
Definition: nntplib.h:214
bug_nntpserver1
char bug_nntpserver1[PATH_LEN]
Definition: init.c:68
cCOLS
int cCOLS
Definition: curses.c:53
nntp_open
int nntp_open(void)
Definition: nntplib.c:1349
t_capabilities::list_headers
t_bool list_headers
Definition: nntplib.h:195
backup_article_name
char * backup_article_name(const char *the_article)
Definition: post.c:330
t_config::keep_dead_articles
t_bool keep_dead_articles
Definition: tinrc.h:217
nntp_rd_fp
static TCP * nntp_rd_fp
Definition: nntplib.c:42
ring_bell
void ring_bell(void)
Definition: screen.c:428
NEED_AUTHINFO
#define NEED_AUTHINFO
Definition: nntplib.h:119
FAKE_NNTP_FP
#define FAKE_NNTP_FP
Definition: tin.h:455
EXIT_FAILURE
#define EXIT_FAILURE
Definition: tin.h:1277
SASL_NONE
@ SASL_NONE
Definition: nntplib.h:174
t_capabilities::list_moderators
t_bool list_moderators
Definition: nntplib.h:203
article_name
char article_name[PATH_LEN]
Definition: init.c:67
my_fputs
#define my_fputs(str, stream)
Definition: tcurses.h:153
s_gets
#define s_gets
Definition: tnntp.h:47
ERR_ACCESS
#define ERR_ACCESS
Definition: nntplib.h:146
t_capabilities::list_overview_fmt
t_bool list_overview_fmt
Definition: nntplib.h:199
t_capabilities::over_msgid
t_bool over_msgid
Definition: nntplib.h:209
t_capabilities::starttls
t_bool starttls
Definition: nntplib.h:213
t_capabilities::implementation
char * implementation
Definition: nntplib.h:212
batch_mode
t_bool batch_mode
Definition: init.c:126
SASL_NTLM
@ SASL_NTLM
Definition: nntplib.h:181
my_printf
#define my_printf
Definition: tcurses.h:169
t_capabilities::xpat
t_bool xpat
Definition: nntplib.h:205
NNTP_ERROR_EXIT
#define NNTP_ERROR_EXIT
Definition: tin.h:1281
ERR_NOART
#define ERR_NOART
Definition: nntplib.h:133
t_cmdlineopts::args
unsigned int args
Definition: tin.h:1471
atoi
int atoi(const char *s)
t_capabilities::list_subscriptions
t_bool list_subscriptions
Definition: nntplib.h:201
CMDLINE_NNTPSERVER
#define CMDLINE_NNTPSERVER
Definition: tin.h:1093
t_group::name
char * name
Definition: tin.h:1773
NNTP_TCP_NAME
#define NNTP_TCP_NAME
Definition: nntplib.h:48
force_no_post
t_bool force_no_post
Definition: init.c:136
FALSE
#define FALSE
Definition: bool.h:70
STRCPY
#define STRCPY(dst, src)
Definition: tin.h:814
debug
unsigned short debug
Definition: debug.c:51
t_capabilities::compress_algorithm
enum c_algorithms compress_algorithm
Definition: nntplib.h:219
verbose
int verbose
Definition: init.c:153
rename_file
void rename_file(const char *old_filename, const char *new_filename)
Definition: misc.c:733
snprintf
#define snprintf
Definition: tin.h:2417
error_message
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:184
strtol
long strtol(const char *str, char **ptr, int use_base)
Definition: string.c:410
process_charsets
void process_charsets(char **line, size_t *max_line_len, const char *network_charset, const char *local_charset, t_bool conv_tex2iso)
Definition: misc.c:2634
ERR_GOODBYE
#define ERR_GOODBYE
Definition: nntplib.h:123
tnntp.h
userid
char userid[PATH_LEN]
Definition: init.c:107
constext
const typedef char constext
Definition: tin.h:1968
OK_XOVER
#define OK_XOVER
Definition: nntplib.h:105
t_capabilities::list_distrib_pats
t_bool list_distrib_pats
Definition: nntplib.h:194
strerror
char * strerror(int n)
Definition: pcregrep.c:477
t_config::cache_overview_files
t_bool cache_overview_files
Definition: tinrc.h:209
t_bool
unsigned t_bool
Definition: bool.h:77
errno
int errno
prompt_yn
int prompt_yn(const char *prompt, t_bool default_answer)
Definition: prompt.c:165
TRUE
#define TRUE
Definition: bool.h:74
tin_errno
int tin_errno
Definition: read.c:59
getserverbyfile
char * getserverbyfile(const char *file)
Definition: nntplib.c:111
t_capabilities::authinfo_sasl
t_bool authinfo_sasl
Definition: nntplib.h:215
strncasecmp
int strncasecmp(const char *p, const char *q, size_t n)
Definition: string.c:484
SASL_GSSAPI
@ SASL_GSSAPI
Definition: nntplib.h:178
SASL_CRAM_MD5
@ SASL_CRAM_MD5
Definition: nntplib.h:176
ERR_NOAUTH
#define ERR_NOAUTH
Definition: nntplib.h:139
t_capabilities::list_newsgroups
t_bool list_newsgroups
Definition: nntplib.h:198
get_val
const char * get_val(const char *env, const char *def)
Definition: misc.c:355
s_close
#define s_close
Definition: tnntp.h:48
s_fdopen
#define s_fdopen
Definition: tnntp.h:44
t_capabilities::hdr
t_bool hdr
Definition: nntplib.h:206
unlink
#define unlink(file)
Definition: tin.h:384
strpbrk
char * strpbrk(const char *str1, const char *str2)
Definition: string.c:305
t_capabilities::list_active_times
t_bool list_active_times
Definition: nntplib.h:193
s_fclose
#define s_fclose
Definition: tnntp.h:46
t_capabilities::post
t_bool post
Definition: nntplib.h:191
t_capabilities::reader
t_bool reader
Definition: nntplib.h:190
clear_message
void clear_message(void)
Definition: screen.c:243
t_capabilities::mode_reader
t_bool mode_reader
Definition: nntplib.h:189
my_malloc
#define my_malloc(size)
Definition: tin.h:2196