+++ /dev/null
-/* $Id: inews.c 7769 2008-04-13 08:11:41Z iulius $
-**
-** Send an article (prepared by someone on the local site) to the
-** master news server.
-*/
-
-#include "config.h"
-#include "clibrary.h"
-#include "portable/time.h"
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <pwd.h>
-#include <sys/stat.h>
-
-#include "inn/innconf.h"
-#include "inn/messages.h"
-#include "libinn.h"
-#include "nntp.h"
-#include "paths.h"
-
-/* Signature handling. The separator will be appended before the signature,
- and at most SIG_MAXLINES will be appended. */
-#define SIG_MAXLINES 4
-#define SIG_SEPARATOR "-- \n"
-
-#define FLUSH_ERROR(F) (fflush((F)) == EOF || ferror((F)))
-#define LPAREN '(' /* For vi :-) */
-#define HEADER_DELTA 20
-#define GECOSTERM(c) \
- ((c) == ',' || (c) == ';' || (c) == ':' || (c) == LPAREN)
-#define HEADER_STRLEN 998
-
-typedef enum _HEADERTYPE {
- HTobs,
- HTreq,
- HTstd
-} HEADERTYPE;
-
-typedef struct _HEADER {
- const char *Name;
- bool CanSet;
- HEADERTYPE Type;
- int Size;
- char *Value;
-} HEADER;
-
-static bool Dump;
-static bool Revoked;
-static bool Spooling;
-static char **OtherHeaders;
-static char SIGSEP[] = SIG_SEPARATOR;
-static FILE *FromServer;
-static FILE *ToServer;
-static int OtherCount;
-static int OtherSize;
-static const char *Exclusions = "";
-static const char * const BadDistribs[] = {
- BAD_DISTRIBS
-};
-
-static HEADER Table[] = {
- /* Name Canset Type */
- { "Path", true, HTstd, 0, NULL },
-#define _path 0
- { "From", true, HTstd, 0, NULL },
-#define _from 1
- { "Newsgroups", true, HTreq, 0, NULL },
-#define _newsgroups 2
- { "Subject", true, HTreq, 0, NULL },
-#define _subject 3
- { "Control", true, HTstd, 0, NULL },
-#define _control 4
- { "Supersedes", true, HTstd, 0, NULL },
-#define _supersedes 5
- { "Followup-To", true, HTstd, 0, NULL },
-#define _followupto 6
- { "Date", true, HTstd, 0, NULL },
-#define _date 7
- { "Organization", true, HTstd, 0, NULL },
-#define _organization 8
- { "Lines", true, HTstd, 0, NULL },
-#define _lines 9
- { "Sender", true, HTstd, 0, NULL },
-#define _sender 10
- { "Approved", true, HTstd, 0, NULL },
-#define _approved 11
- { "Distribution", true, HTstd, 0, NULL },
-#define _distribution 12
- { "Expires", true, HTstd, 0, NULL },
-#define _expires 13
- { "Message-ID", true, HTstd, 0, NULL },
-#define _messageid 14
- { "References", true, HTstd, 0, NULL },
-#define _references 15
- { "Reply-To", true, HTstd, 0, NULL },
-#define _replyto 16
- { "Also-Control", true, HTstd, 0, NULL },
-#define _alsocontrol 17
- { "Xref", false, HTstd, 0, NULL },
- { "Summary", true, HTstd, 0, NULL },
- { "Keywords", true, HTstd, 0, NULL },
- { "Date-Received", false, HTobs, 0, NULL },
- { "Received", false, HTobs, 0, NULL },
- { "Posted", false, HTobs, 0, NULL },
- { "Posting-Version", false, HTobs, 0, NULL },
- { "Relay-Version", false, HTobs, 0, NULL },
-};
-
-#define HDR(_x) (Table[(_x)].Value)
-
-\f
-
-/*
-** Send the server a quit message, wait for a reply.
-*/
-static void
-QuitServer(int x)
-{
- char buff[HEADER_STRLEN];
- char *p;
-
- if (Spooling)
- exit(x);
- if (x)
- warn("article not posted");
- fprintf(ToServer, "quit\r\n");
- if (FLUSH_ERROR(ToServer))
- sysdie("cannot send quit to server");
- if (fgets(buff, sizeof buff, FromServer) == NULL)
- sysdie("warning: server did not reply to quit");
- if ((p = strchr(buff, '\r')) != NULL)
- *p = '\0';
- if ((p = strchr(buff, '\n')) != NULL)
- *p = '\0';
- if (atoi(buff) != NNTP_GOODBYE_ACK_VAL)
- die("server did not reply to quit properly: %s", buff);
- fclose(FromServer);
- fclose(ToServer);
- exit(x);
-}
-
-
-/*
-** Failure handler, called by die. Calls QuitServer to cleanly shut down the
-** connection with the remote server before exiting.
-*/
-static int
-fatal_cleanup(void)
-{
- /* Don't recurse. */
- message_fatal_cleanup = NULL;
-
- /* QuitServer does all the work. */
- QuitServer(1);
- return 1;
-}
-
-
-/*
-** Flush a stdio FILE; exit if there are any errors.
-*/
-static void
-SafeFlush(FILE *F)
-{
- if (FLUSH_ERROR(F))
- sysdie("cannot send text to server");
-}
-
-
-/*
-** Trim trailing spaces, return pointer to first non-space char.
-*/
-static char *
-TrimSpaces(char *p)
-{
- char *start;
-
- for (start = p; ISWHITE(*start); start++)
- continue;
- for (p = start + strlen(start); p > start && CTYPE(isspace, p[-1]); )
- *--p = '\0';
- return start;
-}
-
-
-/*
-** Mark the end of the header starting at p, and return a pointer
-** to the start of the next one. Handles continuations.
-*/
-static char *
-NextHeader(char *p)
-{
- for ( ; ; p++) {
- if ((p = strchr(p, '\n')) == NULL)
- die("article is all headers");
- if (!ISWHITE(p[1])) {
- *p = '\0';
- return p + 1;
- }
- }
-}
-
-
-/*
-** Strip any headers off the article and dump them into the table.
-*/
-static char *
-StripOffHeaders(char *article)
-{
- char *p;
- char *q;
- HEADER *hp;
- char c;
- int i;
-
- /* Set up the other headers list. */
- OtherSize = HEADER_DELTA;
- OtherHeaders = xmalloc(OtherSize * sizeof(char *));
- OtherCount = 0;
-
- /* Scan through buffer, a header at a time. */
- for (i = 0, p = article; ; i++) {
-
- if ((q = strchr(p, ':')) == NULL)
- die("no colon in header line \"%.30s...\"", p);
- if (q[1] == '\n' && !ISWHITE(q[2])) {
- /* Empty header; ignore this one, get next line. */
- p = NextHeader(p);
- if (*p == '\n')
- break;
- }
-
- if (q[1] != '\0' && !ISWHITE(q[1])) {
- if ((q = strchr(q, '\n')) != NULL)
- *q = '\0';
- die("no space after colon in \"%.30s...\"", p);
- }
-
- /* See if it's a known header. */
- c = CTYPE(islower, *p) ? toupper(*p) : *p;
- for (hp = Table; hp < ARRAY_END(Table); hp++)
- if (c == hp->Name[0]
- && p[hp->Size] == ':'
- && ISWHITE(p[hp->Size + 1])
- && strncasecmp(p, hp->Name, hp->Size) == 0) {
- if (hp->Type == HTobs)
- die("obsolete header: %s", hp->Name);
- if (hp->Value)
- die("duplicate header: %s", hp->Name);
- for (q = &p[hp->Size + 1]; ISWHITE(*q); q++)
- continue;
- hp->Value = q;
- break;
- }
-
- /* Too many headers? */
- if (++i > 5 * HEADER_DELTA)
- die("more than %d lines of header", i);
-
- /* No; add it to the set of other headers. */
- if (hp == ARRAY_END(Table)) {
- if (OtherCount >= OtherSize - 1) {
- OtherSize += HEADER_DELTA;
- OtherHeaders = xrealloc(OtherHeaders, OtherSize * sizeof(char *));
- }
- OtherHeaders[OtherCount++] = p;
- }
-
- /* Get start of next header; if it's a blank line, we hit the end. */
- p = NextHeader(p);
- if (*p == '\n')
- break;
- }
-
- return p + 1;
-}
-
-\f
-
-/*
-** See if the user is allowed to cancel the indicated message. Assumes
-** that the Sender or From line has already been filled in.
-*/
-static void
-CheckCancel(char *msgid, bool JustReturn)
-{
- char localfrom[SMBUF];
- char *p;
- char buff[BUFSIZ];
- char remotefrom[SMBUF];
-
- /* Ask the server for the article. */
- fprintf(ToServer, "head %s\r\n", msgid);
- SafeFlush(ToServer);
- if (fgets(buff, sizeof buff, FromServer) == NULL
- || atoi(buff) != NNTP_HEAD_FOLLOWS_VAL) {
- if (JustReturn)
- return;
- die("server has no such article");
- }
-
- /* Read the headers, looking for the From or Sender. */
- remotefrom[0] = '\0';
- while (fgets(buff, sizeof buff, FromServer) != NULL) {
- if ((p = strchr(buff, '\r')) != NULL)
- *p = '\0';
- if ((p = strchr(buff, '\n')) != NULL)
- *p = '\0';
- if (buff[0] == '.' && buff[1] == '\0')
- break;
- if (strncmp(buff, "Sender:", 7) == 0)
- strlcpy(remotefrom, TrimSpaces(&buff[7]), SMBUF);
- else if (remotefrom[0] == '\0' && strncmp(buff, "From:", 5) == 0)
- strlcpy(remotefrom, TrimSpaces(&buff[5]), SMBUF);
- }
- if (remotefrom[0] == '\0') {
- if (JustReturn)
- return;
- die("article is garbled");
- }
- HeaderCleanFrom(remotefrom);
-
- /* Get the local user. */
- strlcpy(localfrom, HDR(_sender) ? HDR(_sender) : HDR(_from), SMBUF);
- HeaderCleanFrom(localfrom);
-
- /* Is the right person cancelling? */
- if (strcasecmp(localfrom, remotefrom) != 0)
- die("article was posted by \"%s\" and you are \"%s\"", remotefrom,
- localfrom);
-}
-
-
-/*
-** See if the user is the news administrator.
-*/
-static bool
-AnAdministrator(char *name, gid_t group)
-{
- struct passwd *pwp;
- struct group *grp;
- char **mem;
- char *p;
-
- if (Revoked)
- return false;
-
- /* Find out who we are. */
- if ((pwp = getpwnam(NEWSUSER)) == NULL)
- /* Silent falure; clients might not have the group. */
- return false;
- if (getuid() == pwp->pw_uid)
- return true;
-
- /* See if the we're in the right group. */
- if ((grp = getgrnam(NEWSGRP)) == NULL || (mem = grp->gr_mem) == NULL)
- /* Silent falure; clients might not have the group. */
- return false;
- if (group == grp->gr_gid)
- return true;
- while ((p = *mem++) != NULL)
- if (strcmp(name, p) == 0)
- return true;
- return false;
-}
-
-
-/*
-** Check the control message, and see if it's legit.
-*/
-static void
-CheckControl(char *ctrl, struct passwd *pwp)
-{
- char *p;
- char *q;
- char save;
- char name[SMBUF];
-
- /* Snip off the first word. */
- for (p = ctrl; ISWHITE(*p); p++)
- continue;
- for (ctrl = p; *p && !ISWHITE(*p); p++)
- continue;
- if (p == ctrl)
- die("emtpy control message");
- save = *p;
- *p = '\0';
-
- if (strcmp(ctrl, "cancel") == 0) {
- for (q = p + 1; ISWHITE(*q); q++)
- continue;
- if (*q == '\0')
- die("message ID missing in cancel");
- if (!Spooling)
- CheckCancel(q, false);
- }
- else if (strcmp(ctrl, "checkgroups") == 0
- || strcmp(ctrl, "ihave") == 0
- || strcmp(ctrl, "sendme") == 0
- || strcmp(ctrl, "newgroup") == 0
- || strcmp(ctrl, "rmgroup") == 0
- || strcmp(ctrl, "sendsys") == 0
- || strcmp(ctrl, "senduuname") == 0
- || strcmp(ctrl, "version") == 0) {
- strlcpy(name, pwp->pw_name, SMBUF);
- if (!AnAdministrator(name, pwp->pw_gid))
- die("ask your news administrator to do the %s for you", ctrl);
- }
- else {
- die("%s is not a valid control message", ctrl);
- }
- *p = save;
-}
-
-\f
-
-/*
-** Parse the GECOS field to get the user's full name. This comes Sendmail's
-** buildfname routine. Ignore leading stuff like "23-" "stuff]-" or
-** "stuff -" as well as trailing whitespace, or anything that comes after
-** a comma, semicolon, or in parentheses. This seems to strip off most of
-** the UCB or ATT stuff people fill out the entries with. Also, turn &
-** into the login name, with perhaps an initial capital. (Everyone seems
-** to hate that, but everyone also supports it.)
-*/
-static char *
-FormatUserName(struct passwd *pwp, char *node)
-{
- char outbuff[SMBUF];
- char *buff;
- char *out;
- char *p;
- int left;
-
-#if !defined(DONT_MUNGE_GETENV)
- memset(outbuff, 0, SMBUF);
- if ((p = getenv("NAME")) != NULL)
- strlcpy(outbuff, p, SMBUF);
- if (strlen(outbuff) == 0) {
-#endif /* !defined(DONT_MUNGE_GETENV) */
-
-
-#ifndef DO_MUNGE_GECOS
- strlcpy(outbuff, pwp->pw_gecos, SMBUF);
-#else
- /* Be very careful here. If we're not, we can potentially overflow our
- * buffer. Remember that on some Unix systems, the content of the GECOS
- * field is under (untrusted) user control and we could be setgid. */
- p = pwp->pw_gecos;
- left = SMBUF - 1;
- if (*p == '*')
- p++;
- for (out = outbuff; *p && !GECOSTERM(*p) && left; p++) {
- if (*p == '&') {
- strncpy(out, pwp->pw_name, left);
- if (CTYPE(islower, *out)
- && (out == outbuff || !CTYPE(isalpha, out[-1])))
- *out = toupper(*out);
- while (*out) {
- out++;
- left--;
- }
- }
- else if (*p == '-'
- && p > pwp->pw_gecos
- && (CTYPE(isdigit, p[-1]) || CTYPE(isspace, p[-1])
- || p[-1] == ']')) {
- out = outbuff;
- left = SMBUF - 1;
- }
- else {
- *out++ = *p;
- left--;
- }
- }
- *out = '\0';
-#endif /* DO_MUNGE_GECOS */
-
-#if !defined(DONT_MUNGE_GETENV)
- }
-#endif /* !defined(DONT_MUNGE_GETENV) */
-
- out = TrimSpaces(outbuff);
- if (out[0])
- buff = concat(pwp->pw_name, "@", node, " (", out, ")", (char *) 0);
- else
- buff = concat(pwp->pw_name, "@", node, (char *) 0);
- return buff;
-}
-
-
-/*
-** Check the Distribution header, and exit on error.
-*/
-static void CheckDistribution(char *p)
-{
- static char SEPS[] = " \t,";
- const char * const *dp;
-
- if ((p = strtok(p, SEPS)) == NULL)
- die("cannot parse Distribution header");
- do {
- for (dp = BadDistribs; *dp; dp++)
- if (uwildmat(p, *dp))
- die("illegal distribution %s", p);
- } while ((p = strtok((char *)NULL, SEPS)) != NULL);
-}
-
-
-/*
-** Process all the headers. FYI, they're done in RFC-order.
-*/
-static void
-ProcessHeaders(bool AddOrg, int linecount, struct passwd *pwp)
-{
- static char PATHFLUFF[] = PATHMASTER;
- HEADER *hp;
- char *p;
- TIMEINFO Now;
- char buff[SMBUF];
- char from[SMBUF];
-
- /* Do some preliminary fix-ups. */
- for (hp = Table; hp < ARRAY_END(Table); hp++) {
- if (!hp->CanSet && hp->Value)
- die("cannot set system header %s", hp->Name);
- if (hp->Value) {
- hp->Value = TrimSpaces(hp->Value);
- if (*hp->Value == '\0')
- hp->Value = NULL;
- }
- }
-
- /* Set From or Sender. */
- if ((p = innconf->fromhost) == NULL)
- sysdie("cannot get hostname");
- if (HDR(_from) == NULL)
- HDR(_from) = FormatUserName(pwp, p);
- else {
- if (strlen(pwp->pw_name) + strlen(p) + 2 > sizeof(buff))
- die("username and host are too long");
- sprintf(buff, "%s@%s", pwp->pw_name, p);
- strlcpy(from, HDR(_from), SMBUF);
- HeaderCleanFrom(from);
- if (strcmp(from, buff) != 0)
- HDR(_sender) = xstrdup(buff);
- }
-
- if (HDR(_date) == NULL) {
- /* Set Date. */
- if (!makedate(-1, true, buff, sizeof(buff)))
- die("cannot generate Date header");
- HDR(_date) = xstrdup(buff);
- }
-
- /* Newsgroups are checked later. */
-
- /* Set Subject; Control overrides the subject. */
- if (HDR(_control)) {
- CheckControl(HDR(_control), pwp);
- }
- else {
- p = HDR(_subject);
- if (p == NULL)
- die("required Subject header is missing or empty");
- else if (HDR(_alsocontrol))
- CheckControl(HDR(_alsocontrol), pwp);
-#if 0
- if (strncmp(p, "Re: ", 4) == 0 && HDR(_references) == NULL)
- die("article subject begins with \"Re: \" but has no references");
-#endif /* 0 */
- }
-
- /* Set Message-ID */
- if (HDR(_messageid) == NULL) {
- if ((p = GenerateMessageID(innconf->domain)) == NULL)
- die("cannot generate Message-ID header");
- HDR(_messageid) = xstrdup(p);
- }
- else if ((p = strchr(HDR(_messageid), '@')) == NULL
- || strchr(++p, '@') != NULL) {
- die("message ID must have exactly one @");
- }
-
- /* Set Path */
- if (HDR(_path) == NULL) {
-#if defined(DO_INEWS_PATH)
- if ((p = innconf->pathhost) != NULL) {
- if (*p)
- HDR(_path) = concat(Exclusions, p, "!", PATHFLUFF, (char *) 0);
- else
- HDR(_path) = concat(Exclusions, PATHFLUFF, (char *) 0);
- }
- else if (innconf->server != NULL) {
- if ((p = GetFQDN(innconf->domain)) == NULL)
- sysdie("cannot get hostname");
- HDR(_path) = concat(Exclusions, p, "!", PATHFLUFF, (char *) 0);
- }
- else {
- HDR(_path) = concat(Exclusions, PATHFLUFF, (char *) 0);
- }
-#else
- HDR(_path) = concat(Exclusions, PATHFLUFF, (char *) 0);
-#endif /* defined(DO_INEWS_PATH) */
- }
-
- /* Reply-To; left alone. */
- /* Sender; set above. */
- /* Followup-To; checked with Newsgroups. */
-
- /* Check Expires. */
- if (GetTimeInfo(&Now) < 0)
- sysdie("cannot get the time");
- if (HDR(_expires) && parsedate(HDR(_expires), &Now) == -1)
- die("cannot parse \"%s\" as an expiration date", HDR(_expires));
-
- /* References; left alone. */
- /* Control; checked above. */
-
- /* Distribution. */
- if ((p = HDR(_distribution)) != NULL) {
- p = xstrdup(p);
- CheckDistribution(p);
- free(p);
- }
-
- /* Set Organization. */
- if (AddOrg
- && HDR(_organization) == NULL
- && (p = innconf->organization) != NULL) {
- HDR(_organization) = xstrdup(p);
- }
-
- /* Keywords; left alone. */
- /* Summary; left alone. */
- /* Approved; left alone. */
-
- /* Set Lines */
- sprintf(buff, "%d", linecount);
- HDR(_lines) = xstrdup(buff);
-
- /* Check Supersedes. */
- if (HDR(_supersedes))
- CheckCancel(HDR(_supersedes), true);
-
- /* Now make sure everything is there. */
- for (hp = Table; hp < ARRAY_END(Table); hp++)
- if (hp->Type == HTreq && hp->Value == NULL)
- die("required header %s is missing or empty", hp->Name);
-}
-
-
-/*
-** Try to append $HOME/.signature to the article. When in doubt, exit
-** out in order to avoid postings like "Sorry, I forgot my .signature
-** -- here's the article again."
-*/
-static char *
-AppendSignature(bool UseMalloc, char *article, char *homedir, int *linesp)
-{
- static char NOSIG[] = "Can't add your .signature (%s), article not posted";
- int i;
- int length;
- size_t artsize;
- char *p;
- char buff[BUFSIZ];
- FILE *F;
-
- /* Open the file. */
- *linesp = 0;
- if (strlen(homedir) > sizeof(buff) - 14)
- die("home directory path too long");
- sprintf(buff, "%s/.signature", homedir);
- if ((F = fopen(buff, "r")) == NULL) {
- if (errno == ENOENT)
- return article;
- fprintf(stderr, NOSIG, strerror(errno));
- QuitServer(1);
- }
-
- /* Read it in. */
- length = fread(buff, 1, sizeof buff - 2, F);
- i = feof(F);
- fclose(F);
- if (length == 0)
- die("signature file is empty");
- if (length < 0)
- sysdie("cannot read signature file");
- if (length == sizeof buff - 2 && !i)
- die("signature is too large");
-
- /* Make sure the buffer ends with \n\0. */
- if (buff[length - 1] != '\n')
- buff[length++] = '\n';
- buff[length] = '\0';
-
- /* Count the lines. */
- for (i = 0, p = buff; (p = strchr(p, '\n')) != NULL; p++)
- if (++i > SIG_MAXLINES)
- die("signature has too many lines");
- *linesp = 1 + i;
-
- /* Grow the article to have the signature. */
- i = strlen(article);
- artsize = i + sizeof(SIGSEP) - 1 + length + 1;
- if (UseMalloc) {
- p = xmalloc(artsize);
- strlcpy(p, article, artsize);
- article = p;
- }
- else
- article = xrealloc(article, artsize);
- strlcat(article, SIGSEP, artsize);
- strlcat(article, buff, artsize);
- return article;
-}
-
-
-/*
-** See if the user has more included text than new text. Simple-minded, but
-** reasonably effective for catching neophyte's mistakes. A line starting
-** with > is included text. Decrement the count on lines starting with <
-** so that we don't reject diff(1) output.
-*/
-static void
-CheckIncludedText(char *p, int lines)
-{
- int i;
-
- for (i = 0; ; p++) {
- switch (*p) {
- case '>':
- i++;
- break;
- case '|':
- i++;
- break;
- case ':':
- i++;
- break;
- case '<':
- i--;
- break;
- }
- if ((p = strchr(p, '\n')) == NULL)
- break;
- }
- if ((i * 2 > lines) && (lines > 40))
- die("more included text than new text");
-}
-
-\f
-
-/*
-** Read stdin into a string and return it. Can't use ReadInDescriptor
-** since that will fail if stdin is a tty.
-*/
-static char *
-ReadStdin(void)
-{
- int size;
- char *p;
- char *article;
- char *end;
- int i;
-
- size = BUFSIZ;
- article = xmalloc(size);
- end = &article[size - 3];
- for (p = article; (i = getchar()) != EOF; *p++ = (char)i)
- if (p == end) {
- article = xrealloc(article, size + BUFSIZ);
- p = &article[size - 3];
- size += BUFSIZ;
- end = &article[size - 3];
- }
-
- /* Force a \n terminator. */
- if (p > article && p[-1] != '\n')
- *p++ = '\n';
- *p = '\0';
- return article;
-}
-
-\f
-
-/*
-** Offer the article to the server, return its reply.
-*/
-static int
-OfferArticle(char *buff, bool Authorized)
-{
- fprintf(ToServer, "post\r\n");
- SafeFlush(ToServer);
- if (fgets(buff, HEADER_STRLEN, FromServer) == NULL)
- sysdie(Authorized ? "Can't offer article to server (authorized)"
- : "Can't offer article to server");
- return atoi(buff);
-}
-
-
-/*
-** Spool article to temp file.
-*/
-static void
-Spoolit(char *article, size_t Length, char *deadfile)
-{
- HEADER *hp;
- FILE *F;
- int i;
-
- /* Try to write to the deadfile. */
- if (deadfile == NULL)
- return;
- F = xfopena(deadfile);
- if (F == NULL)
- sysdie("cannot create spool file");
-
- /* Write the headers and a blank line. */
- for (hp = Table; hp < ARRAY_END(Table); hp++)
- if (hp->Value)
- fprintf(F, "%s: %s\n", hp->Name, hp->Value);
- for (i = 0; i < OtherCount; i++)
- fprintf(F, "%s\n", OtherHeaders[i]);
- fprintf(F, "\n");
- if (FLUSH_ERROR(F))
- sysdie("cannot write headers");
-
- /* Write the article and exit. */
- if (fwrite(article, 1, Length, F) != Length)
- sysdie("cannot write article");
- if (FLUSH_ERROR(F))
- sysdie("cannot write article");
- if (fclose(F) == EOF)
- sysdie("cannot close spool file");
-}
-
-
-/*
-** Print usage message and exit.
-*/
-static void
-Usage(void)
-{
- fprintf(stderr, "Usage: inews [-D] [-h] [header_flags] [article]\n");
- /* Don't call QuitServer here -- connection isn't open yet. */
- exit(1);
-}
-
-
-int
-main(int ac, char *av[])
-{
- static char NOCONNECT[] = "cannot connect to server";
- int i;
- char *p;
- HEADER *hp;
- int j;
- int port;
- int Mode;
- int SigLines;
- struct passwd *pwp;
- char *article;
- char *deadfile;
- char buff[HEADER_STRLEN];
- char SpoolMessage[HEADER_STRLEN];
- bool DoSignature;
- bool AddOrg;
- size_t Length;
- uid_t uid;
-
- /* First thing, set up logging and our identity. */
- message_program_name = "inews";
-
- /* Find out who we are. */
- uid = geteuid();
- if (uid == (uid_t) -1)
- sysdie("cannot get your user ID");
- if ((pwp = getpwuid(uid)) == NULL)
- sysdie("cannot get your passwd entry");
-
- /* Set defaults. */
- Mode = '\0';
- Dump = false;
- DoSignature = true;
- AddOrg = true;
- port = 0;
-
- if (!innconf_read(NULL))
- exit(1);
-
- umask(NEWSUMASK);
-
- /* Parse JCL. */
- while ((i = getopt(ac, av, "DNAVWORShx:a:c:d:e:f:n:p:r:t:F:o:w:")) != EOF)
- switch (i) {
- default:
- Usage();
- /* NOTREACHED */
- case 'D':
- case 'N':
- Dump = true;
- break;
- case 'A':
- case 'V':
- case 'W':
- /* Ignore C News options. */
- break;
- case 'O':
- AddOrg = false;
- break;
- case 'R':
- Revoked = true;
- break;
- case 'S':
- DoSignature = false;
- break;
- case 'h':
- Mode = i;
- break;
- case 'x':
- Exclusions = concat(optarg, "!", (char *) 0);
- break;
- case 'p':
- port = atoi(optarg);
- break;
- /* Header lines that can be specified on the command line. */
- case 'a': HDR(_approved) = optarg; break;
- case 'c': HDR(_control) = optarg; break;
- case 'd': HDR(_distribution) = optarg; break;
- case 'e': HDR(_expires) = optarg; break;
- case 'f': HDR(_from) = optarg; break;
- case 'n': HDR(_newsgroups) = optarg; break;
- case 'r': HDR(_replyto) = optarg; break;
- case 't': HDR(_subject) = optarg; break;
- case 'F': HDR(_references) = optarg; break;
- case 'o': HDR(_organization) = optarg; break;
- case 'w': HDR(_followupto) = optarg; break;
- }
- ac -= optind;
- av += optind;
-
- /* Parse positional arguments; at most one, the input file. */
- switch (ac) {
- default:
- Usage();
- /* NOTREACHED */
- case 0:
- /* Read stdin. */
- article = ReadStdin();
- break;
- case 1:
- /* Read named file. */
- article = ReadInFile(av[0], (struct stat *)NULL);
- if (article == NULL)
- sysdie("cannot read input file");
- break;
- }
-
- if (port == 0)
- port = NNTP_PORT;
-
- /* Try to open a connection to the server. */
- if (NNTPremoteopen(port, &FromServer, &ToServer, buff) < 0) {
- Spooling = true;
- if ((p = strchr(buff, '\n')) != NULL)
- *p = '\0';
- if ((p = strchr(buff, '\r')) != NULL)
- *p = '\0';
- strcpy(SpoolMessage, buff[0] ? buff : NOCONNECT);
- deadfile = concatpath(pwp->pw_dir, "dead.article");
- }
- else {
- /* We now have an open server connection, so close it on failure. */
- message_fatal_cleanup = fatal_cleanup;
-
- /* See if we can post. */
- i = atoi(buff);
-
- /* Tell the server we're posting. */
- setbuf(FromServer, xmalloc(BUFSIZ));
- setbuf(ToServer, xmalloc(BUFSIZ));
- fprintf(ToServer, "mode reader\r\n");
- SafeFlush(ToServer);
- if (fgets(buff, HEADER_STRLEN, FromServer) == NULL)
- sysdie("cannot tell server we're reading");
- if ((j = atoi(buff)) != NNTP_BAD_COMMAND_VAL)
- i = j;
-
- if (i != NNTP_POSTOK_VAL) {
- /* We try to authenticate in case it is all the same possible
- * to post. */
- if (NNTPsendpassword((char *)NULL, FromServer, ToServer) < 0)
- die("you do not have permission to post");
- }
- deadfile = NULL;
- }
-
- /* Basic processing. */
- for (hp = Table; hp < ARRAY_END(Table); hp++)
- hp->Size = strlen(hp->Name);
- if (Mode == 'h')
- article = StripOffHeaders(article);
- for (i = 0, p = article; (p = strchr(p, '\n')) != NULL; i++, p++)
- continue;
- if (innconf->checkincludedtext)
- CheckIncludedText(article, i);
- if (DoSignature)
- article = AppendSignature(Mode == 'h', article, pwp->pw_dir, &SigLines);
- else
- SigLines = 0;
- ProcessHeaders(AddOrg, i + SigLines, pwp);
- Length = strlen(article);
- if ((innconf->localmaxartsize > 0)
- && (Length > (size_t)innconf->localmaxartsize))
- die("article is larger than local limit of %ld bytes",
- innconf->localmaxartsize);
-
- /* Do final checks. */
- if (i == 0 && HDR(_control) == NULL)
- die("article is empty");
- for (hp = Table; hp < ARRAY_END(Table); hp++)
- if (hp->Value && (int)strlen(hp->Value) + hp->Size > HEADER_STRLEN)
- die("%s header is too long", hp->Name);
- for (i = 0; i < OtherCount; i++)
- if ((int)strlen(OtherHeaders[i]) > HEADER_STRLEN)
- die("header too long (maximum length is %d): %.40s...",
- HEADER_STRLEN, OtherHeaders[i]);
-
- if (Dump) {
- /* Write the headers and a blank line. */
- for (hp = Table; hp < ARRAY_END(Table); hp++)
- if (hp->Value)
- printf("%s: %s\n", hp->Name, hp->Value);
- for (i = 0; i < OtherCount; i++)
- printf("%s\n", OtherHeaders[i]);
- printf("\n");
- if (FLUSH_ERROR(stdout))
- sysdie("cannot write headers");
-
- /* Write the article and exit. */
- if (fwrite(article, 1, Length, stdout) != Length)
- sysdie("cannot write article");
- SafeFlush(stdout);
- QuitServer(0);
- }
-
- if (Spooling) {
- warn("warning: %s", SpoolMessage);
- warn("article will be spooled");
- Spoolit(article, Length, deadfile);
- exit(0);
- }
-
- /* Article is prepared, offer it to the server. */
- i = OfferArticle(buff, false);
- if (i == NNTP_AUTH_NEEDED_VAL) {
- /* Posting not allowed, try to authorize. */
- if (NNTPsendpassword((char *)NULL, FromServer, ToServer) < 0)
- sysdie("authorization error");
- i = OfferArticle(buff, true);
- }
- if (i != NNTP_START_POST_VAL)
- die("server doesn't want the article: %s", buff);
-
- /* Write the headers, a blank line, then the article. */
- for (hp = Table; hp < ARRAY_END(Table); hp++)
- if (hp->Value)
- fprintf(ToServer, "%s: %s\r\n", hp->Name, hp->Value);
- for (i = 0; i < OtherCount; i++)
- fprintf(ToServer, "%s\r\n", OtherHeaders[i]);
- fprintf(ToServer, "\r\n");
- if (NNTPsendarticle(article, ToServer, true) < 0)
- sysdie("cannot send article to server");
- SafeFlush(ToServer);
-
- if (fgets(buff, sizeof buff, FromServer) == NULL)
- sysdie("no reply from server after sending the article");
- if ((p = strchr(buff, '\r')) != NULL)
- *p = '\0';
- if ((p = strchr(buff, '\n')) != NULL)
- *p = '\0';
- if (atoi(buff) != NNTP_POSTEDOK_VAL)
- die("cannot send article to server: %s", buff);
-
- /* Close up. */
- QuitServer(0);
- /* NOTREACHED */
- return 1;
-}