# 2.11BSD#431 - /usr/src/new/nntp/xmit/nntpxmit.c

##### Functions

    bzero             X
    catchsig          X
    cleanup           X
    errmsg            X
    getfp             X
    getmsgid          X
    ihave             X
    interrupted       X
    isspecial         X
    lcase             X
    lockfd            X
    log               X
    logstats          X
    main              X
    msgid_ok          X
    requeue           X
    restsig           X
    rewrite           X
    sendarticle       X
    sendnews          X
    sp_strip          X

##### Variables

    Article           X
    Debug             X
    E_fopen           X
    E_unlk            X
    FailedArticles    X
    Fmt               X
    Host              X
    NNTPlog           X
    Pname             X
    Qfile             X
    ReQueue_Fails     X
    Report_Stats      X
    Tbegin            X
    Tend              X
    USAGE             X

##### Macros

    FCLOSE            X
    HZ                X
    MAXFNAME          X
    TOGGLE            X

       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

- in line 256

catchsig defined in line 1053; used 3 times

- in line 94, 408, 1030

cleanup defined in line 836; used 3 times

- in line 415, 423, 1035

errmsg defined in line 515; used 17 times

- in line 92, 200, 374, 465, 478, 500, 823, 855, 917, 959, 970-975(2), 984-988(2), 1010, 1119, 1129

getfp defined in line 790; used 2 times

- in line 91, 410

getmsgid defined in line 570; used 1 times

- in line 750

ihave defined in line 743; used 1 times

- in line 459

interrupted defined in line 1024; used 2 times

- in line 98, 408

isspecial defined in line 604; used 1 times

- in line 702

lcase defined in line 552; used 1 times

- in line 587

lockfd defined in line 1110; used 2 times

- in line 384, 995

log defined in line 1084; used 36 times

- in line 97, 201, 321, 333, 375, 466, 479-484(2), 501-508(3), 757, 763, 824, 856, 918, 955-960(2), 971-976(2), 985-989(2), 1005, 1011, 1033, 1120, 1130
- in /usr/src/new/nntp/xmit/remote.c line 55, 187, 200-208(3), 215, 222, 230, 250

logstats defined in line 275; used 3 times

- in line 96, 259, 1037

main defined in line 161; never used
msgid_ok defined in line 715; used 1 times

- in line 761

requeue defined in line 868; used 9 times

- in line 93, 413, 489, 849, 962, 978, 991, 1013, 1034

restsig defined in line 1070; used 3 times

- in line 95, 417, 425

rewrite defined in line 901; used 1 times

- in line 843

sendarticle defined in line 451; used 2 times

- in line 411, 435

sendnews defined in line 360; used 2 times

- in line 258, 264

sp_strip defined in line 525; used 1 times

- in line 592

#### Defined variables

Article defined in line 114; used 11 times

- in line 263, 410-414(4), 489-490(2), 507, 756, 762, 1034

Debug defined in line 120; used 1 times

- in line 221

E_fopen defined in line 126; used 5 times

- in line 200, 374, 823, 917, 984

E_unlk defined in line 127; used 5 times

- in line 855, 959, 975, 988, 1010

FailedArticles defined in line 133; used 4 times

- in line 872, 891-892(2), 941

Fmt defined in line 125; used 5 times

- in line 184, 232, 327, 336, 1091

Host defined in line 111; used 8 times

- in line 245-246(2), 254-258(2), 264, 319, 332, 1032

NNTPlog defined in line 129; used 2 times

- in line 197-200(2)

Pname defined in line 110; used 20 times

- in line 181-184(4), 191-193(2), 231-232(2), 838, 851, 875, 882-885(2), 911, 930, 940, 946, 998, 1014, 1091

Qfile defined in line 112; used 21 times

- in line 246-264(8), 839, 851-855(3), 911, 930, 970, 983-984(2), 995-998(2), 1004, 1014

ReQueue_Fails defined in line 122; used 3 times

- in line 224, 488, 842

Report_Stats defined in line 121; used 3 times

- in line 218, 258, 1036

Tbegin defined in line 142; used 4 times

- in line 176-178(2), 332, 342

Tend defined in line 142; used 4 times

- in line 298, 313, 332, 342

USAGE defined in line 124; used 2 times

- in line 184, 232

#### Defined macros

FCLOSE defined in line 89; used 9 times

- in line 385, 394, 436, 859, 919, 957, 972, 982, 1008

HZ defined in line 308; used 2 times

- in line 314-315(2)

MAXFNAME defined in line 88; used 1 times

- in line 114

TOGGLE defined in line 119; used 3 times

- in line 218-224(3)

Last modified: 1996-03-22 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 4417 |