home *** CD-ROM | disk | FTP | other *** search
- /*
- * nntp module for nn.
- *
- * The original taken from the nntp 1.5 clientlib.c
- * Modified heavily for nn.
- *
- * Rene' Seindal (seindal@diku.dk) Thu Dec 1 18:41:23 1988
- *
- * I have modified Rene's code quite a lot for 6.4 -- I hope he
- * can still recognize a bit here and a byte there; in any case,
- * any mistakes are mine :-) ++Kim
- */
-
-
- #include "config.h"
-
- /*
- * nn maintains a cache of recently used articles to improve efficiency.
- * To change the size of the cache, define NNTPCACHE in config.h to be
- * the new size of this cache.
- */
-
- #ifndef NNTPCACHE
- #define NNTPCACHE 10
- #endif
-
- #ifdef NNTP
- #include <stdio.h>
- #include "nntp.h"
- #include <sys/socket.h>
- #include <netdb.h>
-
- /* This is necessary due to the definitions in m-XXX.h */
- #if !defined(NETWORK_DATABASE) || defined(NETWORK_BYTE_ORDER)
- #include <netinet/in.h>
- #endif
-
- import char *db_directory, *tmp_directory, *news_active;
-
- export char nntp_server[256]; /* name of nntp server */
- export int use_nntp = 0; /* bool: t iff we use nntp */
- export int nntp_failed = 0; /* bool: t iff connection is broken in
- nntp_get_article() or nntp_get_active() */
-
- export nntp_cache_size = NNTPCACHE;
- export char *nntp_cache_dir = NULL;
-
- export int nntp_local_server = 0;
- export int nntp_debug = 0;
-
- import int silent, no_update;
-
- import int errno, sys_nerr;
- import char *sys_errlist[];
- extern int user_error();
- extern int sys_error();
-
- #define syserr() (errno >= 0 && errno < sys_nerr ? \
- sys_errlist[errno] : "Unknown error.")
-
- import char *mktemp();
-
- static FILE *nntp_in = NULL; /* fp for reading from server */
- static FILE *nntp_out = NULL; /* fp for writing to server */
- static int is_connected = 0; /* bool: t iff we are connected */
- static group_header *group_hd; /* ptr to servers current group */
- static int group_is_set = 0; /* bool: t iff group_hd is set */
- static int try_again = 0; /* bool: t if timeout forces retry */
- static int can_post = 0; /* bool: t iff NNTP server accepts postings */
-
- #define ERR_TIMEOUT 503 /* Response code for timeout */
-
- /*
- * debug_msg: print a debug message.
- *
- * The master appends prefix and str to a log file, and clients
- * prints it as a message.
- *
- * This is controlled via the nntp-debug variable in nn, and
- * the option -D2 (or -D3 if the normal -D option should also
- * be turned on). Debug output from the master is written in
- * $TMP/nnmaster.log.
- */
-
- static debug_msg(prefix, str)
- char *prefix, *str;
- {
- static FILE *f = NULL;
-
- if (who_am_i == I_AM_MASTER) {
- if (f == NULL) {
- f = open_file(relative(tmp_directory, "nnmaster.log"), OPEN_CREATE);
- if (f == NULL) {
- nntp_debug = 0;
- return;
- }
- }
- fprintf(f, "%s %s\n", prefix, str);
- fflush(f);
- return;
- }
-
- msg("NNTP%s %s", prefix, str);
- user_delay(1);
- }
-
- /*
- * io_error: signal an I/O error in talking to the server.
- *
- * An nn client terminates a session with the user. The master
- * simply closes the connection. The flag nntp_failed is set, for
- * use by the master to terminate collection.
- *
- * BUG: if the nntp server is forcibly killed, errno can contain a
- * bogus value, resulting in strange error messages. It is
- * probably better just to write out the numerical value of errno.
- */
-
- static io_error()
- {
- if (who_am_i != I_AM_MASTER) {
- user_error("Lost connection to NNTP server %s: %s", nntp_server, syserr());
- /* NOTREACHED */
- }
- nntp_failed = 1;
- if (is_connected) {
- log_entry('N', "Lost connection to server %s: %s", nntp_server, syserr());
- nntp_close_server();
- }
- }
-
- /*
- * find_server: Find out which host to use as NNTP server.
- *
- * This is done by consulting the file NNTP_SERVER (defined in
- * config.h). Set nntp_server[] to the host's name.
- */
-
- static void find_server()
- {
- char *cp, *name, *getenv();
- char buf[BUFSIZ];
- FILE *fp;
-
- /*
- * This feature cannot normally be enabled, because the database and
- * the users rc file contains references to articles by number, and
- * these numbers are not unique across NNTP servers.
- */
- #ifdef DEBUG
- if ((cp = getenv("NNTPSERVER")) != NULL) {
- strncpy(nntp_server, cp, sizeof nntp_server);
- return;
- }
- #endif /* DEBUG */
-
- name = NNTP_SERVER;
- if (*name != '/')
- name = relative(lib_directory, name);
-
- if ((fp = open_file(name, OPEN_READ)) != NULL) {
- while (fgets(buf, sizeof buf, fp) != 0) {
- if (*buf == '#' || *buf == '\n')
- continue;
- if ((cp = strchr(buf, '\n')) != 0)
- *cp = '\0';
- strncpy(nntp_server, buf, sizeof nntp_server);
- fclose(fp);
- return;
- }
- fclose(fp);
- }
-
- if (who_am_i != I_AM_MASTER)
- printf("\nCannot find name of NNTP server.\nCheck %s\n", name);
-
- sys_error("Failed to find name of NNTP server!");
- }
-
- /*
- * get_server_line: get a line from the server.
- *
- * Expects to be connected to the server.
- * The line can be any kind of line, i.e., either response or text.
- */
-
- static get_server_line(string, size)
- char *string;
- int size;
- {
- register char *cp, *nl;
-
- if (fgets(string, size, nntp_in) == NULL) {
- io_error();
- return -1;
- }
- for (cp = string, nl = NULL; *cp != NUL; cp++) {
- if (*cp == CR) {
- nl = cp;
- break;
- }
- if (nl == NULL && *cp == NL)
- nl = cp;
- }
- if (nl != NULL) *nl = NUL;
-
- return 0;
- }
-
- /*
- * get_server: get a response line from the server.
- *
- * Expects to be connected to the server.
- * Returns the numerical value of the reponse, or -1 in case of errors.
- */
-
- static get_server(string, size)
- char *string;
- int size;
- {
- if (get_server_line(string, size) < 0)
- return -1;
-
- if (nntp_debug) debug_msg("<<<", string);
-
- return isdigit(*string) ? atoi(string) : 0;
- }
-
- /*
- * get_socket: get a connection to the nntp server.
- *
- * Doesn't return in case of errors.
- */
-
- static get_socket()
- {
- int s;
- struct sockaddr_in sin;
- struct servent *getservbyname(), *sp;
- struct hostent *gethostbyname(), *hp;
- #ifdef h_addr
- int x = 0;
- register char **cp;
- #endif
-
- if ((sp = getservbyname("nntp", "tcp")) == NULL)
- sys_error("nntp/tcp: Unknown service.\n");
-
- if ((hp = gethostbyname(nntp_server)) == NULL)
- sys_error("NNTP server %s unknown.\n", nntp_server);
-
- bzero((char *) &sin, sizeof(sin));
- sin.sin_family = hp->h_addrtype;
- sin.sin_port = sp->s_port;
-
- #ifdef h_addr
- /* get a socket and initiate connection -- use multiple addresses */
-
- for (cp = hp->h_addr_list; cp && *cp; cp++) {
- s = socket(hp->h_addrtype, SOCK_STREAM, 0);
- if (s < 0)
- sys_error("Can't get NNTP socket: %s\n", syserr());
- bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
-
- x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
- if (x == 0)
- break;
- if (who_am_i != I_AM_MASTER)
- msg("Connecting to %s failed: %s", nntp_server, syserr());
- (void) close(s);
- s = -1;
- }
- if (x < 0 && who_am_i != I_AM_MASTER)
- user_error("Giving up on NNTP server %s!\n", nntp_server);
- #else /* no name server */
- if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- sys_error("Can't get NNTP socket: %s\n", syserr());
-
- /* And then connect */
- bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
- if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- if (who_am_i == I_AM_MASTER)
- sys_error("Connecting to %s failed: %s", nntp_server, syserr());
- s = -1;
- }
- #endif
- return s;
- }
-
- /*
- * connect_server: initialise a connection to the nntp server.
- *
- * It expects nntp_server[] to be set previously, by a call to
- * nntp_check. It is called from nntp_get_article() and
- * nntp_get_active() if there is no established connection.
- */
-
- static connect_server()
- {
- int sockt_rd, sockt_wr;
- int response;
- char line[NNTP_STRLEN];
-
- if (who_am_i != I_AM_MASTER && !silent)
- msg("Connecting to NNTP server %s ... ", nntp_server);
-
- nntp_failed = 1;
- is_connected = 0;
-
- sockt_rd = get_socket();
- if (sockt_rd < 0)
- return -1;
-
- if ((nntp_in = fdopen(sockt_rd, "r")) == NULL) {
- close(sockt_rd);
- return -1;
- }
- sockt_wr = dup(sockt_rd);
- if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
- close(sockt_wr);
- fclose(nntp_in);
- nntp_in = NULL; /* from above */
- return -1;
- }
-
- /* Now get the server's signon message */
- response = get_server(line, sizeof(line));
-
- if (who_am_i == I_AM_MASTER) {
- if (response != OK_CANPOST && response != OK_NOPOST) {
- log_entry('N', "Failed to connect to NNTP server");
- log_entry('N', "Response: %s", line);
- fclose(nntp_out);
- fclose(nntp_in);
- return -1;
- }
- } else {
- switch (response) {
- case OK_CANPOST:
- can_post = 1;
- break;
- case OK_NOPOST:
- can_post = 0;
- break;
- default:
- user_error(line);
- /* NOTREACHED */
- }
- }
- if (who_am_i != I_AM_MASTER && !silent)
- msg("Connecting to NNTP server %s ... ok (%s)",
- nntp_server, can_post ? "posting is allowed" : "no posting");
-
- is_connected = 1;
- group_is_set = 0;
- nntp_failed = 0;
- try_again = 0;
- return 0;
- }
-
-
- /*
- * put_server: send a line to the nntp server.
- *
- * Expects to be connected to the server.
- */
-
- static put_server(string)
- char *string;
- {
- if (nntp_debug) debug_msg(">>>", string);
-
- fprintf(nntp_out, "%s\r\n", string);
- if (fflush(nntp_out) == EOF) {
- io_error();
- return -1;
- }
- return 0;
- }
-
- /*
- * ask_server: ask the server a question and return the answer.
- *
- * Expects to be connected to the server.
- * Returns the numerical value of the reponse, or -1 in case of
- * errors.
- * Contains some code to handle server timeouts intelligently.
- */
-
- /*VARARGS*/
- static ask_server(va_alist)
- va_dcl
- {
- char buf[NNTP_STRLEN];
- char *fmt;
- int response;
- use_vararg;
-
- start_vararg;
- fmt = va_arg1(char *);
- vsprintf(buf, fmt, va_args2toN);
- end_vararg;
-
- if (put_server(buf) < 0)
- return -1;
- response = get_server(buf, sizeof(buf));
-
- /*
- * Handle the response from the server. Responses are handled as
- * followes:
- *
- * 100-199 Informational. Passed back. (should they be ignored?).
- * 200-299 Ok messages. Passed back.
- * 300-399 Ok and proceed. Can not happen in nn.
- * 400-499 Errors (no article, etc). Passed up and handled there.
- * 500-599 Fatal NNTP errors. Handled below.
- */
- if (response == ERR_GOODBYE || response > ERR_COMMAND) {
- nntp_failed = 1;
- nntp_close_server();
-
- if (response != ERR_TIMEOUT) { /* if not timeout, complain */
- sys_error("NNTP %s response: %d", buf, response);
- /* NOTREACHED */
- }
- try_again = 1;
- group_is_set = 0;
- }
- return response;
- }
-
- /*
- * copy_text: copy text response into file.
- *
- * Copies a text response into an open file.
- * Return -1 on error, 0 otherwise. It is treated as an error, if
- * the returned response it not what was expected.
- */
-
- static int last_copy_blank;
-
- static copy_text(fp)
- register FILE *fp;
- {
- char buf[NNTP_STRLEN];
- register char *cp;
- register int nlines;
-
- nlines = 0;
- last_copy_blank = 0;
- while (get_server_line(buf, sizeof buf) >= 0) {
- cp = buf;
- if (*cp == '.')
- if (*++cp == '\0') {
- if (nlines <= 0) break;
- if (nntp_debug) {
- sprintf(buf, "%d lines", nlines);
- debug_msg("COPY", buf);
- }
- return 0;
- }
- fputs(cp, fp);
- last_copy_blank = (*cp == NUL);
- putc('\n', fp);
- nlines++;
- }
- fclose(fp);
- if (nntp_debug) debug_msg("COPY", "EMPTY");
- return -1;
- }
-
-
- static do_set_group()
- {
- int n;
-
- switch (n = ask_server("GROUP %s", group_hd->group_name)) {
- case OK_GROUP:
- group_is_set = 1;
- return 1;
-
- case ERR_NOGROUP:
- log_entry('N', "NNTP: group %s not found", group_hd->group_name);
- return -1;
-
- default:
- if (try_again) return 0; /* Handle nntp server timeouts */
- break;
- }
- if (!nntp_failed) {
- log_entry('N', "GROUP %s response: %d", group_hd->group_name, n);
- nntp_failed = 1;
- }
- return -1;
- }
-
- /*
- * The following functions implements a simple lru cache of recently
- * accessed articles. It is a simple way to improve effeciency. Files
- * must be kept by name, because the rest of the code expects to be able
- * to open an article multiple times, and get separate file pointers.
- */
-
- struct cache {
- char *file_name; /* file name */
- article_number art; /* article stored in file */
- group_header *grp; /* from this group */
- unsigned time; /* time last accessed */
- } cache[NNTPCACHE];
-
- static unsigned time_counter = 1; /* virtual time */
-
- /*
- * search_cache: search the cache for an (article, group) pair.
- *
- * Returns a pointer to the slot where it is, null otherwise
- */
-
- static struct cache *search_cache(art, gh)
- article_number art;
- group_header *gh;
- {
- struct cache *cptr = cache;
- int i;
-
- if (who_am_i == I_AM_MASTER) return NULL;
-
- if (nntp_cache_size > NNTPCACHE) nntp_cache_size = NNTPCACHE;
-
- for (i = 0; i < nntp_cache_size; i++, cptr++)
- if (cptr->art == art && cptr->grp == gh) {
- cptr->time = time_counter++;
- return cptr;
- }
- return NULL;
- }
-
- /*
- * new_cache_slot: get a free cache slot.
- *
- * Returns a pointer to the allocated slot.
- * Frees the old filename, and allocates a new, unused filename.
- * Cache files can also stored in a common directory defined in
- * ~/.nn or CACHE_DIRECTORY if defined in config.h.
- */
-
- static struct cache *new_cache_slot()
- {
- register struct cache *cptr = cache;
- int i, lru;
- unsigned min_time = time_counter;
- char name[FILENAME];
-
- if (nntp_cache_dir == NULL) {
- #ifdef CACHE_DIRECTORY
- nntp_cache_dir = CACHE_DIRECTORY;
- #else
- if (who_am_i == I_AM_MASTER)
- nntp_cache_dir = db_directory;
- else
- nntp_cache_dir = nn_directory;
- #endif
- }
-
- if (who_am_i == I_AM_MASTER) {
- cptr = &cache[0];
- if (cptr->file_name == NULL)
- cptr->file_name = mk_file_name(nntp_cache_dir, "master_cache");
- return cptr;
- }
-
- for (i = 0; i < nntp_cache_size; i++, cptr++)
- if (min_time > cptr->time) {
- min_time = cptr->time;
- lru = i;
- }
- cptr = &cache[lru];
-
- if (cptr->file_name == NULL) {
- sprintf(name, "%s/nn-%d.%02d~", nntp_cache_dir, process_id, lru);
- cptr->file_name = copy_str(name);
- } else
- unlink(cptr->file_name);
-
- cptr->time = time_counter++;
- return cptr;
- }
-
- /*
- * clean_cache: clean up the cache.
- *
- * Removes all allocated files.
- */
-
- static void clean_cache()
- {
- struct cache *cptr = cache;
- int i;
-
- for (i = 0; i < nntp_cache_size; i++, cptr++)
- if (cptr->file_name)
- unlink(cptr->file_name);
- }
-
- /*
- * nntp_check: Find out whether we need to use NNTP.
- *
- * This is done by comparing the NNTP servers name with whatever
- * gethostname() returns.
- * use_nntp and news_active are initialised as a side effect.
- */
-
- nntp_check()
- {
- char host[128];
-
- if (nntp_local_server) return;
-
- find_server();
- gethostname(host, sizeof host);
- use_nntp = strcmp(host, nntp_server) != 0; /* too simplistic ??? */
-
- if (use_nntp) {
- freeobj(news_active);
- news_active = mk_file_name(db_directory, "ACTIVE");
- }
- }
-
- /*
- * nntp_no_post: Check to see whether posting is allowed.
- */
-
- nntp_no_post()
- {
- if (!is_connected && connect_server() < 0)
- return 1; /* If we cannot connect, neither can inews */
- if (can_post == 0) {
- msg("NNTP server does not allow postings from this host. Sorry!");
- return 1;
- }
- return 0;
- }
-
-
- /*
- * nntp_set_group: set the server's current group.
- *
- * Actual communication is delayed until an article is accessed, to
- * avoid unnecessary traffic.
- */
-
- nntp_set_group(gh)
- group_header *gh;
- {
- group_hd = gh;
- group_is_set = 0;
- return 0;
- }
-
- /*
- * nntp_get_active: get a copy of the active file.
- *
- * If we are the master get a copy of the file from the nntp server.
- * nnadmin just uses the one we already got. In this way the master
- * can maintain a remote copy of the servers active file.
- * We try to be a little smart, if not inefficient, about the
- * modification times on the local active file.
- * Even when the master is running on the nntp server, a separate
- * copy of the active file will be made for access via NFS.
- */
-
- nntp_get_active()
- {
- FILE *old, *new;
- char bufo[NNTP_STRLEN], bufn[NNTP_STRLEN];
- char *new_name;
- int same, n;
-
- if (who_am_i != I_AM_MASTER)
- return access(news_active, 4);
-
- again:
- if (!is_connected && connect_server() < 0)
- return -1;
-
- new_name = mktemp(relative(db_directory, ".actXXXXXX"));
-
- switch (n = ask_server("LIST")) {
- case OK_GROUPS:
- new = open_file(new_name, OPEN_CREATE_RW|MUST_EXIST);
- if (copy_text(new) == 0) break;
- unlink(new_name);
- if (!nntp_failed) {
- log_entry('N', "LIST empty");
- nntp_failed = 1;
- }
- return -1;
- default:
- if (try_again) goto again; /* Handle nntp server timeouts */
- log_entry('N', "LIST response: %d", n);
- return -1;
- }
-
- rewind(new);
- same = 0;
- if ((old = open_file(news_active, OPEN_READ)) != NULL) {
- do {
- fgets(bufo, sizeof bufo, old);
- fgets(bufn, sizeof bufn, new);
- } while (!feof(old) && !feof(new) && strcmp(bufo, bufn) == 0);
- same = feof(old) && feof(new);
- fclose(old);
- }
- fclose(new);
-
- if (same)
- unlink(new_name);
- else
- if (rename(new_name, news_active) != 0)
- sys_error("Cannot rename %s to %s", new_name, news_active);
-
- return 0;
- }
-
- /*
- * nntp_get_article_list: get list of all article numbers in group
- *
- * Sends XHDR command to the server, and parses the following
- * text response to get a list of article numbers which is saved
- * in a list and returned.
- * Return NULL on error. It is treated as an error, if
- * the returned response it not what was expected.
- */
-
- static article_number *article_list = NULL;
- static long art_list_length = 0;
-
- static sort_art_list(f1, f2)
- register article_number *f1, *f2;
- {
- return (*f1 < *f2) ? -1 : (*f1 == *f2) ? 0 : 1;
- }
-
- article_number *nntp_get_article_list(gh)
- group_header *gh;
- {
- char buf[NNTP_STRLEN];
- register article_number *art;
- register char *cp;
- register long count = 0; /* No. of completions plus one */
- int n;
- static int try_listgroup = 1;
-
- again:
- if (!is_connected && connect_server() < 0)
- return NULL;
-
- /* it is really an extreme waste of time to use XHDR since all we */
- /* are interested in is the article numbers (as we do locally). */
- /* If somebody hacks up an nntp server that understands LISTGROUP */
- /* they will get much less load on the nntp server */
- /* It should simply return the existing article numbers is the group*/
- /* -- they don't even have to be sorted (only XHDR needs that) */
-
- if (try_listgroup) {
- switch (n = ask_server("LISTGROUP %s", group_hd->group_name)) {
- case OK_GROUP:
- break;
- default:
- if (try_again) goto again; /* Handle nntp server timeouts */
- log_entry('N', "LISTGROUP response: %d", n);
- case ERR_COMMAND:
- try_listgroup = 0;
- goto again; /* error may have closed down server connection */
- }
- }
- if (!try_listgroup) {
- if (group_is_set == 0)
- switch (do_set_group()) {
- case -1:
- return NULL;
- case 0:
- goto again;
- case 1:
- break;
- }
-
- switch (n = ask_server("XHDR message-id %ld-%ld",
- (long)gh->first_db_article, (long)gh->last_db_article)) {
- case OK_HEAD:
- break;
- default:
- if (try_again) goto again; /* Handle nntp server timeouts */
- log_entry('N', "XHDR response: %d", n);
- case ERR_COMMAND:
- nntp_failed = 2;
- return NULL;
- }
- }
-
- count = 0;
- art = article_list;
-
- while (get_server_line(buf, sizeof buf) >= 0) {
- cp = buf;
- if (*cp == '.' && *++cp == '\0') break;
-
- if (count == art_list_length) {
- art_list_length += 250;
- article_list = resizeobj(article_list, article_number, art_list_length + 1);
- art = article_list + count;
- }
- *art++ = atol(cp);
- count++;
- }
- *art = 0;
-
- if (try_listgroup)
- quicksort(article_list, count, article_number, sort_art_list);
- *art = 0;
- return article_list;
- }
-
- /*
- * nntp_get_article: get an article from the server.
- *
- * Returns a FILE pointer.
- * If necessary the server's current group is set.
- * The article (header and body) are copied into a file, so they
- * are seekable (nn likes that).
- */
-
- static char *mode_cmd[] = {
- "ARTICLE",
- "HEAD",
- "BODY"
- };
-
- FILE *nntp_get_article(article, mode)
- article_number article;
- int mode; /* 0 => whole article, 1 => head only, 2 => body only */
- {
- FILE *tmp;
- static struct cache *cptr;
- int n;
-
- again:
- if (!is_connected && connect_server() < 0) {
- return NULL;
- }
-
- /*
- * Set the server group to the current group
- */
- if (group_is_set == 0)
- switch (do_set_group()) {
- case -1:
- return NULL;
- case 0:
- goto again;
- case 1:
- break;
- }
-
- /*
- * Search the cache for the requested article, and allocate a new
- * slot if necessary (if appending body, we already got it).
- */
-
- if (mode != 2) {
- cptr = search_cache(article, group_hd);
- if (cptr != NULL) goto out;
- cptr = new_cache_slot();
- }
-
- /*
- * Copy the article.
- */
- switch (n = ask_server("%s %ld", mode_cmd[mode], (long)article)) {
- case OK_ARTICLE:
- case OK_HEAD:
- tmp = open_file(cptr->file_name, OPEN_CREATE|MUST_EXIST);
- if (copy_text(tmp) < 0)
- return NULL;
-
- if (mode == 1 && !last_copy_blank)
- fputc(NL, tmp); /* add blank line after header */
-
- cptr->art = article;
- cptr->grp = group_hd;
- fclose(tmp);
- goto out;
-
- case OK_BODY:
- tmp = open_file(cptr->file_name, OPEN_APPEND|MUST_EXIST);
- fseek(tmp, (off_t)0, 2);
- if (copy_text(tmp) < 0)
- return NULL;
- fclose(tmp);
- goto out;
-
- case ERR_NOARTIG:
- return NULL;
-
- default:
- if (try_again) goto again; /* Handle nntp server timeouts */
- log_entry('N', "ARTICLE %ld response: %d", (long)article, n);
- nntp_failed = 1;
- return NULL;
- }
-
- out:
- return open_file(cptr->file_name, OPEN_READ|MUST_EXIST);
- }
-
- /*
- * Return local file name holding article
- */
-
- char *nntp_get_filename(art, gh)
- article_number art;
- group_header *gh;
- {
- struct cache *cptr;
-
- cptr = search_cache(art, gh);
-
- return cptr == NULL ? NULL : cptr->file_name;
- }
-
- /*
- * nntp_close_server: close the connection to the server.
- */
-
- nntp_close_server()
- {
- if (!is_connected)
- return;
-
- if (!nntp_failed) { /* avoid infinite recursion */
- int n;
-
- n = ask_server("QUIT");
- if (n != OK_GOODBYE)
- ; /* WHAT NOW ??? */
- }
-
- (void) fclose(nntp_out);
- (void) fclose(nntp_in);
-
- is_connected = 0;
- }
-
- /*
- * nntp_cleanup: clean up after an nntp session.
- *
- * Called from nn_exit().
- */
-
- nntp_cleanup()
- {
- if (is_connected)
- nntp_close_server();
- clean_cache();
- }
- #endif /* NNTP */
-
-