diff options
author | Thomas E. Dickey <dickey@invisible-island.net> | 2010-04-29 22:00:22 -0400 |
---|---|---|
committer | Thomas E. Dickey <dickey@invisible-island.net> | 2010-04-29 22:00:22 -0400 |
commit | dc748b1c47baadafae2c90f0e188927b11b7e029 (patch) | |
tree | c728869dc6504570b9bffb7459ccbdd1bf264a9f /WWW/Library/Implementation/HTNews.c | |
parent | d4093cadbda3787dfb165954f8f6521790cfac86 (diff) | |
download | lynx-snapshots-dc748b1c47baadafae2c90f0e188927b11b7e029.tar.gz |
snapshot of project "lynx", label v2_8_8dev_6c
Diffstat (limited to 'WWW/Library/Implementation/HTNews.c')
-rw-r--r-- | WWW/Library/Implementation/HTNews.c | 3130 |
1 files changed, 0 insertions, 3130 deletions
diff --git a/WWW/Library/Implementation/HTNews.c b/WWW/Library/Implementation/HTNews.c deleted file mode 100644 index d4163855..00000000 --- a/WWW/Library/Implementation/HTNews.c +++ /dev/null @@ -1,3130 +0,0 @@ -/* - * $LynxId: HTNews.c,v 1.61 2010/04/25 19:46:01 tom Exp $ - * - * NEWS ACCESS HTNews.c - * =========== - * - * History: - * 26 Sep 90 Written TBL - * 29 Nov 91 Downgraded to C, for portable implementation. - */ - -#include <HTUtils.h> /* Coding convention macros */ - -#ifndef DISABLE_NEWS - -/* Implements: -*/ -#include <HTNews.h> - -#include <HTCJK.h> -#include <HTMIME.h> -#include <HTFont.h> -#include <HTFormat.h> -#include <HTTCP.h> -#include <LYUtils.h> -#include <LYStrings.h> - -#define NEWS_PORT 119 /* See rfc977 */ -#define SNEWS_PORT 563 /* See Lou Montulli */ -#define APPEND /* Use append methods */ -int HTNewsChunkSize = 30; /* Number of articles for quick display */ -int HTNewsMaxChunk = 40; /* Largest number of articles in one window */ - -#ifndef DEFAULT_NEWS_HOST -#define DEFAULT_NEWS_HOST "news" -#endif /* DEFAULT_NEWS_HOST */ - -#ifndef NEWS_SERVER_FILE -#define NEWS_SERVER_FILE "/usr/local/lib/rn/server" -#endif /* NEWS_SERVER_FILE */ - -#ifndef NEWS_AUTH_FILE -#define NEWS_AUTH_FILE ".newsauth" -#endif /* NEWS_AUTH_FILE */ - -#ifdef USE_SSL -static SSL *Handle = NULL; -static int channel_s = 1; - -#define NEWS_NETWRITE(sock, buff, size) \ - (Handle ? SSL_write(Handle, buff, size) : NETWRITE(sock, buff, size)) -#define NEWS_NETCLOSE(sock) \ - { (void)NETCLOSE(sock); if (Handle) { SSL_free(Handle); Handle = NULL; } } -static int HTNewsGetCharacter(void); - -#define NEXT_CHAR HTNewsGetCharacter() -#else -#define NEWS_NETWRITE NETWRITE -#define NEWS_NETCLOSE NETCLOSE -#define NEXT_CHAR HTGetCharacter() -#endif /* USE_SSL */ - -#include <HTML.h> -#include <HTAccess.h> -#include <HTParse.h> -#include <HTFormat.h> -#include <HTAlert.h> - -#include <LYNews.h> -#include <LYGlobalDefs.h> -#include <LYLeaks.h> - -#define SnipIn(d,fmt,len,s) sprintf(d, fmt, (int)sizeof(d)-len, s) -#define SnipIn2(d,fmt,tag,len,s) sprintf(d, fmt, tag, (int)sizeof(d)-len, s) - -struct _HTStructured { - const HTStructuredClass *isa; - /* ... */ -}; -struct _HTStream { - HTStreamClass *isa; -}; - -#define LINE_LENGTH 512 /* Maximum length of line of ARTICLE etc */ -#define GROUP_NAME_LENGTH 256 /* Maximum length of group name */ - -/* - * Module-wide variables. - */ -char *HTNewsHost = NULL; /* Default host */ -static char *NewsHost = NULL; /* Current host */ -static char *NewsHREF = NULL; /* Current HREF prefix */ -static int s; /* Socket for NewsHost */ -static int HTCanPost = FALSE; /* Current POST permission */ -static char response_text[LINE_LENGTH + 1]; /* Last response */ - -/* static HText * HT; *//* the new hypertext */ -static HTStructured *target; /* The output sink */ -static HTStructuredClass targetClass; /* Copy of fn addresses */ -static HTStream *rawtarget = NULL; /* The output sink for rawtext */ -static HTStreamClass rawtargetClass; /* Copy of fn addresses */ -static int diagnostic; /* level: 0=none 2=source */ -static BOOL rawtext = NO; /* Flag: HEAD or -mime_headers */ -static HTList *NNTP_AuthInfo = NULL; /* AUTHINFO database */ -static char *name = NULL; -static char *address = NULL; -static char *dbuf = NULL; /* dynamic buffer for long messages etc. */ - -#define PUTC(c) (*targetClass.put_character)(target, c) -#define PUTS(s) (*targetClass.put_string)(target, s) -#define RAW_PUTS(s) (*rawtargetClass.put_string)(rawtarget, s) -#define START(e) (*targetClass.start_element)(target, e, 0, 0, -1, 0) -#define END(e) (*targetClass.end_element)(target, e, 0) -#define MAYBE_END(e) if (HTML_dtd.tags[e].contents != SGML_EMPTY) \ - (*targetClass.end_element)(target, e, 0) -#define FREE_TARGET if (rawtext) (*rawtargetClass._free)(rawtarget); \ - else (*targetClass._free)(target) -#define ABORT_TARGET if (rawtext) (*rawtargetClass._abort)(rawtarget, NULL); \ - else (*targetClass._abort)(target, NULL) - -typedef struct _NNTPAuth { - char *host; - char *user; - char *pass; -} NNTPAuth; - -#ifdef LY_FIND_LEAKS -static void free_news_globals(void) -{ - if (s >= 0) { - NEWS_NETCLOSE(s); - s = -1; - } - FREE(HTNewsHost); - FREE(NewsHost); - FREE(NewsHREF); - FREE(name); - FREE(address); - FREE(dbuf); -} -#endif /* LY_FIND_LEAKS */ - -static void free_NNTP_AuthInfo(void) -{ - HTList *cur = NNTP_AuthInfo; - NNTPAuth *auth = NULL; - - if (!cur) - return; - - while (NULL != (auth = (NNTPAuth *) HTList_nextObject(cur))) { - FREE(auth->host); - FREE(auth->user); - FREE(auth->pass); - FREE(auth); - } - HTList_delete(NNTP_AuthInfo); - NNTP_AuthInfo = NULL; - return; -} - -/* - * Initialize the authentication list by loading the user's $HOME/.newsauth - * file. That file is part of tin's configuration and is used by a few other - * programs. - */ -static void load_NNTP_AuthInfo(void) -{ - FILE *fp; - char fname[LY_MAXPATH]; - char buffer[LINE_LENGTH + 1]; - - LYAddPathToHome(fname, sizeof(fname), NEWS_AUTH_FILE); - - if ((fp = fopen(fname, "r")) != 0) { - while (fgets(buffer, sizeof(buffer), fp) != 0) { - char the_host[LINE_LENGTH + 1]; - char the_pass[LINE_LENGTH + 1]; - char the_user[LINE_LENGTH + 1]; - - if (sscanf(buffer, "%s%s%s", the_host, the_pass, the_user) == 3 - && strlen(the_host) != 0 - && strlen(the_pass) != 0 - && strlen(the_user) != 0) { - NNTPAuth *auth = typecalloc(NNTPAuth); - - if (auth == NULL) - break; - StrAllocCopy(auth->host, the_host); - StrAllocCopy(auth->pass, the_pass); - StrAllocCopy(auth->user, the_user); - - HTList_appendObject(NNTP_AuthInfo, auth); - } - } - fclose(fp); - } -} - -const char *HTGetNewsHost(void) -{ - return HTNewsHost; -} - -void HTSetNewsHost(const char *value) -{ - StrAllocCopy(HTNewsHost, value); -} - -/* Initialisation for this module - * ------------------------------ - * - * Except on the NeXT, we pick up the NewsHost name from - * - * 1. Environment variable NNTPSERVER - * 2. File NEWS_SERVER_FILE - * 3. Compilation time macro DEFAULT_NEWS_HOST - * 4. Default to "news" - * - * On the NeXT, we pick up the NewsHost name from, in order: - * - * 1. WorldWideWeb default "NewsHost" - * 2. Global default "NewsHost" - * 3. News default "NewsHost" - * 4. Compilation time macro DEFAULT_NEWS_HOST - * 5. Default to "news" - */ -static BOOL initialized = NO; -static BOOL initialize(void) -{ -#ifdef NeXTStep - char *cp = NULL; -#endif - - /* - * Get name of Host. - */ -#ifdef NeXTStep - if ((cp = NXGetDefaultValue("WorldWideWeb", "NewsHost")) == 0) { - if ((cp = NXGetDefaultValue("News", "NewsHost")) == 0) { - StrAllocCopy(HTNewsHost, DEFAULT_NEWS_HOST); - } - } - if (cp) { - StrAllocCopy(HTNewsHost, cp); - cp = NULL; - } -#else - if (LYGetEnv("NNTPSERVER")) { - StrAllocCopy(HTNewsHost, LYGetEnv("NNTPSERVER")); - CTRACE((tfp, "HTNews: NNTPSERVER defined as `%s'\n", - HTNewsHost)); - } else { - FILE *fp = fopen(NEWS_SERVER_FILE, TXT_R); - - if (fp) { - char server_name[MAXHOSTNAMELEN + 1]; - - if (fgets(server_name, sizeof server_name, fp) != NULL) { - char *p = strchr(server_name, '\n'); - - if (p != NULL) - *p = '\0'; - StrAllocCopy(HTNewsHost, server_name); - CTRACE((tfp, "HTNews: File %s defines news host as `%s'\n", - NEWS_SERVER_FILE, HTNewsHost)); - } - fclose(fp); - } - } - if (!HTNewsHost) - StrAllocCopy(HTNewsHost, DEFAULT_NEWS_HOST); -#endif /* NeXTStep */ - - s = -1; /* Disconnected */ -#ifdef LY_FIND_LEAKS - atexit(free_news_globals); -#endif - return YES; -} - -/* Send NNTP Command line to remote host & Check Response - * ------------------------------------------------------ - * - * On entry, - * command points to the command to be sent, including CRLF, or is null - * pointer if no command to be sent. - * On exit, - * Negative status indicates transmission error, socket closed. - * Positive status is an NNTP status. - */ -static int response(char *command) -{ - int result; - char *p = response_text; - int ich; - - if (command) { - int status; - int length = strlen(command); - - CTRACE((tfp, "NNTP command to be sent: %s", command)); -#ifdef NOT_ASCII - { - const char *p2; - char *q; - char ascii[LINE_LENGTH + 1]; - - for (p2 = command, q = ascii; *p2; p2++, q++) { - *q = TOASCII(*p2); - } - status = NEWS_NETWRITE(s, ascii, length); - } -#else - status = NEWS_NETWRITE(s, (char *) command, length); -#endif /* NOT_ASCII */ - if (status < 0) { - CTRACE((tfp, "HTNews: Unable to send command. Disconnecting.\n")); - NEWS_NETCLOSE(s); - s = -1; - return status; - } /* if bad status */ - } - /* if command to be sent */ - for (;;) { - ich = NEXT_CHAR; - if (((*p++ = (char) ich) == LF) || - (p == &response_text[LINE_LENGTH])) { - *--p = '\0'; /* Terminate the string */ - CTRACE((tfp, "NNTP Response: %s\n", response_text)); - sscanf(response_text, "%d", &result); - return result; - } - /* if end of line */ - if (ich == EOF) { - *(p - 1) = '\0'; - if (interrupted_in_htgetcharacter) { - CTRACE((tfp, - "HTNews: Interrupted on read, closing socket %d\n", - s)); - } else { - CTRACE((tfp, "HTNews: EOF on read, closing socket %d\n", - s)); - } - NEWS_NETCLOSE(s); /* End of file, close socket */ - s = -1; - if (interrupted_in_htgetcharacter) { - interrupted_in_htgetcharacter = 0; - return (HT_INTERRUPTED); - } - return ((int) EOF); /* End of file on response */ - } - } /* Loop over characters */ -} - -/* Case insensitive string comparisons - * ----------------------------------- - * - * On entry, - * template must be already in upper case. - * unknown may be in upper or lower or mixed case to match. - */ -static BOOL match(const char *unknown, const char *ctemplate) -{ - const char *u = unknown; - const char *t = ctemplate; - - for (; *u && *t && (TOUPPER(*u) == *t); u++, t++) ; /* Find mismatch or end */ - return (BOOL) (*t == 0); /* OK if end of template */ -} - -typedef enum { - NNTPAUTH_ERROR = 0, /* general failure */ - NNTPAUTH_OK = 281, /* authenticated successfully */ - NNTPAUTH_CLOSE = 502 /* server probably closed connection */ -} NNTPAuthResult; - -/* - * This function handles nntp authentication. - FM - */ -static NNTPAuthResult HTHandleAuthInfo(char *host) -{ - HTList *cur = NULL; - NNTPAuth *auth = NULL; - char *UserName = NULL; - char *PassWord = NULL; - char *msg = NULL; - char buffer[512]; - int status, tries; - - /* - * Make sure we have a host. - FM - */ - if (isEmpty(host)) - return NNTPAUTH_ERROR; - - /* - * Check for an existing authorization entry. - FM - */ - if (NNTP_AuthInfo == NULL) { - NNTP_AuthInfo = HTList_new(); - load_NNTP_AuthInfo(); -#ifdef LY_FIND_LEAKS - atexit(free_NNTP_AuthInfo); -#endif - } - - cur = NNTP_AuthInfo; - while (NULL != (auth = (NNTPAuth *) HTList_nextObject(cur))) { - if (!strcmp(auth->host, host)) { - UserName = auth->user; - PassWord = auth->pass; - break; - } - } - - /* - * Handle the username. - FM - */ - buffer[sizeof(buffer) - 1] = '\0'; - tries = 3; - - while (tries) { - if (UserName == NULL) { - HTSprintf0(&msg, gettext("Username for news host '%s':"), host); - UserName = HTPrompt(msg, NULL); - FREE(msg); - if (!(UserName && *UserName)) { - FREE(UserName); - return NNTPAUTH_ERROR; - } - } - sprintf(buffer, "AUTHINFO USER %.*s%c%c", - (int) sizeof(buffer) - 17, UserName, CR, LF); - if ((status = response(buffer)) < 0) { - if (status == HT_INTERRUPTED) - _HTProgress(CONNECTION_INTERRUPTED); - else - HTAlert(FAILED_CONNECTION_CLOSED); - if (auth) { - if (auth->user != UserName) { - FREE(auth->user); - auth->user = UserName; - } - } else { - FREE(UserName); - } - return NNTPAUTH_CLOSE; - } - if (status == 281) { - /* - * Username is accepted and no password is required. - FM - */ - if (auth) { - if (auth->user != UserName) { - FREE(auth->user); - auth->user = UserName; - } - } else { - /* - * Store the accepted username and no password. - FM - */ - if ((auth = typecalloc(NNTPAuth)) != NULL) { - StrAllocCopy(auth->host, host); - auth->user = UserName; - HTList_appendObject(NNTP_AuthInfo, auth); - } - } - return NNTPAUTH_OK; - } - if (status != 381) { - /* - * Not success, nor a request for the password, so it must be an - * error. - FM - */ - HTAlert(response_text); - tries--; - if ((tries > 0) && HTConfirm(gettext("Change username?"))) { - if (!auth || auth->user != UserName) { - FREE(UserName); - } - if ((UserName = HTPrompt(gettext("Username:"), UserName)) - != NULL && - *UserName) { - continue; - } - } - if (auth) { - if (auth->user != UserName) { - FREE(auth->user); - } - FREE(auth->pass); - } - FREE(UserName); - return NNTPAUTH_ERROR; - } - break; - } - - if (status == 381) { - /* - * Handle the password. - FM - */ - tries = 3; - while (tries) { - if (PassWord == NULL) { - HTSprintf0(&msg, gettext("Password for news host '%s':"), host); - PassWord = HTPromptPassword(msg); - FREE(msg); - if (!(PassWord && *PassWord)) { - FREE(PassWord); - return NNTPAUTH_ERROR; - } - } - sprintf(buffer, "AUTHINFO PASS %.*s%c%c", - (int) sizeof(buffer) - 17, PassWord, CR, LF); - if ((status = response(buffer)) < 0) { - if (status == HT_INTERRUPTED) { - _HTProgress(CONNECTION_INTERRUPTED); - } else { - HTAlert(FAILED_CONNECTION_CLOSED); - } - if (auth) { - if (auth->user != UserName) { - FREE(auth->user); - auth->user = UserName; - } - if (auth->pass != PassWord) { - FREE(auth->pass); - auth->pass = PassWord; - } - } else { - FREE(UserName); - FREE(PassWord); - } - return NNTPAUTH_CLOSE; - } - if (status == 502) { - /* - * That's what INN's nnrpd returns. It closes the connection - * after this. - kw - */ - HTAlert(response_text); - if (auth) { - if (auth->user == UserName) - UserName = NULL; - FREE(auth->user); - if (auth->pass == PassWord) - PassWord = NULL; - FREE(auth->pass); - } - FREE(UserName); - FREE(PassWord); - return NNTPAUTH_CLOSE; - } - if (status == 281) { - /* - * Password also is accepted, and everything has been stored. - * - FM - */ - if (auth) { - if (auth->user != UserName) { - FREE(auth->user); - auth->user = UserName; - } - if (auth->pass != PassWord) { - FREE(auth->pass); - auth->pass = PassWord; - } - } else { - if ((auth = typecalloc(NNTPAuth)) != NULL) { - StrAllocCopy(auth->host, host); - auth->user = UserName; - auth->pass = PassWord; - HTList_appendObject(NNTP_AuthInfo, auth); - } - } - return NNTPAUTH_OK; - } - /* - * Not success, so it must be an error. - FM - */ - HTAlert(response_text); - if (!auth || auth->pass != PassWord) { - FREE(PassWord); - } else { - PassWord = NULL; - } - tries--; - if ((tries > 0) && HTConfirm(gettext("Change password?"))) { - continue; - } - if (auth) { - if (auth->user == UserName) - UserName = NULL; - FREE(auth->user); - FREE(auth->pass); - } - FREE(UserName); - break; - } - } - - return NNTPAUTH_ERROR; -} - -/* Find Author's name in mail address - * ---------------------------------- - * - * On exit, - * Returns allocated string which cannot be freed by the - * calling function, and is reallocated on subsequent calls - * to this function. - * - * For example, returns "Tim Berners-Lee" if given any of - * " Tim Berners-Lee <tim@online.cern.ch> " - * or " tim@online.cern.ch ( Tim Berners-Lee ) " - */ -static char *author_name(char *email) -{ - char *p, *e; - - StrAllocCopy(name, email); - CTRACE((tfp, "Trying to find name in: %s\n", name)); - - if ((p = strrchr(name, '(')) && (e = strrchr(name, ')'))) { - if (e > p) { - *e = '\0'; /* Chop off everything after the ')' */ - return HTStrip(p + 1); /* Remove leading and trailing spaces */ - } - } - - if ((p = strrchr(name, '<')) && (e = strrchr(name, '>'))) { - if (e++ > p) { - while ((*p++ = *e++) != 0) /* Remove <...> */ - ; - return HTStrip(name); /* Remove leading and trailing spaces */ - } - } - - return HTStrip(name); /* Default to the whole thing */ -} - -/* Find Author's mail address - * -------------------------- - * - * On exit, - * Returns allocated string which cannot be freed by the - * calling function, and is reallocated on subsequent calls - * to this function. - * - * For example, returns "montulli@spaced.out.galaxy.net" if given any of - * " Lou Montulli <montulli@spaced.out.galaxy.net> " - * or " montulli@spaced.out.galaxy.net ( Lou "The Stud" Montulli ) " - */ -static char *author_address(char *email) -{ - char *p, *at, *e; - - StrAllocCopy(address, email); - CTRACE((tfp, "Trying to find address in: %s\n", address)); - - if ((p = strrchr(address, '<'))) { - if ((e = strrchr(p, '>')) && (at = strrchr(p, '@'))) { - if (at < e) { - *e = '\0'; /* Remove > */ - return HTStrip(p + 1); /* Remove leading and trailing spaces */ - } - } - } - - if ((p = strrchr(address, '(')) && - (e = strrchr(address, ')')) && (at = strchr(address, '@'))) { - if (e > p && at < e) { - *p = '\0'; /* Chop off everything after the ')' */ - return HTStrip(address); /* Remove leading and trailing spaces */ - } - } - - if ((at = strrchr(address, '@')) && at > address) { - p = (at - 1); - e = (at + 1); - while (p > address && !isspace(UCH(*p))) - p--; - while (*e && !isspace(UCH(*e))) - e++; - *e = 0; - return HTStrip(p); - } - - /* - * Default to the first word. - */ - p = address; - while (isspace(UCH(*p))) - p++; /* find first non-space */ - e = p; - while (!isspace(UCH(*e)) && *e != '\0') - e++; /* find next space or end */ - *e = '\0'; /* terminate space */ - - return (p); -} - -/* Start anchor element - * -------------------- - */ -static void start_anchor(const char *href) -{ - BOOL present[HTML_A_ATTRIBUTES]; - const char *value[HTML_A_ATTRIBUTES]; - int i; - - for (i = 0; i < HTML_A_ATTRIBUTES; i++) - present[i] = (BOOL) (i == HTML_A_HREF); - value[HTML_A_HREF] = href; - (*targetClass.start_element) (target, HTML_A, present, value, -1, 0); -} - -/* Start link element - * ------------------ - */ -static void start_link(const char *href, const char *rev) -{ - BOOL present[HTML_LINK_ATTRIBUTES]; - const char *value[HTML_LINK_ATTRIBUTES]; - int i; - - for (i = 0; i < HTML_LINK_ATTRIBUTES; i++) - present[i] = (BOOL) (i == HTML_LINK_HREF || i == HTML_LINK_REV); - value[HTML_LINK_HREF] = href; - value[HTML_LINK_REV] = rev; - (*targetClass.start_element) (target, HTML_LINK, present, value, -1, 0); -} - -/* Start list element - * ------------------ - */ -static void start_list(int seqnum) -{ - BOOL present[HTML_OL_ATTRIBUTES]; - const char *value[HTML_OL_ATTRIBUTES]; - char SeqNum[20]; - int i; - - for (i = 0; i < HTML_OL_ATTRIBUTES; i++) - present[i] = (BOOL) (i == HTML_OL_SEQNUM || i == HTML_OL_START); - sprintf(SeqNum, "%d", seqnum); - value[HTML_OL_SEQNUM] = SeqNum; - value[HTML_OL_START] = SeqNum; - (*targetClass.start_element) (target, HTML_OL, present, value, -1, 0); -} - -/* Paste in an Anchor - * ------------------ - * - * - * On entry, - * HT has a selection of zero length at the end. - * text points to the text to be put into the file, 0 terminated. - * addr points to the hypertext reference address, - * terminated by white space, comma, NULL or '>' - */ -static void write_anchor(const char *text, const char *addr) -{ - char href[LINE_LENGTH + 1]; - const char *p; - char *q; - - for (p = addr; *p && (*p != '>') && !WHITE(*p) && (*p != ','); p++) ; - if (strlen(NewsHREF) + (p - addr) + 1 < sizeof(href)) { - q = href; - strcpy(q, NewsHREF); - strncat(q, addr, p - addr); /* Make complete hypertext reference */ - } else { - q = NULL; - HTSprintf0(&q, "%s%.*s", NewsHREF, (int) (p - addr), addr); - } - - start_anchor(q); - PUTS(text); - END(HTML_A); - - if (q != href) - FREE(q); -} - -/* Write list of anchors - * --------------------- - * - * We take a pointer to a list of objects, and write out each, - * generating an anchor for each. - * - * On entry, - * HT has a selection of zero length at the end. - * text points to a comma or space separated list of addresses. - * On exit, - * *text is NOT any more chopped up into substrings. - */ -static void write_anchors(char *text) -{ - char *start = text; - char *end; - char c; - - for (;;) { - for (; *start && (WHITE(*start)); start++) ; /* Find start */ - if (!*start) - return; /* (Done) */ - for (end = start; - *end && (*end != ' ') && (*end != ','); end++) ; /* Find end */ - if (*end) - end++; /* Include comma or space but not NULL */ - c = *end; - *end = '\0'; - if (*start == '<') - write_anchor(start, start + 1); - else - write_anchor(start, start); - START(HTML_BR); - *end = c; - start = end; /* Point to next one */ - } -} - -/* Abort the connection abort_socket - * -------------------- - */ -static void abort_socket(void) -{ - CTRACE((tfp, "HTNews: EOF on read, closing socket %d\n", s)); - NEWS_NETCLOSE(s); /* End of file, close socket */ - if (rawtext) { - RAW_PUTS("Network Error: connection lost\n"); - } else { - PUTS("Network Error: connection lost"); - PUTC('\n'); - } - s = -1; /* End of file on response */ -} - -/* - * Determine if a line is a valid header line. valid_header - * ------------------------------------------- - */ -static BOOLEAN valid_header(char *line) -{ - char *colon, *space; - - /* - * Blank or tab in first position implies this is a continuation header. - */ - if (line[0] == ' ' || line[0] == '\t') - return (TRUE); - - /* - * Just check for initial letter, colon, and space to make sure we discard - * only invalid headers. - */ - colon = strchr(line, ':'); - space = strchr(line, ' '); - if (isalpha(UCH(line[0])) && colon && space == colon + 1) - return (TRUE); - - /* - * Anything else is a bad header -- it should be ignored. - */ - return (FALSE); -} - -/* post in an Article post_article - * ------------------ - * (added by FM, modeled on Lynx's previous mini inews) - * - * Note the termination condition of a single dot on a line by itself. - * - * On entry, - * s Global socket number is OK - * postfile file with header and article to post. - */ -static void post_article(char *postfile) -{ - char line[512]; - char buf[512]; - char crlf[3]; - char *cp; - int status; - FILE *fd; - int in_header = 1, seen_header = 0, seen_fromline = 0; - int blen = 0, llen = 0; - - /* - * Open the temporary file with the nntp headers and message body. - FM - */ - if ((fd = fopen(NonNull(postfile), TXT_R)) == NULL) { - HTAlert(FAILED_CANNOT_OPEN_POST); - return; - } - - /* - * Read the temporary file and post in maximum 512 byte chunks. - FM - */ - buf[0] = '\0'; - sprintf(crlf, "%c%c", CR, LF); - while (fgets(line, sizeof(line) - 2, fd) != NULL) { - if ((cp = strchr(line, '\n')) != NULL) - *cp = '\0'; - if (line[0] == '.') { - /* - * A single '.' means end of transmission for nntp. Lead dots on - * lines normally are trimmed and the EOF is not registered if the - * dot was not followed by CRLF. We prepend an extra dot for any - * line beginning with one, to retain the one intended, as well as - * avoid a false EOF signal. We know we have room for it in the - * buffer, because we normally send when it would exceed 510. - FM - */ - strcat(buf, "."); - blen++; - } - llen = strlen(line); - if (in_header && !strncasecomp(line, "From:", 5)) { - seen_header = 1; - seen_fromline = 1; - } - if (in_header && line[0] == '\0') { - if (seen_header) { - in_header = 0; - if (!seen_fromline) { - if (blen >= (int) sizeof(buf) - 35) { - IGNORE_RC(NEWS_NETWRITE(s, buf, blen)); - buf[blen = 0] = 0; - } - strcat(buf, "From: anonymous@nowhere.you.know"); - strcat(buf, crlf); - blen += 34; - } - } else { - continue; - } - } else if (in_header) { - if (valid_header(line)) { - seen_header = 1; - } else { - continue; - } - } - strcat(line, crlf); - llen += 2; - if ((blen + llen) >= (int) sizeof(buf) - 1) { - IGNORE_RC(NEWS_NETWRITE(s, buf, blen)); - buf[blen = 0] = 0; - } - strcat(buf, line); - blen += llen; - } - fclose(fd); - HTSYS_remove(postfile); - - /* - * Send the nntp EOF and get the server's response. - FM - */ - if (blen >= (int) sizeof(buf) - 4) { - IGNORE_RC(NEWS_NETWRITE(s, buf, blen)); - buf[blen = 0] = 0; - } - strcat(buf, "."); - strcat(buf, crlf); - blen += 3; - IGNORE_RC(NEWS_NETWRITE(s, buf, blen)); - - status = response(NULL); - if (status == 240) { - /* - * Successful post. - FM - */ - HTProgress(response_text); - } else { - /* - * Shucks, something went wrong. - FM - */ - HTAlert(response_text); - } -} - -#ifdef NEWS_DEBUG -/* for DEBUG 1997/11/07 (Fri) 17:20:16 */ -void debug_print(unsigned char *p) -{ - while (*p) { - if (*p == '\0') - break; - if (*p == 0x1b) - printf("[ESC]"); - else if (*p == '\n') - printf("[NL]"); - else if (*p < ' ' || *p >= 0x80) - printf("(%02x)", *p); - else - putchar(*p); - p++; - } - printf("]\n"); -} -#endif - -static char *decode_mime(char **str) -{ - static char empty[] = ""; - -#ifdef SH_EX - if (HTCJK != JAPANESE) - return *str; -#endif - HTmmdecode(str, *str); - return HTrjis(str, *str) ? *str : empty; -} - -/* Read in an Article read_article - * ------------------ - * - * Note the termination condition of a single dot on a line by itself. - * RFC 977 specifies that the line "folding" of RFC850 is not used, so we - * do not handle it here. - * - * On entry, - * s Global socket number is OK - * HT Global hypertext object is ready for appending text - */ -static int read_article(HTParentAnchor *thisanchor) -{ - char line[LINE_LENGTH + 1]; - char *full_line = NULL; - char *subject = NULL; /* Subject string */ - char *from = NULL; /* From string */ - char *replyto = NULL; /* Reply-to string */ - char *date = NULL; /* Date string */ - char *organization = NULL; /* Organization string */ - char *references = NULL; /* Hrefs for other articles */ - char *newsgroups = NULL; /* Newsgroups list */ - char *followupto = NULL; /* Followup list */ - char *href = NULL; - char *p = line; - char *cp; - const char *ccp; - BOOL done = NO; - - /* - * Read in the HEADer of the article. - * - * The header fields are either ignored, or formatted and put into the - * text. - */ - if (!diagnostic && !rawtext) { - while (!done) { - int ich = NEXT_CHAR; - - *p++ = (char) ich; - if (ich == EOF) { - if (interrupted_in_htgetcharacter) { - interrupted_in_htgetcharacter = 0; - CTRACE((tfp, - "HTNews: Interrupted on read, closing socket %d\n", - s)); - NEWS_NETCLOSE(s); - s = -1; - return (HT_INTERRUPTED); - } - abort_socket(); /* End of file, close socket */ - return (HT_LOADED); /* End of file on response */ - } - if (((char) ich == LF) || (p == &line[LINE_LENGTH])) { - *--p = '\0'; /* Terminate the string */ - CTRACE((tfp, "H %s\n", line)); - - if (line[0] == '\t' || line[0] == ' ') { - int i = 0; - - while (line[i]) { - if (line[i] == '\t') - line[i] = ' '; - i++; - } - if (full_line == NULL) { - StrAllocCopy(full_line, line); - } else { - StrAllocCat(full_line, line); - } - } else { - StrAllocCopy(full_line, line); - } - - if (full_line[0] == '.') { - /* - * End of article? - */ - if (UCH(full_line[1]) < ' ') { - done = YES; - break; - } - } else if (UCH(full_line[0]) < ' ') { - break; /* End of Header? */ - - } else if (match(full_line, "SUBJECT:")) { - StrAllocCopy(subject, HTStrip(strchr(full_line, ':') + 1)); - decode_mime(&subject); - } else if (match(full_line, "DATE:")) { - StrAllocCopy(date, HTStrip(strchr(full_line, ':') + 1)); - - } else if (match(full_line, "ORGANIZATION:")) { - StrAllocCopy(organization, - HTStrip(strchr(full_line, ':') + 1)); - decode_mime(&organization); - - } else if (match(full_line, "FROM:")) { - StrAllocCopy(from, HTStrip(strchr(full_line, ':') + 1)); - decode_mime(&from); - - } else if (match(full_line, "REPLY-TO:")) { - StrAllocCopy(replyto, HTStrip(strchr(full_line, ':') + 1)); - decode_mime(&replyto); - - } else if (match(full_line, "NEWSGROUPS:")) { - StrAllocCopy(newsgroups, HTStrip(strchr(full_line, ':') + 1)); - - } else if (match(full_line, "REFERENCES:")) { - StrAllocCopy(references, HTStrip(strchr(full_line, ':') + 1)); - - } else if (match(full_line, "FOLLOWUP-TO:")) { - StrAllocCopy(followupto, HTStrip(strchr(full_line, ':') + 1)); - - } else if (match(full_line, "MESSAGE-ID:")) { - char *msgid = HTStrip(full_line + 11); - - if (msgid[0] == '<' && msgid[strlen(msgid) - 1] == '>') { - msgid[strlen(msgid) - 1] = '\0'; /* Chop > */ - msgid++; /* Chop < */ - HTAnchor_setMessageID(thisanchor, msgid); - } - - } /* end if match */ - p = line; /* Restart at beginning */ - } /* if end of line */ - } /* Loop over characters */ - FREE(full_line); - - START(HTML_HEAD); - PUTC('\n'); - START(HTML_TITLE); - if (subject && *subject != '\0') - PUTS(subject); - else - PUTS("No Subject"); - END(HTML_TITLE); - PUTC('\n'); - /* - * Put in the owner as a link rel. - */ - if (from || replyto) { - char *temp = NULL; - - StrAllocCopy(temp, author_address(replyto ? replyto : from)); - StrAllocCopy(href, STR_MAILTO_URL); - if (strchr(temp, '%') || strchr(temp, '?')) { - cp = HTEscape(temp, URL_XPALPHAS); - StrAllocCat(href, cp); - FREE(cp); - } else { - StrAllocCat(href, temp); - } - start_link(href, "made"); - PUTC('\n'); - FREE(temp); - } - END(HTML_HEAD); - PUTC('\n'); - - START(HTML_H1); - if (subject && *subject != '\0') - PUTS(subject); - else - PUTS("No Subject"); - END(HTML_H1); - PUTC('\n'); - - if (subject) - FREE(subject); - - START(HTML_DLC); - PUTC('\n'); - - if (from || replyto) { - START(HTML_DT); - START(HTML_B); - PUTS("From:"); - END(HTML_B); - PUTC(' '); - if (from) - PUTS(from); - else - PUTS(replyto); - MAYBE_END(HTML_DT); - PUTC('\n'); - - if (!replyto) - StrAllocCopy(replyto, from); - START(HTML_DT); - START(HTML_B); - PUTS("Reply to:"); - END(HTML_B); - PUTC(' '); - start_anchor(href); - if (*replyto != '<') - PUTS(author_name(replyto)); - else - PUTS(author_address(replyto)); - END(HTML_A); - START(HTML_BR); - MAYBE_END(HTML_DT); - PUTC('\n'); - - FREE(from); - FREE(replyto); - } - - if (date) { - START(HTML_DT); - START(HTML_B); - PUTS("Date:"); - END(HTML_B); - PUTC(' '); - PUTS(date); - MAYBE_END(HTML_DT); - PUTC('\n'); - FREE(date); - } - - if (organization) { - START(HTML_DT); - START(HTML_B); - PUTS("Organization:"); - END(HTML_B); - PUTC(' '); - PUTS(organization); - MAYBE_END(HTML_DT); - PUTC('\n'); - FREE(organization); - } - - /* sanitize some headers - kw */ - if (newsgroups && - ((cp = strchr(newsgroups, '/')) || - (cp = strchr(newsgroups, '(')))) { - *cp = '\0'; - } - if (newsgroups && !*newsgroups) { - FREE(newsgroups); - } - if (followupto && - ((cp = strchr(followupto, '/')) || - (cp = strchr(followupto, '(')))) { - *cp = '\0'; - } - if (followupto && !*followupto) { - FREE(followupto); - } - - if (newsgroups && HTCanPost) { - START(HTML_DT); - START(HTML_B); - PUTS("Newsgroups:"); - END(HTML_B); - PUTC('\n'); - MAYBE_END(HTML_DT); - START(HTML_DD); - write_anchors(newsgroups); - MAYBE_END(HTML_DD); - PUTC('\n'); - } - - if (followupto && !strcasecomp(followupto, "poster")) { - /* - * "Followup-To: poster" has special meaning. Don't use it to - * construct a newsreply link. -kw - */ - START(HTML_DT); - START(HTML_B); - PUTS("Followup to:"); - END(HTML_B); - PUTC(' '); - if (href) { - start_anchor(href); - PUTS("poster"); - END(HTML_A); - } else { - PUTS("poster"); - } - MAYBE_END(HTML_DT); - PUTC('\n'); - FREE(followupto); - } - - if (newsgroups && HTCanPost) { - /* - * We have permission to POST to this host, so add a link for - * posting followups for this article. - FM - */ - if (!strncasecomp(NewsHREF, STR_SNEWS_URL, 6)) - StrAllocCopy(href, "snewsreply://"); - else - StrAllocCopy(href, "newsreply://"); - StrAllocCat(href, NewsHost); - StrAllocCat(href, "/"); - StrAllocCat(href, (followupto ? followupto : newsgroups)); - if (*href == 'n' && - (ccp = HTAnchor_messageID(thisanchor)) && *ccp) { - StrAllocCat(href, ";ref="); - if (strchr(ccp, '<') || strchr(ccp, '&') || - strchr(ccp, ' ') || strchr(ccp, ':') || - strchr(ccp, '/') || strchr(ccp, '%') || - strchr(ccp, ';')) { - char *cp1 = HTEscape(ccp, URL_XPALPHAS); - - StrAllocCat(href, cp1); - FREE(cp1); - } else { - StrAllocCat(href, ccp); - } - } - - START(HTML_DT); - START(HTML_B); - PUTS("Followup to:"); - END(HTML_B); - PUTC(' '); - start_anchor(href); - if (strchr((followupto ? followupto : newsgroups), ',')) { - PUTS("newsgroups"); - } else { - PUTS("newsgroup"); - } - END(HTML_A); - MAYBE_END(HTML_DT); - PUTC('\n'); - } - FREE(newsgroups); - FREE(followupto); - - if (references) { - START(HTML_DT); - START(HTML_B); - PUTS("References:"); - END(HTML_B); - MAYBE_END(HTML_DT); - PUTC('\n'); - START(HTML_DD); - write_anchors(references); - MAYBE_END(HTML_DD); - PUTC('\n'); - FREE(references); - } - - END(HTML_DLC); - PUTC('\n'); - FREE(href); - } - - if (rawtext) { - /* - * No tags, and never do a PUTC. - kw - */ - ; - } else if (diagnostic) { - /* - * Read in the HEAD and BODY of the Article as XMP formatted text. - - * FM - */ - START(HTML_XMP); - PUTC('\n'); - } else { - /* - * Read in the BODY of the Article as PRE formatted text. - FM - */ - START(HTML_PRE); - PUTC('\n'); - } - - p = line; - while (!done) { - int ich = NEXT_CHAR; - - *p++ = (char) ich; - if (ich == EOF) { - if (interrupted_in_htgetcharacter) { - interrupted_in_htgetcharacter = 0; - CTRACE((tfp, - "HTNews: Interrupted on read, closing socket %d\n", - s)); - NEWS_NETCLOSE(s); - s = -1; - return (HT_INTERRUPTED); - } - abort_socket(); /* End of file, close socket */ - return (HT_LOADED); /* End of file on response */ - } - if (((char) ich == LF) || (p == &line[LINE_LENGTH])) { - *p++ = '\0'; /* Terminate the string */ - CTRACE((tfp, "B %s", line)); -#ifdef NEWS_DEBUG /* 1997/11/09 (Sun) 15:56:11 */ - debug_print(line); /* @@@ */ -#endif - if (line[0] == '.') { - /* - * End of article? - */ - if (UCH(line[1]) < ' ') { - done = YES; - break; - } else { /* Line starts with dot */ - if (rawtext) { - RAW_PUTS(&line[1]); - } else { - PUTS(&line[1]); /* Ignore first dot */ - } - } - } else { - if (rawtext) { - RAW_PUTS(line); - } else if (diagnostic || !scan_for_buried_news_references) { - /* - * All lines are passed as unmodified source. - FM - */ - PUTS(line); - } else { - /* - * Normal lines are scanned for buried references to other - * articles. Unfortunately, it could pick up mail - * addresses as well! It also can corrupt uuencoded - * messages! So we don't do this when fetching articles as - * WWW_SOURCE or when downloading (diagnostic is TRUE) or - * if the client has set scan_for_buried_news_references to - * FALSE. Otherwise, we convert all "<...@...>" strings - * preceded by "rticle " to "news:...@..." links, and any - * strings that look like URLs to links. - FM - */ - char *l = line; - char *p2; - - while ((p2 = strstr(l, "rticle <")) != NULL) { - char *q = strrchr(p2, '>'); - char *at = strrchr(p2, '@'); - - if (q && at && at < q) { - char c = q[1]; - - q[1] = 0; /* chop up */ - p2 += 7; - *p2 = 0; - while (*l) { - if (strncmp(l, STR_NEWS_URL, LEN_NEWS_URL) && - strncmp(l, "snews://", 8) && - strncmp(l, "nntp://", 7) && - strncmp(l, "snewspost:", 10) && - strncmp(l, "snewsreply:", 11) && - strncmp(l, "newspost:", 9) && - strncmp(l, "newsreply:", 10) && - strncmp(l, "ftp://", 6) && - strncmp(l, "file:/", 6) && - strncmp(l, "finger://", 9) && - strncmp(l, "http://", 7) && - strncmp(l, "https://", 8) && - strncmp(l, "wais://", 7) && - strncmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) && - strncmp(l, "cso://", 6) && - strncmp(l, "gopher://", 9)) { - PUTC(*l++); - } else { - StrAllocCopy(href, l); - start_anchor(strtok(href, " \r\n\t,>)\"")); - while (*l && !strchr(" \r\n\t,>)\"", *l)) - PUTC(*l++); - END(HTML_A); - FREE(href); - } - } - *p2 = '<'; /* again */ - *q = 0; - start_anchor(p2 + 1); - *q = '>'; /* again */ - PUTS(p2); - END(HTML_A); - q[1] = c; /* again */ - l = q + 1; - } else { - break; /* line has unmatched <> */ - } - } - while (*l) { /* Last bit of the line */ - if (strncmp(l, STR_NEWS_URL, LEN_NEWS_URL) && - strncmp(l, "snews://", 8) && - strncmp(l, "nntp://", 7) && - strncmp(l, "snewspost:", 10) && - strncmp(l, "snewsreply:", 11) && - strncmp(l, "newspost:", 9) && - strncmp(l, "newsreply:", 10) && - strncmp(l, "ftp://", 6) && - strncmp(l, "file:/", 6) && - strncmp(l, "finger://", 9) && - strncmp(l, "http://", 7) && - strncmp(l, "https://", 8) && - strncmp(l, "wais://", 7) && - strncmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) && - strncmp(l, "cso://", 6) && - strncmp(l, "gopher://", 9)) - PUTC(*l++); - else { - StrAllocCopy(href, l); - start_anchor(strtok(href, " \r\n\t,>)\"")); - while (*l && !strchr(" \r\n\t,>)\"", *l)) - PUTC(*l++); - END(HTML_A); - FREE(href); - } - } - } /* if diagnostic or not scan_for_buried_news_references */ - } /* if not dot */ - p = line; /* Restart at beginning */ - } /* if end of line */ - } /* Loop over characters */ - - if (rawtext) - return (HT_LOADED); - - if (diagnostic) - END(HTML_XMP); - else - END(HTML_PRE); - PUTC('\n'); - return (HT_LOADED); -} - -/* Read in a List of Newsgroups - * ---------------------------- - * - * Note the termination condition of a single dot on a line by itself. - * RFC 977 specifies that the line "folding" of RFC850 is not used, - * so we do not handle it here. - */ -static int read_list(char *arg) -{ - char line[LINE_LENGTH + 1]; - char *p; - BOOL done = NO; - BOOL head = NO; - BOOL tail = NO; - BOOL skip_this_line = NO; - BOOL skip_rest_of_line = NO; - int listing = 0; - char *pattern = NULL; - int len = 0; - - /* - * Support head or tail matches for groups to list. - FM - */ - if (arg && strlen(arg) > 1) { - if (*arg == '*') { - tail = YES; - StrAllocCopy(pattern, (arg + 1)); - } else if (arg[strlen(arg) - 1] == '*') { - head = YES; - StrAllocCopy(pattern, arg); - pattern[strlen(pattern) - 1] = '\0'; - } - if (tail || head) { - len = strlen(pattern); - } - - } - - /* - * Read the server's reply. - * - * The lines are scanned for newsgroup names and descriptions. - */ - START(HTML_HEAD); - PUTC('\n'); - START(HTML_TITLE); - PUTS("Newsgroups"); - END(HTML_TITLE); - PUTC('\n'); - END(HTML_HEAD); - PUTC('\n'); - START(HTML_H1); - PUTS("Newsgroups"); - END(HTML_H1); - PUTC('\n'); - p = line; - START(HTML_DLC); - PUTC('\n'); - while (!done) { - int ich = NEXT_CHAR; - char ch = (char) ich; - - if (ich == EOF) { - if (interrupted_in_htgetcharacter) { - interrupted_in_htgetcharacter = 0; - CTRACE((tfp, - "HTNews: Interrupted on read, closing socket %d\n", - s)); - NEWS_NETCLOSE(s); - s = -1; - return (HT_INTERRUPTED); - } - abort_socket(); /* End of file, close socket */ - FREE(pattern); - return (HT_LOADED); /* End of file on response */ - } else if (skip_this_line) { - if (ch == LF) { - skip_this_line = skip_rest_of_line = NO; - p = line; - } - continue; - } else if (skip_rest_of_line) { - if (ch != LF) { - continue; - } - } else if (p == &line[LINE_LENGTH]) { - CTRACE((tfp, "b %.*s%c[...]\n", (LINE_LENGTH), line, ch)); - *p = '\0'; - if (ch == LF) { - ; /* Will be dealt with below */ - } else if (WHITE(ch)) { - ch = LF; /* May treat as line without description */ - skip_this_line = YES; /* ...and ignore until LF */ - } else if (strchr(line, ' ') == NULL && - strchr(line, '\t') == NULL) { - /* No separator found */ - CTRACE((tfp, "HTNews..... group name too long, discarding.\n")); - skip_this_line = YES; /* ignore whole line */ - continue; - } else { - skip_rest_of_line = YES; /* skip until ch == LF found */ - } - } else { - *p++ = ch; - } - if (ch == LF) { - skip_rest_of_line = NO; /* done, reset flag */ - *p = '\0'; /* Terminate the string */ - CTRACE((tfp, "B %s", line)); - if (line[0] == '.') { - /* - * End of article? - */ - if (UCH(line[1]) < ' ') { - done = YES; - break; - } else { /* Line starts with dot */ - START(HTML_DT); - PUTS(&line[1]); - MAYBE_END(HTML_DT); - } - } else if (line[0] == '#') { /* Comment? */ - p = line; /* Restart at beginning */ - continue; - } else { - /* - * Normal lines are scanned for references to newsgroups. - */ - int i = 0; - - /* find whitespace if it exits */ - for (; line[i] != '\0' && !WHITE(line[i]); i++) ; /* null body */ - - if (line[i] != '\0') { - line[i] = '\0'; - if ((head && strncasecomp(line, pattern, len)) || - (tail && (i < len || - strcasecomp((line + (i - len)), pattern)))) { - p = line; /* Restart at beginning */ - continue; - } - START(HTML_DT); - write_anchor(line, line); - listing++; - MAYBE_END(HTML_DT); - PUTC('\n'); - START(HTML_DD); - PUTS(&line[i + 1]); /* put description */ - MAYBE_END(HTML_DD); - } else { - if ((head && strncasecomp(line, pattern, len)) || - (tail && (i < len || - strcasecomp((line + (i - len)), pattern)))) { - p = line; /* Restart at beginning */ - continue; - } - START(HTML_DT); - write_anchor(line, line); - MAYBE_END(HTML_DT); - listing++; - } - } /* if not dot */ - p = line; /* Restart at beginning */ - } /* if end of line */ - } /* Loop over characters */ - if (!listing) { - char *msg = NULL; - - START(HTML_DT); - HTSprintf0(&msg, gettext("No matches for: %s"), arg); - PUTS(msg); - MAYBE_END(HTML_DT); - FREE(msg); - } - END(HTML_DLC); - PUTC('\n'); - FREE(pattern); - return (HT_LOADED); -} - -/* Read in a Newsgroup - * ------------------- - * - * Unfortunately, we have to ask for each article one by one if we - * want more than one field. - * - */ -static int read_group(const char *groupName, - int first_required, - int last_required) -{ - char line[LINE_LENGTH + 1]; - char *author = NULL; - char *subject = NULL; - char *date = NULL; - int i; - char *p; - BOOL done; - - char buffer[LINE_LENGTH + 1]; - char *temp = NULL; - char *reference = NULL; /* Href for article */ - int art; /* Article number WITHIN GROUP */ - int status, count, first, last; /* Response fields */ - - START(HTML_HEAD); - PUTC('\n'); - START(HTML_TITLE); - PUTS("Newsgroup "); - PUTS(groupName); - END(HTML_TITLE); - PUTC('\n'); - END(HTML_HEAD); - PUTC('\n'); - - sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last); - CTRACE((tfp, "Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n", - status, count, first, last, first_required, last_required)); - if (last == 0) { - PUTS(gettext("\nNo articles in this group.\n")); - goto add_post; - } -#define FAST_THRESHOLD 100 /* Above this, read IDs fast */ -#define CHOP_THRESHOLD 50 /* Above this, chop off the rest */ - - if (first_required < first) - first_required = first; /* clip */ - if ((last_required == 0) || (last_required > last)) - last_required = last; - - if (last_required < first_required) { - PUTS(gettext("\nNo articles in this range.\n")); - goto add_post; - } - - if (last_required - first_required + 1 > HTNewsMaxChunk) { /* Trim this block */ - first_required = last_required - HTNewsChunkSize + 1; - } - CTRACE((tfp, " Chunk will be (%d-%d)\n", - first_required, last_required)); - - /* - * Set window title. - */ - HTSprintf0(&temp, gettext("%s, Articles %d-%d"), - groupName, first_required, last_required); - START(HTML_H1); - PUTS(temp); - FREE(temp); - END(HTML_H1); - PUTC('\n'); - - /* - * Link to earlier articles. - */ - if (first_required > first) { - int before; /* Start of one before */ - - if (first_required - HTNewsMaxChunk <= first) - before = first; - else - before = first_required - HTNewsChunkSize; - HTSprintf0(&dbuf, "%s%s/%d-%d", NewsHREF, groupName, - before, first_required - 1); - CTRACE((tfp, " Block before is %s\n", dbuf)); - PUTC('('); - start_anchor(dbuf); - PUTS(gettext("Earlier articles")); - END(HTML_A); - PUTS("...)\n"); - START(HTML_P); - PUTC('\n'); - } - - done = NO; - -/*#define USE_XHDR*/ -#ifdef USE_XHDR - if (count > FAST_THRESHOLD) { - HTSprintf0(&temp, - gettext("\nThere are about %d articles currently available in %s, IDs as follows:\n\n"), - count, groupName); - PUTS(temp); - FREE(temp); - sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF); - status = response(buffer); - if (status == 221) { - p = line; - while (!done) { - int ich = NEXT_CHAR; - - *p++ = ich; - if (ich == EOF) { - if (interrupted_in_htgetcharacter) { - interrupted_in_htgetcharacter = 0; - CTRACE((tfp, - "HTNews: Interrupted on read, closing socket %d\n", - s)); - NEWS_NETCLOSE(s); - s = -1; - return (HT_INTERRUPTED); - } - abort_socket(); /* End of file, close socket */ - return (HT_LOADED); /* End of file on response */ - } - if (((char) ich == '\n') || (p == &line[LINE_LENGTH])) { - *p = '\0'; /* Terminate the string */ - CTRACE((tfp, "X %s", line)); - if (line[0] == '.') { - /* - * End of article? - */ - if (UCH(line[1]) < ' ') { - done = YES; - break; - } else { /* Line starts with dot */ - /* Ignore strange line */ - } - } else { - /* - * Normal lines are scanned for references to articles. - */ - char *space = strchr(line, ' '); - - if (space++) - write_anchor(space, space); - } /* if not dot */ - p = line; /* Restart at beginning */ - } /* if end of line */ - } /* Loop over characters */ - - /* leaving loop with "done" set */ - } /* Good status */ - } -#endif /* USE_XHDR */ - - /* - * Read newsgroup using individual fields. - */ - if (!done) { - START(HTML_B); - if (first == first_required && last == last_required) - PUTS(gettext("All available articles in ")); - else - PUTS("Articles in "); - PUTS(groupName); - END(HTML_B); - PUTC('\n'); - if (LYListNewsNumbers) - start_list(first_required); - else - START(HTML_UL); - for (art = first_required; art <= last_required; art++) { -/*#define OVERLAP*/ -#ifdef OVERLAP - /* - * With this code we try to keep the server running flat out by - * queuing just one extra command ahead of time. We assume (1) - * that the server won't abort if it gets input during output, and - * (2) that TCP buffering is enough for the two commands. Both - * these assumptions seem very reasonable. However, we HAVE had a - * hangup with a loaded server. - */ - if (art == first_required) { - if (art == last_required) { /* Only one */ - sprintf(buffer, "HEAD %d%c%c", - art, CR, LF); - status = response(buffer); - } else { /* First of many */ - sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c", - art, CR, LF, art + 1, CR, LF); - status = response(buffer); - } - } else if (art == last_required) { /* Last of many */ - status = response(NULL); - } else { /* Middle of many */ - sprintf(buffer, "HEAD %d%c%c", art + 1, CR, LF); - status = response(buffer); - } -#else /* Not OVERLAP: */ - sprintf(buffer, "HEAD %d%c%c", art, CR, LF); - status = response(buffer); -#endif /* OVERLAP */ - /* - * Check for a good response (221) for the HEAD request, and if so, - * parse it. Otherwise, indicate the error so that the number of - * listings corresponds to what's claimed for the range, and if we - * are listing numbers via an ordered list, they stay in synchrony - * with the article numbers. - FM - */ - if (status == 221) { /* Head follows - parse it: */ - p = line; /* Write pointer */ - done = NO; - while (!done) { - int ich = NEXT_CHAR; - - *p++ = (char) ich; - if (ich == EOF) { - if (interrupted_in_htgetcharacter) { - interrupted_in_htgetcharacter = 0; - CTRACE((tfp, - "HTNews: Interrupted on read, closing socket %d\n", - s)); - NEWS_NETCLOSE(s); - s = -1; - return (HT_INTERRUPTED); - } - abort_socket(); /* End of file, close socket */ - return (HT_LOADED); /* End of file on response */ - } - if (((char) ich == LF) || - (p == &line[LINE_LENGTH])) { - - *--p = '\0'; /* Terminate & chop LF */ - p = line; /* Restart at beginning */ - CTRACE((tfp, "G %s\n", line)); - switch (line[0]) { - - case '.': - /* - * End of article? - */ - done = (BOOL) (UCH(line[1]) < ' '); - break; - - case 'S': - case 's': - if (match(line, "SUBJECT:")) { - StrAllocCopy(subject, line + 9); - decode_mime(&subject); - } - break; - - case 'M': - case 'm': - if (match(line, "MESSAGE-ID:")) { - char *addr = HTStrip(line + 11) + 1; /* Chop < */ - - addr[strlen(addr) - 1] = '\0'; /* Chop > */ - StrAllocCopy(reference, addr); - } - break; - - case 'f': - case 'F': - if (match(line, "FROM:")) { - char *p2; - - StrAllocCopy(author, strchr(line, ':') + 1); - decode_mime(&author); - p2 = author + strlen(author) - 1; - if (*p2 == LF) - *p2 = '\0'; /* Chop off newline */ - } - break; - - case 'd': - case 'D': - if (LYListNewsDates && match(line, "DATE:")) { - StrAllocCopy(date, - HTStrip(strchr(line, ':') + 1)); - } - break; - - } /* end switch on first character */ - } /* if end of line */ - } /* Loop over characters */ - - PUTC('\n'); - START(HTML_LI); - p = decode_mime(&subject); - HTSprintf0(&temp, "\"%s\"", NonNull(p)); - if (reference) { - write_anchor(temp, reference); - FREE(reference); - } else { - PUTS(temp); - } - FREE(temp); - - if (author != NULL) { - PUTS(" - "); - if (LYListNewsDates) - START(HTML_I); - PUTS(decode_mime(&author)); - if (LYListNewsDates) - END(HTML_I); - FREE(author); - } - if (date) { - if (!diagnostic) { - for (i = 0; date[i]; i++) { - if (date[i] == ' ') { - date[i] = HT_NON_BREAK_SPACE; - } - } - } - sprintf(buffer, " [%.*s]", (int) (sizeof(buffer) - 4), date); - PUTS(buffer); - FREE(date); - } - MAYBE_END(HTML_LI); - /* - * Indicate progress! @@@@@@ - */ - } else if (status == HT_INTERRUPTED) { - interrupted_in_htgetcharacter = 0; - CTRACE((tfp, - "HTNews: Interrupted on read, closing socket %d\n", - s)); - NEWS_NETCLOSE(s); - s = -1; - return (HT_INTERRUPTED); - } else { - /* - * Use the response text on error. - FM - */ - PUTC('\n'); - START(HTML_LI); - START(HTML_I); - if (LYListNewsNumbers) - LYstrncpy(buffer, "Status:", sizeof(buffer) - 1); - else - sprintf(buffer, "Status (ARTICLE %d):", art); - PUTS(buffer); - END(HTML_I); - PUTC(' '); - PUTS(response_text); - MAYBE_END(HTML_LI); - } /* Handle response to HEAD request */ - } /* Loop over article */ - FREE(author); - FREE(subject); - } /* If read headers */ - PUTC('\n'); - if (LYListNewsNumbers) - END(HTML_OL); - else - END(HTML_UL); - PUTC('\n'); - - /* - * Link to later articles. - */ - if (last_required < last) { - int after; /* End of article after */ - - after = last_required + HTNewsChunkSize; - if (after == last) - HTSprintf0(&dbuf, "%s%s", NewsHREF, groupName); /* original group */ - else - HTSprintf0(&dbuf, "%s%s/%d-%d", NewsHREF, groupName, - last_required + 1, after); - CTRACE((tfp, " Block after is %s\n", dbuf)); - PUTC('('); - start_anchor(dbuf); - PUTS(gettext("Later articles")); - END(HTML_A); - PUTS("...)\n"); - } - - add_post: - if (HTCanPost) { - /* - * We have permission to POST to this host, so add a link for posting - * messages to this newsgroup. - FM - */ - char *href = NULL; - - START(HTML_HR); - PUTC('\n'); - if (!strncasecomp(NewsHREF, STR_SNEWS_URL, 6)) - StrAllocCopy(href, "snewspost://"); - else - StrAllocCopy(href, "newspost://"); - StrAllocCat(href, NewsHost); - StrAllocCat(href, "/"); - StrAllocCat(href, groupName); - start_anchor(href); - PUTS(gettext("Post to ")); - PUTS(groupName); - END(HTML_A); - FREE(href); - } else { - START(HTML_HR); - } - PUTC('\n'); - return (HT_LOADED); -} - -/* Load by name. HTLoadNews - * ============= - */ -static int HTLoadNews(const char *arg, - HTParentAnchor *anAnchor, - HTFormat format_out, - HTStream *stream) -{ - char command[262]; /* The whole command */ - char proxycmd[260]; /* The proxy command */ - char groupName[GROUP_NAME_LENGTH]; /* Just the group name */ - int status; /* tcp return */ - int retries; /* A count of how hard we have tried */ - BOOL normal_url; /* Flag: "news:" or "nntp:" (physical) URL */ - BOOL group_wanted; /* Flag: group was asked for, not article */ - BOOL list_wanted; /* Flag: list was asked for, not article */ - BOOL post_wanted; /* Flag: new post to group was asked for */ - BOOL reply_wanted; /* Flag: followup post was asked for */ - BOOL spost_wanted; /* Flag: new SSL post to group was asked for */ - BOOL sreply_wanted; /* Flag: followup SSL post was asked for */ - BOOL head_wanted = NO; /* Flag: want HEAD of single article */ - int first, last; /* First and last articles asked for */ - char *cp = 0; - char *ListArg = NULL; - char *ProxyHost = NULL; - char *ProxyHREF = NULL; - char *postfile = NULL; - -#ifdef USE_SSL - char SSLprogress[256]; -#endif /* USE_SSL */ - - diagnostic = (format_out == WWW_SOURCE || /* set global flag */ - format_out == HTAtom_for("www/download") || - format_out == HTAtom_for("www/dump")); - rawtext = NO; - - CTRACE((tfp, "HTNews: Looking for %s\n", arg)); - - if (!initialized) - initialized = initialize(); - if (!initialized) - return -1; /* FAIL */ - - FREE(NewsHREF); - command[0] = '\0'; - command[sizeof(command) - 1] = '\0'; - proxycmd[0] = '\0'; - proxycmd[sizeof(proxycmd) - 1] = '\0'; - - { - const char *p1 = arg; - - /* - * We will ask for the document, omitting the host name & anchor. - * - * Syntax of address is - * xxx@yyy Article - * <xxx@yyy> Same article - * xxxxx News group (no "@") - * group/n1-n2 Articles n1 to n2 in group - */ - normal_url = (BOOL) (!strncmp(arg, STR_NEWS_URL, LEN_NEWS_URL) || - !strncmp(arg, "nntp:", 5)); - spost_wanted = (BOOL) (!normal_url && strstr(arg, "snewspost:") != NULL); - sreply_wanted = (BOOL) (!(normal_url || spost_wanted) && - strstr(arg, "snewsreply:") != NULL); - post_wanted = (BOOL) (!(normal_url || spost_wanted || sreply_wanted) && - strstr(arg, "newspost:") != NULL); - reply_wanted = (BOOL) (!(normal_url || spost_wanted || sreply_wanted || - post_wanted) && - strstr(arg, "newsreply:") != NULL); - group_wanted = (BOOL) ((!(spost_wanted || sreply_wanted || - post_wanted || reply_wanted) && - strchr(arg, '@') == NULL) && - (strchr(arg, '*') == NULL)); - list_wanted = (BOOL) ((!(spost_wanted || sreply_wanted || - post_wanted || reply_wanted || - group_wanted) && - strchr(arg, '@') == NULL) && - (strchr(arg, '*') != NULL)); - -#ifndef USE_SSL - if (!strncasecomp(arg, "snewspost:", 10) || - !strncasecomp(arg, "snewsreply:", 11)) { - HTAlert(FAILED_CANNOT_POST_SSL); - return HT_NOT_LOADED; - } -#endif /* !USE_SSL */ - if (post_wanted || reply_wanted || spost_wanted || sreply_wanted) { - /* - * Make sure we have a non-zero path for the newsgroup(s). - FM - */ - if ((p1 = strrchr(arg, '/')) != NULL) { - p1++; - } else if ((p1 = strrchr(arg, ':')) != NULL) { - p1++; - } - if (!(p1 && *p1)) { - HTAlert(WWW_ILLEGAL_URL_MESSAGE); - return (HT_NO_DATA); - } - if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') { - if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, HTNewsHost); - } else { - if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, cp); - } - FREE(cp); - HTSprintf0(&NewsHREF, "%s://%.*s/", - (post_wanted ? - "newspost" : - (reply_wanted ? - "newreply" : - (spost_wanted ? - "snewspost" : "snewsreply"))), - (int) sizeof(command) - 15, NewsHost); - - /* - * If the SSL daemon is being used as a proxy, reset p1 to the - * start of the proxied URL rather than to the start of the - * newsgroup(s). - FM - */ - if (spost_wanted && strncasecomp(arg, "snewspost:", 10)) - p1 = strstr(arg, "snewspost:"); - if (sreply_wanted && strncasecomp(arg, "snewsreply:", 11)) - p1 = strstr(arg, "snewsreply:"); - - /* p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION); */ - /* - * Don't use HTParse because news: access doesn't follow - * traditional rules. For instance, if the article reference - * contains a '#', the rest of it is lost -- JFG 10/7/92, from a - * bug report - */ - } else if (isNNTP_URL(arg)) { - if (((*(arg + 5) == '\0') || - (!strcmp((arg + 5), "/") || - !strcmp((arg + 5), "//") || - !strcmp((arg + 5), "///"))) || - ((!strncmp((arg + 5), "//", 2)) && - (!(cp = strchr((arg + 7), '/')) || *(cp + 1) == '\0'))) { - p1 = "*"; - group_wanted = FALSE; - list_wanted = TRUE; - } else if (*(arg + 5) != '/') { - p1 = (arg + 5); - } else if (*(arg + 5) == '/' && *(arg + 6) != '/') { - p1 = (arg + 6); - } else { - p1 = (cp + 1); - } - if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') { - if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, HTNewsHost); - } else { - if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, cp); - } - FREE(cp); - SnipIn2(command, "%s//%.*s/", STR_NNTP_URL, 9, NewsHost); - StrAllocCopy(NewsHREF, command); - } else if (!strncasecomp(arg, STR_SNEWS_URL, 6)) { -#ifdef USE_SSL - if (((*(arg + 6) == '\0') || - (!strcmp((arg + 6), "/") || - !strcmp((arg + 6), "//") || - !strcmp((arg + 6), "///"))) || - ((!strncmp((arg + 6), "//", 2)) && - (!(cp = strchr((arg + 8), '/')) || *(cp + 1) == '\0'))) { - p1 = "*"; - group_wanted = FALSE; - list_wanted = TRUE; - } else if (*(arg + 6) != '/') { - p1 = (arg + 6); - } else if (*(arg + 6) == '/' && *(arg + 7) != '/') { - p1 = (arg + 7); - } else { - p1 = (cp + 1); - } - if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') { - if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, HTNewsHost); - } else { - if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, cp); - } - FREE(cp); - sprintf(command, "%s//%.250s/", STR_SNEWS_URL, NewsHost); - StrAllocCopy(NewsHREF, command); -#else - HTAlert(gettext("This client does not contain support for SNEWS URLs.")); - return HT_NOT_LOADED; -#endif /* USE_SSL */ - } else if (!strncasecomp(arg, "news:/", 6)) { - if (((*(arg + 6) == '\0') || - !strcmp((arg + 6), "/") || - !strcmp((arg + 6), "//")) || - ((*(arg + 6) == '/') && - (!(cp = strchr((arg + 7), '/')) || *(cp + 1) == '\0'))) { - p1 = "*"; - group_wanted = FALSE; - list_wanted = TRUE; - } else if (*(arg + 6) != '/') { - p1 = (arg + 6); - } else { - p1 = (cp + 1); - } - if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') { - if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, HTNewsHost); - } else { - if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, cp); - } - FREE(cp); - SnipIn(command, "news://%.*s/", 9, NewsHost); - StrAllocCopy(NewsHREF, command); - } else { - p1 = (arg + 5); /* Skip "news:" prefix */ - if (*p1 == '\0') { - p1 = "*"; - group_wanted = FALSE; - list_wanted = TRUE; - } - if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) { - NEWS_NETCLOSE(s); - s = -1; - } - StrAllocCopy(NewsHost, HTNewsHost); - StrAllocCopy(NewsHREF, STR_NEWS_URL); - } - - /* - * Set up any proxy for snews URLs that returns NNTP responses for Lynx - * to convert to HTML, instead of doing the conversion itself, and for - * handling posts or followups. - TZ & FM - */ - if (!strncasecomp(p1, STR_SNEWS_URL, 6) || - !strncasecomp(p1, "snewspost:", 10) || - !strncasecomp(p1, "snewsreply:", 11)) { - StrAllocCopy(ProxyHost, NewsHost); - if ((cp = HTParse(p1, "", PARSE_HOST)) != NULL && *cp != '\0') { - SnipIn2(command, "%s//%.*s", STR_SNEWS_URL, 10, cp); - StrAllocCopy(NewsHost, cp); - } else { - SnipIn2(command, "%s//%.*s", STR_SNEWS_URL, 10, NewsHost); - } - command[sizeof(command) - 2] = '\0'; - FREE(cp); - sprintf(proxycmd, "GET %.*s%c%c%c%c", - (int) sizeof(proxycmd) - 9, command, - CR, LF, CR, LF); - CTRACE((tfp, "HTNews: Proxy command is '%.*s'\n", - (int) (strlen(proxycmd) - 4), proxycmd)); - strcat(command, "/"); - StrAllocCopy(ProxyHREF, NewsHREF); - StrAllocCopy(NewsHREF, command); - if (spost_wanted || sreply_wanted) { - /* - * Reset p1 so that it points to the newsgroup(s). - */ - if ((p1 = strrchr(arg, '/')) != NULL) { - p1++; - } else { - p1 = (strrchr(arg, ':') + 1); - } - } else { - /* - * Reset p1 so that it points to the newsgroup (or a wildcard), - * or the article. - */ - if (!(cp = strrchr((p1 + 6), '/')) || *(cp + 1) == '\0') { - p1 = "*"; - group_wanted = FALSE; - list_wanted = TRUE; - } else { - p1 = (cp + 1); - } - } - } - - /* - * Set up command for a post, listing, or article request. - FM - */ - if (post_wanted || reply_wanted || spost_wanted || sreply_wanted) { - strcpy(command, "POST"); - } else if (list_wanted) { - if (strlen(p1) > 249) { - FREE(ProxyHost); - FREE(ProxyHREF); - HTAlert(URL_TOO_LONG); - return -400; - } - SnipIn(command, "XGTITLE %.*s", 11, p1); - } else if (group_wanted) { - char *slash = strchr(p1, '/'); - - first = 0; - last = 0; - if (slash) { - *slash = '\0'; - if (strlen(p1) >= sizeof(groupName)) { - FREE(ProxyHost); - FREE(ProxyHREF); - HTAlert(URL_TOO_LONG); - return -400; - } - LYstrncpy(groupName, p1, sizeof(groupName) - 1); - *slash = '/'; - (void) sscanf(slash + 1, "%d-%d", &first, &last); - if ((first > 0) && (isdigit(UCH(*(slash + 1)))) && - (strchr(slash + 1, '-') == NULL || first == last)) { - /* - * We got a number greater than 0, which will be loaded as - * first, and either no range or the range computes to - * zero, so make last negative, as a flag to select the - * group and then fetch an article by number (first) - * instead of by messageID. - FM - */ - last = -1; - } - } else { - if (strlen(p1) >= sizeof(groupName)) { - FREE(ProxyHost); - FREE(ProxyHREF); - HTAlert(URL_TOO_LONG); - return -400; - } - LYstrncpy(groupName, p1, sizeof(groupName) - 1); - } - SnipIn(command, "GROUP %.*s", 9, groupName); - } else { - int add_open = (strchr(p1, '<') == 0); - int add_close = (strchr(p1, '>') == 0); - - if (strlen(p1) + add_open + add_close >= 252) { - FREE(ProxyHost); - FREE(ProxyHREF); - HTAlert(URL_TOO_LONG); - return -400; - } - sprintf(command, "ARTICLE %s%.*s%s", - add_open ? "<" : "", - (int) (sizeof(command) - (11 + add_open + add_close)), - p1, - add_close ? ">" : ""); - } - - { - char *p = command + strlen(command); - - /* - * Terminate command with CRLF, as in RFC 977. - */ - *p++ = CR; /* Macros to be correct on Mac */ - *p++ = LF; - *p++ = 0; - } - StrAllocCopy(ListArg, p1); - } /* scope of p1 */ - - if (!*arg) { - FREE(NewsHREF); - FREE(ProxyHost); - FREE(ProxyHREF); - FREE(ListArg); - return NO; /* Ignore if no name */ - } - - if (!(post_wanted || reply_wanted || spost_wanted || sreply_wanted || - (group_wanted && last != -1) || list_wanted)) { - head_wanted = anAnchor->isHEAD; - if (head_wanted && !strncmp(command, "ARTICLE ", 8)) { - /* overwrite "ARTICLE" - hack... */ - strcpy(command, "HEAD "); - for (cp = command + 5;; cp++) - if ((*cp = *(cp + 3)) == '\0') - break; - } - rawtext = (BOOL) (head_wanted || keep_mime_headers); - } - if (rawtext) { - rawtarget = HTStreamStack(WWW_PLAINTEXT, - format_out, - stream, anAnchor); - if (!rawtarget) { - FREE(NewsHost); - FREE(NewsHREF); - FREE(ProxyHost); - FREE(ProxyHREF); - FREE(ListArg); - HTAlert(gettext("No target for raw text!")); - return (HT_NOT_LOADED); - } /* Copy routine entry points */ - rawtargetClass = *rawtarget->isa; - } else - /* - * Make a hypertext object with an anchor list. - */ - if (!(post_wanted || reply_wanted || spost_wanted || sreply_wanted)) { - target = HTML_new(anAnchor, format_out, stream); - targetClass = *target->isa; /* Copy routine entry points */ - } - - /* - * Now, let's get a stream setup up from the NewsHost. - */ - for (retries = 0; retries < 2; retries++) { - if (s < 0) { - /* CONNECTING to news host */ - char url[260]; - - if (!strcmp(NewsHREF, STR_NEWS_URL)) { - SnipIn(url, "lose://%.*s/", 9, NewsHost); - } else if (ProxyHREF) { - SnipIn(url, "%.*s", 1, ProxyHREF); - } else { - SnipIn(url, "%.*s", 1, NewsHREF); - } - CTRACE((tfp, "News: doing HTDoConnect on '%s'\n", url)); - - _HTProgress(gettext("Connecting to NewsHost ...")); - -#ifdef USE_SSL - if (!using_proxy && - (!strncmp(arg, STR_SNEWS_URL, 6) || - !strncmp(arg, "snewspost:", 10) || - !strncmp(arg, "snewsreply:", 11))) - status = HTDoConnect(url, "NNTPS", SNEWS_PORT, &s); - else - status = HTDoConnect(url, "NNTP", NEWS_PORT, &s); -#else - status = HTDoConnect(url, "NNTP", NEWS_PORT, &s); -#endif /* USE_SSL */ - - if (status == HT_INTERRUPTED) { - /* - * Interrupt cleanly. - */ - CTRACE((tfp, - "HTNews: Interrupted on connect; recovering cleanly.\n")); - _HTProgress(CONNECTION_INTERRUPTED); - if (!(post_wanted || reply_wanted || - spost_wanted || sreply_wanted)) { - ABORT_TARGET; - } - FREE(NewsHost); - FREE(NewsHREF); - FREE(ProxyHost); - FREE(ProxyHREF); - FREE(ListArg); -#ifdef USE_SSL - if (Handle) { - SSL_free(Handle); - Handle = NULL; - } -#endif /* USE_SSL */ - if (postfile) { - HTSYS_remove(postfile); - FREE(postfile); - } - return HT_NOT_LOADED; - } - if (status < 0) { - NEWS_NETCLOSE(s); - s = -1; - CTRACE((tfp, "HTNews: Unable to connect to news host.\n")); - if (retries < 1) - continue; - if (!(post_wanted || reply_wanted || - spost_wanted || sreply_wanted)) { - ABORT_TARGET; - } - HTSprintf0(&dbuf, gettext("Could not access %s."), NewsHost); - FREE(NewsHost); - FREE(NewsHREF); - FREE(ProxyHost); - FREE(ProxyHREF); - FREE(ListArg); - if (postfile) { - HTSYS_remove(postfile); - FREE(postfile); - } - return HTLoadError(stream, 500, dbuf); - } else { - CTRACE((tfp, "HTNews: Connected to news host %s.\n", - NewsHost)); -#ifdef USE_SSL - /* - * If this is an snews url, then do the SSL stuff here - */ - if (!using_proxy && - (!strncmp(url, "snews", 5) || - !strncmp(url, "snewspost:", 10) || - !strncmp(url, "snewsreply:", 11))) { - Handle = HTGetSSLHandle(); - SSL_set_fd(Handle, s); - HTSSLInitPRNG(); - status = SSL_connect(Handle); - - if (status <= 0) { - unsigned long SSLerror; - - CTRACE((tfp, - "HTNews: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n", - url, status)); - SSL_load_error_strings(); - while ((SSLerror = ERR_get_error()) != 0) { - CTRACE((tfp, "HTNews: SSL: %s\n", - ERR_error_string(SSLerror, NULL))); - } - HTAlert("Unable to make secure connection to remote host."); - NEWS_NETCLOSE(s); - s = -1; - if (!(post_wanted || reply_wanted || - spost_wanted || sreply_wanted)) - (*targetClass._abort) (target, NULL); - FREE(NewsHost); - FREE(NewsHREF); - FREE(ProxyHost); - FREE(ProxyHREF); - FREE(ListArg); - if (postfile) { -#ifdef VMS - while (remove(postfile) == 0) ; /* loop through all versions */ -#else - remove(postfile); -#endif /* VMS */ - FREE(postfile); - } - return HT_NOT_LOADED; - } - sprintf(SSLprogress, - "Secure %d-bit %s (%s) NNTP connection", - SSL_get_cipher_bits(Handle, NULL), - SSL_get_cipher_version(Handle), - SSL_get_cipher(Handle)); - _HTProgress(SSLprogress); - } -#endif /* USE_SSL */ - HTInitInput(s); /* set up buffering */ - if (proxycmd[0]) { - status = NEWS_NETWRITE(s, proxycmd, strlen(proxycmd)); - CTRACE((tfp, - "HTNews: Proxy command returned status '%d'.\n", - status)); - } - if (((status = response(NULL)) / 100) != 2) { - NEWS_NETCLOSE(s); - s = -1; - if (status == HT_INTERRUPTED) { - _HTProgress(CONNECTION_INTERRUPTED); - if (!(post_wanted || reply_wanted || - spost_wanted || sreply_wanted)) { - ABORT_TARGET; - } - FREE(NewsHost); - FREE(NewsHREF); - FREE(ProxyHost); - FREE(ProxyHREF); - FREE(ListArg); - if (postfile) { - HTSYS_remove(postfile); - FREE(postfile); - } - return (HT_NOT_LOADED); - } - if (retries < 1) - continue; - FREE(ProxyHost); - FREE(ProxyHREF); - FREE(ListArg); - FREE(postfile); - if (!(post_wanted || reply_wanted || - spost_wanted || sreply_wanted)) { - ABORT_TARGET; - } - if (response_text[0]) { - HTSprintf0(&dbuf, - gettext("Can't read news info. News host %.20s responded: %.200s"), - NewsHost, response_text); - } else { - HTSprintf0(&dbuf, - gettext("Can't read news info, empty response from host %s"), - NewsHost); - } - return HTLoadError(stream, 500, dbuf); - } - if (status == 200) { - HTCanPost = TRUE; - } else { - HTCanPost = FALSE; - if (post_wanted || reply_wanted || - spost_wanted || sreply_wanted) { - HTAlert(CANNOT_POST); - FREE(NewsHREF); - if (ProxyHREF) { - StrAllocCopy(NewsHost, ProxyHost); - FREE(ProxyHost); - FREE(ProxyHREF); - } - FREE(ListArg); - if (postfile) { - HTSYS_remove(postfile); - FREE(postfile); - } - return (HT_NOT_LOADED); - } - } - } - } - /* If needed opening */ - if (post_wanted || reply_wanted || - spost_wanted || sreply_wanted) { - if (!HTCanPost) { - HTAlert(CANNOT_POST); - FREE(NewsHREF); - if (ProxyHREF) { - StrAllocCopy(NewsHost, ProxyHost); - FREE(ProxyHost); - FREE(ProxyHREF); - } - FREE(ListArg); - if (postfile) { - HTSYS_remove(postfile); - FREE(postfile); - } - return (HT_NOT_LOADED); - } - if (postfile == NULL) { - postfile = LYNewsPost(ListArg, - (BOOLEAN) (reply_wanted || sreply_wanted)); - } - if (postfile == NULL) { - HTProgress(CANCELLED); - FREE(NewsHREF); - if (ProxyHREF) { - StrAllocCopy(NewsHost, ProxyHost); - FREE(ProxyHost); - FREE(ProxyHREF); - } - FREE(ListArg); - return (HT_NOT_LOADED); - } - } else { - /* - * Ensure reader mode, but don't bother checking the status for - * anything but HT_INTERRUPTED or a 480 Authorization request, - * because if the reader mode command is not needed, the server - * probably returned a 500, which is irrelevant at this point. - - * FM - */ - char buffer[20]; - - sprintf(buffer, "mode reader%c%c", CR, LF); - if ((status = response(buffer)) == HT_INTERRUPTED) { - _HTProgress(CONNECTION_INTERRUPTED); - break; - } - if (status == 480) { - NNTPAuthResult auth_result = HTHandleAuthInfo(NewsHost); - - if (auth_result == NNTPAUTH_CLOSE) { - if (s != -1 && !(ProxyHost || ProxyHREF)) { - NEWS_NETCLOSE(s); - s = -1; - } - } - if (auth_result != NNTPAUTH_OK) { - break; - } - if ((status = response(buffer)) == HT_INTERRUPTED) { - _HTProgress(CONNECTION_INTERRUPTED); - break; - } - } - } - - Send_NNTP_command: -#ifdef NEWS_DEB - if (postfile) - printf("postfile = %s, command = %s", postfile, command); - else - printf("command = %s", command); -#endif - if ((status = response(command)) == HT_INTERRUPTED) { - _HTProgress(CONNECTION_INTERRUPTED); - break; - } - if (status < 0) { - if (retries < 1) { - continue; - } else { - break; - } - } - /* - * For some well known error responses which are expected to occur in - * normal use, break from the loop without retrying and without closing - * the connection. It is unlikely that these are leftovers from a - * timed-out connection (but we do some checks to see whether the - * response corresponds to the last command), or that they will give - * anything else when automatically retried. - kw - */ - if (status == 411 && group_wanted && - !strncmp(command, "GROUP ", 6) && - !strncasecomp(response_text + 3, " No such group ", 15) && - !strcmp(response_text + 18, groupName)) { - - HTAlert(response_text); - break; - } else if (status == 430 && !group_wanted && !list_wanted && - !strncmp(command, "ARTICLE <", 9) && - !strcasecomp(response_text + 3, " No such article")) { - - HTAlert(response_text); - break; - } - if ((status / 100) != 2 && - status != 340 && - status != 480) { - if (retries) { - if (list_wanted && !strncmp(command, "XGTITLE", 7)) { - sprintf(command, "LIST NEWSGROUPS%c%c", CR, LF); - goto Send_NNTP_command; - } - HTAlert(response_text); - } else { - _HTProgress(response_text); - } - NEWS_NETCLOSE(s); - s = -1; - /* - * Message might be a leftover "Timeout-disconnected", so try again - * if the retries maximum has not been reached. - */ - continue; - } - - /* - * Post or load a group, article, etc - */ - if (status == 480) { - NNTPAuthResult auth_result; - - /* - * Some servers return 480 for a failed XGTITLE. - FM - */ - if (list_wanted && !strncmp(command, "XGTITLE", 7) && - strstr(response_text, "uthenticat") == NULL && - strstr(response_text, "uthor") == NULL) { - sprintf(command, "LIST NEWSGROUPS%c%c", CR, LF); - goto Send_NNTP_command; - } - /* - * Handle Authorization. - FM - */ - if ((auth_result = HTHandleAuthInfo(NewsHost)) == NNTPAUTH_OK) { - goto Send_NNTP_command; - } else if (auth_result == NNTPAUTH_CLOSE) { - if (s != -1 && !(ProxyHost || ProxyHREF)) { - NEWS_NETCLOSE(s); - s = -1; - } - if (retries < 1) - continue; - } - status = HT_NOT_LOADED; - } else if (post_wanted || reply_wanted || - spost_wanted || sreply_wanted) { - /* - * Handle posting of an article. - FM - */ - if (status != 340) { - HTAlert(CANNOT_POST); - if (postfile) { - HTSYS_remove(postfile); - } - } else { - post_article(postfile); - } - FREE(postfile); - status = HT_NOT_LOADED; - } else if (list_wanted) { - /* - * List available newsgroups. - FM - */ - _HTProgress(gettext("Reading list of available newsgroups.")); - status = read_list(ListArg); - } else if (group_wanted) { - /* - * List articles in a news group. - FM - */ - if (last < 0) { - /* - * We got one article number rather than a range following the - * slash which followed the group name, or the range was zero, - * so now that we have selected that group, load ARTICLE and - * the the number (first) as the command and go back to send it - * and check the response. - FM - */ - sprintf(command, "%s %d%c%c", - head_wanted ? "HEAD" : "ARTICLE", - first, CR, LF); - group_wanted = FALSE; - retries = 2; - goto Send_NNTP_command; - } - _HTProgress(gettext("Reading list of articles in newsgroup.")); - status = read_group(groupName, first, last); - } else { - /* - * Get an article from a news group. - FM - */ - _HTProgress(gettext("Reading news article.")); - status = read_article(anAnchor); - } - if (status == HT_INTERRUPTED) { - _HTProgress(CONNECTION_INTERRUPTED); - status = HT_LOADED; - } - if (!(post_wanted || reply_wanted || - spost_wanted || sreply_wanted)) { - if (status == HT_NOT_LOADED) { - ABORT_TARGET; - } else { - FREE_TARGET; - } - } - FREE(NewsHREF); - if (ProxyHREF) { - StrAllocCopy(NewsHost, ProxyHost); - FREE(ProxyHost); - FREE(ProxyHREF); - } - FREE(ListArg); - if (postfile) { - HTSYS_remove(postfile); - FREE(postfile); - } - return status; - } /* Retry loop */ - -#if 0 - HTAlert(gettext("Sorry, could not load requested news.")); - NXRunAlertPanel(NULL, "Sorry, could not load `%s'.", NULL, NULL, NULL, arg); - /* No -- message earlier wil have covered it */ -#endif - - if (!(post_wanted || reply_wanted || - spost_wanted || sreply_wanted)) { - ABORT_TARGET; - } - FREE(NewsHREF); - if (ProxyHREF) { - StrAllocCopy(NewsHost, ProxyHost); - FREE(ProxyHost); - FREE(ProxyHREF); - } - FREE(ListArg); - if (postfile) { - HTSYS_remove(postfile); - FREE(postfile); - } - return HT_NOT_LOADED; -} - -/* - * This function clears all authorization information by - * invoking the free_NNTP_AuthInfo() function, which normally - * is invoked at exit. It allows a browser command to do - * this at any time, for example, if the user is leaving - * the terminal for a period of time, but does not want - * to end the current session. - FM - */ -void HTClearNNTPAuthInfo(void) -{ - /* - * Need code to check cached documents and do something to ensure that any - * protected documents no longer can be accessed without a new retrieval. - * - FM - */ - - /* - * Now free all of the authorization info. - FM - */ - free_NNTP_AuthInfo(); -} - -#ifdef USE_SSL -static int HTNewsGetCharacter(void) -{ - if (!Handle) - return HTGetCharacter(); - else - return HTGetSSLCharacter((void *) Handle); -} - -int HTNewsProxyConnect(int sock, - const char *url, - HTParentAnchor *anAnchor, - HTFormat format_out, - HTStream *sink) -{ - int status; - const char *arg = url; - char SSLprogress[256]; - - s = channel_s = sock; - Handle = HTGetSSLHandle(); - SSL_set_fd(Handle, s); - HTSSLInitPRNG(); - status = SSL_connect(Handle); - - if (status <= 0) { - unsigned long SSLerror; - - channel_s = -1; - CTRACE((tfp, - "HTNews: Unable to complete SSL handshake for '%s', SSL_connect=%d, SSL error stack dump follows\n", - url, status)); - SSL_load_error_strings(); - while ((SSLerror = ERR_get_error()) != 0) { - CTRACE((tfp, "HTNews: SSL: %s\n", ERR_error_string(SSLerror, NULL))); - } - HTAlert("Unable to make secure connection to remote host."); - NEWS_NETCLOSE(s); - s = -1; - return HT_NOT_LOADED; - } - sprintf(SSLprogress, "Secure %d-bit %s (%s) NNTP connection", - SSL_get_cipher_bits(Handle, NULL), - SSL_get_cipher_version(Handle), - SSL_get_cipher(Handle)); - _HTProgress(SSLprogress); - status = HTLoadNews(arg, anAnchor, format_out, sink); - channel_s = -1; - return status; -} -#endif /* USE_SSL */ - -#ifdef GLOBALDEF_IS_MACRO -#define _HTNEWS_C_1_INIT { "news", HTLoadNews, NULL } -GLOBALDEF(HTProtocol, HTNews, _HTNEWS_C_1_INIT); -#define _HTNEWS_C_2_INIT { "nntp", HTLoadNews, NULL } -GLOBALDEF(HTProtocol, HTNNTP, _HTNEWS_C_2_INIT); -#define _HTNEWS_C_3_INIT { "newspost", HTLoadNews, NULL } -GLOBALDEF(HTProtocol, HTNewsPost, _HTNEWS_C_3_INIT); -#define _HTNEWS_C_4_INIT { "newsreply", HTLoadNews, NULL } -GLOBALDEF(HTProtocol, HTNewsReply, _HTNEWS_C_4_INIT); -#define _HTNEWS_C_5_INIT { "snews", HTLoadNews, NULL } -GLOBALDEF(HTProtocol, HTSNews, _HTNEWS_C_5_INIT); -#define _HTNEWS_C_6_INIT { "snewspost", HTLoadNews, NULL } -GLOBALDEF(HTProtocol, HTSNewsPost, _HTNEWS_C_6_INIT); -#define _HTNEWS_C_7_INIT { "snewsreply", HTLoadNews, NULL } -GLOBALDEF(HTProtocol, HTSNewsReply, _HTNEWS_C_7_INIT); -#else -GLOBALDEF HTProtocol HTNews = -{"news", HTLoadNews, NULL}; -GLOBALDEF HTProtocol HTNNTP = -{"nntp", HTLoadNews, NULL}; -GLOBALDEF HTProtocol HTNewsPost = -{"newspost", HTLoadNews, NULL}; -GLOBALDEF HTProtocol HTNewsReply = -{"newsreply", HTLoadNews, NULL}; -GLOBALDEF HTProtocol HTSNews = -{"snews", HTLoadNews, NULL}; -GLOBALDEF HTProtocol HTSNewsPost = -{"snewspost", HTLoadNews, NULL}; -GLOBALDEF HTProtocol HTSNewsReply = -{"snewsreply", HTLoadNews, NULL}; -#endif /* GLOBALDEF_IS_MACRO */ - -#endif /* not DISABLE_NEWS */ |