"Fossies" - the Fresh Open Source Software Archive 
Member "tin-2.6.1/src/nntplib.c" (22 Dec 2021, 55748 Bytes) of package /linux/misc/tin-2.6.1.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "nntplib.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
2.4.5_vs_2.6.0.
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
28 char *nntp_server = NULL;
29 #ifdef NO_POSTING
30 t_bool can_post = FALSE;
31 #else
32 t_bool can_post = TRUE;
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 *
79 get_nntp_fp(
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 *
111 getserverbyfile(
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
136 if (cmdline.args & CMDLINE_NNTPSERVER) {
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()");
251 s_fclose(nntp_rd_fp);
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()");
259 s_fclose(nntp_rd_fp);
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);
267 s_fclose(nntp_rd_fp);
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)
827 s_fclose(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 */
839 signal_context = cReconnect;
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)) {
848 unlink(backup_article_name(article_name));
849 rename_file(article_name, dead_article);
850 if (tinrc.keep_dead_articles)
851 append_file(dead_article, dead_articles);
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 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 */
906 char *
907 get_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");
960 nntp_caps.type = BROKEN;
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 */
993 static void
994 close_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 */
1026 int
1027 check_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 */
1043 nntp_caps.type = CAPABILITIES;
1044 nntp_caps.version = 0;
1045 nntp_caps.mode_reader = FALSE;
1046 nntp_caps.reader = FALSE;
1047 nntp_caps.post = FALSE;
1048 nntp_caps.list_active = FALSE;
1049 nntp_caps.list_active_times = FALSE;
1050 nntp_caps.list_distrib_pats = FALSE;
1051 nntp_caps.list_headers = FALSE;
1052 FreeAndNull(nntp_caps.headers_range);
1053 FreeAndNull(nntp_caps.headers_id);
1054 nntp_caps.list_newsgroups = FALSE;
1055 nntp_caps.list_overview_fmt = FALSE;
1056 nntp_caps.list_motd = FALSE;
1057 nntp_caps.list_subscriptions = FALSE;
1058 nntp_caps.list_distributions = FALSE;
1059 nntp_caps.list_moderators = FALSE;
1060 nntp_caps.list_counts = FALSE;
1061 nntp_caps.xpat = TRUE; /* toggles to false if fails, INN > 2.7.0 announces it */
1062 nntp_caps.hdr = FALSE;
1063 nntp_caps.hdr_cmd = NULL;
1064 nntp_caps.over = FALSE;
1065 nntp_caps.over_msgid = FALSE;
1066 nntp_caps.over_cmd = NULL;
1067 nntp_caps.newnews = FALSE;
1068 FreeAndNull(nntp_caps.implementation);
1069 nntp_caps.starttls = FALSE;
1070 nntp_caps.authinfo_user = FALSE;
1071 nntp_caps.authinfo_sasl = FALSE;
1072 nntp_caps.authinfo_state = FALSE;
1073 nntp_caps.sasl = SASL_NONE;
1074 nntp_caps.compress = FALSE;
1075 nntp_caps.compress_algorithm = COMPRESS_NONE;
1076 # if 0
1077 nntp_caps.streaming = FALSE;
1078 nntp_caps.ihave = FALSE;
1079 # endif /* 0 */
1080 # ifndef BROKEN_LISTGROUP
1081 nntp_caps.broken_listgroup = FALSE;
1082 # else
1083 nntp_caps.broken_listgroup = TRUE;
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) */
1092 if (!nntp_caps.version && nntp_caps.type == CAPABILITIES) {
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))
1114 nntp_caps.list_active_times = TRUE;
1115 else if (!strncasecmp(d, "ACTIVE", 6))
1116 nntp_caps.list_active = TRUE;
1117 else if (!strncasecmp(d, "DISTRIB.PATS", 12))
1118 nntp_caps.list_distrib_pats = TRUE;
1119 else if (!strncasecmp(d, "DISTRIBUTIONS", 13)) /* RFC 6048 */
1120 nntp_caps.list_distributions = TRUE;
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))
1124 nntp_caps.list_newsgroups = TRUE;
1125 else if (!strncasecmp(d, "OVERVIEW.FMT", 12)) /* OVER requires OVERVIEW.FMT, but not vice versa */
1126 nntp_caps.list_overview_fmt = TRUE;
1127 else if (!strncasecmp(d, "MOTD", 4)) /* RFC 6048 */
1128 nntp_caps.list_motd = TRUE;
1129 else if (!strncasecmp(d, "SUBSCRIPTIONS", 13)) /* RFC 6048 */
1130 nntp_caps.list_subscriptions = TRUE;
1131 else if (!strncasecmp(d, "MODERATORS", 10)) /* RFC 6048 */
1132 nntp_caps.list_moderators = TRUE;
1133 else if (!strncasecmp(d, "COUNTS", 6)) /* RFC 6048 */
1134 nntp_caps.list_counts = TRUE;
1135 d = strpbrk(d, " \t");
1136 }
1137 } else if (!strncasecmp(ptr, "IMPLEMENTATION", 14)) {
1138 FreeIfNeeded(nntp_caps.implementation);
1139 nntp_caps.implementation = my_strdup(ptr + 14);
1140 str_trim(nntp_caps.implementation);
1141 } else if (!strcasecmp(ptr, "MODE-READER")) {
1142 if (!nntp_caps.reader)
1143 nntp_caps.mode_reader = TRUE;
1144 } else if (!strcasecmp(ptr, "READER")) { /* if we saw READER, "LIST ACTIVE" and "LIST NEWSGROUPS" must be implemented */
1145 nntp_caps.reader = TRUE;
1146 nntp_caps.mode_reader = FALSE;
1147 nntp_caps.list_newsgroups = TRUE;
1148 nntp_caps.list_active = TRUE;
1149 } else if (!strcasecmp(ptr, "POST"))
1150 nntp_caps.post = TRUE;
1151 else if (!strcasecmp(ptr, "NEWNEWS"))
1152 nntp_caps.newnews = TRUE;
1153 else if (!strcasecmp(ptr, "XPAT")) /* extension, RFC 2980 */
1154 nntp_caps.xpat = TRUE;
1155 else if (!strcasecmp(ptr, "STARTTLS"))
1156 nntp_caps.starttls = TRUE;
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]))) {
1161 nntp_caps.list_overview_fmt = nntp_caps.over = TRUE;
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"))
1168 nntp_caps.over_msgid = TRUE;
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];
1177 nntp_caps.list_headers = nntp_caps.hdr = TRUE;
1178 nntp_caps.headers_range = my_strdup("");
1179 nntp_caps.headers_id = my_strdup("");
1180 } else if (!strncasecmp(ptr, "AUTHINFO", 8)) {
1181 d = ptr + 8;
1182 d = strpbrk(d, " \t");
1183 if (d == NULL) /* AUTHINFO without args */
1184 nntp_caps.authinfo_state = TRUE;
1185 while (d != NULL && (d + 1 < (ptr + strlen(ptr)))) {
1186 d++;
1187 if (!strncasecmp(d, "USER", 4))
1188 nntp_caps.authinfo_user = TRUE;
1189 if (!strncasecmp(d, "SASL", 4))
1190 nntp_caps.authinfo_sasl = TRUE;
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 */
1199 nntp_caps.authinfo_sasl = TRUE;
1200 nntp_caps.sasl |= SASL_CRAM_MD5;
1201 }
1202 if (!strncasecmp(d, "DIGEST-MD5", 10)) { /* RFC 2831 */
1203 nntp_caps.authinfo_sasl = TRUE;
1204 nntp_caps.sasl |= SASL_DIGEST_MD5;
1205 }
1206 if (!strncasecmp(d, "PLAIN", 5)) { /* RFC 4616 */
1207 nntp_caps.authinfo_sasl = TRUE;
1208 nntp_caps.sasl |= SASL_PLAIN;
1209 }
1210 if (!strncasecmp(d, "GSSAPI", 6)) { /* RFC 4752 */
1211 nntp_caps.authinfo_sasl = TRUE;
1212 nntp_caps.sasl |= SASL_GSSAPI;
1213 }
1214 if (!strncasecmp(d, "EXTERNAL", 8)) { /* RFC 4422 */
1215 nntp_caps.authinfo_sasl = TRUE;
1216 nntp_caps.sasl |= SASL_EXTERNAL;
1217 }
1218 /* inn also can do these */
1219 if (!strncasecmp(d, "OTP", 3)) { /* RFC 2444 */
1220 nntp_caps.authinfo_sasl = TRUE;
1221 nntp_caps.sasl |= SASL_OTP;
1222 }
1223 if (!strncasecmp(d, "NTLM", 4)) { /* Microsoft */
1224 nntp_caps.authinfo_sasl = TRUE;
1225 nntp_caps.sasl |= SASL_NTLM;
1226 }
1227 if (!strncasecmp(d, "LOGIN", 5)) { /* Microsoft */
1228 nntp_caps.authinfo_sasl = TRUE;
1229 nntp_caps.sasl |= SASL_LOGIN;
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)) {
1238 nntp_caps.compress = TRUE;
1239 nntp_caps.compress_algorithm |= COMPRESS_DEFLATE;
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
1251 nntp_caps.type = NONE;
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 */
1279 static int
1280 mode_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:
1311 can_post = TRUE && !force_no_post;
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 */
1349 int
1350 nntp_open(
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:
1413 can_post = TRUE && !force_no_post;
1414 break;
1415
1416 case OK_NOPOST:
1417 can_post = FALSE;
1418 break;
1419
1420 default:
1421 if (ret >= 200 && ret <= 299) {
1422 can_post = TRUE && !force_no_post;
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
1463 if (nntp_caps.type == CAPABILITIES && !nntp_caps.reader) {
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
1496 if ((nntp_caps.type == CAPABILITIES && nntp_caps.mode_reader) || nntp_caps.type != CAPABILITIES) {
1497 if ((ret = mode_reader(&sec))) {
1498 if (nntp_caps.type == CAPABILITIES)
1499 can_post = nntp_caps.post && !force_no_post;
1500
1501 return ret;
1502 }
1503 if (nntp_caps.type == CAPABILITIES)
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 }
1516 can_post = nntp_caps.post && !force_no_post;
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 */
1622 nntp_caps.xpat = TRUE;
1623 # if 0
1624 switch (new_nntp_command("XPAT Newsgroups <0> *", ERR_NOART, line, sizeof(line))) {
1625 case ERR_NOART:
1626 nntp_caps.xpat = TRUE;
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
1686 if (tinrc.cache_overview_files)
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 */
1723 void
1724 nntp_close(
1725 t_bool send_no_quit)
1726 {
1727 #ifdef NNTP_ABLE
1728 if (read_news_via_nntp && !read_saved_news) {
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 */
1750 int
1751 get_only_respcode(
1752 char *message,
1753 size_t mlen)
1754 {
1755 int respcode;
1756 char *end, *ptr;
1757
1758 ptr = tin_fgets(FAKE_NNTP_FP, FALSE);
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);
1793 ptr = tin_fgets(FAKE_NNTP_FP, FALSE);
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 */
1834 int
1835 get_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))
1855 tin_done(EXIT_FAILURE, _(txt_auth_failed), nntp_caps.type == CAPABILITIES ? ERR_AUTHFAIL : ERR_ACCESS);
1856
1857 if (nntp_caps.type == CAPABILITIES)
1858 can_post = nntp_caps.post && !force_no_post;
1859 else {
1860 put_server("MODE READER");
1861 if (get_only_respcode(message, mlen) == OK_CANPOST)
1862 can_post = TRUE && !force_no_post;
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);
1879 ptr = tin_fgets(FAKE_NNTP_FP, FALSE);
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 */
1914 FILE *
1915 nntp_command(
1916 const char *command,
1917 int success,
1918 char *message,
1919 size_t mlen)
1920 {
1921 DEBUG_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
1928 if (!bool_equal(dangerous_signal_exit, TRUE)) {
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 */
1951 int
1952 new_nntp_command(
1953 const char *command,
1954 int success,
1955 char *message,
1956 size_t mlen)
1957 {
1958 int respcode = 0;
1959
1960 DEBUG_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
1967 if (!bool_equal(dangerous_signal_exit, TRUE)) {
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
1984 static void
1985 list_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 */