1 /*
2 * Copyright (c) 1996-2005 Michael T Pins. All rights reserved.
3 *
4 * nntp module for nn.
5 *
6 * The original taken from the nntp 1.5 clientlib.c
7 * Modified heavily for nn.
8 *
9 * Rene' Seindal (seindal@diku.dk) Thu Dec 1 18:41:23 1988
10 *
11 * I have modified Rene's code quite a lot for 6.4 -- I hope he
12 * can still recognize a bit here and a byte there; in any case,
13 * any mistakes are mine :-) ++Kim
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <stdarg.h>
20 #include <sys/param.h>
21 #include <ctype.h>
22 #include "config.h"
23 #include "global.h"
24 #include "fullname.h"
25 #include "hash.h"
26 #include "hostname.h"
27 #include "libnov.h"
28 #include "nn_term.h"
29
30 /*
31 * nn maintains a cache of recently used articles to improve efficiency.
32 * To change the size of the cache, define NNTPCACHE in config.h to be
33 * the new size of this cache.
34 */
35
36 #ifndef NNTPCACHE
37 #define NNTPCACHE 10
38 #endif
39
40 #ifdef NNTP
41 #include "nntp.h"
42 #include "patchlevel.h"
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <string.h>
46 #include <strings.h>
47
48 #ifndef EXCELAN
49 #include <netdb.h>
50 #endif
51
52 #include <errno.h>
53 #include <pwd.h>
54
55 #ifdef NOV
56 #include "newsoverview.h"
57 #endif
58
59 /* This is necessary due to the definitions in m-XXX.h */
60
61 #if !defined(NETWORK_DATABASE) || defined(NETWORK_BYTE_ORDER)
62 #include <netinet/in.h>
63 #endif
64
65 #ifdef EXCELAN
66
67 #ifndef IPPORT_NNTP
68 #define IPPORT_NNTP 119
69 #endif
70
71 #endif
72
73 /* nntp.c */
74
75 static int reconnect_server(int);
76 static int connect_server(void);
77 static void debug_msg(char *prefix, char *str);
78 static void find_server(void);
79 static char *find_domain(const char *domainFile);
80 static int get_server_line(char *string, int size);
81 static int get_server(char *string, int size);
82 static int get_socket(void);
83 static void put_server(char *string);
84 static int copy_text(register FILE * fp);
85 static struct cache *search_cache(article_number art, group_header * gh);
86 static struct cache *new_cache_slot(void);
87 static void clean_cache(void);
88 static void set_domain(void);
89 static void nntp_doauth(void);
90
91 #ifndef NOV
92 static int sort_art_list(register article_number * f1, register article_number * f2);
93 #endif /* !NOV */
94
95 #ifdef NeXT
96 static char *strdup(char *);
97 #endif /* NeXT */
98
99 extern char *db_directory, *tmp_directory, *news_active;
100 extern char delayed_msg[];
101
102 static char host_name[MAXHOSTNAMELEN];
103 char domain[MAXHOSTNAMELEN];
104 static char last_put[NNTP_STRLEN];
105
106 int nntp_failed = 0;/* bool: t iff connection is broken in
107 * nntp_get_article() or nntp_get_active() */
108
109 int nntp_cache_size = NNTPCACHE;
110 char *nntp_cache_dir = NULL;
111 char *nntp_server = NNTP_SERVER; /* name of nntp server */
112 char *nntp_user, *nntp_password; /* so can set on command line */
113
114 int nntp_local_server = 0;
115 int nntp_debug = 0;
116
117 extern char *home_directory;
118 extern int silent;
119
120 extern char *mktemp();
121
122 static FILE *nntp_in = NULL; /* fp for reading from server */
123 static FILE *nntp_out = NULL;/* fp for writing to server */
124 static int reconnecting = 0;
125 static int is_connected = 0; /* bool: t iff we are connected */
126 static int need_auth = 0;
127 static group_header *group_hd; /* ptr to servers current group */
128 static int can_post = 0; /* bool: t iff NNTP server accepts postings */
129
130 #define ERR_TIMEOUT 503 /* Response code for timeout */
131 /* Same value as ERR_FAULT */
132
133 #ifdef NO_RENAME
134 static int
rename(char * old,char * new)135 rename(char *old, char *new)
136 {
137 if (unlink(new) < 0 && errno != ENOENT)
138 return -1;
139 if (link(old, new) < 0)
140 return -1;
141 return unlink(old);
142 }
143
144 #endif /* NO_RENAME */
145
146 /*
147 * debug_msg: print a debug message.
148 *
149 * The master appends prefix and str to a log file, and clients
150 * prints it as a message.
151 *
152 * This is controlled via the nntp-debug variable in nn, and
153 * the option -D2 (or -D3 if the normal -D option should also
154 * be turned on). Debug output from the master is written in
155 * $TMP/nnmaster.log.
156 */
157
158 static void
debug_msg(char * prefix,char * str)159 debug_msg(char *prefix, char *str)
160 {
161 static FILE *f = NULL;
162
163 if (who_am_i == I_AM_MASTER) {
164 if (f == NULL) {
165 f = open_file(relative(tmp_directory, "nnmaster.log"), OPEN_CREATE);
166 if (f == NULL) {
167 nntp_debug = 0;
168 return;
169 }
170 }
171 fprintf(f, "%s %s\n", prefix, str);
172 fflush(f);
173 return;
174 }
175 msg("NNTP%s %s", prefix, str);
176 user_delay(3);
177 }
178
179
180 /*
181 * find_server: Find out which host to use as NNTP server.
182 *
183 * This is done by consulting the file NNTP_SERVER (defined in
184 * config.h). Set nntp_server[] to the host's name.
185 */
186
187 static void
find_server(void)188 find_server(void)
189 {
190 char *cp, *name;
191 char buf[BUFSIZ];
192 FILE *fp;
193
194 /*
195 * This feature cannot normally be enabled, because the database and the
196 * users rc file contains references to articles by number, and these
197 * numbers are not unique across NNTP servers.
198 */
199
200 #ifdef NOV
201
202 /*
203 * But, since we no longer have a database to worry about, give the user
204 * the rope. If he wants to hang himself, then let him! :-) Let the user
205 * worry about keeping his .newsrc straight.
206 */
207 if ((cp = getenv("NNTPSERVER")) != NULL) {
208 nntp_server = cp;
209 return;
210 } else if (*nntp_server != '/')
211 return; /* variable was set on cmd line, or in init
212 * file */
213 #endif /* NOV */
214
215 name = nntp_server; /* default, or variable was set to a filename */
216
217 if ((fp = open_file(name, OPEN_READ)) != NULL) {
218 while (fgets(buf, sizeof buf, fp) != 0) {
219 if (*buf == '#' || *buf == '\n')
220 continue;
221 if ((cp = strchr(buf, '\n')) != 0)
222 *cp = '\0';
223 nntp_server = strdup(buf);
224 if (!nntp_server)
225 sys_error("Failed to allocate space for name of NNTP server!");
226 fclose(fp);
227 return;
228 }
229 fclose(fp);
230 }
231 if (who_am_i != I_AM_MASTER)
232 printf("\nCannot find name of NNTP server.\nCheck %s\n", name);
233
234 sys_error("Failed to find name of NNTP server!");
235 }
236
237
238 /*
239 * find_domain Get the domain name for posting from a named file.
240 * Handle blank lines and comments.
241 *
242 * Parameters: "file" is the name of the file to read.
243 *
244 * Returns: Pointer to static data area containing the
245 * first non-blank/comment line in the file.
246 * NULL on error (or lack of entry in file).
247 *
248 * Side effects: None.
249 */
250
251 static char *
find_domain(const char * domainFile)252 find_domain(const char *domainFile)
253 {
254 register FILE *fp;
255 register char *cp;
256 static char buf[MAXHOSTNAMELEN];
257 char *index();
258
259 if (domainFile == NULL)
260 return (NULL);
261
262 fp = fopen(domainFile, "r");
263 if (fp == NULL)
264 return (NULL);
265
266 while (fgets(buf, sizeof (buf), fp) != NULL) {
267 if (*buf == '\n' || *buf == '#')
268 continue;
269 cp = index(buf, '\n');
270 if (cp)
271 *cp = '\0';
272 (void) fclose(fp);
273 return (buf);
274 }
275
276 (void) fclose(fp);
277 return (NULL);
278 }
279
280
281 /*
282 * get_server_line: get a line from the server.
283 *
284 * Expects to be connected to the server.
285 * The line can be any kind of line, i.e., either response or text.
286 * Returns length of line if no error.
287 * If error and master, then return -1, else terminate.
288 */
289
290 static int
get_server_line(register char * string,register int size)291 get_server_line(register char *string, register int size)
292 {
293 int retry = 2;
294
295 while (fgets(string, size, nntp_in) == NULL)
296 retry = (reconnect_server(retry));
297
298 size = strlen(string);
299 if (size < 2 || !(string[size - 2] == '\r' && string[size - 1] == '\n'))
300 return size; /* XXX */
301
302 string[size - 2] = '\0'; /* nuke CRLF */
303 return size - 2;
304 }
305
306
307 /*
308 * get_server: get a response line from the server.
309 *
310 * Expects to be connected to the server.
311 * Returns the numerical value of the reponse, or -1 in case of errors.
312 */
313
314 static int
get_server(char * string,int size)315 get_server(char *string, int size)
316 {
317 if (get_server_line(string, size) < 0)
318 return -1;
319
320 if (nntp_debug)
321 debug_msg("<<<", string);
322
323 return isdigit(*string) ? atoi(string) : 0;
324 }
325
326 /*
327 * get_socket: get a connection to the nntp server.
328 *
329 * Errors can happen when YP services or DNS are temporarily down or
330 * hung, so we log errors and return failure rather than exitting if we
331 * are the master. The effects of retrying every 15 minutes (or whatever
332 * the -r interval is) are not that bad. Dave Olson, SGI
333 */
334
335 static int
get_socket(void)336 get_socket(void)
337 {
338 int s;
339 struct sockaddr_in sin;
340
341 #ifndef EXCELAN
342 struct servent *sp;
343 struct hostent *hp;
344
345 #ifdef h_addr
346 int x = 0;
347 register char **cp;
348 #endif /* h_addr */
349
350 if ((sp = getservbyname("nntp", "tcp")) == NULL)
351 return sys_warning("nntp/tcp: Unknown service.\n");
352
353 s = who_am_i == I_AM_MASTER ? 10 : 2;
354 while ((hp = gethostbyname(nntp_server)) == NULL) {
355 if (--s < 0)
356 goto host_err;
357 sleep(10);
358 }
359
360 clearobj(&sin, sizeof(sin), 1);
361 sin.sin_family = hp->h_addrtype;
362 sin.sin_port = sp->s_port;
363
364 #else /* EXCELAN */
365 char *machine;
366
367 clearobj(&sin, sizeof(sin), 1);
368 sin.sin_family = AF_INET;
369 sin.sin_port = htons(0);
370 #endif /* EXCELAN */
371
372 #ifdef h_addr
373 /* get a socket and initiate connection -- use multiple addresses */
374
375 s = x = -1;
376 for (cp = hp->h_addr_list; cp && *cp; cp++) {
377 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
378 if (s < 0)
379 goto sock_err;
380
381 #ifdef NO_MEMMOVE
382 bcopy(*cp, (char *) &sin.sin_addr, hp->h_length);
383 #else
384 memmove((char *) &sin.sin_addr, *cp, hp->h_length);
385 #endif /* NO_MEMMOVE */
386
387 /* Quick hack to work around interrupting system calls.. */
388 while ((x = connect(s, (struct sockaddr *) & sin, sizeof(sin))) < 0 &&
389 errno == EINTR)
390 sleep(1);
391 if (x == 0)
392 break;
393 if (who_am_i != I_AM_MASTER)
394 msg("Connecting to %s failed: %s", nntp_server, strerror(errno));
395 (void) close(s);
396 s = -1;
397 }
398 if (x < 0)
399 sys_warning("Giving up on NNTP server %s!", nntp_server);
400 #else /* h_addr */ /* no name server */
401
402 #ifdef EXCELAN
403 if ((s = socket(SOCK_STREAM, NULL, &sin, SO_KEEPALIVE)) < 0)
404 goto sock_err;
405
406 sin.sin_port = htons(IPPORT_NNTP);
407 machine = nntp_server;
408 if ((sin.sin_addr.s_addr = rhost(&machine)) == -1) {
409 (void) close(s);
410 goto host_err;
411 }
412 /* And then connect */
413 if (connect(s, &sin) < 0)
414 goto conn_err;
415 #else /* not EXCELAN */
416 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
417 goto sock_err;
418
419 /* And then connect */
420
421 #ifdef NO_MEMMOVE
422 bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
423 #else
424 memmove((char *) &sin.sin_addr, hp->h_addr, hp->h_length);
425 #endif /* NO_MEMMOVE */
426
427 if (connect(s, (struct sockaddr *) & sin, sizeof(sin)) < 0)
428 goto conn_err;
429 #endif /* EXCELAN */
430
431 #endif /* h_addr */
432
433 return s;
434
435 host_err:
436 sys_warning("NNTP server %s unknown.\n", nntp_server);
437 return -1;
438
439 sock_err:
440 sys_warning("Can't get NNTP socket: %s", strerror(errno));
441 return -1;
442
443 #ifndef h_addr
444 conn_err:
445 (void) close(s);
446 if (who_am_i == I_AM_MASTER)
447 sys_warning("Connecting to %s failed: %s", nntp_server, strerror(errno));
448 return -1;
449 #endif
450
451 }
452
453
454 /*
455 * connect_server: initialise a connection to the nntp server.
456 *
457 * It expects nntp_server[] to be set previously, by a call to
458 * nntp_check. It is called from nntp_get_article() and
459 * nntp_get_active() if there is no established connection.
460 * This gets called at initialization, and whenever it looks
461 * like the server may have closed the connection on us.
462 */
463
464 static int
connect_server(void)465 connect_server(void)
466 {
467 int sockt_rd, sockt_wr;
468 int response;
469 int triedauth = 0;
470 char line[NNTP_STRLEN];
471
472 if (who_am_i == I_AM_VIEW)
473 nntp_check();
474
475 if (who_am_i != I_AM_MASTER && !silent && !reconnecting)
476 msg("Connecting to NNTP server %s ...", nntp_server);
477
478 nntp_failed = 1;
479 is_connected = 0;
480
481 sockt_rd = get_socket();
482 if (sockt_rd < 0)
483 return -1;
484
485 if ((nntp_in = fdopen(sockt_rd, "r")) == NULL) {
486 close(sockt_rd);
487 return -1;
488 }
489 sockt_wr = dup(sockt_rd);
490 if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
491 close(sockt_wr);
492 fclose(nntp_in);
493 nntp_in = NULL; /* from above */
494 return -1;
495 }
496 /* Now get the server's signon message */
497 response = get_server(line, sizeof(line));
498
499 if (who_am_i == I_AM_MASTER) {
500 if (response != OK_CANPOST && response != OK_NOPOST) {
501 log_entry('N', "Failed to connect to NNTP server");
502 log_entry('N', "Response: %s", line);
503 fclose(nntp_out);
504 fclose(nntp_in);
505 return -1;
506 }
507 } else if (reconnecting && need_auth) {
508 nntp_doauth();
509 triedauth++;
510 } else {
511 switch (response) {
512 case OK_AUTH:
513 break;
514 case OK_CANPOST:
515 can_post = 1;
516 break;
517 case OK_NOPOST:
518 can_post = 0;
519 break;
520 default:
521 nn_exitmsg(1, "%s", line);
522 /* NOTREACHED */
523 }
524 }
525
526 retrymode:
527 /* Try and speak NNRP style reader protocol */
528 sprintf(line, "MODE READER");
529 put_server(line);
530 /* See what we got if from a NNRP compiant server like INN's nnrpd */
531 response = get_server(line, sizeof(line));
532
533 switch (response) {
534 case OK_CANPOST:
535 can_post = 1;
536 break;
537 case OK_NOPOST:
538 can_post = 0;
539 break;
540 case ERR_NEEDAUTH:
541 if (!triedauth) {
542 nntp_doauth();
543 triedauth++;
544 goto retrymode;
545 }
546 break; /* will probably fail hard later */
547 case ERR_COMMAND:
548 default:
549 /* if it doesn't understand MODE READER, we dont care.. :-) */
550 break;
551 }
552
553 if (!can_post && !triedauth) {
554
555 /*
556 * sometimes servers say no posting until auth done, so try. it might
557 * be better if this was controlled by some variable, since some
558 * people are OK with readonly access
559 */
560 msg("No post, so trying authorization; if readonly OK, just skip");
561 nntp_doauth();
562 triedauth++;
563 goto retrymode;
564 }
565 if (who_am_i != I_AM_MASTER && !silent && !reconnecting)
566 msg("Connecting to NNTP server %s ... ok (%s)",
567 nntp_server, can_post ? "posting is allowed" : "no posting");
568
569 is_connected = 1;
570 nntp_failed = 0;
571 return 0;
572 }
573
574
575 /*
576 * reconnect_server -- reconnect to server after a timeout.
577 * Re-establish correct group and resend last command to get back to the
578 * correct state.
579 *
580 * Returns: -1 if the error is fatal (and we should exit).
581 * # of retries left otherwise.
582 *
583 * Side Effects: If fails to connect and retry==0, calls nn_exitmsg.
584 */
585
586 static int
reconnect_server(int retry)587 reconnect_server(int retry)
588 {
589 char buf[NNTP_STRLEN];
590
591 retry--;
592 msg("Reconnecting");
593 if (nntp_debug) {
594 msg("retry = %d", retry);
595 user_delay(2);
596 }
597 reconnecting = 1;
598 strcpy(buf, last_put);
599 nntp_close_server();
600 if (connect_server() < 0) {
601 if (nntp_debug)
602 debug_msg("failed to connect", "");
603 if (retry > 0)
604 return (retry);
605 else
606 nn_exitmsg(1, "failed to reconnect to server");
607 }
608 if (group_hd)
609 if (nntp_set_group(group_hd) < 1)
610 nn_exitmsg(1, "unable to set group");
611
612 if (nntp_debug)
613 debug_msg(" Resend last command", buf);
614 put_server(buf);
615 reconnecting = 0;
616 return (retry);
617 }
618
619
620 /*
621 * put_server: send a line to the nntp server.
622 *
623 * Expects to be connected to the server.
624 */
625
626 static void
put_server(char * string)627 put_server(char *string)
628 {
629 if (nntp_debug)
630 debug_msg(">>>", string);
631
632 strcpy(last_put, string);
633 fprintf(nntp_out, "%s\r\n", string);
634 (void) fflush(nntp_out);
635 return;
636 }
637
638 /*
639 * ask_server: ask the server a question and return the answer.
640 *
641 * Expects to be connected to the server.
642 * Returns the numerical value of the reponse, or -1 in case of
643 * errors.
644 * Contains some code to handle server timeouts intelligently.
645 */
646
647 /* LIST XXX returns fatal ERR_FAULT code if requested list does not exist */
648 /* This is only fatal for LIST ACTIVE -- else change to ERR_NOGROUPS */
649 static int fix_list_response = 0;
650 static char ask_reply[NNTP_STRLEN];
651
652 static int
ask_server(char * fmt,...)653 ask_server(char *fmt,...)
654 {
655 char ask[NNTP_STRLEN];
656 int response;
657 int fix_err;
658 va_list ap;
659
660 fix_err = fix_list_response;
661 fix_list_response = 0;
662
663 va_start(ap, fmt);
664 vsprintf(ask, fmt, ap);
665 va_end(ap);
666
667 put_server(ask);
668 response = get_server(ask_reply, sizeof(ask_reply));
669
670 /*
671 * Handle the response from the server. Responses are handled as
672 * followes:
673 *
674 * 100-199 Informational. Passed back. (should they be ignored?).
675 * 200-299 Ok messages. Passed back. 300-399 Ok and proceed. Can
676 * not happen in nn. 400-499 Errors (no article, etc). Passed up
677 * and handled there. 480 (NEED_AUTH) handle specially. Done here
678 * because some servers can send it at any point, after an idle period,
679 * as well as at various points during connect. 500-599 Fatal NNTP
680 * errors. Handled below.
681 */
682
683 if (response == ERR_TIMEOUT) {
684 (void) reconnect_server(1);
685 response = get_server(ask_reply, sizeof(ask_reply));
686 }
687 if (response == ERR_NEEDAUTH) {
688
689 /*
690 * try auth, but just once, at least for now, and then retry the
691 * original command, and continue
692 */
693 nntp_doauth();
694 put_server(ask);
695 response = get_server(ask_reply, sizeof(ask_reply));
696 }
697 if (response == ERR_GOODBYE || response > ERR_COMMAND) {
698 if (fix_err && (response == ERR_FAULT || response == ERR_CMDSYN))
699 return ERR_NOGROUP;
700
701 nntp_failed = 1;
702 nntp_close_server();
703
704 if (response != ERR_GOODBYE) {
705 /* if not goodbye, complain */
706 sys_error("NNTP %s response: %d", ask_reply, response);
707 /* NOTREACHED */
708 }
709 if (response == ERR_GOODBYE) {
710 sys_warning("NNTP %s response: %d", ask_reply, response);
711 }
712 }
713 return response;
714 }
715
716 /*
717 * copy_text: copy text response into file.
718 *
719 * Copies a text response into an open file.
720 * Return -1 on error, 0 otherwise. It is treated as an error, if
721 * the returned response it not what was expected.
722 */
723
724 static int last_copy_blank;
725
726 static int
copy_text(register FILE * fp)727 copy_text(register FILE * fp)
728 {
729 char buf[NNTP_STRLEN * 2];
730 register char *cp;
731 register int nlines;
732
733 nlines = 0;
734 last_copy_blank = 0;
735 while (get_server_line(buf, sizeof buf) >= 0) {
736 cp = buf;
737 if (*cp == '.')
738 if (*++cp == '\0') {
739 if (nlines <= 0)
740 break;
741 if (nntp_debug) {
742 sprintf(buf, "%d lines", nlines);
743 debug_msg("COPY", buf);
744 }
745 return 0;
746 }
747 last_copy_blank = (*cp == NUL);
748 fputs(cp, fp);
749 putc('\n', fp);
750 nlines++;
751 }
752 fclose(fp);
753 if (nntp_debug)
754 debug_msg("COPY", "EMPTY");
755 return -1;
756 }
757
758
759 /*
760 * The following functions implements a simple lru cache of recently
761 * accessed articles. It is a simple way to improve effeciency. Files
762 * must be kept by name, because the rest of the code expects to be able
763 * to open an article multiple times, and get separate file pointers.
764 */
765
766 struct cache {
767 char *file_name; /* file name */
768 article_number art; /* article stored in file */
769 group_header *grp; /* from this group */
770 unsigned time; /* time last accessed */
771 } cache[NNTPCACHE];
772
773 static unsigned time_counter = 1; /* virtual time */
774
775 /*
776 * search_cache: search the cache for an (article, group) pair.
777 *
778 * Returns a pointer to the slot where it is, null otherwise
779 */
780
781 static struct cache *
search_cache(article_number art,group_header * gh)782 search_cache(article_number art, group_header * gh)
783 {
784 struct cache *cptr = cache;
785 int i;
786
787 if (who_am_i == I_AM_MASTER)
788 return NULL;
789
790 if (nntp_cache_size > NNTPCACHE)
791 nntp_cache_size = NNTPCACHE;
792
793 for (i = 0; i < nntp_cache_size; i++, cptr++)
794 if (cptr->art == art && cptr->grp == gh) {
795 cptr->time = time_counter++;
796 return cptr;
797 }
798 return NULL;
799 }
800
801 /*
802 * new_cache_slot: get a free cache slot.
803 *
804 * Returns a pointer to the allocated slot.
805 * Frees the old filename, and allocates a new, unused filename.
806 * Cache files can also stored in a common directory defined in
807 * ~/.nn or CACHE_DIRECTORY if defined in config.h.
808 */
809
810 static struct cache *
new_cache_slot(void)811 new_cache_slot(void)
812 {
813 register struct cache *cptr = cache;
814 int i, lru = 0;
815 unsigned min_time = time_counter;
816 char name[FILENAME];
817
818 if (nntp_cache_dir == NULL) {
819
820 #ifdef CACHE_DIRECTORY
821 nntp_cache_dir = CACHE_DIRECTORY;
822 #else /* CACHE_DIRECTORY */
823 if (who_am_i == I_AM_MASTER)
824 nntp_cache_dir = db_directory;
825 else
826 nntp_cache_dir = nn_directory;
827 #endif /* CACHE_DIRECTORY */
828 }
829 if (who_am_i == I_AM_MASTER) {
830 cptr = &cache[0];
831 if (cptr->file_name == NULL)
832 cptr->file_name = mk_file_name(nntp_cache_dir, "master_cache");
833 return cptr;
834 }
835 for (i = 0; i < nntp_cache_size; i++, cptr++)
836 if (min_time > cptr->time) {
837 min_time = cptr->time;
838 lru = i;
839 }
840 cptr = &cache[lru];
841
842 if (cptr->file_name == NULL) {
843 sprintf(name, "%s/nn-%d.%02d~", nntp_cache_dir, process_id, lru);
844 cptr->file_name = copy_str(name);
845 } else
846 unlink(cptr->file_name);
847
848 cptr->time = time_counter++;
849 return cptr;
850 }
851
852 /*
853 * clean_cache: clean up the cache.
854 *
855 * Removes all allocated files.
856 */
857
858 static void
clean_cache(void)859 clean_cache(void)
860 {
861 struct cache *cptr = cache;
862 int i;
863
864 for (i = 0; i < nntp_cache_size; i++, cptr++)
865 if (cptr->file_name)
866 unlink(cptr->file_name);
867 }
868
869 /*
870 * set_domain: set the domain name
871 */
872
873 static void
set_domain(void)874 set_domain(void)
875 {
876
877 #if !defined(DOMAIN) || !defined(HIDDENNET)
878 char *cp;
879 #endif
880
881 #ifdef DOMAIN
882
883 #ifdef HIDDENNET
884 strncpy(domain, DOMAIN, MAXHOSTNAMELEN);
885 #else
886 strcpy(domain, host_name);
887 cp = index(domain, '.');
888 if (cp == NULL) {
889 strcat(domain, ".");
890 strncat(domain, DOMAIN, MAXHOSTNAMELEN - sizeof(host_name) - 1);
891 }
892 #endif /* HIDDENNET */
893
894 #else /* DOMAIN */
895
896 /*
897 * if domain is defined in DOMAIN_FILE, use it
898 */
899 cp = find_domain(DOMAIN_FILE);
900 if (cp) {
901 strncpy(domain, cp, MAXHOSTNAMELEN);
902 domain[MAXHOSTNAMELEN-1] = 0; /* ensure nul-terminated */
903 return;
904 }
905
906 domain[0] = '\0';
907
908 cp = index(host_name, '.');
909 if (cp == NULL) {
910 FILE *resolv;
911
912 if ((resolv = fopen("/etc/resolv.conf", "r")) != NULL) {
913 char line[MAXHOSTNAMELEN + 1];
914 char *p;
915
916 line[MAXHOSTNAMELEN] = '\0';
917 while (fgets(line, MAXHOSTNAMELEN, resolv)) {
918 if (strncmp(line, "domain", 6) == 0) {
919 for (p = &line[6]; *p && isspace(*p); p++);
920 (void) strncpy(domain, p, MAXHOSTNAMELEN);
921 p = domain + strlen(domain) - 1;
922 while ((p >= domain) && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
923 p--;
924 *++p = '\0';
925 break;
926 }
927 }
928 }
929 if (domain[0] == '\0')
930 nn_exitmsg(1, "hostname=%s, You need a fully qualified domain name", host_name);
931 } else {
932
933 #ifdef HIDDENNET
934 strncpy(domain, ++cp, MAXHOSTNAMELEN);
935 #else
936 strncpy(domain, host_name, MAXHOSTNAMELEN);
937 #endif
938 }
939 #endif /* DOMAIN */
940 domain[MAXHOSTNAMELEN-1] = 0; /* ensure nul-terminated */
941 }
942
943 /*
944 * nntp_check: Find out whether we need to use NNTP.
945 *
946 * This is done by comparing the NNTP servers name with whatever
947 * nn_gethostname() returns.
948 * use_nntp and news_active are initialised as a side effect.
949 */
950
951 void
nntp_check(void)952 nntp_check(void)
953 {
954 char *server_real_name = NULL;
955 struct hostent *hp;
956
957 nn_gethostname(host_name, sizeof host_name);
958 set_domain();
959
960 last_put[0] = '\0';
961
962 if (nntp_local_server)
963 return;
964
965 find_server();
966
967 if ((hp = gethostbyname(host_name)) != NULL)
968 strncpy(host_name, hp->h_name, sizeof host_name);
969
970 if ((hp = gethostbyname(nntp_server)) != NULL)
971 server_real_name = hp->h_name;
972 else
973 nn_exitmsg(1, "NNTPSERVER is invalid");
974 use_nntp = (strcmp(host_name, server_real_name) != 0);
975
976 if (use_nntp) {
977 freeobj(news_active);
978
979 #ifndef NOV
980 news_active = mk_file_name(db_directory, "ACTIVE");
981 #else /* NOV */
982 news_active = mk_file_name(nn_directory, "ACTIVE");
983 #endif /* NOV */
984 }
985 }
986
987
988 /*
989 * nntp_set_group -- set the server's current group.
990 *
991 * Parameters: gh is the structure containing the group name.
992 *
993 * Returns: 1 if OK.
994 * -1 if no such group.
995 * 0 otherwise.
996 */
997
998 int
nntp_set_group(group_header * gh)999 nntp_set_group(group_header * gh)
1000 {
1001 int n;
1002
1003 switch (n = ask_server("GROUP %s", gh->group_name)) {
1004 case OK_GROUP:
1005 group_hd = gh;
1006 return 1;
1007
1008 case ERR_NOGROUP:
1009 log_entry('N', "NNTP: group %s not found\n", gh->group_name);
1010 return -1;
1011
1012 default: /* ??? returns this if connection down */
1013 log_entry('N', "GROUP %s response: %d", group_hd->group_name, n);
1014 nntp_failed = 1;
1015 return -1; /* What did it return? */
1016 }
1017 }
1018
1019
1020 #ifndef NOV
1021 /*
1022 * nntp_get_active: get a copy of the active file.
1023 *
1024 * If we are the master get a copy of the file from the nntp server.
1025 * nnadmin just uses the one we already got. In this way the master
1026 * can maintain a remote copy of the servers active file.
1027 * We try to be a little smart, if not inefficient, about the
1028 * modification times on the local active file.
1029 * Even when the master is running on the nntp server, a separate
1030 * copy of the active file will be made for access via NFS.
1031 */
1032
1033 int
nntp_get_active(void)1034 nntp_get_active(void)
1035 {
1036 FILE *old, *new;
1037 char bufo[NNTP_STRLEN], bufn[NNTP_STRLEN];
1038 char *new_name;
1039 int same, n;
1040
1041 if (who_am_i != I_AM_MASTER)
1042 return access(news_active, 4);
1043
1044 if (!is_connected && connect_server() < 0)
1045 return -1;
1046
1047 new_name = mktemp(relative(db_directory, ".actXXXXXX"));
1048
1049 switch (n = ask_server("LIST")) {
1050 case OK_GROUPS:
1051 new = open_file(new_name, OPEN_CREATE_RW | MUST_EXIST);
1052 if (copy_text(new) == 0) {
1053 if (fflush(new) != EOF)
1054 break;
1055 fclose(new);
1056 }
1057 unlink(new_name);
1058 if (!nntp_failed) {
1059 log_entry('N', "LIST empty");
1060 nntp_failed = 1;
1061 }
1062 return -1;
1063 default:
1064 log_entry('N', "LIST response: %d", n);
1065 return -1;
1066 }
1067
1068 rewind(new);
1069 same = 0;
1070 if ((old = open_file(news_active, OPEN_READ)) != NULL) {
1071 do {
1072 fgets(bufo, sizeof bufo, old);
1073 fgets(bufn, sizeof bufn, new);
1074 } while (!feof(old) && !feof(new) && strcmp(bufo, bufn) == 0);
1075 same = feof(old) && feof(new);
1076 fclose(old);
1077 }
1078 fclose(new);
1079
1080 if (same)
1081 unlink(new_name);
1082 else if (rename(new_name, news_active) != 0)
1083 sys_error("Cannot rename %s to %s", new_name, news_active);
1084
1085 return 0;
1086 }
1087
1088 #endif /* !NOV */
1089
1090 /*
1091 * nntp_get_newsgroups: get a copy of the newsgroups file.
1092 *
1093 * Use the "LIST NEWSGROUPS" command to get the newsgroup descriptions.
1094 * Based on code from: olson%anchor.esd@sgi.com (Dave Olson)
1095 */
1096
1097 FILE *
nntp_get_newsgroups(void)1098 nntp_get_newsgroups(void)
1099 {
1100 char *new_name;
1101 FILE *new;
1102 int n;
1103
1104 new_name = mktemp(relative(tmp_directory, "nngrXXXXXX"));
1105 new = open_file(new_name, OPEN_CREATE_RW | OPEN_UNLINK);
1106 if (new == NULL)
1107 return NULL;
1108
1109 if (!is_connected && connect_server() < 0)
1110 goto err;
1111
1112 fix_list_response = 1;
1113 switch (n = ask_server("LIST NEWSGROUPS")) {
1114 case ERR_NOGROUP: /* really ERR_FAULT */
1115 goto err;
1116
1117 case OK_GROUPS:
1118 if (copy_text(new) == 0) {
1119 if (fflush(new) != EOF)
1120 break;
1121 fclose(new);
1122 }
1123 if (!nntp_failed) {
1124 log_entry('N', "LIST NEWSGROUPS empty");
1125 nntp_failed = 1;
1126 }
1127 return NULL;
1128
1129 default:
1130 log_entry('N', "LIST NEWSGROUPS response: %d", n);
1131 goto err;
1132 }
1133 rewind(new);
1134 return new;
1135
1136 err:
1137 fclose(new);
1138 return NULL;
1139 }
1140
1141 #ifndef NOV
1142 /*
1143 * nntp_get_article_list: get list of all article numbers in group
1144 *
1145 * Sends XHDR command to the server, and parses the following
1146 * text response to get a list of article numbers which is saved
1147 * in a list and returned.
1148 * Return NULL on error. It is treated as an error, if
1149 * the returned response it not what was expected.
1150 */
1151
1152 static article_number *article_list = NULL;
1153 static long art_list_length = 0;
1154
1155 static int
sort_art_list(register article_number * f1,register article_number * f2)1156 sort_art_list(register article_number * f1, register article_number * f2)
1157 {
1158 return (*f1 < *f2) ? -1 : (*f1 == *f2) ? 0 : 1;
1159 }
1160
1161 article_number *
nntp_get_article_list(group_header * gh)1162 nntp_get_article_list(group_header * gh)
1163 {
1164 char buf[NNTP_STRLEN];
1165 register article_number *art;
1166 register char *cp;
1167 register long count = 0; /* No. of completions plus one */
1168 int n;
1169 static int try_listgroup = 1;
1170
1171 if (!is_connected && connect_server() < 0)
1172 return NULL;
1173
1174 /* it is really an extreme waste of time to use XHDR since all we */
1175 /* are interested in is the article numbers (as we do locally). */
1176 /* If somebody hacks up an nntp server that understands LISTGROUP */
1177 /* they will get much less load on the nntp server */
1178 /* It should simply return the existing article numbers is the group */
1179 /* -- they don't even have to be sorted (only XHDR needs that) */
1180
1181 if (try_listgroup) {
1182 switch (n = ask_server("LISTGROUP %s", group_hd->group_name)) {
1183 case OK_GROUP:
1184 break;
1185 default:
1186 log_entry('N', "LISTGROUP response: %d", n);
1187 return NULL;
1188 case ERR_COMMAND:
1189 try_listgroup = 0;
1190 }
1191 }
1192 if (!try_listgroup) {
1193 switch (n = ask_server("XHDR message-id %ld-%ld",
1194 (long) gh->first_db_article, (long) gh->last_db_article)) {
1195 case OK_HEAD:
1196 break;
1197 default:
1198 log_entry('N', "XHDR response: %d", n);
1199 return NULL;
1200 case ERR_COMMAND:
1201 nntp_failed = 2;
1202 return NULL;
1203 }
1204 }
1205 count = 0;
1206 art = article_list;
1207
1208 while (get_server_line(buf, sizeof buf) >= 0) {
1209 cp = buf;
1210 if (*cp == '.' && *++cp == '\0')
1211 break;
1212
1213 if (count == art_list_length) {
1214 art_list_length += 250;
1215 article_list = resizeobj(article_list, article_number, art_list_length + 1);
1216 art = article_list + count;
1217 }
1218 *art++ = atol(cp);
1219 count++;
1220 }
1221
1222 if (article_list != NULL) {
1223 *art = 0;
1224 if (try_listgroup && count > 1)
1225 quicksort(article_list, count, article_number, sort_art_list);
1226 }
1227 return article_list;
1228 }
1229
1230 #endif /* NOV */
1231
1232 /*
1233 * nntp_get_article: get an article from the server.
1234 *
1235 * Returns a FILE pointer.
1236 * If necessary the server's current group is set.
1237 * The article (header and body) are copied into a file, so they
1238 * are seekable (nn likes that).
1239 */
1240
1241 static char *mode_cmd[] = {
1242 "ARTICLE",
1243 "HEAD",
1244 "BODY"
1245 };
1246
1247 FILE *
nntp_get_article(article_number article,int mode)1248 nntp_get_article(article_number article, int mode)
1249 /* mode: 0 => whole article, 1 => head only, 2 => body only */
1250 {
1251 FILE *tmp;
1252 static struct cache *cptr;
1253 int n;
1254
1255 if (!is_connected && connect_server() < 0) {
1256 return NULL;
1257 }
1258
1259 /*
1260 * Search the cache for the requested article, and allocate a new slot if
1261 * necessary (if appending body, we already got it).
1262 */
1263
1264 if (mode != 2) {
1265 cptr = search_cache(article, group_hd);
1266 if (cptr != NULL)
1267 goto out;
1268 cptr = new_cache_slot();
1269 }
1270
1271 /*
1272 * Copy the article.
1273 */
1274 switch (n = ask_server("%s %ld", mode_cmd[mode], (long) article)) {
1275 case OK_ARTICLE:
1276 case OK_HEAD:
1277 tmp = open_file(cptr->file_name, OPEN_CREATE | MUST_EXIST);
1278 if (copy_text(tmp) < 0)
1279 return NULL;
1280
1281 if (mode == 1 && !last_copy_blank)
1282 fputc(NL, tmp); /* add blank line after header */
1283
1284 if (fclose(tmp) == EOF)
1285 goto err;
1286 cptr->art = article;
1287 cptr->grp = group_hd;
1288 goto out;
1289
1290 case OK_BODY:
1291 tmp = open_file(cptr->file_name, OPEN_APPEND | MUST_EXIST);
1292 fseek(tmp, 0, 2);
1293 if (copy_text(tmp) < 0)
1294 return NULL;
1295 if (fclose(tmp) == EOF)
1296 goto err;
1297 goto out;
1298
1299 case ERR_NOARTIG:
1300 /* Matt Heffron: ANUNEWS on VMS uses no such article error */
1301 case ERR_NOART:
1302 return NULL;
1303
1304 default:
1305 /* Matt Heffron: Which group? */
1306 log_entry('N', "ARTICLE %ld response: %d (in Group %s)",
1307 (long) article, n, group_hd->group_name);
1308 nntp_failed = 1;
1309 return NULL;
1310 }
1311
1312 out:
1313 return open_file(cptr->file_name, OPEN_READ | MUST_EXIST);
1314
1315 err:
1316 /* sys_error('N', "Cannot write temporary file %s", cptr->file_name); */
1317 nn_exitmsg(1, "Cannot write temporary file %s", cptr->file_name);
1318 return NULL; /* for lint, we can't actually get here */
1319 }
1320
1321 /*
1322 * Return local file name holding article
1323 */
1324
1325 char *
nntp_get_filename(article_number art,group_header * gh)1326 nntp_get_filename(article_number art, group_header * gh)
1327 {
1328 struct cache *cptr;
1329
1330 cptr = search_cache(art, gh);
1331
1332 return cptr == NULL ? NULL : cptr->file_name;
1333 }
1334
1335 /*
1336 * nntp_close_server: close the connection to the server.
1337 */
1338
1339 void
nntp_close_server(void)1340 nntp_close_server(void)
1341 {
1342 if (!is_connected)
1343 return;
1344
1345 if (!nntp_failed) /* avoid infinite recursion */
1346 put_server("QUIT");
1347
1348 (void) fclose(nntp_out);
1349 (void) fclose(nntp_in);
1350
1351 is_connected = 0;
1352 }
1353
1354
1355 /*
1356 * nntp_cleanup: clean up after an nntp session.
1357 *
1358 * Called from nn_exit().
1359 */
1360
1361 void
nntp_cleanup(void)1362 nntp_cleanup(void)
1363 {
1364 if (is_connected)
1365 nntp_close_server();
1366 is_connected = 0;
1367 clean_cache();
1368 }
1369
1370
1371 /*************************************************************/
1372
1373 #ifdef NOV
1374 /*
1375 ** Prime the nntp server to snarf the overview file for a newsgroup.
1376 ** Sends the XOVER command and prepares to read the result.
1377 */
1378 struct novgroup *
nntp_get_overview(group_header * gh,article_number first,article_number last)1379 nntp_get_overview(group_header * gh, article_number first, article_number last)
1380 {
1381 int n;
1382
1383 if (!is_connected && connect_server() < 0) {
1384 return NULL;
1385 }
1386 switch (nntp_set_group(gh)) {
1387 case -1:
1388 case 0:
1389 return NULL;
1390 }
1391
1392 n = ask_server("XOVER %d-%d", first, last);
1393 switch (n) {
1394
1395 case OK_NOV:
1396 return novstream(nntp_in);
1397
1398 default:
1399 log_entry('N', "XOVER response: %d", n);
1400 return NULL;
1401
1402 }
1403 }
1404
1405 /*
1406 * nntp_fopen_list(cmd): Send some variant of a LIST command to the
1407 * NNTP server. returns NULL if the file to be LISTed doesn't exist
1408 * on the server, else returns the nntp_in FILE descriptor, thus
1409 * simulating fopen().
1410 * nntp_fgets() is later used to read a line from the nntp_in FILE.
1411 */
1412
1413 FILE *
nntp_fopen_list(char * cmd)1414 nntp_fopen_list(char *cmd)
1415 {
1416 int n;
1417
1418 if (!is_connected && connect_server() < 0)
1419 return NULL;
1420
1421 fix_list_response = 1;
1422 switch (n = ask_server(cmd)) {
1423 case ERR_NOGROUP: /* really ERR_FAULT - no such file on server */
1424 return NULL;
1425
1426 case OK_GROUPS: /* aka NNTP_LIST_FOLLOWS_VAL */
1427 return nntp_in;
1428
1429 default:
1430 log_entry('N', "`%s' response: %d", cmd, n);
1431 return NULL;
1432 }
1433 }
1434
1435 /*
1436 * nntp_fgets() - Get a line from a file stored on the NNTP server.
1437 * Strips any hidden "." at beginning of line and returns a pointer to
1438 * the line. line will be terminated by NL NUL.
1439 * Returns NULL when NNTP sends the terminating "." to indicate EOF.
1440 */
1441 char *
nntp_fgets(char * buf,int bufsize)1442 nntp_fgets(char *buf, int bufsize)
1443 {
1444 char *cp;
1445 register int size;
1446
1447 if ((size = get_server_line(buf, bufsize - 1)) < 0)
1448 return NULL; /* Can't happen with NOV (we'd rather die
1449 * first) */
1450
1451 cp = buf;
1452 if (*cp == '.') {
1453 if (*++cp == '\0')
1454 return NULL;
1455 }
1456 cp[size] = '\n';
1457 cp[size + 1] = '\0';
1458 return cp;
1459 }
1460
1461 #endif /* NOV */
1462
1463 /* do all the AUTH stuff; used from 3 places that I've found where
1464 * various news servers ask for authentication. Given that we exit
1465 * on failed auth, probably should just be void.
1466 * OLSON: oops, loops on empty user input "forever". Should fix
1467 */
1468 static void
nntp_doauth(void)1469 nntp_doauth(void)
1470 {
1471 int len;
1472 char line[NNTP_STRLEN], *nl, *pass = NULL;
1473
1474 strcpy(line, "authinfo user ");
1475 len = strlen(line);
1476
1477 log_entry('N', "NNTP Authentication");
1478
1479 if (nntp_user) {
1480 msg("NNTP authentication");
1481 strcpy(line + len, nntp_user);
1482 } else {
1483 init_term(1);
1484 clrdisp();
1485 prompt("Authentication required; enter username: ");
1486 (void) fgets(line + len, sizeof(line) - len, stdin);
1487 if ((nl = strpbrk(line, "\r\n")))
1488 *nl = '\0';
1489 nntp_user = strdup(line + len);
1490 }
1491 put_server(line);
1492 if ((len = get_server(line, sizeof(line))) == OK_NEEDPASS) {
1493 strcpy(line, "authinfo pass ");
1494 len = strlen(line);
1495
1496 if (nntp_password)
1497 strcpy(line + len, nntp_password);
1498 else {
1499
1500 #ifdef USEGETPASSPHRASE
1501 pass = getpassphrase("Enter password: ");
1502 #else
1503 pass = getpass("Enter password: ");
1504 #endif
1505
1506 strcat(line, pass);
1507 }
1508 put_server(line);
1509 if ((len = get_server(line, sizeof(line))) == OK_AUTH) {
1510 if (!nntp_password) {
1511 nntp_password = strdup(pass);
1512 clrdisp();
1513 msg("Remembering password for reconnections");
1514 }
1515 } else {
1516 nn_exitmsg(1, "Authentication failed (%d): %s", len, line);
1517 }
1518 }
1519 need_auth = 1;
1520 }
1521
1522
1523 /* ZZZZZ */
1524
1525 /*
1526 * The rest of this is cribbed (and modified) from the old mini-inews.
1527 *
1528 * Simply accept input on stdin (or via a named file) and dump this
1529 * to the server; add a From: and Path: line if missing in the original.
1530 * Print meaningful errors from the server.
1531 * Limit .signature files to MAX_SIGNATURE lines.
1532 *
1533 * Original by Steven Grady <grady@ucbvax.Berkeley.EDU>, with thanks from
1534 * Phil Lapsley <phil@ucbvax.berkeley.edu>
1535 */
1536
1537 /*
1538 * gen_frompath -- generate From: and Path: lines, in the form
1539 *
1540 * From: Full Name <user@host.domain>
1541 * Path: host!user
1542 *
1543 * This routine should only be called if the message doesn't have
1544 * a From: line in it.
1545 */
1546
1547 static void
gen_frompath(void)1548 gen_frompath(void)
1549 {
1550 struct passwd *passwd;
1551
1552 passwd = getpwuid(getuid());
1553
1554 fprintf(nntp_out, "From: ");
1555 fprintf(nntp_out, "%s ", full_name());
1556
1557 fprintf(nntp_out, "<%s@%s>\r\n",
1558 passwd->pw_name,
1559 domain);
1560
1561 #ifdef HIDDENNET
1562 /* Only the login name - nntp server will add uucp name */
1563 fprintf(nntp_out, "Path: %s\r\n", passwd->pw_name);
1564 #else /* HIDDENNET */
1565 fprintf(nntp_out, "Path: %s!%s\r\n", host_name, passwd->pw_name);
1566 #endif /* HIDDENNET */
1567 }
1568
1569
1570 /*
1571 * lower -- convert a character to lower case, if it's
1572 * upper case.
1573 *
1574 * Parameters: "c" is the character to be
1575 * converted.
1576 *
1577 * Returns: "c" if the character is not
1578 * upper case, otherwise the lower
1579 * case eqivalent of "c".
1580 *
1581 * Side effects: None.
1582 */
1583
1584 static char
lower(register char c)1585 lower(register char c)
1586 {
1587 if (isascii(c) && isupper(c))
1588 c = c - 'A' + 'a';
1589 return (c);
1590 }
1591
1592 /*
1593 * strneql -- determine if two strings are equal in the first n
1594 * characters, ignoring case.
1595 *
1596 * Parameters: "a" and "b" are the pointers
1597 * to characters to be compared.
1598 * "n" is the number of characters to compare.
1599 *
1600 * Returns: 1 if the strings are equal, 0 otherwise.
1601 *
1602 * Side effects: None.
1603 */
1604
1605 static int
strneql(register char * a,register char * b,int n)1606 strneql(register char *a, register char *b, int n)
1607 {
1608 while (n && lower(*a) == lower(*b)) {
1609 if (*a == '\0')
1610 return (1);
1611 a++;
1612 b++;
1613 n--;
1614 }
1615 if (n)
1616 return (0);
1617 else
1618 return (1);
1619 }
1620
1621
1622 /*
1623 * valid_header -- determine if a line is a valid header line
1624 *
1625 * Parameters: "h" is the header line to be checked.
1626 *
1627 * Returns: 1 if valid, 0 otherwise
1628 *
1629 * Side Effects: none
1630 *
1631 */
1632
1633 static int
valid_header(register char * h)1634 valid_header(register char *h)
1635 {
1636 char *colon, *space;
1637
1638 /*
1639 * blank or tab in first position implies this is a continuation header
1640 */
1641 if (h[0] == ' ' || h[0] == '\t')
1642 return (1);
1643
1644 /*
1645 * just check for initial letter, colon, and space to make sure we
1646 * discard only invalid headers
1647 */
1648 colon = index(h, ':');
1649 space = index(h, ' ');
1650 if (isalpha(h[0]) && colon && space == colon + 1)
1651 return (1);
1652
1653 /*
1654 * anything else is a bad header -- it should be ignored
1655 */
1656 return (0);
1657 }
1658
1659 int
nntp_post(char * temp_file)1660 nntp_post(char *temp_file)
1661 {
1662 int n, response;
1663 FILE *in = fopen(temp_file, "r");
1664 char s[4 * NNTP_STRLEN];
1665 int seen_fromline, in_header, seen_header;
1666 register char *cp;
1667
1668 if (!in) {
1669 sprintf(delayed_msg, "Posting failed because we couldn't re-open file %s.", temp_file);
1670 return 1;
1671 }
1672 if (!is_connected && connect_server() < 0)
1673 return 1;
1674
1675 switch (n = ask_server("POST")) {
1676 case CONT_POST:
1677 break;
1678 default:
1679 sprintf(delayed_msg, "Request to post failed with error %d, %s", n, ask_reply);
1680 fclose(in);
1681 return 1;
1682 }
1683
1684 in_header = 1;
1685 seen_header = 0;
1686 seen_fromline = 0;
1687
1688 while (fgets(s, sizeof(s), in) != NULL) {
1689 if ((cp = strchr(s, '\n')))
1690 *cp = '\0';
1691 if (s[0] == '.') /* Single . is eof, so put in extra one */
1692 (void) fputc('.', nntp_out);
1693 if (in_header && strneql(s, "From:", sizeof("From:") - 1)) {
1694 seen_header = 1;
1695 seen_fromline = 1;
1696 }
1697 if (in_header && s[0] == '\0') {
1698 if (seen_header) {
1699 in_header = 0;
1700 if (!seen_fromline)
1701 gen_frompath();
1702 fprintf(nntp_out, "User-Agent: nn/%s.%s\r\n", RELEASE, PATCHLEVEL);
1703 } else {
1704 continue;
1705 }
1706 } else if (in_header) {
1707 if (valid_header(s))
1708 seen_header = 1;
1709 else
1710 continue;
1711 }
1712 fprintf(nntp_out, "%s\r\n", s);
1713 }
1714
1715 fprintf(nntp_out, ".\r\n");
1716 (void) fflush(nntp_out);
1717 response = get_server(ask_reply, sizeof(ask_reply));
1718 switch (response) {
1719 case OK_POSTED:
1720 break;
1721 case ERR_POSTFAIL:
1722 msg("Article not accepted by server; not posted.");
1723 user_delay(2);
1724 return 1;
1725 default:
1726 msg("Remote error: %s", ask_reply);
1727 user_delay(2);
1728 return 1;
1729 }
1730 return 0;
1731 }
1732
1733 #ifdef NeXT
1734 /*
1735 * posted to comp.sys.next.programmer:
1736 *
1737 *
1738 * From: moser@ifor.math.ethz.ch (Dominik Moser,CLV A4,2 40 19,720 49 89)
1739 * Subject: Re: Compile problems (pgp 2.6.3i)
1740 * Date: 10 Jul 1996 06:50:42 GMT
1741 * Organization: Swiss Federal Institute of Technology (ETHZ)
1742 * References: <4rrhvj$6fr@bagan.srce.hr>
1743 * Message-ID: <4rvjs2$6oh@elna.ethz.ch>
1744 *
1745 * Most systems don't have this (yet)
1746 */
1747 static char *
strdup(char * str)1748 strdup(char *str)
1749 {
1750 char *p;
1751
1752 if ((p = malloc(strlen(str) + 1)) == NULL)
1753 return ((char *) NULL);
1754
1755 (void) strcpy(p, str);
1756
1757 return (p);
1758 }
1759
1760 #endif /* NeXT */
1761
1762 #endif /* NNTP */
1763