home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / N / TCPIP / NNTP-1.000 / NNTP-1 / nntp.1.5.11t / xmit / nntpxmit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-17  |  27.1 KB  |  1,167 lines

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