mj@dfv.rwth-aachen.de (Martin Junius) (01/28/91)
---- Cut Here and feed the following to sh ---- #!/bin/sh # This is part 03 of a multipart archive # ============= rfmail.c ============== if test -f 'rfmail.c' -a X"$1" != X"-c"; then echo 'x - skipping rfmail.c (File already exists)' else echo 'x - extracting rfmail.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'rfmail.c' && /*:ts=4*/ /***************************************************************************** X * FIDOGATE --- Gateway software UNIX <-> FIDO X * X * $Id: rfmail.c,v 2.18 91/01/05 13:07:34 mj Exp $ X * X * Read mail or news from standard input and put it into spool-directory. X * rfmail is called by the included version of rmail (mail receiver) X * and by relaynews from the CNews software. X * X * $Log: rfmail.c,v $ X * Revision 2.18 91/01/05 13:07:34 mj X * Improved generation of FIDO `^AREPLY', use `References:' or `In-Reply-To:' X * even if no `Message-ID:' present. Fixed function get_header(). X * X * Revision 2.17 90/12/09 18:36:48 mj X * Support for new `X' header in intermediate file taken from X-F[D[D[DRFC `X-Flags:'. X * Crash mail is now possible via `X-Flags: C'. X * X * Revision 2.16 90/12/09 17:35:13 mj X * Fixed a small bug in subject handling. X * X * Revision 2.15 90/12/02 21:22:25 mj X * Changed program header to mention both authors of the original X * software posted to alt.sources. X * X * Revision 2.14 90/12/01 17:49:49 mj X * Major new feature: rfmail is now able to handle news articles posted X * to several newsgroup. For each newsgroup in the Newsgroups header a X * seperate spool file for the corresponding FIDO EchoMail area is created. X * X * Revision 2.13 90/11/23 21:52:14 mj X * Changed date output to be compliant with FTS-0001: `DD MON YY HH:MM:SS', X * so QMail hopefully won't mess around with rfmail's dates any more. X * X * Revision 2.12 90/11/23 20:42:36 mj X * Output of ^AREPLY no longer depends on real versus fake net address. X * X * Revision 2.11 90/11/20 21:09:35 mj X * Added support for ^AINTL kludge. X * X * Revision 2.10 90/11/05 20:50:55 mj X * Changed my signature in all program headers. X * X * Revision 2.9 90/11/01 14:53:43 mj X * Take last message id from RFC822 Message-ID/References header. X * X * Revision 2.8 90/11/01 14:35:05 mj X * Enabled generation of ^AREPLY kludge. X * X * Revision 2.7 90/10/29 21:20:15 mj X * Enabled generation of ^AMSGID kludge. X * X * Revision 2.6 90/09/16 17:36:25 mj X * Some changes. (what?) X * X * Revision 2.5 90/09/15 14:22:54 mj X * Bug in newsgroup<->area conversion removed. X * X * Revision 2.4 90/09/13 21:58:38 mj X * Two major changes: X * Improved handling of user names in addresses and real names. X * Normally we use real name from header lines, but in case of X * special addressing via '%', e.g. "... % MAUS AC", we must use X * name in address, because "% ..." is not in real name. X * rfmail now handles news messages too. `-n' option implemented for X * this purpose. Look up newsgroup<->area conversion in Areas file X * and generate tear, origin, seen-by and path line. No more need X * for the rfnews program. X * X * Revision 2.3 90/09/08 18:49:03 mj X * Move strsaveline() to xalloc.c X * X * Revision 2.2 90/09/03 17:57:44 mj X * Some changes. Generation of ^AMSGID and ^AREPLY not active X * at this point. X * X * Revision 2.1 90/08/12 14:16:05 mj X * Added output of FIDO `^AMSGID:' and `^AREPLY:' kludges to the X * spool file for fpack. These lines are generated from RFC 822 X * header lines `Message-ID:' and `References:'. X * X * Revision 2.0 90/08/12 11:58:33 mj X * Rewrote much of the code of rfmail.c: Parsing of header is now X * much cleaner, changed the names of some functions, introduced X * other ones. Sending of mail to more than one user now really X * works. X * X * Revision 1.6 90/08/09 19:18:05 mj X * `Comment-To:' header line no longer makes it's way into the FIDO X * message. X * X * Revision 1.5 90/07/29 18:12:23 mj X * Place real net/node in message header for FIDO netmail. Also X * a `^AFMPT x' kludge is generated in this case. Recipient of X * our mail now gets real address from message, which should X * make replying much easier. X * X * Revision 1.4 90/07/07 18:23:53 mj X * Support for point addressing and the IFNA `^ATOPT x' kludge X * added to rfmail. X * X * Revision 1.3 90/07/01 13:46:04 mj X * Removed all calls to alloca(). All unsave malloc()'s without X * checking the returned pointer are now done via xmalloc(). X * Fixed a malloc() error in rmail. X * X * Revision 1.2 90/06/28 22:04:43 mj X * Much rework of the sources, no more hsu.h and other clean up. X * rmail improved, now handles special XENIX quirks. X * X * Revision 1.1 90/06/21 21:09:34 mj X * Everything seems to work, so this delta was made. X * X * Revision 1.0 90/06/19 18:33:19 mj X * Initial revision X * X * X ***************************************************************************** X * This version hacked and maintained by: X * _____ _____ X * | |___ | Martin Junius FIDO: 2:242/6.1 2:242/6.0 X * | | | | | | Republikplatz 3 DOMAIN: mju@dfv.rwth-aachen.de X * |_|_|_|_____| D-5100 Aachen Tel. (Voice) 0241-86931 X * X * Original version of these programs and files: X * X * Teemu Torma X * Heikki Suonsivu FIDO: 2:504/1 UUCP: ...!mcsun!santra!hsu X * X *****************************************************************************/ X #include "fidogate.h" X #include <varargs.h> #include <pwd.h> X X X #define PROGRAMNAME "rfmail $Revision: 2.18 $" X X X #define ORIGIN "Link to news/mail at hippo.uucp" X X extern struct passwd *getpwuid(); extern int getopt(); extern char *optarg; extern int optind; extern void exit(); extern char *spoolfile(), *basename(); extern char *regex(); extern time_t time(); extern FILE *popen(); X X /* Verbosity */ int verbose = INIT_VERBOSE; X /* Private mail (default) */ bool private = TRUE; X /* News-article */ int newsmode = FALSE; X /* Our net/node information */ Node this; X /* X * Global array to save message header X */ #define MAXHEADERS 50 X char *headers[MAXHEADERS]; X X X /* X * get_header() --- Get specific message header from headers[] X * X * Example: X * To: mj@hippo.uucp (Martin Junius) X * Use: X * get_header("To") -> "mj@hippo.uucp (Martin Junius)" X */ X char *get_header(name) char *name; { register int i, len; X X len = strlen(name); X for(i=0; i<MAXHEADERS && headers[i]; i++) X if(!strncmp(headers[i], name, len) && headers[i][len]==':') X return(headers[i] + strlen(name) + 2); /* +2 for ": " */ X return(NULL); } X X X /* X * Extract address/name from `From:', `Reply-To:' or `To:' field. X * Understood formats: X * full name <address> X * address (full name) X * address X */ X void get_address(field, address) char *field, *address; { register char *cp, *np; X X if ((cp = strchr(field, '(')) && strchr(cp + 1, ')')) { X /* format is 'address (full name)' */ X for (np = field; np < cp - 1; np++) X *address++ = *np; X *address = 0; X } X else if ((cp = strchr(field, '<')) && (np = strchr(cp + 1, '>'))) { X /* format is 'full name <address>' */ X cp++; X while (cp < np) X *address++ = *cp++; X *address = 0; X } X else X /* line contains only address */ X strcpy(address, field); } X void get_name(field, name) char *field; char *name; { register char *cp, *np; X X if ((cp = strchr(field, '(')) && (np = strchr(cp + 1, ')'))) { X /* format is 'address (full name)' */ X for(cp++; *cp && cp<np; cp++) X *name++ = *cp; X *name= 0; X } X else if ((cp = strchr(field, '<')) && (np = strchr(cp + 1, '>'))) { X /* format is 'full name <address>' */ X for(cp=field; *cp && *cp!='<' && *(cp+1)!='<'; cp++) X *name++ = *cp; X *name = 0; X } X else X /* line contains only address */ X *name = 0; } X X X /* X * Get return address for returning undeliverable mail to sender. X * Extract it from Reply-To: or From: fields. X */ X void get_return_address(address) char *address; { char buffer[BUFSIZ]; char *field; X X *address = 0; X X /* check is there is Reply-To: field */ X if(field = get_header("Reply-To")) { X get_address(field, address); X return; X } X X /* no Reply-To:, check for From: */ X if(field = get_header("From")) { X get_address(field, address); X return; X } X X /* not found, send it to root */ X strcpy(address, "root"); } X X X /* X * Open stream associated with programs standard input. Program is invoked X * with given argument list. Popen(3S) would invoke mailer thru sh(1), X * so this uses less memory and is faster. X */ X FILE *open_mailer(program, args, pid) char *program, **args; int *pid; { FILE *fp; int fd[2]; X X /* create pipe */ X if (pipe(fd) == -1) { X perror("rfmail: pipe"); X exit(EX_OSERR); X } X X switch (*pid = fork()) { X case -1: /* Error */ X perror("rfmail: fork failed"); X exit(EX_OSERR); X case 0: /* Child */ X (void) close(0); X if (dup(fd[0]) == 0) { X (void) close(fd[0]); X (void) close(fd[1]); X (void) execvp(program, args); X perror(program); X } X else X perror("rfmail: dup"); X exit(EX_OSERR); X default: /* Parent */ X (void) close(fd[0]); X if ((fp = fdopen(fd[1], "w")) == NULL) { X perror("rfmail: fdopen"); X exit(EX_OSERR); X } X } X return fp; } X X X /* X * In case of error send mail back to sender. X * First argument is file pointer for mail file, the following X * are format string and args for printf(). X */ X /**VARARGS**/ void sendback(va_alist) va_dcl { va_list args; FILE *mail; char *fmt; X #ifdef RETURN_FAILED_MAIL FILE *mailer; char to[128]; char *argv[3]; int pid; char buffer[BUFSIZ]; int i; #endif /* RETURN_FAILED_MAIL */ X X va_start(args); X mail = va_arg(args, FILE *); X fmt = va_arg(args, char *); X #ifdef RETURN_FAILED_MAIL X get_return_address(to); X log("Mail failed, return to %s", to); X X argv[0] = RMAIL; X argv[1] = to; X argv[2] = NULL; X mailer = open_mailer(RMAIL, argv, &pid); X if(!mailer) { X log("$Unable to invoke mailer for returned mail"); X va_end(args); X return; X } X X /* print correct header for mailer */ X fprintf(mailer, "From rfmail %s\n", date("%a %h %d %T 19%y", (long *) 0)); X fprintf(mailer, "Received: by %s (%s/%s)\n", X internode(this), PROGRAMNAME, this.name); X fprintf(mailer, "\tid AA%05d; %s\n", getpid(), X date("%a, %d %h %y %T %o (%z)", (long *) 0)); X fprintf(mailer, "Date: %s\n", date("%a, %d %h %y %T %o", (long *) 0)); X fprintf(mailer, "From: Gateway to FIDONet <%s@%s>\n", X "rfmail", internode(this)); X fprintf(mailer, "Subject: Returned mail: Unable to deliver mail to FIDONet\n"); X fprintf(mailer, "Message-Id: <%s.AA%05d@%s>\n", X date("%y%m%q%H%M", (long *) 0), getpid(), X internode(this)); X fprintf(mailer, "To: %s\n", to); X fprintf(mailer, "\n"); X fprintf(mailer, " ----- Transcript of session follows -----\n"); X vfprintf(mailer, fmt, args); X fprintf(mailer, "\n\n"); X fprintf(mailer, " ----- Unsent message follows -----\n"); X X /* Copy removed header line */ X for(i=0; i<MAXHEADERS && headers[i]; i++) X fprintf(mailer, "%s\n", headers[i]); X fprintf(mailer, "\n"); X X /* now copy the message to mailer */ X rewind(mail); X while (fgets(buffer, BUFSIZ, mail)) X fputs(buffer, mailer); X X /* mail is now sent, close mailer */ X fclose(mailer); X #else /**!RETURN_FAILED_MAIL**/ X mail = va_arg(args, FILE*); X fmt = va_arg(args, char *); X X vfprintf(stderr, fmt, args); #endif /* not RETURN_FAILED_MAIL */ X X va_end(args); } X X X /* Check that net/node exists and mail can be send to there (ie. it X is not down or in hold). Also be will replace name with sysop's name, X if mail is for sysop (note that alias will override this sysop-name). X Mail will be send back to sender, if this checking fails. */ X int valid_netnode(name, node, mail) X char *name; X Node node; X FILE *mail; { #ifdef NODELIST_SUPPORT X Node *entry; X X if (entry = node_entry(node)) X switch (entry->type) X { X case HOLD: X /* node is in hold */ X sendback(mail, "Node %s is in hold", ascnode(node)); X return EX_NOHOST; X case DOWN: X /* node is down */ X sendback(mail, "Node %s is currently down", ascnode(node)); X return EX_NOHOST; X default: X /* everything was fine */ X if (!strcmp(name, "sysop") || !strcmp(name, "Sysop")) X (void) strcpy(name, entry->sysop); X return EX_OK; X } X X /* we didn't find node */ X sendback(mail, "Node %s does not exist", ascnode(node)); X return EX_NOHOST; #else X return EX_OK; #endif } X X X /* X * Return sender of mail. Figure it out from From: field or from X * our uid if it's not administrative uid (e.g. uucp or nuucp). X * If not there, try $HOME/.fullname, then X * $HOME/.realname, then X * environment string NAME. X * If neither method works, return NULL. X * If $HOME is not defined, skip both .fullname and .realname X */ X char *mail_sender() { struct passwd *pwd; static char name[36]; char buffer[BUFSIZ]; register char *cp, *np; register int cnt; Node dummynode; FILE *fp; char *from; X X name[35] = 0; X buffer[0] = 0; X X if(from = get_header("From")) X strcpy(buffer, from); X X if(*buffer) { X debug(2, "Checking From: field"); X /* X * Parse the name out from From: field. there are basically X * two kinds of formats: 'User Name <address>' or X * 'address (User Name)'. We'll try to figure it out X * which format sender uses. X */ X if ((cp = strchr(buffer, '<')) && (np = strchr(cp, '>'))) { X /* Format is 'From: Name <address>' */ X for(np = buffer, X cnt = 0; np < cp - 1 && cnt < 35; X np++, cnt++) X name[cnt] = *np; X name[cnt] = 0; X debug(2, "Got name %s, fmt name <address>", name); X } X else if ((cp = strchr(buffer, '(')) && (np = strchr(cp, ')'))) { X /* Format is 'From: address (Name)' */ X for (cnt = 0, cp++; cp < np && cnt < 35; cp++, cnt++) X name[cnt] = *cp; X name[cnt] = 0; X debug(2, "Got name %s, fmt address (name)", name); X } X else { X debug(5, "Could no find realname in <> or ()"); X /* Try parse it with parse_address */ X if (parse_address(buffer, name, &dummynode)) { X (void) strncpy(name, buffer, 35); X name[35] = 0; X debug(2, "No format in From: line, name %s", name); X } X else { X name[35] = 0; X debug(2, "Name %s parsed from address", name); X } X } X return name; X } X X /* hmm.. no From: field in mail. let's try to figure this problem X out some other way. If our uid is some user's uid, we'll use X that uid to show user's name, otherwise we'll return NULL. */ X X if (getuid() >= USERUID && (pwd = getpwuid( (int) getuid()))) X { X /* There are two commonly used gecos-formats: So called USG X format used by System III and System V machines and BSD X format used by BSD machines. In USG format name is X sandwitched between '-' and '(' characters and in BSD X format name is first this in gecos-field up to first comma X in it. In many machines there's only name in gecos. */ X X if ((cp = strchr(pwd->pw_gecos, '-')) && (np = strchr(cp, '('))) X { X /* USG format 'stuff-name(stuff)' */ X for (cnt = 0, cp++; cnt < 35 && cp < np; cp++, cnt++) X name[cnt] = *cp; X name[cnt] = 0; X debug(3, "Got name from USG fmt, name %s", name); X } X else X if (cp = strchr(pwd->pw_gecos, ',')) X { X /* BSD format 'name,stuff...' */ X for (cp = buffer, cnt = 0; cnt < 35 && *cp != ','; cp++, cnt++) X name[cnt] = *cp; X name[cnt] = 0; X debug(3, "Got name from BSD format, name %s", name); X } X else X if (*pwd->pw_gecos) X { X /* non-empty gecos, assume that there's only name */ X (void) strncpy(name, pwd->pw_gecos, 35); X name[35] = 0; X debug(3, "No fmt in gecos, name %s", name); X } X else X { X /* Lazy administrator or user who want's to be anonymous X (or this is some administrative uid with no explanation X which)... We'll use only the username. */ X X (void) strncpy(name, pwd->pw_name, 35); X /* never heard over 35 char usernames???? I haven't but... */ X name[35] = 0; X debug(3, "No gecos, name = %s"); X } X return name; X } X X *buffer = 0; X if (cp = getenv("HOME")) X { X debug(5, "Try .fullname"); X if (fp = fopen(sprintfs("%s/%s", cp, ".fullname"), "r")) X { X if (!fgets(buffer, BUFSIZ, fp)) *buffer = 0; X fclose(fp); X strncpy(name, buffer, 35); X if (!strempty(name)) X { X strclean(name); X debug(2, "Got name %s from .fullname", name); X return name; X } X else X debug(1, "Empty name '%s' in .fullname", name); X } X debug(5, "Try .realname"); X if (fp = fopen(sprintfs("%s/%s", cp, ".realname"), "r")) X { X if (!fgets(buffer, BUFSIZ, fp)) *buffer = 0; X fclose(fp); X strncpy(name, buffer, 35); X if (!strempty(name)) X { X strclean(name); X debug(2, "Got name %s from .realname", name); X return name; X } X else X debug(1, "Empty name '%s' in .realname", name); X } X } X X if (cp = getenv("NAME")) X { X debug(5, "Name defined, use it"); X strncpy(name, buffer, 35); X if (!strempty(name)) X { X strclean(name); X debug(2, "Got name %s from environment NAME", name); X return name; X } X } X X debug(2, "No name, uid = %d", getuid()); X X return (char *) 0; } X X X /* Get net/node information from info. If zone, net, or point are missing, X they will be returned as -1TRUE is returned, if X everything went fine, otherwise FALSE. */ X bool getnode(info, node) X char *info; X Node *node; { X /* Extract zone information */ X if (strchr(info, ':')) X { X for (node->zone = 0; *info != ':'; info++) X if (isdigit(*info)) X node->zone = node->zone * 10 + *info - '0'; X else X { X debug(1, "Invalid character in zone '%c' %02x", *info, *info); X return FALSE; X } X info++; X } X else X node->zone = -1; X X /* Extract net information if net is present, otherwise X set net to -1. */ X X if (strchr(info, '/')) X { X for (node->net = 0; *info != '/'; info++) X if (isdigit(*info)) X node->net = node->net * 10 + *info - '0'; X else X { X debug(1, "Invalid character in net '%c' %02x", *info, *info); X return FALSE; X } X info++; X } X else X node->net = -1; X X /* Exract node information, set to -1 if empty. */ X X if (*info) X for (node->node = 0; *info && *info != '.'; info++) X if (isdigit(*info)) X node->node = node->node * 10 + *info - '0'; X else X { X debug(1, "Invalid characer in node '%c' %02x", *info, *info); X return FALSE; X } X else X node->node = -1; X X /* Exract point information, set to -1 if empty. */ X X if (*info) X for (node->point = 0; *info; info++) X if (isdigit(*info)) X node->point = node->point * 10 + *info - '0'; X else X { X debug(1, "Invalid characer in node '%c' %02x", *info, *info); X return FALSE; X } X else X node->point = -1; X X debug(2, "Got alias %s", ascnode(*node)); X return TRUE; } X /* Compare receiver and user in aliasfile. Each line in aliasfile contains X alias, optional net/node information separated by commas zero or X more times, white space(s) and rest of the line literally to whom X mail should be send. Net and node information is format 'net/node'. X if net is omitted then every net is counted, if node is omitted, X then all nodes in that net. If net is omitted, slash may be left off. X X E.g following are valid aliases: X X tot,504/ Teemu Torma X foo Foo Bar X sysop,504/1,504/9 System Operator */ X bool aliascmp(alias, to, node) char *alias, *to; Node *node; { char buffer[BUFSIZ]; char *cp; Node anode; X X while (*alias && *alias != ',' && !isspace(*alias) && *to) X if (*alias++ != *to++) X return FALSE; X X if (isspace(*alias)) /* match */ X return TRUE; X X if (*alias == ',') { X /* copy alias to buffer and terminate the it after first space */ X strcpy(buffer, alias + 1); X for (cp = buffer; *cp; cp++) X if (isspace(*cp)) { X *cp = 0; X break; X } X X /* get net/node information from buffer one at the time */ X for (cp = strtok(buffer, ","); cp; cp = strtok((char *) 0, ",")) { X debug(2, "Got node '%s'", cp); X if (getnode(cp, &anode)) { X if ((anode.zone == -1 || anode.zone == node->zone ) && X (anode.net == -1 || anode.net == node->net ) && X (anode.node == -1 || anode.node == node->node ) && X (anode.point == -1 || anode.point == node->point) ) X return TRUE; X } X else X return FALSE; X } X } X else X debug(1, "Invalid alias, %c is not ',' or white space", *alias); X X return FALSE; } X X X /* Return receiver's name. If name is aliased, return it, otherwise X return receiver's name. */ X char *receiver(to, node) char *to; Node *node; { static char name[36]; char buffer[BUFSIZ]; register int cnt; register char *cp; FILE *fp; int i, c, convert_flag; X X debug(3, "Name for alias checking: %s", to); X X if (fp = fopen(ALIAS, "r")) { X while (fgets(buffer, BUFSIZ, fp)) { X buffer[strlen(buffer) - 1] = 0; X if (*buffer != '#') X debug(3, "Checking for alias %s", buffer); X if (*buffer != '#' && aliascmp(buffer, to, node)) { X /* match, save the alias. */ X for (cp = buffer; *cp && !isspace(*cp); cp++) X /* skip alias itself */; X while (isspace(*cp)) X cp++; X if (*cp) { X for (cnt = 0; *cp && cnt < 35; cnt++, cp++) X name[cnt] = *cp; X name[cnt] = 0; X debug(2, "Got alias %s", name); X fclose(fp); X return name; X } X else X debug(1, "Missing alias"); X } X } X fclose(fp); X } X else X log("$Unable to open aliasfile %s", ALIAS); X X /* X * Alias not found. Return the the original receiver with all X * '_' characters replaced by space and all words capitalized. X */ X convert_flag = isupper(*to) ? -1 : 1; X for(i=0; *to && i<35; i++, to++) { X c = *to; X switch(c) { X case '_': X name[i] = ' '; X if(!convert_flag) X convert_flag = 1; X break; X case '%': X if(convert_flag != -1) X convert_flag = 2; X /**Fall thru**/ X default: X if(convert_flag > 0) { X name[i] = islower(c) ? toupper(c) : c; X if(convert_flag == 1) X convert_flag = 0; X } X else X name[i] = c; X break; X } X } X name[i] = 0; X X debug(2, "No alias, name %s", name); X return name; } X X X /* X * Return from field for FIDO message. X * Alias checking is done via receiver(). X */ X char *mail_receiver(address, node) char *address; Node *node; { char *cp; int found = 0; char name[36]; char realname[36]; char addr[128]; char *to; Node dummy; X X realname[0] = 0; X X if(address) { X /* X * Address is argument X */ X debug(2, "Address to parse: %s", address); X if(cp = parse_address(address, name, node)) { X log("Parse failed: %s", cp); X return NULL; X } X } X else { X /* X * Address is echo feed X */ X node->zone = MY_ZONE; X node->net = ECHOFEED_NET; X node->node = ECHOFEED_NODE; X node->point = 0; X strcpy(name, "All"); X } X X /* X * Try to look up a better real name in header fields X * X * Standard RFC822 header line X */ X if(to = get_header("To")) { X debug(2, "Checking To: field"); X get_address(to, addr); X if(!address || !strcmp(address, addr)) { X get_name(to, realname); X found = 1; X } X } X X /* X * User-defined header line for gateway software X * (can be patched into news reader) X */ X if(!found && (to = get_header("Comment-To"))) { X debug(2, "Checking Comment-To: field"); X get_address(to, addr); X if(!address || !strcmp(address, addr)) { X get_name(to, realname); X found = 1; X } X } X X /* X * Header generated by nn's `r' command X */ X if(!found && (to = get_header("Orig-To"))) { X debug(2, "Checking Orig-To: field"); X get_address(to, addr); X if(!address || !strcmp(address, addr)) { X get_name(to, realname); X found = 1; X } X } X X /* X * News message: get name from address taken out of header line X */ X if(found && !address) X parse_address(addr, name, &dummy); X X /* X * Use real name from header line, if no special addressing with '%' X */ X if(found && !strchr(name, '%') && *realname) X strcpy(name, realname); X X return receiver(name, node); } X X X /* X * Get date field for FIDO message. Look for `Date:' header or use X * current time. X */ X char *fido_date() { time_t timevar; struct tm *localtime(); struct tm *mtime; static char timebuf[20]; /* literal months */ static char *months[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", X (char *) 0, }; /* Literal weekdays */ static char *wkdays[] = { X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", (char *) 0, }; char *header_date; X X if(header_date = get_header("Date")) { X /* try to extract date and other information from it */ X debug(2, "Found date: '%s'", header_date); X timevar = getdate(header_date, NULL); X } X else X timevar = time(0); X X debug(2, "timevar %ld", timevar); X mtime = localtime(&timevar); X /* Save date in FTS-0001 format ! */ X sprintf(timebuf, "%02d %3s %02d %02d:%02d:%02d", X mtime->tm_mday, months[mtime->tm_mon], mtime->tm_year, X mtime->tm_hour, mtime->tm_min, mtime->tm_sec ); X debug(1, "Returning %s", timebuf); X return timebuf; } X X X char *estrtok(s, sep) char *s, *sep; { X char *p; X X if (p = strtok(s, sep)) X return p; X return ""; } X X X /* X * Output mail/news messages into spool file(s) for fpack X */ X #define NGROUPS 10 #define NFIDO 6 X #define FIDO_NODE 0 #define FIDO_TO 1 #define FIDO_FROM 2 #define FIDO_SUBJECT 3 #define FIDO_DATE 4 #define FIDO_FLAGS 5 X int send_mail(fp, to) FILE *fp; /* Temporary file message body */ char *to; /* Adress to send to (news = NULL) */ { char buffer[BUFSIZ]; char groups[BUFSIZ]; char *group[NGROUPS]; Node node; char *p; char *fido_header[NFIDO]; char *area; char *newsgroup; FILE *fpareas; int status; int i; X X if(to) X debug(1, "Sending mail to %s", to); X else X debug(1, "Sending news"); X X /* X * Get to/from/node/subject/date X */ X fido_header[FIDO_TO] = mail_receiver(to, &node); X if(!fido_header[FIDO_TO]) { X sendback(fp, "Illegal address %s", to); X return(EX_NOHOST); X } X X fido_header[FIDO_FROM] = mail_sender(); X if(!fido_header[FIDO_FROM]) X fido_header[FIDO_FROM] = "Gateway to FIDONET"; X X fido_header[FIDO_NODE] = ascnode(node); X X fido_header[FIDO_SUBJECT] = get_header("Subject"); X if(!fido_header[FIDO_SUBJECT]) X fido_header[FIDO_SUBJECT] = "Mail from Gateway to FIDONET"; X X fido_header[FIDO_DATE] = fido_date(); X X fido_header[FIDO_FLAGS] = get_header("X-Flags"); X if(!fido_header[FIDO_FLAGS]) X fido_header[FIDO_FLAGS] = ""; X X debug(2, "FIDO Node: %s", fido_header[FIDO_NODE]); X debug(2, "FIDO To: %s", fido_header[FIDO_TO]); X debug(2, "FIDO From: %s", fido_header[FIDO_FROM]); X debug(2, "FIDO Subject: %s", fido_header[FIDO_SUBJECT]); X debug(2, "FIDO Date: %s", fido_header[FIDO_DATE]); X debug(2, "FIDO Flags: %s", fido_header[FIDO_DATE]); X X if(newsmode) { X /* X * News message: get newsgroups and convert to FIDO areas X */ X p = get_header("Newsgroups"); X if(!p) { X sendback(fp, "No Newsgroups header in news message"); X return(EX_DATAERR); X } X strcpy(groups, p); X debug(3, "Newsgroups '%s'", groups); X X for(i=0, p=strtok(groups, ","); p && i<NGROUPS; p=strtok(NULL, ","), i++) X group[i] = p; X for(; i<NGROUPS; i++) X group[i] = NULL; X X fpareas = pfopen(LIBDIR, "Areas", "r"); X if(!fpareas) { X log("$Can't open areas file"); X sendback(fp, "Internal error: can't open file"); X return(EX_IOERR); X } X X for(i=0; i<NGROUPS && group[i]; i++) { X p = group[i]; X debug(3, "Look up newsgroup %s", p); X rewind(fpareas); X while(getcl(buffer, BUFSIZ, fpareas)) { X area = estrtok(buffer, " \t"); X newsgroup = estrtok(NULL, " \t"); X debug(4, "Checking newsgroup %s", newsgroup); X if(!strcmp(newsgroup, p)) { /* Found */ X debug(4, "Found area %s", area); X break; X } X area = NULL; X } X if(!area) { X log("No EchoMail area found for newsgroup '%s'", p); /* area = "JUNK"; /**/ X } X if(area) { X debug(3, "Sending message to area '%s'", area); X status = send_message(fp, node, fido_header, area); X if(status) X return(status); X } X } X } X else { X /* X * NetMail message X */ X return(send_message(fp, node, fido_header, NULL)); X } X X return EX_OK; } X X int send_message(fp, node, fido_header, area) FILE *fp; Node node; char *fido_header[]; char *area; { char buffer[BUFSIZ]; FILE *sf; char *sfile; char *header; X X /* X * Open spool file for output X */ X sf = fopen(sfile = spoolfile("M."), "w"); X if(!sf) { X log("$Unable to open spoolfile %s", sfile); X return(EX_CANTCREAT); X } X X /* set correct permissions for spoolfile and lock it */ X chmod(sfile, 0600); X lock(fileno(sf)); X X /* X * Header for fpack X * N destination node X * T destination name X * F from name X * S subject X * D date X * X flags (P=private, C=crash) X */ X fprintf(sf, "N %s\n", fido_header[FIDO_NODE]); X fprintf(sf, "T %s\n", fido_header[FIDO_TO]); X fprintf(sf, "F %s\n", fido_header[FIDO_FROM]); X fprintf(sf, "S %s\n", fido_header[FIDO_SUBJECT]); X fprintf(sf, "D %s\n", fido_header[FIDO_DATE]); X fprintf(sf, "X %s%s\n", private ? "P" : "", fido_header[FIDO_FLAGS]); X fprintf(sf, "\n"); X X X if(newsmode) { X /* X * Add AREA:... line for echo mail X */ X fprintf(sf, "AREA:%s\n", area); X } X else { X /* X * Add IFNA kludges for zone/point addressing X */ X if(private && (node.zone != MY_ZONE)) X fprintf(sf, "\001INTL %s %s\n", ascnoden(node), ascnoden(this)); X if(private && REAL_POINT) X fprintf(sf, "\001FMPT %d\n", REAL_POINT); X if(node.point) X fprintf(sf, "\001TOPT %d\n", node.point); X } X X /* X * Add kludge for MSGID / REPLY X */ X if(header = get_header("Message-ID")) { X if(print_msgid(sf, "MSGID", header)) X print_local_msgid(sf); X if(header = get_header("References")) X print_msgid(sf, "REPLY", header); X else if(header = get_header("In-Reply-To")) X print_msgid(sf, "REPLY", header); X } X else { X print_local_msgid(sf); X if(header = get_header("In-Reply-To")) X print_msgid(sf, "REPLY", header); X } X X /* X * Add some header lines X */ X if(header = get_header("From")) X fprintf(sf, "From: %s\n", header); X if(header = get_header("Reply-To")) X fprintf(sf, "Reply-To: %s\n", header); X if(header = get_header("To")) X fprintf(sf, "To: %s\n", header); X if(header = get_header("Cc")) X fprintf(sf, "Cc: %s\n", header); X if(header = get_header("Newsgroups")) X if(strchr(header, ',')) /* Posted to multiple groups */ X fprintf(sf, "Newsgroups: %s\n", header); X fprintf(sf, "\n"); X X /* X * Copy mail file X */ X debug(3, "Copying mail"); X rewind(fp); X while(fgets(buffer, BUFSIZ, fp)) X fputs(buffer, sf); X /* X * Done X */ X fclose(sf); X return EX_OK; } X X X /* X * Generate new FIDO kludge: `^AMSGID:' and `^AREPLY:' X */ X int print_msgid(fp, name, message_id) FILE *fp; char *name; char *message_id; { char *id, *host, *p; char *savep; Node node; long atol(); X X savep = message_id = strsave(message_id); X /* X * Format of message_id is "<identification@host.domain> ..." X * We want the the last one in the chain, which is the message id X * of the article replied to. X */ X message_id = strrchr(message_id, '<'); X if(!message_id) X goto error; X id = message_id+1; X host = strchr(message_id, '@'); X if(!host) X goto error; X *host++ = 0; X p = strchr(host, '>'); X if(!p) X goto error; X *p = 0; X X /* X * First let's test id. If it is entirely numeric, we can use X * it for the 32-bit number in ^AMSGID, else give up. X */ X for(p=id; *p; p++) X if(!isdigit(*p)) X goto error; X X /* X * Try to interprete host as a FIDO node. If this succeedes X * print out host as <zone>:<net>/<node>[.<point>], else X * print host as we got it from message_id. X */ X if(!parseinternode(host, &node)) X host = ascnode(node); X fprintf(fp, "\001%s: %s %08lx\n", name, host, atol(id)); X X free(savep); X return(0); X error: X free(savep); X return(-1); } X X X /* X * Generate local `^AMSGID:' if none is found in message header X */ X print_local_msgid(fp) FILE *fp; { long msgid; X X msgid = sequencer(MSGIDSEQ); X fprintf(fp, "\001MSGID: %s %08lx\n", ascnode(this), msgid); } X X X main(argc, argv) int argc; char *argv[]; { int cnt, c; FILE *mail; char buffer[BUFSIZ], tmpfile[L_tmpnam]; int status = EX_OK; char *error; Node *node, mynode; int header_count; X X newsmode = FALSE; X X while ((c = getopt(argc, argv, "npvV:")) != EOF) X switch (c) { X case 'n': X /* Set news-mode */ X newsmode = TRUE; X private = FALSE; X break; X case 'v': X /* set more verbosity */ X verbose++; X break; X case 'V': X verbose = atoi(optarg); X break; X case 'p': X /* this is not private message */ X private = FALSE; X break; X default: X fprintf(stderr, "%s\n\n", PROGRAMNAME); X fprintf(stderr, "usage: rfmail [-npv] [-V verbose_level] user ...\n\n"); X exit(EX_USAGE); X break; X } X X /* X * Create temp file for saving standard input and open it X */ X tmpnam(tmpfile); X if((mail = fopen(tmpfile, "w+")) == NULL) { X log("$Can not open %s for writing", tmpfile); X exit(EX_CANTCREAT); X } X /* protect mail file */ X chmod(tmpfile, 0600); X X /* X * Read headers from stdin X */ X header_count = 0; X while(fgets(buffer, BUFSIZ, stdin)) { X if(*buffer == '\n') /* End of header lines */ X break; X if(header_count < MAXHEADERS) X headers[header_count++] = strsaveline(buffer); X } X X /* X * Copy remainder of stdin to temporary file X */ X while(fgets(buffer, BUFSIZ, stdin)) X fputs(buffer, mail); X X /* X * If message is for echo mail (-n flag) then add X * tear, origin, seen-by and path line. X */ X if(newsmode) { X fprintf(mail, "\n--- %s\n", PROGRAMNAME); X fprintf(mail, " * Origin: %s (%d:%d/%d.%d)\n", ORIGIN, X REAL_ZONE, REAL_NET, REAL_NODE, REAL_POINT); X fprintf(mail, "SEEN-BY: %d/%d ", MY_NET, MY_NODE); X if(ECHOFEED_NET != MY_NET) X fprintf(mail, "%d/", ECHOFEED_NET); X fprintf(mail,"%d\n", ECHOFEED_NODE); X fprintf(mail, "\001PATH: %d/%d\n", MY_NET, MY_NODE); X } X #ifdef NODELIST_SUPPORT X /* update nodelist-index if needed */ X if (error = update_index()) { X /* there was error while updating nodelist-index */ X if (*error == '$') X sendback(mail, "%s: %s", error + 1, strerror(errno)); X else X sendback(mail, "%s", error); X exit(EX_SOFTWARE); X } #endif X X mynode.zone = REAL_ZONE; X mynode.net = REAL_NET; X mynode.node = REAL_NODE; X mynode.point = REAL_POINT; X strcpy(mynode.name, MY_NAME); X #ifdef NODELIST_SUPPORT X if ((node = node_entry(mynode)) == NULL) { X (void) fprintf(stderr, "Unable to this node from nodelist\n"); X log("No %s in nodelist", ascnode(mynode)); X exit(EX_SOFTWARE); X } #else X node = &mynode; #endif X this = *node; X X X rewind(mail); X X if(newsmode) X /* X * Send mail to echo feed for news messages X */ X status = send_mail(mail, NULL); X else X /* X * Send mail to addresses from command line args X */ X for(cnt = optind; cnt < argc; cnt++) X if((status = send_mail(mail, argv[cnt])) != EX_OK) X break; X X /* remove temporary file */ X fclose(mail); X unlink(tmpfile); X X exit(status); } SHAR_EOF chmod 0644 rfmail.c || echo 'restore of rfmail.c failed' Wc_c="`wc -c < 'rfmail.c'`" test 34750 -eq "$Wc_c" || echo 'rfmail.c: original size 34750, current size' "$Wc_c" fi true || echo 'restore of funcs.c failed' echo End of part 3, continue with part 4 exit 0 -- _____ _____ | |___ | Martin Junius FIDO: 2:242/6.1 2:242/6.0 | | | | | | Republikplatz 3 DOMAIN: mju@dfv.rwth-aachen.de |_|_|_|_____| D-5100 Aachen Tel. (Voice) 0241-86931