home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / nn6.4 / part10 / nntp.c < prev   
Encoding:
C/C++ Source or Header  |  1990-06-07  |  22.1 KB  |  968 lines

  1. /*
  2.  * nntp module for nn.
  3.  *
  4.  * The original taken from the nntp 1.5 clientlib.c
  5.  * Modified heavily for nn.
  6.  *
  7.  * Rene' Seindal (seindal@diku.dk) Thu Dec  1 18:41:23 1988
  8.  *
  9.  * I have modified Rene's code quite a lot for 6.4 -- I hope he
  10.  * can still recognize a bit here and a byte there; in any case,
  11.  * any mistakes are mine :-)  ++Kim
  12.  */
  13.  
  14.  
  15. #include "config.h"
  16.  
  17. /*
  18.  *     nn maintains a cache of recently used articles to improve efficiency.
  19.  *     To change the size of the cache, define NNTPCACHE in config.h to be
  20.  *    the new size of this cache.
  21.  */
  22.  
  23. #ifndef NNTPCACHE
  24. #define NNTPCACHE    10
  25. #endif
  26.  
  27. #ifdef NNTP
  28. #include <stdio.h>
  29. #include "nntp.h"
  30. #include <sys/socket.h>
  31. #include <netdb.h>
  32.  
  33. /* This is necessary due to the definitions in m-XXX.h */
  34. #if !defined(NETWORK_DATABASE) || defined(NETWORK_BYTE_ORDER)
  35. #include <netinet/in.h>
  36. #endif
  37.  
  38. import char *db_directory, *tmp_directory, *news_active;
  39.  
  40. export char nntp_server[256];    /* name of nntp server */
  41. export int use_nntp = 0;    /* bool: t iff we use nntp */
  42. export int nntp_failed = 0;    /* bool: t iff connection is broken in
  43.                    nntp_get_article() or nntp_get_active() */
  44.  
  45. export nntp_cache_size = NNTPCACHE;
  46. export char *nntp_cache_dir = NULL;
  47.  
  48. export int nntp_local_server = 0;
  49. export int nntp_debug = 0;
  50.  
  51. import int silent, no_update;
  52.  
  53. import int errno, sys_nerr;
  54. import char *sys_errlist[];
  55. extern int user_error();
  56. extern int sys_error();
  57.  
  58. #define syserr() (errno >= 0 && errno < sys_nerr ? \
  59.           sys_errlist[errno] : "Unknown error.")
  60.  
  61. import char *mktemp();
  62.  
  63. static FILE *nntp_in = NULL;        /* fp for reading from server */
  64. static FILE *nntp_out = NULL;        /* fp for writing to server */
  65. static int is_connected = 0;        /* bool: t iff we are connected */
  66. static group_header *group_hd;        /* ptr to servers current group */
  67. static int group_is_set = 0;            /* bool: t iff group_hd is set */
  68. static int try_again = 0;        /* bool: t if timeout forces retry */
  69. static int can_post = 0;        /* bool: t iff NNTP server accepts postings */
  70.  
  71. #define ERR_TIMEOUT    503        /* Response code for timeout */
  72.  
  73. /*
  74.  * debug_msg: print a debug message.
  75.  *
  76.  *    The master appends prefix and str to a log file, and clients
  77.  *    prints it as a message.
  78.  *
  79.  *    This is controlled via the nntp-debug variable in nn, and
  80.  *    the option -D2 (or -D3 if the normal -D option should also
  81.  *    be turned on).  Debug output from the master is written in
  82.  *    $TMP/nnmaster.log.
  83.  */
  84.  
  85. static debug_msg(prefix, str)
  86. char *prefix, *str;
  87. {
  88.     static FILE *f = NULL;
  89.     
  90.     if (who_am_i == I_AM_MASTER) {
  91.     if (f == NULL) {
  92.         f = open_file(relative(tmp_directory, "nnmaster.log"), OPEN_CREATE);
  93.         if (f == NULL) {
  94.         nntp_debug = 0;
  95.         return;
  96.         }
  97.     }        
  98.     fprintf(f, "%s %s\n", prefix, str);
  99.     fflush(f);
  100.     return;
  101.     }
  102.  
  103.     msg("NNTP%s %s", prefix, str);
  104.     user_delay(1);
  105. }
  106.  
  107. /*
  108.  * io_error: signal an I/O error in talking to the server.
  109.  *
  110.  *     An nn client terminates a session with the user.  The master
  111.  *    simply closes the connection.  The flag nntp_failed is set, for
  112.  *    use by the master to terminate collection.
  113.  *
  114.  *    BUG: if the nntp server is forcibly killed, errno can contain a
  115.  *    bogus value, resulting in strange error messages.  It is
  116.  *    probably better just to write out the numerical value of errno.
  117.  */
  118.  
  119. static io_error()
  120. {
  121.     if (who_am_i != I_AM_MASTER) {
  122.     user_error("Lost connection to NNTP server %s: %s", nntp_server, syserr());
  123.         /* NOTREACHED */
  124.     }
  125.     nntp_failed = 1;
  126.     if (is_connected) {
  127.     log_entry('N', "Lost connection to server %s: %s", nntp_server, syserr());
  128.     nntp_close_server();
  129.     }
  130. }
  131.  
  132. /*
  133.  * find_server: Find out which host to use as NNTP server.
  134.  *
  135.  *     This is done by consulting the file NNTP_SERVER (defined in
  136.  *     config.h).  Set nntp_server[] to the host's name.
  137.  */
  138.  
  139. static void find_server()
  140. {
  141.     char *cp, *name, *getenv();
  142.     char buf[BUFSIZ];
  143.     FILE *fp;
  144.  
  145.     /*
  146.      * This feature cannot normally be enabled, because the database and
  147.      * the users rc file contains references to articles by number, and
  148.      * these numbers are not unique across NNTP servers.
  149.      */
  150. #ifdef DEBUG
  151.     if ((cp = getenv("NNTPSERVER")) != NULL) {
  152.     strncpy(nntp_server, cp, sizeof nntp_server);
  153.     return;
  154.     }
  155. #endif /* DEBUG */
  156.  
  157.     name = NNTP_SERVER;
  158.     if (*name != '/')
  159.     name = relative(lib_directory, name);
  160.  
  161.     if ((fp = open_file(name, OPEN_READ)) != NULL) {
  162.     while (fgets(buf, sizeof buf, fp) != 0) {
  163.         if (*buf == '#' || *buf == '\n')
  164.         continue;
  165.         if ((cp = strchr(buf, '\n')) != 0)
  166.         *cp = '\0';
  167.         strncpy(nntp_server, buf, sizeof nntp_server);
  168.         fclose(fp);
  169.         return;
  170.     }
  171.     fclose(fp);
  172.     }
  173.  
  174.     if (who_am_i != I_AM_MASTER)
  175.     printf("\nCannot find name of NNTP server.\nCheck %s\n", name);
  176.  
  177.     sys_error("Failed to find name of NNTP server!");
  178. }
  179.  
  180. /*
  181.  * get_server_line: get a line from the server.
  182.  *
  183.  *     Expects to be connected to the server.
  184.  *     The line can be any kind of line, i.e., either response or text.
  185.  */
  186.  
  187. static get_server_line(string, size)
  188.     char *string;
  189.     int size;
  190. {
  191.     register char *cp, *nl;
  192.  
  193.     if (fgets(string, size, nntp_in) == NULL) {
  194.     io_error();
  195.     return -1;
  196.     }
  197.     for (cp = string, nl = NULL; *cp != NUL; cp++) {
  198.     if (*cp == CR) {
  199.         nl = cp;
  200.         break;
  201.     }
  202.     if (nl == NULL && *cp == NL)
  203.         nl = cp;
  204.     }
  205.     if (nl != NULL) *nl = NUL;
  206.  
  207.     return 0;
  208. }
  209.  
  210. /*
  211.  * get_server: get a response line from the server.
  212.  *
  213.  *     Expects to be connected to the server.
  214.  *     Returns the numerical value of the reponse, or -1 in case of errors.
  215.  */
  216.  
  217. static get_server(string, size)
  218.     char *string;
  219.     int size;
  220. {
  221.     if (get_server_line(string, size) < 0)
  222.     return -1;
  223.  
  224.     if (nntp_debug) debug_msg("<<<", string);
  225.  
  226.     return isdigit(*string) ? atoi(string) : 0;
  227. }
  228.  
  229. /*
  230.  * get_socket:  get a connection to the nntp server.
  231.  *
  232.  *     Doesn't return in case of errors.
  233.  */
  234.  
  235. static get_socket()
  236. {
  237.     int s;
  238.     struct sockaddr_in sin;
  239.     struct servent *getservbyname(), *sp;
  240.     struct hostent *gethostbyname(), *hp;
  241. #ifdef h_addr
  242.     int     x = 0;
  243.     register char **cp;
  244. #endif
  245.  
  246.     if ((sp = getservbyname("nntp", "tcp")) ==  NULL)
  247.     sys_error("nntp/tcp: Unknown service.\n");
  248.  
  249.     if ((hp = gethostbyname(nntp_server)) == NULL)
  250.     sys_error("NNTP server %s unknown.\n", nntp_server);
  251.  
  252.     bzero((char *) &sin, sizeof(sin));
  253.     sin.sin_family = hp->h_addrtype;
  254.     sin.sin_port = sp->s_port;
  255.  
  256. #ifdef h_addr
  257.     /* get a socket and initiate connection -- use multiple addresses */
  258.  
  259.     for (cp = hp->h_addr_list; cp && *cp; cp++) {
  260.     s = socket(hp->h_addrtype, SOCK_STREAM, 0);
  261.     if (s < 0)
  262.         sys_error("Can't get NNTP socket: %s\n", syserr());
  263.     bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
  264.  
  265.     x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
  266.     if (x == 0)
  267.         break;
  268.     if (who_am_i != I_AM_MASTER)
  269.         msg("Connecting to %s failed: %s", nntp_server, syserr());
  270.     (void) close(s);
  271.     s = -1;
  272.     }
  273.     if (x < 0 && who_am_i != I_AM_MASTER)
  274.     user_error("Giving up on NNTP server %s!\n", nntp_server);
  275. #else                    /* no name server */
  276.     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  277.     sys_error("Can't get NNTP socket: %s\n", syserr());
  278.  
  279.     /* And then connect */
  280.     bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
  281.     if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
  282.     if (who_am_i == I_AM_MASTER)
  283.         sys_error("Connecting to %s failed: %s", nntp_server, syserr());
  284.     s = -1;
  285.     }
  286. #endif
  287.     return s;
  288. }
  289.  
  290. /*
  291.  * connect_server: initialise a connection to the nntp server.
  292.  *
  293.  *     It expects nntp_server[] to be set previously, by a call to
  294.  *     nntp_check.  It is called from nntp_get_article() and
  295.  *    nntp_get_active() if there is no established connection.
  296.  */
  297.  
  298. static connect_server()
  299. {
  300.     int sockt_rd, sockt_wr;
  301.     int response;
  302.     char line[NNTP_STRLEN];
  303.     
  304.     if (who_am_i != I_AM_MASTER && !silent)
  305.     msg("Connecting to NNTP server %s ... ", nntp_server);
  306.  
  307.     nntp_failed = 1;
  308.     is_connected = 0;
  309.  
  310.     sockt_rd = get_socket();
  311.     if (sockt_rd < 0)
  312.     return -1;
  313.  
  314.     if ((nntp_in = fdopen(sockt_rd, "r")) == NULL) {
  315.     close(sockt_rd);
  316.         return -1;
  317.     }
  318.     sockt_wr = dup(sockt_rd);
  319.     if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
  320.     close(sockt_wr);
  321.     fclose(nntp_in);
  322.         nntp_in = NULL;               /* from above */
  323.         return -1;
  324.     }
  325.  
  326.     /* Now get the server's signon message */
  327.     response = get_server(line, sizeof(line));
  328.  
  329.     if (who_am_i == I_AM_MASTER) {
  330.     if (response != OK_CANPOST && response != OK_NOPOST) {
  331.         log_entry('N', "Failed to connect to NNTP server");
  332.         log_entry('N', "Response: %s", line);
  333.         fclose(nntp_out);
  334.         fclose(nntp_in);
  335.         return -1;
  336.     }
  337.     } else {
  338.     switch (response) {
  339.     case OK_CANPOST:
  340.         can_post = 1;
  341.         break;
  342.     case OK_NOPOST:
  343.         can_post = 0;
  344.         break;
  345.     default:
  346.         user_error(line);
  347.         /* NOTREACHED */
  348.     }
  349.     }
  350.     if (who_am_i != I_AM_MASTER && !silent)
  351.     msg("Connecting to NNTP server %s ... ok (%s)",
  352.         nntp_server, can_post ? "posting is allowed" : "no posting");
  353.  
  354.     is_connected = 1;
  355.     group_is_set = 0;
  356.     nntp_failed = 0;
  357.     try_again = 0;
  358.     return 0;
  359. }
  360.  
  361.  
  362. /*
  363.  * put_server:  send a line to the nntp server.
  364.  *
  365.  *     Expects to be connected to the server.
  366.  */
  367.  
  368. static put_server(string)
  369.     char *string;
  370. {
  371.     if (nntp_debug) debug_msg(">>>", string);
  372.  
  373.     fprintf(nntp_out, "%s\r\n", string);
  374.     if (fflush(nntp_out) == EOF) {
  375.     io_error();
  376.     return -1;
  377.     }
  378.     return 0;
  379. }
  380.  
  381. /*
  382.  * ask_server:  ask the server a question and return the answer.
  383.  *
  384.  *    Expects to be connected to the server.
  385.  *    Returns the numerical value of the reponse, or -1 in case of
  386.  *    errors.
  387.  *    Contains some code to handle server timeouts intelligently.
  388.  */
  389.  
  390. /*VARARGS*/
  391. static ask_server(va_alist)
  392. va_dcl
  393. {
  394.     char buf[NNTP_STRLEN];
  395.     char *fmt;
  396.     int response;
  397.     use_vararg;
  398.  
  399.     start_vararg;
  400.     fmt = va_arg1(char *);
  401.     vsprintf(buf, fmt, va_args2toN);
  402.     end_vararg;
  403.  
  404.     if (put_server(buf) < 0)
  405.     return -1;
  406.     response = get_server(buf, sizeof(buf));
  407.  
  408.     /*
  409.      * Handle the response from the server.  Responses are handled as
  410.      * followes:
  411.      *
  412.      * 100-199    Informational.  Passed back. (should they be ignored?).
  413.      * 200-299    Ok messages.  Passed back.
  414.      * 300-399    Ok and proceed.  Can not happen in nn.
  415.      * 400-499    Errors (no article, etc).  Passed up and handled there.
  416.      * 500-599    Fatal NNTP errors.  Handled below.
  417.      */
  418.     if (response == ERR_GOODBYE || response > ERR_COMMAND) {
  419.     nntp_failed = 1;
  420.     nntp_close_server();
  421.  
  422.     if (response != ERR_TIMEOUT) {    /* if not timeout, complain */
  423.         sys_error("NNTP %s response: %d", buf, response);
  424.         /* NOTREACHED */
  425.     }
  426.     try_again = 1;
  427.     group_is_set = 0;
  428.     }
  429.     return response;
  430. }
  431.  
  432. /*
  433.  * copy_text: copy text response into file.
  434.  *
  435.  *     Copies a text response into an open file.
  436.  *    Return -1 on error, 0 otherwise.  It is treated as an error, if
  437.  *    the returned response it not what was expected.
  438.  */
  439.  
  440. static int last_copy_blank;
  441.  
  442. static copy_text(fp)
  443. register FILE *fp;
  444. {
  445.     char buf[NNTP_STRLEN];
  446.     register char *cp;
  447.     register int nlines;
  448.  
  449.     nlines = 0;
  450.     last_copy_blank = 0;
  451.     while (get_server_line(buf, sizeof buf) >= 0) {
  452.     cp = buf;
  453.     if (*cp == '.')
  454.         if (*++cp == '\0') {
  455.         if (nlines <= 0) break;
  456.         if (nntp_debug) {
  457.             sprintf(buf, "%d lines", nlines);
  458.             debug_msg("COPY", buf);
  459.         }
  460.         return 0;
  461.         }
  462.     fputs(cp, fp);
  463.     last_copy_blank = (*cp == NUL);
  464.     putc('\n', fp);
  465.     nlines++;
  466.     }
  467.     fclose(fp);
  468.     if (nntp_debug) debug_msg("COPY", "EMPTY");
  469.     return -1;
  470. }
  471.  
  472.  
  473. static do_set_group()
  474. {
  475.     int n;
  476.  
  477.     switch (n = ask_server("GROUP %s", group_hd->group_name)) {
  478.      case OK_GROUP:
  479.     group_is_set = 1;
  480.     return 1;
  481.  
  482.      case ERR_NOGROUP:
  483.     log_entry('N', "NNTP: group %s not found", group_hd->group_name);
  484.     return -1;
  485.  
  486.      default:
  487.     if (try_again) return 0;    /* Handle nntp server timeouts */
  488.     break;
  489.     }
  490.     if (!nntp_failed) {
  491.     log_entry('N', "GROUP %s response: %d", group_hd->group_name, n);
  492.     nntp_failed = 1;
  493.     }
  494.     return -1;
  495. }
  496.  
  497. /*
  498.  * The following functions implements a simple lru cache of recently
  499.  * accessed articles.  It is a simple way to improve effeciency.  Files
  500.  * must be kept by name, because the rest of the code expects to be able
  501.  * to open an article multiple times, and get separate file pointers.
  502.  */
  503.  
  504. struct cache {
  505.     char        *file_name;    /* file name */
  506.     article_number    art;        /* article stored in file */
  507.     group_header    *grp;        /* from this group */
  508.     unsigned        time;        /* time last accessed */
  509. } cache[NNTPCACHE];
  510.  
  511. static unsigned time_counter = 1;        /* virtual time */
  512.  
  513. /*
  514.  * search_cache: search the cache for an (article, group) pair.
  515.  *
  516.  *     Returns a pointer to the slot where it is, null otherwise
  517.  */
  518.  
  519. static struct cache *search_cache(art, gh)
  520.     article_number art;
  521.     group_header *gh;
  522. {
  523.     struct cache *cptr = cache;
  524.     int i;
  525.  
  526.     if (who_am_i == I_AM_MASTER) return NULL;
  527.  
  528.     if (nntp_cache_size > NNTPCACHE) nntp_cache_size = NNTPCACHE;
  529.  
  530.     for (i = 0; i < nntp_cache_size; i++, cptr++)
  531.     if (cptr->art == art && cptr->grp == gh) {
  532.         cptr->time = time_counter++;
  533.         return cptr;
  534.     }
  535.     return NULL;
  536. }
  537.  
  538. /*
  539.  * new_cache_slot: get a free cache slot.
  540.  *
  541.  *     Returns a pointer to the allocated slot.
  542.  *     Frees the old filename, and allocates a new, unused filename.
  543.  *    Cache files can also stored in a common directory defined in
  544.  *    ~/.nn or CACHE_DIRECTORY if defined in config.h.
  545.  */
  546.  
  547. static struct cache *new_cache_slot()
  548. {
  549.     register struct cache *cptr = cache;
  550.     int i, lru;
  551.     unsigned min_time = time_counter;
  552.     char name[FILENAME];
  553.  
  554.     if (nntp_cache_dir == NULL) {
  555. #ifdef CACHE_DIRECTORY
  556.     nntp_cache_dir = CACHE_DIRECTORY;
  557. #else
  558.     if (who_am_i == I_AM_MASTER)
  559.         nntp_cache_dir = db_directory;
  560.     else
  561.         nntp_cache_dir = nn_directory;
  562. #endif
  563.     }
  564.  
  565.     if (who_am_i == I_AM_MASTER) {
  566.     cptr = &cache[0];
  567.     if (cptr->file_name == NULL)
  568.         cptr->file_name = mk_file_name(nntp_cache_dir, "master_cache");
  569.     return cptr;
  570.     }
  571.  
  572.     for (i = 0; i < nntp_cache_size; i++, cptr++)
  573.     if (min_time > cptr->time) {
  574.         min_time = cptr->time;
  575.         lru = i;
  576.     }
  577.     cptr = &cache[lru];
  578.  
  579.     if (cptr->file_name == NULL) {
  580.     sprintf(name, "%s/nn-%d.%02d~", nntp_cache_dir, process_id, lru);
  581.     cptr->file_name = copy_str(name);
  582.     } else
  583.     unlink(cptr->file_name);
  584.  
  585.     cptr->time = time_counter++;
  586.     return cptr;
  587. }
  588.  
  589. /*
  590.  * clean_cache: clean up the cache.
  591.  *
  592.  *     Removes all allocated files.
  593.  */
  594.  
  595. static void clean_cache()
  596. {
  597.     struct cache *cptr = cache;
  598.     int i;
  599.  
  600.     for (i = 0; i < nntp_cache_size; i++, cptr++)
  601.     if (cptr->file_name)
  602.         unlink(cptr->file_name);
  603. }
  604.  
  605. /*
  606.  * nntp_check: Find out whether we need to use NNTP.
  607.  *
  608.  *     This is done by comparing the NNTP servers name with whatever
  609.  *     gethostname() returns.
  610.  *    use_nntp and news_active are initialised as a side effect.
  611.  */
  612.  
  613. nntp_check()
  614. {
  615.     char host[128];
  616.  
  617.     if (nntp_local_server) return;
  618.  
  619.     find_server();
  620.     gethostname(host, sizeof host);
  621.     use_nntp = strcmp(host, nntp_server) != 0; /* too simplistic ??? */
  622.  
  623.     if (use_nntp) {
  624.     freeobj(news_active);
  625.     news_active = mk_file_name(db_directory, "ACTIVE");
  626.     }
  627. }
  628.  
  629. /*
  630.  * nntp_no_post: Check to see whether posting is allowed.
  631.  */
  632.  
  633. nntp_no_post()
  634. {
  635.     if (!is_connected && connect_server() < 0)
  636.     return 1;            /* If we cannot connect, neither can inews */
  637.     if (can_post == 0) {
  638.     msg("NNTP server does not allow postings from this host.  Sorry!");
  639.     return 1;
  640.     }
  641.     return 0;
  642. }
  643.  
  644.  
  645. /*
  646.  * nntp_set_group: set the server's current group.
  647.  *
  648.  *     Actual communication is delayed until an article is accessed, to
  649.  *     avoid unnecessary traffic.
  650.  */
  651.  
  652. nntp_set_group(gh)
  653.     group_header *gh;
  654. {
  655.     group_hd = gh;
  656.     group_is_set = 0;
  657.     return 0;
  658. }
  659.  
  660. /*
  661.  * nntp_get_active:  get a copy of the active file.
  662.  *
  663.  *     If we are the master get a copy of the file from the nntp server.
  664.  *     nnadmin just uses the one we already got.  In this way the master
  665.  *    can maintain a remote copy of the servers active file.
  666.  *    We try to be a little smart, if not inefficient, about the
  667.  *    modification times on the local active file.
  668.  *    Even when the master is running on the nntp server, a separate
  669.  *    copy of the active file will be made for access via NFS.
  670.  */
  671.  
  672. nntp_get_active()
  673. {
  674.     FILE *old, *new;
  675.     char bufo[NNTP_STRLEN], bufn[NNTP_STRLEN];
  676.     char *new_name;
  677.     int same, n;
  678.  
  679.     if (who_am_i != I_AM_MASTER)
  680.     return access(news_active, 4);
  681.  
  682.  again:
  683.     if (!is_connected && connect_server() < 0)
  684.     return -1;
  685.  
  686.     new_name = mktemp(relative(db_directory, ".actXXXXXX"));
  687.  
  688.     switch (n = ask_server("LIST")) {
  689.      case OK_GROUPS:
  690.     new = open_file(new_name, OPEN_CREATE_RW|MUST_EXIST);
  691.     if (copy_text(new) == 0) break;
  692.     unlink(new_name);
  693.     if (!nntp_failed) {
  694.         log_entry('N', "LIST empty");
  695.         nntp_failed = 1;
  696.     }
  697.     return -1;
  698.      default:
  699.     if (try_again) goto again; /* Handle nntp server timeouts */
  700.     log_entry('N', "LIST response: %d", n);
  701.     return -1;
  702.     }
  703.  
  704.     rewind(new);
  705.     same = 0;
  706.     if ((old = open_file(news_active, OPEN_READ)) != NULL) {
  707.     do {
  708.         fgets(bufo, sizeof bufo, old);
  709.         fgets(bufn, sizeof bufn, new);
  710.     } while (!feof(old) && !feof(new) && strcmp(bufo, bufn) == 0);
  711.     same = feof(old) && feof(new);
  712.     fclose(old);
  713.     }
  714.     fclose(new);
  715.  
  716.     if (same)
  717.     unlink(new_name);
  718.     else
  719.     if (rename(new_name, news_active) != 0)
  720.         sys_error("Cannot rename %s to %s", new_name, news_active);
  721.  
  722.     return 0;
  723. }
  724.  
  725. /*
  726.  * nntp_get_article_list: get list of all article numbers in group
  727.  *
  728.  *     Sends XHDR command to the server, and parses the following
  729.  *    text response to get a list of article numbers which is saved
  730.  *    in a list and returned.
  731.  *    Return NULL on error.  It is treated as an error, if
  732.  *    the returned response it not what was expected.
  733.  */
  734.  
  735. static article_number *article_list = NULL;
  736. static long art_list_length = 0;
  737.  
  738. static sort_art_list(f1, f2)
  739. register article_number *f1, *f2;
  740. {
  741.     return (*f1 < *f2) ? -1 : (*f1 == *f2) ? 0 : 1;
  742. }
  743.  
  744. article_number *nntp_get_article_list(gh)
  745. group_header *gh;
  746. {
  747.     char buf[NNTP_STRLEN];
  748.     register article_number *art;
  749.     register char *cp;
  750.     register long count = 0;    /* No. of completions plus one */
  751.     int n;
  752.     static int try_listgroup = 1;
  753.  
  754.  again:
  755.     if (!is_connected && connect_server() < 0)
  756.     return NULL;
  757.  
  758.     /* it is really an extreme waste of time to use XHDR since all we    */
  759.     /* are interested in is the article numbers (as we do locally).    */
  760.     /* If somebody hacks up an nntp server that understands LISTGROUP    */
  761.     /* they will get much less load on the nntp server            */
  762.     /* It should simply return the existing article numbers is the group*/
  763.     /* -- they don't even have to be sorted (only XHDR needs that)    */
  764.  
  765.     if (try_listgroup) {
  766.     switch (n = ask_server("LISTGROUP %s", group_hd->group_name)) {
  767.      case OK_GROUP:
  768.         break;
  769.      default:
  770.         if (try_again) goto again; /* Handle nntp server timeouts */
  771.         log_entry('N', "LISTGROUP response: %d", n);
  772.      case ERR_COMMAND:
  773.         try_listgroup = 0;
  774.         goto again;    /* error may have closed down server connection */
  775.     }
  776.     }
  777.     if (!try_listgroup) {
  778.     if (group_is_set == 0)
  779.         switch (do_set_group()) {
  780.          case -1:
  781.         return NULL;
  782.          case 0:
  783.         goto again;
  784.          case 1:
  785.         break;
  786.         }
  787.  
  788.     switch (n = ask_server("XHDR message-id %ld-%ld",
  789.         (long)gh->first_db_article, (long)gh->last_db_article)) {
  790.      case OK_HEAD:
  791.         break;
  792.      default:
  793.         if (try_again) goto again; /* Handle nntp server timeouts */
  794.         log_entry('N', "XHDR response: %d", n);
  795.      case ERR_COMMAND:
  796.         nntp_failed = 2;
  797.         return NULL;
  798.     }
  799.     }
  800.  
  801.     count = 0;
  802.     art = article_list;
  803.     
  804.     while (get_server_line(buf, sizeof buf) >= 0) {
  805.     cp = buf;
  806.     if (*cp == '.' && *++cp == '\0') break;
  807.  
  808.     if (count == art_list_length) {
  809.         art_list_length += 250;
  810.         article_list = resizeobj(article_list, article_number, art_list_length + 1);
  811.         art = article_list + count;
  812.     }
  813.     *art++ = atol(cp);
  814.     count++;
  815.     }
  816.     *art = 0;
  817.  
  818.     if (try_listgroup)
  819.     quicksort(article_list, count, article_number, sort_art_list);
  820.     *art = 0;
  821.     return article_list;
  822. }
  823.  
  824. /*
  825.  * nntp_get_article: get an article from the server.
  826.  *
  827.  *     Returns a FILE pointer.
  828.  *    If necessary the server's current group is set.
  829.  *    The article (header and body) are copied into a file, so they
  830.  *    are seekable (nn likes that).
  831.  */
  832.  
  833. static char *mode_cmd[] = {
  834.     "ARTICLE",
  835.     "HEAD",
  836.     "BODY"
  837. };
  838.  
  839. FILE *nntp_get_article(article, mode)
  840. article_number article;
  841. int mode;    /* 0 => whole article, 1 => head only, 2 => body only */
  842. {
  843.     FILE *tmp;
  844.     static struct cache *cptr;
  845.     int n;
  846.     
  847.  again:
  848.     if (!is_connected && connect_server() < 0) {
  849.     return NULL;
  850.     }
  851.  
  852.     /*
  853.      * Set the server group to the current group
  854.      */
  855.     if (group_is_set == 0)
  856.     switch (do_set_group()) {
  857.      case -1:
  858.         return NULL;
  859.      case 0:
  860.         goto again;
  861.      case 1:
  862.         break;
  863.     }
  864.  
  865.     /*
  866.      * Search the cache for the requested article, and allocate a new
  867.      * slot if necessary (if appending body, we already got it).
  868.      */
  869.  
  870.     if (mode != 2) {
  871.     cptr = search_cache(article, group_hd);
  872.     if (cptr != NULL) goto out;
  873.     cptr = new_cache_slot();
  874.     }
  875.     
  876.     /*
  877.      * Copy the article.
  878.      */
  879.     switch (n = ask_server("%s %ld", mode_cmd[mode], (long)article)) {
  880.      case OK_ARTICLE:
  881.      case OK_HEAD:
  882.     tmp = open_file(cptr->file_name, OPEN_CREATE|MUST_EXIST);
  883.     if (copy_text(tmp) < 0)
  884.         return NULL;
  885.  
  886.     if (mode == 1 && !last_copy_blank)
  887.         fputc(NL, tmp); /* add blank line after header */
  888.  
  889.     cptr->art = article;
  890.     cptr->grp = group_hd;
  891.     fclose(tmp);
  892.     goto out;
  893.  
  894.      case OK_BODY:
  895.     tmp = open_file(cptr->file_name, OPEN_APPEND|MUST_EXIST);
  896.     fseek(tmp, (off_t)0, 2);
  897.     if (copy_text(tmp) < 0)
  898.         return NULL;
  899.     fclose(tmp);
  900.     goto out;
  901.     
  902.      case ERR_NOARTIG:
  903.     return NULL;
  904.  
  905.      default:
  906.     if (try_again) goto again; /* Handle nntp server timeouts */
  907.     log_entry('N', "ARTICLE %ld response: %d", (long)article, n);
  908.     nntp_failed = 1;
  909.     return NULL;
  910.     }
  911.  
  912.  out:
  913.     return open_file(cptr->file_name, OPEN_READ|MUST_EXIST);
  914. }
  915.  
  916. /*
  917.  *    Return local file name holding article
  918.  */
  919.  
  920. char *nntp_get_filename(art, gh)
  921. article_number art;
  922. group_header *gh;
  923. {
  924.     struct cache *cptr;
  925.  
  926.     cptr = search_cache(art, gh);
  927.  
  928.     return cptr == NULL ? NULL : cptr->file_name;
  929. }
  930.  
  931. /*
  932.  * nntp_close_server: close the connection to the server.
  933.  */
  934.  
  935. nntp_close_server()
  936. {
  937.     if (!is_connected)
  938.     return;
  939.  
  940.     if (!nntp_failed) {            /* avoid infinite recursion */
  941.     int n;
  942.  
  943.     n = ask_server("QUIT");
  944.     if (n != OK_GOODBYE)
  945.         ;                /* WHAT NOW ??? */
  946.     }
  947.  
  948.     (void) fclose(nntp_out);
  949.     (void) fclose(nntp_in);
  950.  
  951.     is_connected = 0;
  952. }
  953.  
  954. /*
  955.  * nntp_cleanup:  clean up after an nntp session.
  956.  *
  957.  *    Called from nn_exit().
  958.  */
  959.  
  960. nntp_cleanup()
  961. {
  962.     if (is_connected)
  963.     nntp_close_server();
  964.     clean_cache();
  965. }
  966. #endif /* NNTP */
  967.  
  968.