1: /* nntpxmit - transmit netnews articles across the internet with nntp
   2: **
   3: ** This program is for transmitting netnews articles between sites
   4: ** that offer the NNTP service, internet style. There are two forms
   5: ** of article transmission that can be used in this environment, since
   6: ** the communication is interactive (and relatively more immediate,
   7: ** when compared to batched file transfer protocols, like UUCP). They
   8: ** are: active send (I have `x', do you want it?) and polling (what
   9: ** have you gotten lately?).
  10: **
  11: ** 		A C T I V E   S E N D
  12: **
  13: ** Sites on the UUCP network generally use active send, without asking
  14: ** in advance (that is, unless you got an article from your neighbor,
  15: ** or their site is listed in the Path: header already, you assume
  16: ** they don't have it and send it along). There is an ihave/sendme
  17: ** protocol for doing active send over batched links, but I claim that
  18: ** it won't work well because of the high latency between queueing
  19: ** and actual transfer that UUCP links typically have. That is, you'll
  20: ** still end up with a high rate of duplicate articles being sent over
  21: ** that type of link.
  22: **
  23: ** With NNTP-based IHAVE, the update window in which another site can
  24: ** give the remote the article you just offered him is the time between
  25: ** the remote telling you it doesn't have the article, and your
  26: ** completed transfer of the article (pretty small). In practice, we
  27: ** still get duplicates, but generally from two problems: synchronized
  28: ** transmission of an article from two different neighbors (this can
  29: ** only be fixed by putting inews(1) into nntpd), and by articles
  30: ** being accepting during an expire(1) run (expire locks out inews
  31: ** processing while it is running, and articles collect until expire
  32: ** is done; since accepted article message-ids aren't added to
  33: ** the history file until expire is done, several clients can offer
  34: ** you the same article, and you'll accept all the copies offered you.
  35: ** When rnews gets run after expire, it will reject the duplicates).
  36: **
  37: ** 		P O L L I N G
  38: **
  39: ** Polling presents some article and distribution security problems,
  40: ** because the server has no contol over what a transmission client
  41: ** will ask for, and it must therefore control what it tells a client
  42: ** in response to a query.
  43: **
  44: ** Articles that appear in local newsgroup hierarchies, or appear in
  45: ** the generally distributed USENET newsgroups with local distributions
  46: ** have to be filtered out from the list of message-IDs that the server
  47: ** gives to a client in response to a NEWNEWS query, or filtered when
  48: ** the server fetches the articles off the disk in response to an
  49: ** ARTICLE command (and therefore has complete access to the required
  50: ** information). Otherwise, distributions will leak.
  51: **
  52: ** The other problem with polling is that a good client should keep track
  53: ** of when it last successfully polled a server, so that it doesn't force
  54: ** h server to dump its entire history file across the network, and this
  55: ** involves more file locking and manipulations routines.
  56: **
  57: ** nntpxmit only implements active send, for now.
  58: **
  59: ** Erik E. Fair <fair@ucbarpa.berkeley.edu>, Dec 4, 1987
  60: */
  61: 
  62: #include "nntpxmit.h"
  63: #include <stdio.h>
  64: #include <errno.h>
  65: #include <ctype.h>
  66: #include <sys/types.h>
  67: #include <sys/time.h>
  68: #ifdef  BSD4_2
  69: #include <sys/resource.h>
  70: #else
  71: #include <sys/times.h>
  72: extern  time_t  time();
  73: #endif	BSD4_2
  74: #include <sys/file.h>
  75: #include <fcntl.h>
  76: #include <signal.h>
  77: #ifdef USG
  78: #include "sysexits.h"
  79: #else
  80: #include <sysexits.h>
  81: #endif
  82: #ifdef  SYSLOG
  83: #include <syslog.h>
  84: #endif	SYSLOG
  85: #include "nntp.h"
  86: #include "llist.h"
  87: 
  88: #define MAXFNAME    BUFSIZ  /* maximum filename size - big enough? */
  89: #define FCLOSE(fp)  (void) fclose(fp); (fp) = (FILE *)NULL
  90: 
  91: FILE    *getfp();
  92: char    *errmsg();
  93: void    requeue();
  94: void    catchsig();
  95: void    restsig();
  96: void    logstats();
  97: void    log();
  98: int interrupted();
  99: 
 100: /*
 101: ** Globals that certain things need.
 102: **
 103: ** Various subroutines want the program name to report errors.
 104: ** The queue file, queue file pointer and current article name are
 105: ** there to write out the state of the queue file from a signal handler
 106: ** (that is, the list of unsent and (possibly) failed articles) so
 107: ** that when next we try sending to a given remote site, we don't send
 108: ** stuff we've already sent.
 109: */
 110: char    *Pname;         /* this program's invocation name */
 111: char    *Host;          /* current remote host */
 112: char    *Qfile;         /* current queue file we're operating on */
 113: FILE    *Qfp;           /* the (FILE *) for above */
 114: char    Article[MAXFNAME];  /* current article filename */
 115: 
 116: /*
 117: ** Some flags, toggled by arguments
 118: */
 119: #define TOGGLE(boolean) (boolean) = !(boolean)
 120: char    Debug = FALSE;
 121: char    Report_Stats = TRUE;
 122: char    ReQueue_Fails = TRUE;
 123: 
 124: char    *USAGE = "USAGE: nntpxmit [-d][-s][-r][-T][-F][-D] hostname|hostname:file [...]";
 125: char    *Fmt = "%s: %s\n";
 126: char    *E_fopen = "fopen(%s, \"%s\"): %s";
 127: char    *E_unlk = "unlink(%s): %s";
 128: #ifdef  USELOG
 129: char    *NNTPlog = USELOG;  /* yet another external log file */
 130: FILE    *Logfp = (FILE *)NULL;
 131: #endif	USELOG
 132: 
 133: ll_t    FailedArticles;     /* list of failed articles */
 134: 
 135: struct {
 136:     u_long  offered;
 137:     u_long  accepted;
 138:     u_long  rejected;
 139:     u_long  failed;
 140: } Stats = {0L, 0L, 0L, 0L};
 141: 
 142: double Tbegin, Tend;        /* transfer timestamps */
 143: 
 144: extern  int errno;
 145: extern  int strncmp();
 146: extern  char    *rindex();
 147: extern  char    *index();
 148: extern  char    *mktemp();
 149: extern  char    *strcpy();
 150: 
 151: #ifdef  USG
 152: void
 153: bzero(s, l)
 154: register caddr_t s;
 155: register int    l;
 156: {
 157:     while(l-- > 0) *s++ = 0;
 158: }
 159: #endif	USG
 160: 
 161: main(ac, av)
 162: int ac;
 163: char    *av[];
 164: {
 165:     register int    i;
 166:     int transport = T_IP_TCP;   /* default is IP/TCP */
 167:     int isQfile = TRUE;     /* file arg is a Queue file */
 168: #ifdef  USELOG
 169:     char    *amode = "a";
 170: #endif	USELOG
 171: #ifdef  BSD4_2
 172:     struct timeval tod;
 173:     struct timezone tz;
 174: 
 175:     (void) gettimeofday(&tod, &tz);
 176:     Tbegin = tod.tv_sec + (double)tod.tv_usec/1000000.;
 177: #else
 178:     Tbegin = (double) time((time_t *)NULL);
 179: #endif	BSD4_2
 180: 
 181:     Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]);
 182: 
 183:     if (ac < 2) {
 184:         fprintf(stderr, Fmt, Pname, USAGE);
 185:         exit(EX_USAGE);
 186:     }
 187: 
 188: #ifdef  SYSLOG
 189:     /* 4.2 BSD openlog has only two args */
 190: #ifdef  LOG_LOCAL7
 191:     (void) openlog(Pname, LOG_PID, LOG_LOCAL7);
 192: #else
 193:     (void) openlog(Pname, LOG_PID);
 194: #endif	LOG_LOCAL_7
 195: #endif	SYSLOG
 196: #ifdef  USELOG
 197:     if ((Logfp = fopen(NNTPlog, amode)) == (FILE *)NULL) {
 198:         char    buf[BUFSIZ];
 199: 
 200:         sprintf(buf, E_fopen, NNTPlog, amode, errmsg(errno));
 201:         log(L_NOTICE, buf);
 202:     }
 203: #endif	USELOG
 204: 
 205:     for(i = 1; i < ac; i++) {
 206:         if (av[i][0] == '-') {
 207:             switch(av[i][1]) {
 208:             case 'T':
 209:                 transport = T_IP_TCP;
 210:                 break;
 211:             case 'D':
 212:                 transport = T_DECNET;
 213:                 break;
 214:             case 'F':
 215:                 transport = T_FD;
 216:                 break;
 217:             case 's':
 218:                 TOGGLE(Report_Stats);
 219:                 break;
 220:             case 'd':
 221:                 TOGGLE(Debug);
 222:                 break;
 223:             case 'r':
 224:                 TOGGLE(ReQueue_Fails);
 225:                 break;
 226:             case 'a':
 227:                 isQfile = FALSE;
 228:                 break;
 229:             default:
 230:                 fprintf(stderr, "%s: no such option: -%c\n",
 231:                     Pname, av[i][1]);
 232:                 fprintf(stderr, Fmt, Pname, USAGE);
 233:                 exit(EX_USAGE);
 234:             }
 235:             continue;
 236:         }
 237: 
 238:         /*
 239: 		** OK, it wasn't an option, therefore it must be a
 240: 		** hostname, filename pair.
 241: 		**
 242: 		** If the user typed host::file, then it's DECNET,
 243: 		** whether they remembered the "-D" option or not.
 244: 		*/
 245:         Host = av[i];
 246:         if ((Qfile = index(Host, ':')) != (char *)NULL) {
 247:             if (Qfile[1] == ':') {
 248:                 transport = T_DECNET;
 249:                 *Qfile++ = '\0';
 250:             } else if (transport != T_FD)
 251:                 transport = T_IP_TCP;
 252:             *Qfile++ = '\0';
 253:         } else
 254:             Qfile = Host;
 255: 
 256:         bzero((caddr_t)&Stats, sizeof(Stats));
 257:         if (isQfile) {
 258:             if (sendnews(Host, transport, Qfile, isQfile) && Report_Stats) {
 259:                 logstats();
 260:             }
 261:         } else {
 262:             /* one-shot */
 263:             (void) strcpy(Article, Qfile);
 264:             exit(sendnews(Host, transport, Qfile, isQfile) ? EX_OK : EX_TEMPFAIL);
 265:         }
 266:     }
 267:     exit(EX_OK);
 268: }
 269: 
 270: /*
 271: ** Calculate how much time we've used,
 272: ** and report that (and the transfer statistics).
 273: **
 274: */
 275: void
 276: logstats()
 277: {
 278:     static double ouser = 0.0, osys = 0.0;
 279:     double user, sys;
 280:     char buf[BUFSIZ];
 281: #ifdef  USELOG
 282: #ifdef  BSD4_2
 283:     extern  time_t  time();
 284: #endif	BSD4_2
 285:     time_t  tstamp;
 286:     char    *tp;
 287:     extern  char    *ctime();
 288: #endif	USELOG
 289: #ifdef  BSD4_2
 290:     struct rusage self, kids;
 291:     struct timeval tod;
 292:     struct timezone tzdummy;
 293: 
 294:     (void) getrusage(RUSAGE_SELF, &self);
 295:     (void) getrusage(RUSAGE_CHILDREN, &kids);
 296:     (void) gettimeofday(&tod, &tzdummy);
 297: 
 298:     Tend = tod.tv_sec + (double)tod.tv_usec/1000000.;
 299: 
 300:     user = self.ru_utime.tv_sec + kids.ru_utime.tv_sec +
 301:         (double) self.ru_utime.tv_usec/1000000. +
 302:         (double) kids.ru_utime.tv_usec/1000000.;
 303: 
 304:     sys = self.ru_stime.tv_sec + kids.ru_stime.tv_sec +
 305:         (double) self.ru_stime.tv_usec/1000000. +
 306:         (double) kids.ru_stime.tv_usec/1000000.;
 307: #else
 308: #define HZ  60.0    /* typical system clock ticks - param.h */
 309:     struct tms  cpu;
 310: 
 311:     (void) times(&cpu);
 312: 
 313:     Tend = (double) time((time_t *)NULL);
 314:     user = (double)(cpu.tms_utime + cpu.tms_cutime) / HZ;
 315:     sys  = (double)(cpu.tms_stime + cpu.tms_cstime) / HZ;
 316: #endif	BSD4_2
 317:     sprintf(buf,
 318:         "%s stats %lu offered %lu accepted %lu rejected %lu failed",
 319:         Host, Stats.offered, Stats.accepted, Stats.rejected,
 320:         Stats.failed);
 321:     log(L_INFO, buf);
 322: #ifdef  USELOG
 323:     if (Logfp != (FILE *)NULL) {
 324:         (void) time(&tstamp);
 325:         tp = ctime(&tstamp);
 326:         tp[19] = '\0';
 327:         fprintf(Logfp, Fmt, &tp[4], buf);
 328:     }
 329: #endif	USELOG
 330: 
 331:     sprintf(buf, "%s xmit user %.1f system %.1f elapsed %.1f",
 332:         Host, (user - ouser), (sys - osys), (Tend - Tbegin));
 333:     log(L_INFO, buf);
 334: #ifdef  USELOG
 335:     if (Logfp != (FILE *)NULL) {
 336:         fprintf(Logfp, Fmt, &tp[4], buf);
 337:         (void) fflush(Logfp);
 338:     }
 339: #endif	USELOG
 340: 
 341:     /* reset reference point */
 342:     Tbegin = Tend;
 343:     ouser = user;
 344:     osys = sys;
 345: }
 346: 
 347: /*
 348: ** Given a hostname to connect to, and a file of filenames (which contain
 349: ** netnews articles), send those articles to the named host using NNTP.
 350: **
 351: ** Return code behavior is different depending upon isQfile.
 352: **
 353: **	TRUE	- return TRUE if we contacted the remote and started
 354: **		  transferring news - this is to decide whether to
 355: **		  record CPU and transfer statistics.
 356: **
 357: **	FALSE	- a one-shot file transfer - return TRUE or FALSE depending
 358: **		  upon whether we successfully transferred the one article.
 359: */
 360: sendnews(host, transport, file, isQfile)
 361: char    *host, *file;
 362: int transport, isQfile;
 363: {
 364:     register FILE   *fp;
 365: #ifdef  FTRUNCATE
 366:     char    *mode = "r+";       /* so we can use ftruncate() */
 367: #else
 368:     char    *mode = "r";
 369: #endif	FTRUNCATE
 370: 
 371:     if ((Qfp = fopen(file, mode)) == (FILE *)NULL) {
 372:         char    buf[BUFSIZ];
 373: 
 374:         sprintf(buf, E_fopen, file, mode, errmsg(errno));
 375:         log(L_WARNING, buf);
 376:         return(FALSE);
 377:     }
 378: 
 379:     /*
 380: 	** interlock with other copies of this process.
 381: 	** non-blocking.
 382: 	*/
 383:     if (isQfile) {
 384:         if (!lockfd(fileno(Qfp), file, DONT_BLOCK)) {
 385:             FCLOSE(Qfp);
 386:             return(FALSE);
 387:         }
 388:     }
 389: 
 390:     /*
 391: 	** Open a connection to the remote server
 392: 	*/
 393:     if (hello(host, transport) == FAIL) {
 394:         FCLOSE(Qfp);
 395:         return(FALSE);
 396:     }
 397: 
 398:     if (isQfile) {
 399:         /*
 400: 		** We're sending a batch queue:
 401: 		**	open article
 402: 		**	get message-ID
 403: 		**	send "IHAVE <message-ID>" to remote
 404: 		**	read their reply
 405: 		**	send article if appropriate
 406: 		**	iterate to end of queue file
 407: 		*/
 408:         catchsig(interrupted);
 409: 
 410:         while((fp = getfp(Qfp, Article, sizeof(Article))) != (FILE *)NULL) {
 411:             if (!sendarticle(host, fp)) {
 412:                 (void) fclose(fp);
 413:                 requeue(Article);
 414:                 Article[0] = '\0';
 415:                 cleanup();
 416:                 goodbye(DONT_WAIT);
 417:                 restsig();
 418:                 return(TRUE);
 419:             }
 420:             (void) fclose(fp);
 421:         }
 422: 
 423:         cleanup();
 424:         goodbye(WAIT);
 425:         restsig();
 426:         return(TRUE);
 427:     } else {
 428:         /*
 429: 		** Qfp is a netnews article - this is a one-shot
 430: 		** operation, exit code dependent upon remote's
 431: 		** acceptance of the article
 432: 		*/
 433:         register int    retcode;
 434: 
 435:         retcode = sendarticle(host, Qfp);
 436:         FCLOSE(Qfp);
 437:         goodbye(retcode ? WAIT : DONT_WAIT);
 438:         return(retcode && Stats.accepted == 1 && Stats.failed == 0);
 439:     }
 440: }
 441: 
 442: /*
 443: ** Perform one transfer operation:
 444: **	Give IHAVE command
 445: **	Wait for reply, and send article if they ask for it
 446: **	Wait for transfer confirmation, and requeue the article
 447: **		if they drop it.
 448: **	Watch all network I/O for errors, return FALSE if
 449: **		the connection fails and we have to cleanup.
 450: */
 451: sendarticle(host, fp)
 452: char    *host;
 453: FILE    *fp;
 454: {
 455:     register int    code;
 456:     char    buf[BUFSIZ];
 457:     char    *e_xfer = "%s xfer: %s";
 458: 
 459:     switch(code = ihave(fp)) {
 460:     case CONT_XFER:
 461:         /*
 462: 		** They want it. Give it to 'em.
 463: 		*/
 464:         if (!sendfile(fp)) {
 465:             sprintf(buf, e_xfer, host, errmsg(errno));
 466:             log(L_NOTICE, buf);
 467:             Stats.failed++;
 468:             return(FALSE);
 469:         }
 470:         /*
 471: 		** Did the article transfer OK?
 472: 		** Stay tuned to this same socket to find out!
 473: 		*/
 474:         if ((code = readreply(buf, sizeof(buf))) != OK_XFERED) {
 475:             Stats.failed++;
 476:             if (code < 0) {
 477:                 if (errno > 0) {
 478:                     sprintf(buf, e_xfer, host, errmsg(errno));
 479:                     log(L_NOTICE, buf);
 480:                 } else {
 481:                     char errbuf[BUFSIZ];
 482: 
 483:                     sprintf(errbuf, e_xfer, host, buf);
 484:                     log(L_NOTICE, errbuf);
 485:                 }
 486:                 return(FALSE);
 487:             }
 488:             if (ReQueue_Fails && code != ERR_XFERRJCT) {
 489:                 requeue(Article);
 490:                 Article[0] = '\0';
 491:             }
 492:         }
 493:         break;
 494:     case ERR_GOTIT:
 495:         /* they don't want it */
 496:         break;
 497:     default:
 498:         if (code < 0) {
 499:             if (errno > 0) {
 500:                 sprintf(buf, e_xfer, host, errmsg(errno));
 501:                 log(L_NOTICE, buf);
 502:             } else {
 503:                 sprintf(buf, e_xfer, host, "ihave");
 504:                 log(L_NOTICE, buf);
 505:             }
 506:         } else {
 507:             sprintf(buf, "%s improper response to IHAVE: %d while offering %s", host, code, Article);
 508:             log(L_WARNING, buf);
 509:         }
 510:         return(FALSE);
 511:     }
 512:     return(TRUE);
 513: }
 514: 
 515: char *
 516: errmsg(code)
 517: int code;
 518: {
 519:     return(strerror(code));
 520: }
 521: 
 522: /*
 523: ** strip leading and trailing spaces
 524: */
 525: char *
 526: sp_strip(s)
 527: register char   *s;
 528: {
 529:     register char   *cp;
 530: 
 531:     if (s == NULL)
 532:         return(NULL);
 533: 
 534:     if (*s == '\0')
 535:         return(s);
 536: 
 537:     cp = &s[strlen(s) - 1];
 538:     while(cp > s && isspace(*cp))
 539:         cp--;
 540: 
 541:     *++cp = '\0';   /* zap trailing spaces */
 542: 
 543:     for(cp = s; *cp && isspace(*cp); cp++)
 544:         continue;
 545: 
 546:     return(cp); /* return pointer to first non-space */
 547: }
 548: 
 549: /*
 550: ** convert `s' to lower case
 551: */
 552: char *
 553: lcase(s)
 554: register char   *s;
 555: {
 556:     register char   *cp;
 557: 
 558:     if (s == (char *)NULL)
 559:         return(s);
 560: 
 561:     for(cp = s; *cp != '\0'; cp++)
 562:         if (isupper(*cp))
 563:             *cp = tolower(*cp);
 564:     return(s);
 565: }
 566: 
 567: /*
 568: ** Get the message-id header field data with a minimum of fuss.
 569: */
 570: char *
 571: getmsgid(fp)
 572: FILE *fp;
 573: {
 574:     static  char    buf[BUFSIZ];
 575:     static  char    *msgid = "message-id";
 576:     register char   *cp, *cp2;
 577: 
 578:     while(fgets(buf, sizeof(buf), fp) != (char *)NULL) {
 579:         switch(buf[0]) {
 580:         case '\n':
 581:             return((char *)NULL);   /* EOH, we failed */
 582:         case 'M':
 583:         case 'm':
 584:             if ((cp = index(buf, ':')) == (char *)NULL)
 585:                 continue;
 586:             *cp++ = '\0';
 587:             if (strncmp(lcase(buf), msgid, sizeof(*msgid)) == 0) {
 588:                 /* dump extraneous trash - umass.bitnet */
 589:                 /* hope nobody quotes an '>' in a msgid */
 590:                 if ((cp2 = index(cp, '>')) != (char *)NULL)
 591:                     *++cp2 = '\0';
 592:                 return(sp_strip(cp));
 593:             }
 594:             break;
 595:         }
 596:     }
 597:     return((char *)NULL);   /* EOF, we failed */
 598: }
 599: 
 600: #ifdef  notdef  /* nobody obeys the triply damned protocol anyway! */
 601: /*
 602: ** Special characters, see RFC822, appendix D.
 603: */
 604: isspecial(c)
 605: char    c;
 606: {
 607:     char    *specials = "()<>@,;:\\\".[]";
 608: 
 609:     return(index(specials, c) != (char *)NULL ? TRUE : FALSE);
 610: }
 611: 
 612: /*
 613: ** Check on the validity of an RFC822 message-id
 614: **
 615: ** By The Book, RFC822 Appendix D.
 616: **	msg-id		= "<" addr-spec ">"
 617: **	addr-spec	= local-part "@" domain
 618: **	local-part	= word *("." word)
 619: **	word		= atom / quoted-string
 620: **	domain 		= sub-domain *("." sub-domain)
 621: **	sub-domain	= domain-ref / domain-literal
 622: **	domain-ref	= atom
 623: **	domain-literal	= "[" *(dtext / quoted-pair) "]"
 624: **
 625: ** NOTE: close reading of the RFC822 spec indicates that a fully
 626: **	qualified domain name (i.e. one with at least one dot) is
 627: **	NOT required in the domain part of the addr-spec. However,
 628: **	I've decided to be an asshole and require them, since we'll
 629: **	all die a slow death later on if I don't at this juncture.
 630: **	To disable, if you disagree with me, see the last return
 631: **	statement. - Erik E. Fair <fair@ucbarpa.berkeley.edu>
 632: **	May 30, 1986
 633: */
 634: msgid_ok(id)
 635: register char   *id;
 636: {
 637:     register Langle = FALSE;
 638:     register Rangle = FALSE;
 639:     register local_part = FALSE;
 640:     register at = FALSE;
 641:     register dot = FALSE;
 642: 
 643:     /* skip up to the opening angle bracket */
 644:     if (id == (char *)NULL || (id = index(id, '<')) == (char *)NULL)
 645:         return(FALSE);      /* don't waste my time! */
 646: 
 647:     for(; *id != '\0'; id++) {
 648:         switch(*id) {
 649:         case '<':
 650:             if (Langle) return(FALSE);
 651:             Langle = local_part = TRUE;
 652:             break;
 653:         case '>':
 654:             if (Rangle || !Langle || !at) return(FALSE);
 655:             else Rangle = TRUE;
 656:             break;
 657:         case '@':       /* should be a domain spec */
 658:             at = TRUE;
 659:             local_part = FALSE;
 660:             break;
 661:         case '.':
 662:             dot = at;
 663:             break;
 664:         case '\\':
 665:             /*
 666: 			** quoted pair; this disallows NULs, but how
 667: 			** many mailers would die if someone used one?
 668: 			*/
 669:             if (!local_part || (*++id) == '\0') return(FALSE);
 670:             break;
 671:         case '"':
 672:             /*
 673: 			** quoted string
 674: 			*/
 675:             if (!local_part) return(FALSE);
 676:             do {
 677:                 switch(*++id) {
 678:                 case '\\':
 679:                     if ((*++id) == '\0') return(FALSE);
 680:                     break;
 681:                 case '\r':
 682:                     return(FALSE);
 683:                 }
 684:             } while(*id != '\0' && *id != '"');
 685:             break;
 686:         case '[':
 687:             /*
 688: 			** domain literal
 689: 			*/
 690:             if (local_part) return(FALSE);
 691:             do {
 692:                 switch(*++id) {
 693:                 case '\\':
 694:                     if ((*++id) == '\0') return(FALSE);
 695:                     break;
 696:                 case '\r':
 697:                     return(FALSE);
 698:                 }
 699:             } while(*id != '\0' && *id != ']');
 700:             break;
 701:         default:
 702:             if (!isascii(*id) || iscntrl(*id) || isspace(*id) || isspecial(*id))
 703:                 return(FALSE);  /* quit immediately */
 704:             break;
 705:         }
 706:     }
 707:     return(at && dot && Langle && Rangle);
 708: }
 709: #else notdef
 710: 
 711: /*
 712: ** Simpleton's check for message ID syntax.
 713: ** A concession to the realities of the ARPA Internet.
 714: */
 715: msgid_ok(s)
 716: register char *s;
 717: {
 718:     register char   c;
 719:     register in_msgid = FALSE;
 720: 
 721:     if (s == (char *)NULL)
 722:         return(FALSE);
 723: 
 724:     while((c = *s++) != '\0') {
 725:         if (!isascii(c) || iscntrl(c) || isspace(c))
 726:             return(FALSE);
 727:         switch(c) {
 728:         case '<':
 729:             in_msgid = TRUE;
 730:             break;
 731:         case '>':
 732:             return(in_msgid);
 733:         }
 734:     }
 735:     return(FALSE);
 736: }
 737: #endif	notdef
 738: 
 739: /*
 740: ** Read the header of a netnews article, snatch the message-id therefrom,
 741: ** and ask the remote if they have that one already.
 742: */
 743: ihave(fp)
 744: FILE    *fp;
 745: {
 746:     register int    code;
 747:     register char   *id;
 748:     char    buf[BUFSIZ];
 749: 
 750:     if ((id = getmsgid(fp)) == (char *)NULL || *id == '\0') {
 751:         /*
 752: 		** something botched locally with the article
 753: 		** so we don't send it, but we don't break off
 754: 		** communications with the remote either.
 755: 		*/
 756:         sprintf(buf, "%s: message-id missing!", Article);
 757:         log(L_DEBUG, buf);
 758:         return(ERR_GOTIT);
 759:     }
 760: 
 761:     if (!msgid_ok(id)) {
 762:         sprintf(buf, "%s: message-id syntax error: %s", Article, id);
 763:         log(L_DEBUG, buf);
 764:         return(ERR_GOTIT);
 765:     }
 766: 
 767:     sprintf(buf, "IHAVE %s", id);
 768:     Stats.offered++;
 769: 
 770:     switch(code = converse(buf, sizeof(buf))) {
 771:     case CONT_XFER:
 772:         Stats.accepted++;
 773:         rewind(fp);
 774:         return(code);
 775:     case ERR_GOTIT:
 776:         Stats.rejected++;
 777:         return(code);
 778:     default:
 779:         return(code);
 780:     }
 781: }
 782: 
 783: /*
 784: ** Given that fp points to an open file containing filenames,
 785: ** open and return a file pointer to the next filename in the file.
 786: ** Don't you love indirection?
 787: **
 788: ** Returns a valid FILE pointer or NULL if end of file.
 789: */
 790: FILE *
 791: getfp(fp, filename, fnlen)
 792: register FILE   *fp;
 793: char    *filename;
 794: register int    fnlen;
 795: {
 796:     register FILE   *newfp = (FILE *)NULL;
 797:     register char   *cp;
 798:     char    *mode = "r";
 799: 
 800:     while(newfp == (FILE *)NULL) {
 801:         if (fgets(filename, fnlen, fp) == (char *)NULL)
 802:             return((FILE *)NULL);       /* EOF, tell caller */
 803: 
 804:         filename[fnlen - 1] = '\0'; /* make sure */
 805: 
 806:         /* if fgets() ever forgets the '\n', we're fucked */
 807:         if (*(cp = &filename[strlen(filename) - 1]) == '\n')
 808:             *cp = '\0';
 809: 
 810:         if (filename[0] == '\0')
 811:             continue;
 812: 
 813:         if ((newfp = fopen(filename, mode)) == (FILE *)NULL) {
 814:             /*
 815: 			** The only permissible error is `file non-existant'
 816: 			** anything else indicates something is seriously
 817: 			** wrong, and we should go away to let the shell
 818: 			** script clean up.
 819: 			*/
 820:             if (errno != ENOENT) {
 821:                 char    buf[BUFSIZ];
 822: 
 823:                 sprintf(buf, E_fopen, filename, mode, errmsg(errno));
 824:                 log(L_WARNING, buf);
 825:                 goodbye(DONT_WAIT);
 826:                 exit(EX_OSERR);
 827:             }
 828:         }
 829:     }
 830:     return(newfp);
 831: }
 832: 
 833: /*
 834: ** OK, clean up any mess and requeue failed articles
 835: */
 836: cleanup()
 837: {
 838:     dprintf(stderr, "%s: cleanup()\n", Pname);
 839:     if (Qfp == (FILE *)NULL || Qfile == (char *)NULL)
 840:         return;
 841: 
 842:     if ((ReQueue_Fails && Stats.failed > 0) || !feof(Qfp)) {
 843:         rewrite();
 844:     } else {
 845:         /*
 846: 		** Nothing to clean up after, reset stuff and
 847: 		** nuke the queue file.
 848: 		*/
 849:         requeue((char *)NULL);
 850:         if (feof(Qfp)) {
 851:             dprintf(stderr, "%s: unlink(%s)\n", Pname, Qfile);
 852:             if (unlink(Qfile) < 0) {
 853:                 char    buf[BUFSIZ];
 854: 
 855:                 sprintf(buf, E_unlk, Qfile, errmsg(errno));
 856:                 log(L_WARNING, buf);
 857:             }
 858:         }
 859:         FCLOSE(Qfp);
 860:     }
 861: }
 862: 
 863: /*
 864: ** Add an article file name to an allocated linked list,
 865: ** so that we can rewrite it back to the queue file later.
 866: ** Calling this with a NULL pointer resets the internal pointer.
 867: */
 868: void
 869: requeue(article)
 870: char *article;
 871: {
 872:     static ll_t *lp = &FailedArticles;
 873: 
 874:     if (article == (char *)NULL) {
 875:         dprintf(stderr, "%s: requeue(): reset\n", Pname);
 876:         goto reset;     /* this is for our static pointer */
 877:     }
 878: 
 879:     if (*article == '\0')
 880:         return;
 881: 
 882:     dprintf(stderr, "%s: requeue(%s)\n", Pname, article);
 883:     if ((lp = l_alloc(lp, article, strlen(article) + 1)) == (ll_t *)NULL) {
 884:         fprintf(stderr, "%s: requeue(%s) failed, dumping fail list\n",
 885:             Pname, article);
 886:         /*
 887: 		** Wow! Did you know that this could blow the stack
 888: 		** if we recurse too deeply? I sure didn't!
 889: 		*/
 890: reset:
 891:         l_free(&FailedArticles);
 892:         lp = &FailedArticles;
 893:     }
 894: }
 895: 
 896: /*
 897: ** Note that if I'm not running as "news" or "usenet" (or whatever
 898: ** account is supposed to own netnews), the resultant file will be the
 899: ** wrong ownership, permissions, etc.
 900: */
 901: rewrite()
 902: {
 903:     register ll_t   *lp;
 904:     register FILE   *tmpfp;
 905:     register int    nart = 0;
 906:     char    *mode = "w+";
 907:     char    *template = "/tmp/nntpxmitXXXXXX";
 908:     char    buf[BUFSIZ];
 909:     static char *tempfile = (char *)NULL;
 910: 
 911:     dprintf(stderr, "%s: rewrite(%s)\n", Pname, Qfile);
 912: 
 913:     if (tempfile == (char *)NULL)       /* should only need this once */
 914:         tempfile = mktemp(template);
 915: 
 916:     if ((tmpfp = fopen(tempfile, mode)) == (FILE *)NULL) {
 917:         sprintf(buf, E_fopen, tempfile, mode, errmsg(errno));
 918:         log(L_WARNING, buf);
 919:         FCLOSE(Qfp);
 920:         return;
 921:     }
 922: 
 923:     /*
 924: 	** Requeue the rest of the queue file first,
 925: 	** so that failed articles (if any) go to the end
 926: 	** of the new file.
 927: 	*/
 928:     if (!feof(Qfp)) {
 929:         dprintf(stderr, "%s: copying the unused portion of %s to %s\n",
 930:             Pname, Qfile, tempfile);
 931:         while(fgets(buf, sizeof(buf), Qfp) != (char *)NULL)
 932:             (void) fputs(buf, tmpfp);
 933:     }
 934: 
 935:     /*
 936: 	** Here we write out the filenames of articles which
 937: 	** failed at the remote end.
 938: 	*/
 939:     dprintf(stderr, "%s: writing failed article filenames to %s\n",
 940:         Pname, tempfile);
 941:     L_LOOP(lp, FailedArticles) {
 942:         fprintf(tmpfp, "%s\n", lp->l_item);
 943:         nart++;
 944:     }
 945:     dprintf(stderr, "%s: wrote %d article filenames to %s\n",
 946:         Pname, nart, tempfile);
 947: 
 948:     (void) fflush(tmpfp);
 949:     /*
 950: 	** If writing the temp file failed (maybe /tmp is full?)
 951: 	** back out and leave the queue file exactly as it is.
 952: 	*/
 953:     if (ferror(tmpfp)) {
 954:         sprintf(buf, "rewrite(): copy to %s failed", tempfile);
 955:         log(L_WARNING, buf);
 956:         (void) fclose(tmpfp);
 957:         FCLOSE(Qfp);
 958:         if (unlink(tempfile) < 0) {
 959:             sprintf(buf, E_unlk, tempfile, errmsg(errno));
 960:             log(L_WARNING, buf);
 961:         }
 962:         requeue((char *)NULL);      /* reset */
 963:         return;
 964:     }
 965: 
 966:     rewind(tmpfp);
 967: #ifdef  FTRUNCATE
 968:     rewind(Qfp);
 969:     if (ftruncate(fileno(Qfp), (off_t)0) < 0) {
 970:         sprintf(buf, "ftruncate(%s, 0): %s", Qfile, errmsg(errno));
 971:         log(L_WARNING, buf);
 972:         FCLOSE(Qfp);
 973:         (void) fclose(tmpfp);
 974:         if (unlink(tempfile) < 0) {
 975:             sprintf(buf, E_unlk, tempfile, errmsg(errno));
 976:             log(L_WARNING, buf);
 977:         }
 978:         requeue((char *)NULL);      /* reset */
 979:         return;
 980:     }
 981: #else
 982:     FCLOSE(Qfp);    /* we just nuked our lock here (lockfd) */
 983:     if ((Qfp = fopen(Qfile, mode)) == (FILE *)NULL) {
 984:         sprintf(buf, E_fopen, Qfile, mode, errmsg(errno));
 985:         log(L_WARNING, buf);
 986:         (void) fclose(tmpfp);
 987:         if (unlink(tempfile) < 0) {
 988:             sprintf(buf, E_unlk, tempfile, errmsg(errno));
 989:             log(L_WARNING, buf);
 990:         }
 991:         requeue((char *)NULL);      /* reset */
 992:         return;
 993:     }
 994:     /* Try to get our lock back (but continue whether we do or not) */
 995:     (void) lockfd(fileno(Qfp), Qfile, DONT_BLOCK);
 996: #endif	FTRUNCATE
 997: 
 998:     dprintf(stderr, "%s: copying %s back to %s\n", Pname, tempfile, Qfile);
 999:     while(fgets(buf, sizeof(buf), tmpfp) != (char *)NULL)
1000:         (void) fputs(buf, Qfp);
1001: 
1002:     (void) fflush(Qfp);
1003:     if (ferror(Qfp)) {
1004:         sprintf(buf, "rewrite(): copy to %s failed", Qfile);
1005:         log(L_WARNING, buf);
1006:     }
1007:     (void) fclose(tmpfp);
1008:     FCLOSE(Qfp);
1009:     if (unlink(tempfile) < 0) {
1010:         sprintf(buf, E_unlk, tempfile, errmsg(errno));
1011:         log(L_WARNING, buf);
1012:     }
1013:     requeue((char *)NULL);      /* reset */
1014:     dprintf(stderr, "%s: rewrite(%s): done\n", Pname, Qfile);
1015:     return;
1016: }
1017: 
1018: /*
1019: ** Signal stuff
1020: **
1021: ** There's probably too much stuff to do in this signal
1022: ** handler, but we're going to exit anyway...
1023: */
1024: interrupted(sig)
1025: int sig;
1026: {
1027:     char buf[BUFSIZ];
1028: 
1029: #ifndef RELSIG
1030:     catchsig(SIG_IGN);  /* for System V - hope we're quick enough */
1031: #endif	RELSIG
1032:     sprintf(buf, "%s signal %d", Host, sig);
1033:     log(L_NOTICE, buf);
1034:     requeue(Article);
1035:     cleanup();
1036:     if (Report_Stats)
1037:         logstats();
1038:     goodbye(DONT_WAIT);
1039:     exit(EX_TEMPFAIL);
1040: }
1041: 
1042: struct {
1043:     int signo;
1044:     ifunp   state;
1045: } SigList[] = {
1046:     {SIGHUP},
1047:     {SIGINT},
1048:     {SIGQUIT},
1049:     {SIGTERM},
1050:     {NULL}
1051: };
1052: 
1053: void
1054: catchsig(handler)
1055: ifunp   handler;
1056: {
1057:     register int    i;
1058: 
1059:     if (handler != SIG_IGN) {
1060:         for(i = 0; SigList[i].signo != NULL; i++) {
1061:             SigList[i].state = signal(SigList[i].signo, handler);
1062:         }
1063:     } else {
1064:         for(i = 0; SigList[i].signo != NULL; i++) {
1065:             (void) signal(SigList[i].signo, handler);
1066:         }
1067:     }
1068: }
1069: 
1070: void
1071: restsig()
1072: {
1073:     register int    i;
1074: 
1075:     for(i = 0; SigList[i].signo != NULL; i++) {
1076:         if (SigList[i].state != (ifunp)(-1))
1077:             (void) signal(SigList[i].signo, SigList[i].state);
1078:     }
1079: }
1080: 
1081: /*
1082: ** log stuff
1083: */
1084: void
1085: log(importance, error)
1086: int importance;
1087: char    *error;
1088: {
1089:     FILE    *report = (importance == L_INFO ? stdout : stderr);
1090: 
1091:     fprintf(report, Fmt, Pname, error);
1092: #ifdef  SYSLOG
1093:     switch(importance) {
1094:     case L_INFO:    importance = LOG_INFO;      break;
1095:     case L_DEBUG:   importance = LOG_DEBUG;     break;
1096:     case L_NOTICE:  importance = LOG_NOTICE;    break;
1097:     case L_WARNING: importance = LOG_WARNING;   break;
1098:     default:    importance = LOG_DEBUG;     break;
1099:     }
1100:     syslog(importance, error);
1101: #endif	SYSLOG
1102: }
1103: 
1104: /*
1105: ** Lock a file descriptor
1106: **
1107: ** NOTE: if the appropriate system calls are unavailable,
1108: ** this subroutine is a no-op.
1109: */
1110: lockfd(fd, file, non_blocking)
1111: int fd, non_blocking;
1112: char    *file;          /* just for error reporting */
1113: {
1114:     char    buf[BUFSIZ];
1115: #ifdef  USG
1116: #ifdef  F_TLOCK
1117:     if (lockf(fd, (non_blocking ? F_TLOCK : F_LOCK), 0) < 0) {
1118:         if (errno != EACCES) {
1119:             sprintf(buf, "lockf(%s): %s\n", file, errmsg(errno));
1120:             log(L_WARNING, buf);
1121:         }
1122:         return(FALSE);
1123:     }
1124: #endif	F_TLOCK
1125: #else
1126: #ifdef  LOCK_EX
1127:     if (flock(fd, LOCK_EX|(non_blocking ? LOCK_NB : 0)) < 0) {
1128:         if (errno != EWOULDBLOCK) {
1129:             sprintf(buf, "flock(%s): %s\n", file, errmsg(errno));
1130:             log(L_WARNING, buf);
1131:         }
1132:         return(FALSE);
1133:     }
1134: #endif	LOCK_EX
1135: #endif	USG
1136:     return(TRUE);
1137: }

Defined functions

bzero defined in line 152; used 1 times
catchsig defined in line 1053; used 3 times
cleanup defined in line 836; used 3 times
errmsg defined in line 515; used 17 times
getfp defined in line 790; used 2 times
getmsgid defined in line 570; used 1 times
ihave defined in line 743; used 1 times
interrupted defined in line 1024; used 2 times
isspecial defined in line 604; used 1 times
lcase defined in line 552; used 1 times
lockfd defined in line 1110; used 2 times
log defined in line 1084; used 36 times
logstats defined in line 275; used 3 times
main defined in line 161; never used
msgid_ok defined in line 715; used 1 times
requeue defined in line 868; used 9 times
restsig defined in line 1070; used 3 times
rewrite defined in line 901; used 1 times
sendarticle defined in line 451; used 2 times
sendnews defined in line 360; used 2 times
sp_strip defined in line 525; used 1 times

Defined variables

Article defined in line 114; used 11 times
Debug defined in line 120; used 1 times
E_fopen defined in line 126; used 5 times
E_unlk defined in line 127; used 5 times
FailedArticles defined in line 133; used 4 times
Fmt defined in line 125; used 5 times
Host defined in line 111; used 8 times
NNTPlog defined in line 129; used 2 times
Pname defined in line 110; used 20 times
Qfile defined in line 112; used 21 times
ReQueue_Fails defined in line 122; used 3 times
Report_Stats defined in line 121; used 3 times
Tbegin defined in line 142; used 4 times
Tend defined in line 142; used 4 times
USAGE defined in line 124; used 2 times

Defined macros

FCLOSE defined in line 89; used 9 times
HZ defined in line 308; used 2 times
MAXFNAME defined in line 88; used 1 times
TOGGLE defined in line 119; used 3 times
Last modified: 1996-03-22
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 8020
Valid CSS Valid XHTML 1.0 Strict