tin  2.6.1
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.6.1.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 : 2021-02-27
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
28char *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
42static 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 */
78FILE *
80 FILE *fp)
81{
82 return (fp == FAKE_NNTP_FP ? nntp_rd_fp : fp);
83}
84
85
86#if 0 /* unused */
87FILE *
88get_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 */
110char *
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
201static int
202server_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)
299static int
300get_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 */
600static int
601get_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 */
676static int
677get_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
747void
748u_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 */
775void
776put_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 */
813static int
814reconnect(
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
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
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 if (s_gets(last_put, NNTP_STRLEN, nntp_rd_fp) == NULL)
876 *last_put = '\0';
877# ifdef DEBUG
878 if (debug & DEBUG_NNTP)
879 debug_print_file("NNTP", "<<<%s%s", logtime(), last_put);
880# endif /* DEBUG */
881 DEBUG_IO((stderr, _("Read (%s)\n"), last_put));
882 }
883 DEBUG_IO((stderr, _("Resend last command (%s)\n"), buf));
884 put_server(buf);
885 did_reconnect = TRUE;
886 retry = NNTP_TRY_RECONNECT;
887 }
888
889 return retry;
890}
891
892
893/*
894 * Read a line of data from the NNTP socket. If something gives, do reconnect
895 *
896 * Parameters: "string" has the buffer space for the line received.
897 * "size" is maximum size of the buffer to read.
898 *
899 * Returns: NULL on end of stream, or a line of data.
900 * Basically, we try to act like fgets() on an NNTP stream.
901 *
902 * Side effects: Talks to server, changes contents of "string".
903 * Reopens connection when necessary and requested.
904 * Exits via tin_done() if fatal error occurs.
905 */
906char *
907get_server(
908 char *string,
909 int size)
910{
911 static int retry_cnt = 0;
912
913 reconnected_in_last_get_server = FALSE;
914 errno = 0;
915
916 /*
917 * NULL socket reads indicates socket has closed. Try a few times more
918 *
919 * Leave the s_gets() after a timeout for these cases:
920 * -some servers do not close the connection but simply do not send any
921 * response data
922 * -the network connection went down
923 */
924# if defined(HAVE_ALARM) && defined(SIGALRM)
925 alarm((unsigned) tinrc.nntp_read_timeout_secs);
926# endif /* HAVE_ALARM && SIGALRM */
927 while (nntp_rd_fp == NULL || s_gets(string, size, nntp_rd_fp) == NULL) {
928 if (errno == EINTR) {
929 errno = 0;
930# if defined(HAVE_ALARM) && defined(SIGALRM)
931 alarm((unsigned) tinrc.nntp_read_timeout_secs); /* Restart the timer */
932# endif /* HAVE_ALARM && SIGALRM */
933 continue;
934 }
935# if defined(HAVE_ALARM) && defined(SIGALRM)
936 alarm(0);
937# endif /* HAVE_ALARM && SIGALRM */
938 if (quitting) /* Don't bother to reconnect */
939 tin_done(NNTP_ERROR_EXIT, NULL); /* And don't try to disconnect again! */
940
941# ifdef DEBUG
942 if (errno != 0 && errno != EINTR) /* Will only confuse end users */
943 perror_message("get_server()");
944# endif /* DEBUG */
945
946 /*
947 * Reconnect only if command was not "QUIT" anyway (in which case a
948 * reconnection would be useless because the connection will be
949 * closed immediately). Also prevents tin from asking to reconnect
950 * when user is quitting tin if tinrc.auto_reconnect is false.
951 */
952 if (strcmp(last_put, "QUIT")) {
953 /*
954 * Typhoon v2.1.1.363 colses the connection right after an unknown
955 * command, (i.e. CAPABILITIES) so we avoid the reissue it on a
956 * reconnect if it was the last command.
957 */
958 if (!strcmp(last_put, "CAPABILITIES")) {
959 strcpy(last_put, "MODE READER");
961 }
962 retry_cnt = reconnect(++retry_cnt); /* Will abort when out of tries */
963 reconnected_in_last_get_server = TRUE;
964 } else {
965 /*
966 * Use standard NNTP closing message and response code if user is
967 * quitting tin and leave loop.
968 */
969 strncpy(string, _(txt_nntp_ok_goodbye), (size_t) (size - 3));
970 strcat(string, "\r\n"); /* tin_fgets() needs CRLF */
971 break;
972 }
973 }
974# if defined(HAVE_ALARM) && defined(SIGALRM)
975 alarm(0);
976# endif /* HAVE_ALARM && SIGALRM */
977 retry_cnt = 0;
978 return string;
979}
980
981
982/*
983 * Send "QUIT" command and close the connection to the server
984 *
985 * Side effects: Closes the connection to the server.
986 * You can't use "put_server" or "get_server" after this
987 * routine is called.
988 *
989 * TODO: remember servers response string and if it contains anything else
990 * than just "." (i.e. transfer statistics) present it to the user?
991 *
992 */
993static void
994close_server(
995 t_bool send_no_quit)
996{
997 if (!send_no_quit && nntp_wr_fp && nntp_rd_fp) {
998
999 if (!batch_mode || verbose) {
1000 char *msg;
1001
1002 msg = strunc(_(txt_disconnecting), cCOLS - 1);
1003 my_fputs(msg, stdout);
1004 my_fputc('\n', stdout);
1005 free(msg);
1006 }
1007 nntp_command("QUIT", OK_GOODBYE, NULL, 0);
1008 quitting = TRUE; /* Don't reconnect just for this */
1009 }
1010 if (nntp_wr_fp)
1011 (void) s_fclose(nntp_wr_fp);
1012 if (nntp_rd_fp)
1013 (void) s_fclose(nntp_rd_fp);
1014 s_end();
1015 nntp_wr_fp = nntp_rd_fp = NULL;
1016}
1017
1018
1019/*
1020 * Try and use CAPABILITIES here. Get this list before issuing other NNTP
1021 * commands because the correct methods may be mentioned in the list of
1022 * extensions.
1023 *
1024 * Sets up: t_capabilities nntp_caps
1025 */
1026int
1027check_extensions(
1028 int rvl)
1029{
1030 char *d;
1031 char *ptr;
1032 char buf[NNTP_STRLEN];
1033 int i;
1034 int ret = 0;
1035
1036 buf[0] = '\0';
1037
1038 /* rvl > 0 = manually send "CAPABILITIES" to avoid endless AUTH loop */
1039 i = rvl ? rvl : new_nntp_command("CAPABILITIES", INF_CAPABILITIES, buf, sizeof(buf));
1040 switch (i) {
1041 case INF_CAPABILITIES:
1042 /* clear capabilities */
1044 nntp_caps.version = 0;
1061 nntp_caps.xpat = TRUE; /* toggles to false if fails, INN > 2.7.0 announces it */
1063 nntp_caps.hdr_cmd = NULL;
1066 nntp_caps.over_cmd = NULL;
1076# if 0
1077 nntp_caps.streaming = FALSE;
1078 nntp_caps.ihave = FALSE;
1079# endif /* 0 */
1080# ifndef BROKEN_LISTGROUP
1082# else
1084# endif /* !BROKEN_LISTGROUP */
1085
1086 while ((ptr = tin_fgets(FAKE_NNTP_FP, FALSE)) != NULL) {
1087# ifdef DEBUG
1088 if (debug & DEBUG_NNTP)
1089 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1090# endif /* DEBUG */
1091 /* look for version number(s) */
1093 if (!strncasecmp(ptr, "VERSION", 7)) {
1094 d = ptr + 7;
1095 d = strpbrk(d, " \t");
1096 while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1097 d++;
1098 nntp_caps.version = (unsigned int) atoi(d);
1099 d = strpbrk(d, " \t");
1100 }
1101 }
1102 }
1103 /* we currently only support CAPABILITIES VERSION 2 */
1104 if (nntp_caps.version == 2) {
1105 /*
1106 * check for LIST variants
1107 */
1108 if (!strncasecmp(ptr, "LIST", 4)) {
1109 d = ptr + 4;
1110 d = strpbrk(d, " \t");
1111 while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1112 d++;
1113 if (!strncasecmp(d, "ACTIVE.TIMES", 12))
1115 else if (!strncasecmp(d, "ACTIVE", 6))
1117 else if (!strncasecmp(d, "DISTRIB.PATS", 12))
1119 else if (!strncasecmp(d, "DISTRIBUTIONS", 13)) /* RFC 6048 */
1121 else if (!strncasecmp(d, "HEADERS", 7))
1122 nntp_caps.list_headers = TRUE; /* HDR requires LIST HEADERS, but not vice versa */
1123 else if (!strncasecmp(d, "NEWSGROUPS", 10))
1125 else if (!strncasecmp(d, "OVERVIEW.FMT", 12)) /* OVER requires OVERVIEW.FMT, but not vice versa */
1127 else if (!strncasecmp(d, "MOTD", 4)) /* RFC 6048 */
1129 else if (!strncasecmp(d, "SUBSCRIPTIONS", 13)) /* RFC 6048 */
1131 else if (!strncasecmp(d, "MODERATORS", 10)) /* RFC 6048 */
1133 else if (!strncasecmp(d, "COUNTS", 6)) /* RFC 6048 */
1135 d = strpbrk(d, " \t");
1136 }
1137 } else if (!strncasecmp(ptr, "IMPLEMENTATION", 14)) {
1141 } else if (!strcasecmp(ptr, "MODE-READER")) {
1142 if (!nntp_caps.reader)
1144 } else if (!strcasecmp(ptr, "READER")) { /* if we saw READER, "LIST ACTIVE" and "LIST NEWSGROUPS" must be implemented */
1149 } else if (!strcasecmp(ptr, "POST"))
1151 else if (!strcasecmp(ptr, "NEWNEWS"))
1153 else if (!strcasecmp(ptr, "XPAT")) /* extension, RFC 2980 */
1155 else if (!strcasecmp(ptr, "STARTTLS"))
1157 /*
1158 * NOTE: if we saw OVER, LIST OVERVIEW.FMT _must_ be implemented
1159 */
1160 else if (!strncasecmp(ptr, &xover_cmds[1], strlen(&xover_cmds[1]))) {
1162 nntp_caps.over_cmd = &xover_cmds[1];
1163 d = ptr + strlen(&xover_cmds[1]);
1164 d = strpbrk(d, " \t");
1165 while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1166 d++;
1167 if (!strcasecmp(d, "MSGID"))
1169 d = strpbrk(d, " \t");
1170 }
1171 }
1172 /*
1173 * NOTE: if we saw HDR, LIST HEADERS _must_ be implemented
1174 */
1175 else if (!strncasecmp(ptr, &xhdr_cmds[1], strlen(&xhdr_cmds[1]))) {
1176 nntp_caps.hdr_cmd = &xhdr_cmds[1];
1180 } else if (!strncasecmp(ptr, "AUTHINFO", 8)) {
1181 d = ptr + 8;
1182 d = strpbrk(d, " \t");
1183 if (d == NULL) /* AUTHINFO without args */
1185 while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1186 d++;
1187 if (!strncasecmp(d, "USER", 4))
1189 if (!strncasecmp(d, "SASL", 4))
1191 d = strpbrk(d, " \t");
1192 }
1193 } else if (!strncasecmp(ptr, "SASL", 4)) {
1194 d = ptr + 4;
1195 d = strpbrk(d, " \t");
1196 while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1197 d++;
1198 if (!strncasecmp(d, "CRAM-MD5", 8)) { /* RFC 2195 */
1201 }
1202 if (!strncasecmp(d, "DIGEST-MD5", 10)) { /* RFC 2831 */
1205 }
1206 if (!strncasecmp(d, "PLAIN", 5)) { /* RFC 4616 */
1209 }
1210 if (!strncasecmp(d, "GSSAPI", 6)) { /* RFC 4752 */
1213 }
1214 if (!strncasecmp(d, "EXTERNAL", 8)) { /* RFC 4422 */
1217 }
1218 /* inn also can do these */
1219 if (!strncasecmp(d, "OTP", 3)) { /* RFC 2444 */
1222 }
1223 if (!strncasecmp(d, "NTLM", 4)) { /* Microsoft */
1226 }
1227 if (!strncasecmp(d, "LOGIN", 5)) { /* Microsoft */
1230 }
1231 }
1232 } else if (!strncasecmp(ptr, "COMPRESS", 8)) { /* RFC 8054 */
1233 d = ptr + 8;
1234 d = strpbrk(d, " \t");
1235 while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1236 d++;
1237 if (!strncasecmp(d, "DEFLATE", 7)) {
1240 }
1241 }
1242 }
1243# if 0 /* we don't need these */
1244 else if (!strcasecmp(ptr, "IHAVE"))
1245 nntp_caps.ihave = TRUE;
1246 else if (!strcasecmp(ptr, "STREAMING"))
1247 nntp_caps.streaming = TRUE;
1248# endif /* 0 */
1249 /* XZVER, XZHDR, ... */
1250 } else
1252 }
1253 break;
1254
1255 /*
1256 * XanaNewz 2 Server Version 2.0.0.3 doesn't know CAPABILITIES
1257 * but responds with 400 _without_ closing the connection. If
1258 * you use tin on a XanaNewz 2 Server comment out the following
1259 * case.
1260 */
1261# if 1
1262 case ERR_GOODBYE:
1263 ret = i;
1264 error_message(2, "%s", buf);
1265 break;
1266# endif /* 1 */
1267
1268 default:
1269 break;
1270 }
1271
1272 return ret;
1273}
1274
1275
1276/*
1277 * Switch INN into NNRP mode with 'mode reader'
1278 */
1279static int
1280mode_reader(
1281 t_bool *sec)
1282{
1283 int ret = 0;
1284
1285 if (!nntp_caps.reader) {
1286 char line[NNTP_STRLEN];
1287# ifdef DEBUG
1288 if ((debug & DEBUG_NNTP) && verbose > 1)
1289 debug_print_file("NNTP", "mode_reader(MODE READER)");
1290# endif /* DEBUG */
1291 DEBUG_IO((stderr, "nntp_command(MODE READER)\n"));
1292 put_server("MODE READER");
1293
1294 /*
1295 * According to RFC 3977 (5.3), MODE READER may only return the
1296 * following response codes:
1297 *
1298 * 200 (OK_CANPOST) Hello, you can post
1299 * 201 (OK_NOPOST) Hello, you can't post
1300 * 502 (ERR_ACCESS) Service unavailable
1301 *
1302 * However, there are servers out there (e.g. Delegate 9.8.x) that do
1303 * not implement this command and therefore return ERR_COMMAND (500).
1304 * Unfortunately there are some new servers out there (i.e. INN 2.4.0
1305 * (20020220 prerelease) which do return ERR_COMMAND if they are feed
1306 * only servers.
1307 */
1308
1309 switch ((ret = get_respcode(line, sizeof(line)))) {
1310 case OK_CANPOST:
1312 *sec = TRUE;
1313 ret = 0;
1314 break;
1315
1316 case OK_NOPOST:
1317 can_post = FALSE;
1318 *sec = TRUE;
1319 ret = 0;
1320 break;
1321
1322 case ERR_GOODBYE:
1323 case ERR_ACCESS:
1324 error_message(2, "%s", line);
1325 return ret;
1326
1327 case ERR_COMMAND:
1328# if 1
1329 ret = 0;
1330 break;
1331# endif /* 1 */
1332
1333 default:
1334 break;
1335 }
1336 }
1337 return ret;
1338}
1339#endif /* NNTP_ABLE */
1340
1341
1342/*
1343 * Open a connection to the NNTP server. Authenticate if necessary or
1344 * desired, and test if the server supports XOVER.
1345 * Returns: 0 success
1346 * > 0 NNTP error response code
1347 * < 0 -errno from system call or similar error
1348 */
1349int
1351 void)
1352{
1353#ifdef NNTP_ABLE
1354 char *linep;
1355 char line[NNTP_STRLEN]= { '\0' };
1356 int ret;
1357 t_bool sec = FALSE;
1358 /* It appears that is_reconnect guards code that should be run only once */
1359 static t_bool is_reconnect = FALSE;
1360
1361 if (!read_news_via_nntp)
1362 return 0;
1363
1364# ifdef DEBUG
1365 if ((debug & DEBUG_NNTP) && verbose > 1)
1366 debug_print_file("NNTP", "nntp_open() BEGIN");
1367# endif /* DEBUG */
1368
1369 if (nntp_server == NULL) {
1370 error_message(2, _(txt_cannot_get_nntp_server_name));
1371 error_message(2, _(txt_server_name_in_file_env_var), NNTP_SERVER_FILE);
1372 return -EHOSTUNREACH;
1373 }
1374
1375 if (!batch_mode || verbose) {
1376 if (nntp_tcp_port != IPPORT_NNTP)
1377 wait_message(0, _(txt_connecting_port), nntp_server, nntp_tcp_port);
1378 else
1379 wait_message(0, _(txt_connecting), nntp_server);
1380 }
1381
1382# ifdef DEBUG
1383 if ((debug & DEBUG_NNTP) && verbose > 1)
1384 debug_print_file("NNTP", "nntp_open() %s:%d", nntp_server, nntp_tcp_port);
1385# endif /* DEBUG */
1386
1387 ret = server_init(nntp_server, NNTP_TCP_NAME, nntp_tcp_port, line, sizeof(line));
1388 DEBUG_IO((stderr, "server_init returns %d,%s\n", ret, line));
1389
1390 if ((!batch_mode || verbose) && ret >= 0)
1391 my_fputc('\n', stdout);
1392
1393# ifdef DEBUG
1394 if ((debug & DEBUG_NNTP) && verbose > 1)
1395 debug_print_file("NNTP", "nntp_open() %s", line);
1396# endif /* DEBUG */
1397
1398 switch (ret) {
1399 /*
1400 * ret < 0 : some error from system call
1401 * ret > 0 : NNTP response code
1402 *
1403 * According to the ietf-nntp mailinglist:
1404 * 200 you may (try to) do anything
1405 * 201 you may not POST
1406 * All unrecognised 200 series codes should be assumed as success.
1407 * All unrecognised 300 series codes should be assumed as notice to continue.
1408 * All unrecognised 400 series codes should be assumed as temporary error.
1409 * All unrecognised 500 series codes should be assumed as error.
1410 */
1411
1412 case OK_CANPOST:
1414 break;
1415
1416 case OK_NOPOST:
1417 can_post = FALSE;
1418 break;
1419
1420 default:
1421 if (ret >= 200 && ret <= 299) {
1423 break;
1424 }
1425 if (ret < 0)
1426 error_message(2, _(txt_failed_to_connect_to_server), nntp_server);
1427 else
1428 error_message(2, "%s", line);
1429
1430 return ret;
1431 }
1432 if (!is_reconnect && *line) {
1433 /* remove leading whitespace and save server's initial response */
1434 linep = line;
1435 while (isspace((int) *linep))
1436 linep++;
1437
1438 STRCPY(bug_nntpserver1, linep);
1439 }
1440
1441 /*
1442 * Find out which NNTP extensions are available
1443 * - Typhoon v2.1.1.363 closes the connection after an unknown command
1444 * (i.e. CAPABILITIES) but as we are not allowed to cache CAPABILITIES
1445 * we reissue the command on reconnect. To prevent a loop we catch this
1446 * case.
1447 *
1448 * TODO: The authentication method required may be mentioned in the list
1449 * of extensions. (For details about authentication methods, see
1450 * RFC 4643).
1451 */
1452 if (nntp_caps.type != BROKEN)
1453 check_extensions(0);
1454
1455 /*
1456 * If the user wants us to authenticate on connection startup, do it now.
1457 * Some news servers return "201 no posting" first, but after successful
1458 * authentication you get a "200 posting allowed". To find out if we are
1459 * allowed to post after authentication issue a "MODE READER" again and
1460 * interpret the response code.
1461 */
1462
1464 if (nntp_caps.mode_reader) {
1465 char buf[NNTP_STRLEN];
1466
1467# ifdef DEBUG
1468 if ((debug & DEBUG_NNTP) && verbose > 1)
1469 debug_print_file("NNTP", "nntp_open(MODE READER)");
1470# endif /* DEBUG */
1471 put_server("MODE READER");
1472 switch (get_only_respcode(buf, sizeof(buf))) {
1473 /* just honor critical errors */
1474 case ERR_GOODBYE:
1475 case ERR_ACCESS:
1476 error_message(2, "%s", buf);
1477 return -1;
1478
1479 default:
1480 break;
1481 }
1482 check_extensions(0);
1483 }
1484 }
1485
1486 if (force_auth_on_conn_open) {
1487# ifdef DEBUG
1488 if ((debug & DEBUG_NNTP) && verbose > 1)
1489 debug_print_file("NNTP", "nntp_open(authenticate(force_auth_on_conn_open))");
1490# endif /* DEBUG */
1491
1492 if (!authenticate(nntp_server, userid, FALSE)) /* 3rd parameter is FALSE as we need to get prompted for username password here */
1493 return -1;
1494 }
1495
1497 if ((ret = mode_reader(&sec))) {
1500
1501 return ret;
1502 }
1504 check_extensions(0);
1505 }
1506
1507 if (nntp_caps.type == CAPABILITIES) {
1508 if (!nntp_caps.reader) {
1509# ifdef DEBUG
1510 if ((debug & DEBUG_NNTP) && verbose > 1)
1511 debug_print_file("NNTP", "CAPABILITIES did not announce READER");
1512# endif /* DEBUG */
1513 error_message(2, _("CAPABILITIES did not announce READER")); /* TODO: -> lang.c */
1514 return -1; /* give up */
1515 }
1517 }
1518
1519 if (!is_reconnect && *line) {
1520# if 0
1521 /*
1522 * gives wrong results if RFC 3977 server requests auth after
1523 * CAPABILITIES is parsed (with no posting allowed) and after auth
1524 * posting is allowed. as we will inform the user later on when he
1525 * actually tries to post it should do no harm to skip this message
1526 */
1527 /* Inform user if he cannot post */
1528 if (!can_post && !batch_mode)
1529 wait_message(0, "%s\n", _(txt_cannot_post));
1530# endif /* 0 */
1531
1532 /* Remove leading white space and save server's second response */
1533 linep = line;
1534 while (isspace((int) *linep))
1535 linep++;
1536
1537 STRCPY(bug_nntpserver2, linep);
1538
1539 /*
1540 * Show user last server response line, do some nice formatting if
1541 * response is longer than a screen wide.
1542 *
1543 * TODO: This only breaks the line once, but the response could be
1544 * longer than two lines ...
1545 */
1546 if (!batch_mode || verbose) {
1547 char *chr1, *chr2;
1548 int j;
1549
1550 j = atoi(get_val("COLUMNS", "80"));
1551 chr1 = my_strdup((sec ? bug_nntpserver2 : bug_nntpserver1));
1552
1553 if (j > MIN_COLUMNS_ON_TERMINAL && ((int) strlen(chr1)) >= j) {
1554 chr2 = chr1 + strlen(chr1) - 1;
1555 while (chr2 - chr1 >= j)
1556 chr2--;
1557 while (chr2 > chr1 && *chr2 != ' ')
1558 chr2--;
1559 if (chr2 != chr1)
1560 *chr2 = '\n';
1561 }
1562
1563 wait_message(0, "%s\n", chr1);
1564 free(chr1);
1565 }
1566 }
1567
1568 /*
1569 * If CAPABILITIES failed, check if NNTP supports XOVER or OVER command
1570 * We have to check that we _don't_ get an ERR_COMMAND
1571 */
1572 if (nntp_caps.type != CAPABILITIES) {
1573 int i, j = 0;
1574
1575 for (i = 0; i < 2 && j >= 0; i++) {
1576 j = new_nntp_command(&xover_cmds[i], ERR_NCING, line, sizeof(line));
1577 switch (j) {
1578 case ERR_COMMAND:
1579 break;
1580
1581 case OK_XOVER: /* unexpected multiline ok, e.g.: Synchronet 3.13 NNTP Service 1.92 or on reconnect if last cmd was GROUP */
1582 nntp_caps.over_cmd = &xover_cmds[i];
1583# ifdef DEBUG
1584 if ((debug & DEBUG_NNTP) && verbose > 1)
1585 debug_print_file("NNTP", "nntp_open() %s skipping data", &xover_cmds[i]);
1586# endif /* DEBUG */
1587 while (tin_fgets(FAKE_NNTP_FP, FALSE))
1588 ;
1589 j = -1;
1590 break;
1591
1592 default:
1593 nntp_caps.over_cmd = &xover_cmds[i];
1594 j = -1;
1595 break;
1596 }
1597 }
1598 for (i = 0, j = 0; i < 2 && j >= 0; i++) {
1599 j = new_nntp_command(&xhdr_cmds[i], ERR_CMDSYN, line, sizeof(line));
1600 switch (j) {
1601 case ERR_COMMAND:
1602 break;
1603
1604 case 221: /* unexpected multiline ok, e.g.: SoftVelocity Discussions 2.5q */
1605 nntp_caps.hdr_cmd = &xhdr_cmds[i];
1606# ifdef DEBUG
1607 if ((debug & DEBUG_NNTP) && verbose > 1)
1608 debug_print_file("NNTP", "nntp_open() %s skipping data", &xhdr_cmds[i]);
1609# endif /* DEBUG */
1610 while (tin_fgets(FAKE_NNTP_FP, FALSE))
1611 ;
1612 j = -1;
1613 break;
1614
1615 default: /* usually ERR_CMDSYN (args missing), Typhoon/Twister sends ERR_NCING */
1616 nntp_caps.hdr_cmd = &xhdr_cmds[i];
1617 j = -1;
1618 break;
1619 }
1620 }
1621 /* no XPAT probing here, we do when it's needed */
1623# if 0
1624 switch (new_nntp_command("XPAT Newsgroups <0> *", ERR_NOART, line, sizeof(line))) {
1625 case ERR_NOART:
1627 break;
1628
1629 default:
1630 break;
1631 }
1632# endif /* 0 */
1633 } else {
1634 if (!nntp_caps.over_cmd) {
1635 /*
1636 * CAPABILITIES didn't mention OVER or XOVER, try XOVER
1637 */
1638 switch (new_nntp_command(xover_cmds, ERR_NCING, line, sizeof(line))) {
1639 case ERR_COMMAND:
1640 break;
1641
1642 case OK_XOVER: /* unexpected multiline ok, e.g.: Synchronet 3.13 NNTP Service 1.92 or on reconnect if last cmd was GROUP */
1643 nntp_caps.over_cmd = xover_cmds;
1644# ifdef DEBUG
1645 if ((debug & DEBUG_NNTP) && verbose > 1)
1646 debug_print_file("NNTP", "nntp_open() %s skipping data", xover_cmds);
1647# endif /* DEBUG */
1648 while (tin_fgets(FAKE_NNTP_FP, FALSE))
1649 ;
1650 break;
1651
1652 default:
1653 nntp_caps.over_cmd = xover_cmds;
1654 break;
1655 }
1656 }
1657 if (!nntp_caps.hdr_cmd) {
1658 /*
1659 * CAPABILITIES didn't mention HDR or XHDR, try XHDR
1660 */
1661 switch (new_nntp_command(xhdr_cmds, ERR_NCING, line, sizeof(line))) {
1662 case ERR_COMMAND:
1663 break;
1664
1665 case 221: /* unexpected multiline ok, e.g.: SoftVelocity Discussions 2.5q */
1666 nntp_caps.hdr_cmd = xhdr_cmds;
1667# ifdef DEBUG
1668 if ((debug & DEBUG_NNTP) && verbose > 1)
1669 debug_print_file("NNTP", "nntp_open() %s skipping data", xhdr_cmds);
1670# endif /* DEBUG */
1671 while (tin_fgets(FAKE_NNTP_FP, FALSE))
1672 ;
1673 break;
1674
1675 default: /* ERR_NCING or ERR_CMDSYN */
1676 nntp_caps.hdr_cmd = xhdr_cmds;
1677 break;
1678 }
1679 }
1680 }
1681
1682 if (!nntp_caps.over_cmd) {
1683 if (!is_reconnect && !batch_mode) {
1684 wait_message(2, _(txt_no_xover_support));
1685
1687 wait_message(2, _(txt_caching_on));
1688 else
1689 wait_message(2, _(txt_caching_off));
1690 }
1691 }
1692# if 0
1693 else {
1694 /*
1695 * TODO: issue warning if old index files found?
1696 * in index_newsdir?
1697 */
1698 }
1699# endif /* 0 */
1700
1701 if (!is_reconnect && !batch_mode && show_description && check_for_new_newsgroups) {
1702 /*
1703 * TODO:
1704 * - add a tinrc var to turn LIST MOTD on/off?
1705 * (currently done automatically for -d, -q and -Q)
1706 */
1707 if (nntp_caps.list_motd)
1708 list_motd();
1709 }
1710
1711 is_reconnect = TRUE;
1712
1713#endif /* NNTP_ABLE */
1714
1715 DEBUG_IO((stderr, "nntp_open okay\n"));
1716 return 0;
1717}
1718
1719
1720/*
1721 * 'Public' function to shutdown the NNTP connection
1722 */
1723void
1725 t_bool send_no_quit)
1726{
1727#ifdef NNTP_ABLE
1729# ifdef DEBUG
1730 if ((debug & DEBUG_NNTP) && verbose > 1)
1731 debug_print_file("NNTP", "nntp_close(%s) END", bool_unparse(send_no_quit));
1732# endif /* DEBUG */
1733 close_server(send_no_quit);
1734 }
1735#endif /* NNTP_ABLE */
1736}
1737
1738
1739#ifdef NNTP_ABLE
1740/*
1741 * Get a response code from the server.
1742 * Returns:
1743 * +ve NNTP return code
1744 * -1 on an error or user abort. We don't differentiate.
1745 * If 'message' is not NULL, then any trailing text after the response
1746 * code is copied into it.
1747 * Does not perform authentication if required; use get_respcode()
1748 * instead.
1749 */
1750int
1751get_only_respcode(
1752 char *message,
1753 size_t mlen)
1754{
1755 int respcode;
1756 char *end, *ptr;
1757
1759
1760 if (tin_errno || ptr == NULL) {
1761# ifdef DEBUG
1762 if ((debug & DEBUG_NNTP) && verbose > 1)
1763 debug_print_file("NNTP", "Error: tin_error<>0 or ptr==NULL in get_only_respcode()");
1764# endif /* DEBUG */
1765 return -1;
1766 }
1767
1768# ifdef DEBUG
1769 if (debug & DEBUG_NNTP)
1770 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1771# endif /* DEBUG */
1772 respcode = (int) strtol(ptr, &end, 10);
1773 if (end == ptr) /* no leading numbers in response */
1774 respcode = -1;
1775 DEBUG_IO((stderr, "get_only_respcode(%d)\n", respcode));
1776
1777 /*
1778 * we also reconnect on ERR_FAULT if last_put was ARTICLE or LIST or POST
1779 * as inn (2.2.3) sends ERR_FAULT on timeout
1780 *
1781 * what about other LIST cmds? (ACTIVE|COUNTS|OVERVIEW.FMT|...)
1782 */
1783 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")) {
1784 /*
1785 * Maybe server timed out.
1786 * If so, retrying will force a reconnect.
1787 */
1788# ifdef DEBUG
1789 if ((debug & DEBUG_NNTP) && verbose > 1)
1790 debug_print_file("NNTP", "get_only_respcode() timeout");
1791# endif /* DEBUG */
1792 put_server(last_put);
1794
1795 if (tin_errno || ptr == NULL) {
1796# ifdef DEBUG
1797 if ((debug & DEBUG_NNTP) && verbose > 1)
1798 debug_print_file("NNTP", "Error: tin_errno<>0 or ptr==NULL in get_only_respcode(retry)");
1799# endif /* DEBUG */
1800 return -1;
1801 }
1802
1803# ifdef DEBUG
1804 if (debug & DEBUG_NNTP)
1805 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1806# endif /* DEBUG */
1807 respcode = (int) strtol(ptr, &end, 10);
1808 if (end == ptr) /* no leading numbers in response */
1809 respcode = -1;
1810 DEBUG_IO((stderr, "get_only_respcode(%d)\n", respcode));
1811 }
1812 if (message != NULL && mlen > 1 && *end != '\0') /* Pass out the rest of the text */
1813 my_strncpy(message, ++end, mlen - 1);
1814
1815 return respcode;
1816}
1817
1818
1819/*
1820 * Get a response code from the server.
1821 * Returns:
1822 * +ve NNTP return code
1823 * -1 on an error
1824 * If 'message' is not NULL, then any trailing text after the response
1825 * code is copied into it.
1826 * Performs authentication if required and repeats the last command if
1827 * necessary after a timeout.
1828 *
1829 * TODO: make this handle 401 and 483 (RFC 3977) return codes.
1830 * as 401 requires the examination of the returned text besides the
1831 * return value, we have to "fix" all nntp_command(..., NULL, 0) and
1832 * get_only_respcode(NULL, 0) calls to do this properly.
1833 */
1834int
1835get_respcode(
1836 char *message,
1837 size_t mlen)
1838{
1839 int respcode;
1840 char savebuf[NNTP_STRLEN];
1841 char *ptr, *end;
1842
1843 respcode = get_only_respcode(message, mlen);
1844 if ((respcode == ERR_NOAUTH) || (respcode == NEED_AUTHINFO)) {
1845 /*
1846 * Server requires authentication.
1847 */
1848# ifdef DEBUG
1849 if ((debug & DEBUG_NNTP) && verbose > 1)
1850 debug_print_file("NNTP", "get_respcode() authentication");
1851# endif /* DEBUG */
1852 STRCPY(savebuf, last_put);
1853
1854 if (!authenticate(nntp_server, userid, FALSE))
1856
1859 else {
1860 put_server("MODE READER");
1861 if (get_only_respcode(message, mlen) == OK_CANPOST)
1863 }
1864 if (curr_group != NULL) {
1865 DEBUG_IO((stderr, _("Rejoin current group\n")));
1866 snprintf(last_put, sizeof(last_put), "GROUP %s", curr_group->name);
1867 put_server(last_put);
1868 if (s_gets(last_put, NNTP_STRLEN, nntp_rd_fp) == NULL)
1869 *last_put = '\0';
1870# ifdef DEBUG
1871 if (debug & DEBUG_NNTP)
1872 debug_print_file("NNTP", "<<<%s%s", logtime(), last_put);
1873# endif /* DEBUG */
1874 DEBUG_IO((stderr, _("Read (%s)\n"), last_put));
1875 }
1876 STRCPY(last_put, savebuf);
1877
1878 put_server(last_put);
1880
1881 if (tin_errno) {
1882# ifdef DEBUG
1883 if ((debug & DEBUG_NNTP) && verbose > 1)
1884 debug_print_file("NNTP", "Error: tin_errno <> 0");
1885# endif /* DEBUG */
1886 return -1;
1887 }
1888
1889# ifdef DEBUG
1890 if (debug & DEBUG_NNTP)
1891 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1892# endif /* DEBUG */
1893 if (ptr == NULL)
1894 return -1;
1895
1896 respcode = (int) strtol(ptr, &end, 10);
1897 if (end == ptr) /* no leading numbers in response */
1898 return -1;
1899
1900 if (message != NULL && mlen > 1) /* Pass out the rest of the text */
1901 strncpy(message, end, mlen - 1);
1902 }
1903 return respcode;
1904}
1905
1906
1907/*
1908 * Do an NNTP command. Send command to server, and read the reply.
1909 * If the reply code matches success, then return an open file stream
1910 * Return NULL if we did not see the response we wanted.
1911 * If message is not NULL, then the trailing text of the reply string is
1912 * copied into it for the caller to process.
1913 */
1914FILE *
1915nntp_command(
1916 const char *command,
1917 int success,
1918 char *message,
1919 size_t mlen)
1920{
1921DEBUG_IO((stderr, "nntp_command(%s)\n", command));
1922# ifdef DEBUG
1923 if ((debug & DEBUG_NNTP) && verbose > 1)
1924 debug_print_file("NNTP", "nntp_command(%s)", command);
1925# endif /* DEBUG */
1926 put_server(command);
1927
1929 if (get_respcode(message, mlen) != success) {
1930# ifdef DEBUG
1931 if ((debug & DEBUG_NNTP) && verbose > 1)
1932 debug_print_file("NNTP", "nntp_command(%s) NOT_OK", command);
1933# endif /* DEBUG */
1934 /* error_message(2, "%s", message); */
1935 return (FILE *) 0;
1936 }
1937 }
1938# ifdef DEBUG
1939 if ((debug & DEBUG_NNTP) && verbose > 1)
1940 debug_print_file("NNTP", "nntp_command(%s) OK", command);
1941# endif /* DEBUG */
1942 return FAKE_NNTP_FP;
1943}
1944
1945
1946/*
1947 * same as above, but with a slightly more useful return code.
1948 * TODO: use it instead of nntp_command in the rest of the code
1949 * (wherever it is more useful).
1950 */
1951int
1952new_nntp_command(
1953 const char *command,
1954 int success,
1955 char *message,
1956 size_t mlen)
1957{
1958 int respcode = 0;
1959
1960DEBUG_IO((stderr, "new_nntp_command(%s)\n", command));
1961# ifdef DEBUG
1962 if ((debug & DEBUG_NNTP) && verbose > 1)
1963 debug_print_file("NNTP", "new_nntp_command(%s)", command);
1964# endif /* DEBUG */
1965 put_server(command);
1966
1968 if ((respcode = get_respcode(message, mlen)) != success) {
1969# ifdef DEBUG
1970 if ((debug & DEBUG_NNTP) && verbose > 1)
1971 debug_print_file("NNTP", "new_nntp_command(%s) NOT_OK - Expected: %d, got: %d", command, success, respcode);
1972# endif /* DEBUG */
1973 return respcode;
1974 }
1975 }
1976# ifdef DEBUG
1977 if ((debug & DEBUG_NNTP) && verbose > 1)
1978 debug_print_file("NNTP", "new_nntp_command(%s) OK", command);
1979# endif /* DEBUG */
1980 return respcode;
1981}
1982
1983
1984static void
1985list_motd(
1986 void)
1987{
1988 char *ptr;
1989 char *p;
1990 char buf[NNTP_STRLEN];
1991 int i;
1992 size_t len;
1993 unsigned int l = 0;
1994
1995 buf[0] = '\0';
1996 i = new_nntp_command("LIST MOTD", OK_MOTD, buf, sizeof(buf));
1997
1998 switch (i) {
1999 case OK_MOTD:
2000# ifdef HAVE_COLOR
2001 fcol(tinrc.col_message);
2002# endif /* HAVE_COLOR */
2003 while ((ptr = tin_fgets(FAKE_NNTP_FP, FALSE)) != NULL) {
2004# ifdef DEBUG
2005 if (debug & DEBUG_NNTP)
2006 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
2007# endif /* DEBUG */
2008 /*
2009 * RFC 6048 2.5.2 "The information MUST be in UTF-8"
2010 *
2011 * TODO: - store a hash value of the entire motd in the server-rc
2012 * and only if it differs from the old value display the
2013 * motd?
2014 * - use some sort of pager?
2015 * - -> lang.c
2016 */
2017 p = my_strdup(ptr);
2018 len = strlen(p);
2019 process_charsets(&p, &len, "UTF-8", tinrc.mm_local_charset, FALSE);
2020 my_printf("%s%s\n", _("MOTD: "), p);
2021 free(p);
2022 l++;
2023 }
2024# ifdef HAVE_COLOR
2025 fcol(tinrc.col_normal);
2026# endif /* HAVE_COLOR */
2027 if (l) {
2028 my_flush();
2029 sleep((l >> 1) | 0x01);
2030 }
2031 break;
2032
2033 default: /* common response codes are 500, 501, 503 */
2034 break;
2035 }
2036}
2037#endif /* NNTP_ABLE */
#define bool_equal(a, b)
Definition: bool.h:82
unsigned t_bool
Definition: bool.h:77
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
#define bool_unparse(b)
Definition: bool.h:83
#define DEBUG_IO(x)
Definition: debug.h:62
#define DEBUG_NNTP
Definition: debug.h:47
char article_name[PATH_LEN]
Definition: init.c:67
int verbose
Definition: init.c:154
constext txt_cannot_post[]
Definition: lang.c:130
t_bool check_for_new_newsgroups
Definition: init.c:128
t_bool show_description
Definition: init.c:153
struct t_capabilities nntp_caps
Definition: init.c:513
int tin_errno
Definition: read.c:59
char dead_article[PATH_LEN]
Definition: init.c:71
t_bool read_saved_news
Definition: init.c:152
char bug_nntpserver2[PATH_LEN]
Definition: init.c:69
char userid[PATH_LEN]
Definition: init.c:107
int signal_context
Definition: signal.c:105
t_bool dangerous_signal_exit
Definition: init.c:131
char bug_nntpserver1[PATH_LEN]
Definition: init.c:68
int cCOLS
Definition: curses.c:53
struct t_group * curr_group
Definition: group.c:55
struct t_config tinrc
Definition: init.c:192
unsigned short debug
Definition: debug.c:51
struct t_cmdlineopts cmdline
Definition: init.c:190
t_bool read_news_via_nntp
Definition: init.c:151
char dead_articles[PATH_LEN]
Definition: init.c:72
t_bool batch_mode
Definition: init.c:127
t_bool force_no_post
Definition: init.c:137
static char buf[16]
Definition: langinfo.c:50
char * nntp_server
Definition: nntplib.c:28
int nntp_open(void)
Definition: nntplib.c:1350
char * getserverbyfile(const char *file)
Definition: nntplib.c:111
FILE * get_nntp_fp(FILE *fp)
Definition: nntplib.c:79
t_bool can_post
Definition: nntplib.c:32
void nntp_close(t_bool send_no_quit)
Definition: nntplib.c:1724
static TCP * nntp_rd_fp
Definition: nntplib.c:42
#define NNTP_TRY_RECONNECT
Definition: nntplib.h:54
#define OK_CANPOST
Definition: nntplib.h:90
#define NNTP_STRLEN
Definition: nntplib.h:155
#define ERR_NCING
Definition: nntplib.h:127
#define NNTP_TCP_NAME
Definition: nntplib.h:48
#define ERR_GOODBYE
Definition: nntplib.h:123
#define OK_MOTD
Definition: nntplib.h:97
#define NNTP_SERVER_FILE
Definition: nntplib.h:45
#define ERR_COMMAND
Definition: nntplib.h:144
@ COMPRESS_DEFLATE
Definition: nntplib.h:184
@ COMPRESS_NONE
Definition: nntplib.h:184
#define OK_XOVER
Definition: nntplib.h:105
#define ERR_FAULT
Definition: nntplib.h:147
@ SASL_PLAIN
Definition: nntplib.h:175
@ SASL_DIGEST_MD5
Definition: nntplib.h:177
@ SASL_OTP
Definition: nntplib.h:180
@ SASL_NTLM
Definition: nntplib.h:181
@ SASL_NONE
Definition: nntplib.h:174
@ SASL_GSSAPI
Definition: nntplib.h:178
@ SASL_CRAM_MD5
Definition: nntplib.h:176
@ SASL_EXTERNAL
Definition: nntplib.h:179
@ SASL_LOGIN
Definition: nntplib.h:182
#define ERR_CMDSYN
Definition: nntplib.h:145
@ CAPABILITIES
Definition: nntplib.h:171
@ NONE
Definition: nntplib.h:171
@ BROKEN
Definition: nntplib.h:171
#define OK_GOODBYE
Definition: nntplib.h:93
#define OK_NOPOST
Definition: nntplib.h:91
#define ERR_AUTHFAIL
Definition: nntplib.h:140
#define ERR_NOAUTH
Definition: nntplib.h:139
#define ERR_NOART
Definition: nntplib.h:133
#define ERR_ACCESS
Definition: nntplib.h:146
#define NEED_AUTHINFO
Definition: nntplib.h:119
#define INF_CAPABILITIES
Definition: nntplib.h:87
static char * end
Definition: plp_snprintf.c:205
int errno
long strtol(const char *str, char **ptr, int use_base)
Definition: string.c:417
char * strpbrk(const char *str1, const char *str2)
Definition: string.c:312
char * str_trim(char *string)
Definition: string.c:539
int atoi(const char *s)
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:2656
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:224
_Noreturn void tin_done(int ret, const char *fmt,...)
Definition: misc.c:562
void perror_message(const char *fmt,...)
Definition: screen.c:260
void append_file(char *old_filename, char *new_filename)
Definition: misc.c:120
const char * get_val(const char *env, const char *def)
Definition: misc.c:361
void ring_bell(void)
Definition: screen.c:480
char * my_strdup(const char *str)
Definition: string.c:139
void rename_file(const char *old_filename, const char *new_filename)
Definition: misc.c:742
void clear_message(void)
Definition: screen.c:283
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:317
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
void my_strncpy(char *p, const char *q, size_t n)
Definition: string.c:196
char * backup_article_name(const char *the_article)
Definition: post.c:341
int strncasecmp(const char *p, const char *q, size_t n)
Definition: string.c:491
int strcasecmp(const char *p, const char *q)
Definition: string.c:475
int prompt_yn(const char *prompt, t_bool default_answer)
Definition: prompt.c:165
char * strunc(const char *message, int len)
Definition: string.c:1076
#define strerror(n)
Definition: proto.h:700
t_bool list_newsgroups
Definition: nntplib.h:198
t_bool list_active_times
Definition: nntplib.h:193
t_bool over
Definition: nntplib.h:208
const char * hdr_cmd
Definition: nntplib.h:207
t_bool hdr
Definition: nntplib.h:206
t_bool list_distributions
Definition: nntplib.h:202
t_bool list_counts
Definition: nntplib.h:204
t_bool list_overview_fmt
Definition: nntplib.h:199
t_bool list_distrib_pats
Definition: nntplib.h:194
t_bool compress
Definition: nntplib.h:218
t_bool post
Definition: nntplib.h:191
char * implementation
Definition: nntplib.h:212
t_bool over_msgid
Definition: nntplib.h:209
char * headers_range
Definition: nntplib.h:196
t_bool authinfo_state
Definition: nntplib.h:216
t_bool list_moderators
Definition: nntplib.h:203
t_bool list_subscriptions
Definition: nntplib.h:201
const char * over_cmd
Definition: nntplib.h:210
unsigned int version
Definition: nntplib.h:188
t_bool starttls
Definition: nntplib.h:213
char * headers_id
Definition: nntplib.h:197
t_bool mode_reader
Definition: nntplib.h:189
enum sasl_types sasl
Definition: nntplib.h:217
t_bool xpat
Definition: nntplib.h:205
enum extension_type type
Definition: nntplib.h:187
t_bool authinfo_sasl
Definition: nntplib.h:215
t_bool list_headers
Definition: nntplib.h:195
t_bool list_motd
Definition: nntplib.h:200
t_bool list_active
Definition: nntplib.h:192
t_bool reader
Definition: nntplib.h:190
enum c_algorithms compress_algorithm
Definition: nntplib.h:219
t_bool authinfo_user
Definition: nntplib.h:214
t_bool broken_listgroup
Definition: nntplib.h:224
t_bool newnews
Definition: nntplib.h:211
unsigned int args
Definition: tin.h:1503
char nntpserver[255]
Definition: tin.h:1501
t_bool keep_dead_articles
Definition: tinrc.h:228
t_bool cache_overview_files
Definition: tinrc.h:220
t_bool auto_reconnect
Definition: tinrc.h:217
char mm_local_charset[LEN]
Definition: tinrc.h:115
char * name
Definition: tin.h:1817
#define my_flush()
Definition: tcurses.h:177
#define my_fprintf
Definition: tcurses.h:176
#define my_fputs(str, stream)
Definition: tcurses.h:159
#define my_printf
Definition: tcurses.h:175
#define my_fputc(ch, stream)
Definition: tcurses.h:158
@ cReconnect
Definition: tin.h:107
#define STRCPY(dst, src)
Definition: tin.h:820
#define CMDLINE_NNTPSERVER
Definition: tin.h:1104
#define EHOSTUNREACH
Definition: tin.h:130
#define my_malloc(size)
Definition: tin.h:2245
#define unlink(file)
Definition: tin.h:387
#define NNTP_ERROR_EXIT
Definition: tin.h:1306
#define FreeIfNeeded(p)
Definition: tin.h:2252
#define EXIT_FAILURE
Definition: tin.h:1302
#define _(Text)
Definition: tin.h:94
#define snprintf
Definition: tin.h:2464
#define FreeAndNull(p)
Definition: tin.h:2253
const char constext
Definition: tin.h:2018
FILE TCP
Definition: tin.h:2312
#define MIN_COLUMNS_ON_TERMINAL
Definition: tin.h:1096
#define FAKE_NNTP_FP
Definition: tin.h:458
#define s_fdopen
Definition: tnntp.h:44
#define s_dup
Definition: tnntp.h:50
#define s_end()
Definition: tnntp.h:51
#define s_gets
Definition: tnntp.h:47
#define s_puts
Definition: tnntp.h:49
#define s_close
Definition: tnntp.h:48
#define s_flush
Definition: tnntp.h:45
#define MAXHOSTNAMELEN
Definition: tnntp.h:114
#define s_fclose
Definition: tnntp.h:46