rsalz@uunet.uu.net (Rich Salz) (03/07/89)
Submitted-by: Heikki Suonsivu <hsu@santra.hut.fi> Posting-number: Volume 18, Issue 3 Archive-name: fnet/part02 #!/bin/sh # this is part 2 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file rfmail.c continued # CurArch=2 if test ! -r s2_seq_.tmp then echo "Please unpack part 1 first!" exit 1; fi ( read Scheck if test "$Scheck" != $CurArch then echo "Please unpack part $Scheck next!" exit 1; else exit 0; fi ) < s2_seq_.tmp || exit 1 echo "x - Continuing file rfmail.c" sed 's/^X//' << 'SHAR_EOF' >> rfmail.c X} X X/* Get return address from mail. Extract it from Reply-To: or From: fields. */ X Xvoid Xget_return_address(mail, address) X FILE *mail; X char *address; X{ X char buffer[BUFSIZ]; X X *address = 0; X X /* check is there is Reply-To: field */ X (void) rewind(mail); X while (fgets(buffer, BUFSIZ, mail) && *buffer != '\n') X if (!strncmp(buffer, "Reply-To: ", 10)) X { X get_address(buffer + 10, address); X return; X } X X /* no Reply-To:, check for From: */ X (void) rewind(mail); X while (fgets(buffer, BUFSIZ, mail) && *buffer != '\n') X if (!strncmp(buffer, "From: ", 6)) X { X get_address(buffer + 6, address); X return; X } X X /* not found, send it to uucp */ X (void) strcpy(address, "uucp"); X} X X/* Send mail back to sender. If Reply-To: of From:-field is present, X we'll use address specified in that, otherwise we will not return X mail (it also should be able to parse From_ fields). First argument X is file containing this mail, others are for vprintf(3S) to be used X as transcript. */ X X/* VARARGS */ Xvoid Xsendback(va_alist) X va_dcl X{ X va_list args; X FILE *mail; X X#ifdef RETURN_FAILED_MAIL X FILE *mailer; X char *fmt, buffer[BUFSIZ]; X char to[128]; X#endif /* RETURN_FAILED_MAIL */ X X va_start(args); X mail = va_arg(args, FILE *); X fmt = va_arg(args, char *); X X#ifdef RETURN_FAILED_MAIL X get_return_address(mail, to); X log("Mail failed, return to %s", to); X X /* generate shell command and open it */ X (void) sprintf(buffer, "exec %s %s", RMAIL, to); X if (mailer = popen(buffer, "w")) X { X /* print correct header for mailer */ X (void) fprintf(mailer, "From %d!MAILER-DAEMON %s remote from %d\n", X this.node, date("%a %h %d %T 19%y", (long *) 0), X this.net); X (void) fprintf(mailer, "Received: by %s (%s/%s)\n", X internode(this), PROGRAMNAME, this.name); X (void) fprintf(mailer, "\tid AA%05d; %s\n", getpid(), X date("%a, %d %h %y %T %o (%z)", (long *) 0)); X (void) fprintf(mailer, "Date: %s\n", date("%a, %d %h %y %T %o", X (long *) 0)); X (void) fprintf(mailer, "From: FidoNet Mail <%s@%s>\n", X "MAILER-DAEMON", internode(this)); X (void) fprintf(mailer, "Subject: Returned mail: %s\n", X "Unable to deliver mail to FidoNet"); X (void) fprintf(mailer, "Message-Id: <%s.AA%05d@%s>\n", X date("%y%m%q%H%M", (long *) 0), getpid(), X internode(this)); X (void) fprintf(mailer, "To: %s\n", to); X (void) fprintf(mailer, "\n"); X (void) fprintf(mailer, " ----- Transcript of session follows -----\n"); X (void) vfprintf(mailer, fmt, args); X (void) fprintf(mailer, "\n\n"); X (void) fprintf(mailer, " ----- Unsent message follows -----\n"); X X /* now copy the message to mailer */ X (void) rewind(mail); X while (fgets(buffer, BUFSIZ, mail)) X (void) fputs(buffer, mailer); X X /* mail is now sent, close mailer */ X (void) pclose(mailer); X } X else X log("$Unable to invoke mailer for returned mail"); X#else /* not RETURN_FAILED_MAIL */ X /* get dummy mail-file pointer */ X mail = va_arg(args, FILE*); X fmt = va_arg(args, char *); X X /* just print error to stderr, sendmail will return mail to sender X due to non-zero exit code. */ X (void) vfprintf(stderr, fmt, args); X#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 Xint Xvalid_netnode(name, node, mail) X char *name; X Node node; X FILE *mail; X{ 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; 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 Xchar * Xsender(mail, field) X FILE *mail; X char *field; X{ X struct passwd *pwd; X static char name[36]; X char buffer[BUFSIZ]; X register char *cp, *np; X register int cnt; X Node dummynode; X FILE *fp; X X name[35] = 0; X (void) rewind(mail); X while (fgets(buffer, BUFSIZ, mail)) X { X buffer[strlen(buffer) - 1] = 0; /* strip newline */ X if (!strncmp(buffer, field, strlen(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 { X /* Format is 'From: Name <address>' */ X for (np = buffer + strlen(field), 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 X if ((cp = strchr(buffer, '(')) && (np = strchr(cp, ')'))) X { 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 { X debug(5, "Could no find realname in <> or ()"); X /* Try parse it with parse_address */ X if (parse_address(buffer + strlen(field), name, &dummynode)) X { X (void) strncpy(name, buffer + strlen(field), 35); X name[35] = 0; X debug(2, "No format in %s, name %s", field, name); X } X else X { X name[35] = 0; X debug(2, "Name %s parsed from address", name); X } X } X return name; X } X } X X /* If not searching for sender, don't try to use uid */ X X if (strcmp(field, "From: ")) return NULL; 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 /* Found nothing reasonable. we could put there path, but it's quite X complicated to parse it and it's quite often more than 35 characters X and partial paths are not very informative. Reader can figure X it out by himself (or herself) and tell sender to use mailer... */ 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 Xbool Xgetnode(info, node) X char *info; X Node *node; X{ 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} X 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 Xbool Xaliascmp(alias, to, node) X char *alias, *to; X Node node; X{ X char buffer[BUFSIZ]; X char *cp; X 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 { X /* copy alias to buffer and terminate the it after first space */ X (void) strcpy(buffer, alias + 1); X for (cp = buffer; *cp; cp++) X if (isspace(*cp)) X { 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 { X debug(2, "Got node '%s'", cp); X if (getnode(cp, &anode)) X { 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 Xchar * Xreceiver(to, node) X char *to; X Node node; X{ X static char name[36]; X char buffer[BUFSIZ]; X register int cnt; X register char *cp; X FILE *fp; X X if (fp = fopen(ALIAS, "r")) X { X while (fgets(buffer, BUFSIZ, fp)) X { 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 { 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 { 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 return name; X } X else X debug(1, "Missing alias"); X } X } X (void) fclose(fp); X } X else X log("$Unable to open aliasfile %s", ALIAS); X X /* Alias not found. Return the the original receiver with all X _-characters replaced by space and all words calitalized. */ X X for (cp = NULL, cnt = 0; *to && cnt < 35; cnt++, cp = to++) X { X if (*to == '_') X name[cnt] = ' '; X else X if (!cp || *cp == '_' || *to == ' ') X name[cnt] = toupper(*to); X else X name[cnt] = tolower(*to); X } X name[cnt] = 0; X debug(2, "No alias, name %s", name); X return name; X} X X/* Return receiver's name, took Comment-To: field. If not found, X call receiver() and return what it returns */ X Xchar *newsreceiver(fp, to, node) X FILE *fp; X char *to; X Node node; X{ X char *cp; X X debug(2, "Checking To: field"); X if (cp = sender(fp, "To: ")) X { X debug(1, "Got recipent %s from To: field", cp); X strcpy(to, cp); X } X else X { X debug(2, "Checking Comment-To: field"); X if (cp = sender(fp, "Comment-To: ")) X { X debug(1, "Got recipent %s from Comment-To field", cp); X strcpy(to, cp); X } X } X X return receiver(to, node); X} X X/* Get date from mail. Look for Date: field, if present and time is in X correct format (same than ARPA-net mail uses), it will be used, otherwise X current time will be taken. */ X Xchar * Xlgetdate(fp) X FILE *fp; X{ X time_t timevar; X struct tm *localtime(); X struct tm *mtime; X static char timebuf[20]; X char buffer[BUFSIZ]; X /* literal months */ X static char *months[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", X (char *) 0, X }; X /* Literal weekdays */ X static char *wkdays[] = { X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", (char *) 0, X }; X X (void) rewind(fp); X while (fgets(buffer, BUFSIZ, fp)) X /* did we find Date: */ X if (!strncmp(buffer, "Date: ", 6)) X { X buffer[strlen(buffer) - 1] = '\0'; X /* try to extract date and other information from it */ X debug(2, "Found date: '%s'", buffer + 6); X#ifdef OLD_VERSION X if (sscanf(buffer + 6, "%3s, %2d %3s %2d %2d:%2d:%*2d %*s", X wday, &mday, month, &year, &hour, &min) == 6) X { X debug(2, "Correct date format"); X /* save date in SEAdog format */ X (void) sprintf(timebuf, "%3s %2d %3s %2d %02d:%02d", X wday, mday, month, year, hour, min); X return timebuf; X } X#else X timevar = getdate(buffer + 6, (struct timeb *) NULL); X debug(2, "timevar %ld", timevar); X mtime = localtime(&timevar); X /* Save date in SEAdog format */ X (void) sprintf(timebuf, "%3s %2d %3s %2d %02d:%02d", X wkdays[mtime->tm_wday], mtime->tm_mday, X months[mtime->tm_mon], mtime->tm_year, X mtime->tm_hour, mtime->tm_min); X debug(1, "Returning %s", timebuf); X return timebuf; X#endif X } X X (void) rewind(fp); X debug(2, "No Date: field in mail"); X X /* we did not found Date: field, let's use current time */ X return date("%a %d %h %y %T", (long *) NULL); X} X X/* Send mail from filepoiner fp to user to. To is in format of X net!node!receiver, where net and node are destnation and X receiver is receivers name in which blankos are replaces by X _-characters or alias. */ X Xint Xsendmail(fp, to) X FILE *fp; X char *to; X{ X char buffer[BUFSIZ], *cp; X FILE *sf; X char *sfile; X char hostname[10]; X Node node; X char name[36]; X int status, nodate, nosubject, nofrom; X X (void) gethostname(hostname, 10); X X /* get receiver and net/node */ X if (cp = parse_address(to, name, &node)) X { X sendback(fp, "Parse failed: %s", cp); X return EX_NOHOST; X } X X debug(1, "Sending mail to %s at %s", name, ascnode(node)); X X if ((status = valid_netnode(name, node, fp)) != EX_OK) X return status; X X debug(1, "Netnode ok, opening spool file"); X X if (sf = fopen(sfile = spoolfile("M."), "w")) X { X /* set correct permissions for spoolfile and lock it */ X (void) chmod(sfile, 0600); X (void) lock(fileno(sf)); X X debug(1, "chmod and lock ok"); X X /* save net and node information */ X (void) fprintf(sf, "N %s\n", ascnode(node)); X X /* save receiver. If using newsreceiver seem to cause X trouble, use normal receiver if not in news mode */ X X (void) fprintf(sf, "T %s\n", newsreceiver(fp, name, node)); X X /* save sender ... */ X debug(10, "Trying to find sender (From)"); X (void) fprintf(sf, "F %s\n", (cp = sender(fp, "From: ")) ? X cp : "Usenet"); X X /* try to find subject ... */ X (void) rewind(fp); X while (fgets(buffer, BUFSIZ, fp)) X if (!strncmp("Subject: ", buffer, 9)) X { X (void) fprintf(sf, "S %s", buffer + 9); X buffer[strlen(buffer) - 1] = 0; X debug(2, "Subject: %s", buffer + 9); X break; X } X X if (feof(fp)) X /* we didn't find subject.. */ X (void) fprintf(sf, "S Mail from Usenet\n"); X X /* save date */ X (void) fprintf(sf, "D %s\n", lgetdate(fp)); X X /* if not public, mark that message is private */ X if (!public) X (void) fprintf(sf, "P \n"); X X /* done with header information, now we'll copy the hole mail X after the header */ X X (void) putc('\n', sf); X X debug(2, "Copying mail"); X X /* Now copy mail, also add Received field to mail */ X (void) rewind(fp); X while (fgets(buffer, BUFSIZ, fp) && (!strncmp(buffer, "From ", 5) || X !strncmp(buffer, ">From ", 6) || X !strncmp(buffer, "AREA:", 5))) X (void) fputs(buffer, sf); X X (void) fprintf(sf, "Received: from %s%s by %s (%s%s/%s)\n", X hostname, DOMAIN, internode(this), "rfmail", X version, this.name); X (void) fprintf(sf, "\tid AA%05d; %s\n", getpid(), X date("%a, %d %h %y %T %o (%z)", (long *) NULL)); X X /* Copy header. Try to avoid duplicating headers already in X fidonet header. Only first occurence of header is processed, X others will be ignored. Header is considered to end at first X empty line. */ X X nodate = TRUE; X nosubject = TRUE; X nofrom = TRUE; X do { X X /* Date is in mail header, so skip header Date: */ X if (nodate) X if (!strncmp(buffer, "Date:", 5)) X { X nodate = FALSE; X continue; X } X X /* Print subject only if it doesnt fit in fidonet header correctly */ X if (nosubject) X if (!strncmp(buffer, "Subject:", 8)) X { X if (strlen(buffer + 8) > 72) X fputs(buffer, sf); X nosubject = FALSE; X continue; X } X X /* Print From field only if it doesn't fit in fidonet header */ X if (nofrom) X if (!strncmp(buffer, "From:", 5)) X { X if (strlen(buffer + 5) > 36) X fputs(buffer, sf); X nofrom = FALSE; X continue; X } X X fputs(buffer, sf); X } while (fgets(buffer, BUFSIZ, fp)); X X /* done. */ X (void) fclose(sf); X return EX_OK; X } X else X { X log("$Unable to open spoolfile"); X return EX_CANTCREAT; X } X /* NOTREACHED */ X} X X/*ARGSUSED*/ Xint Xmain(argc, argv, envp) X int argc; X char **argv, **envp; X{ X int cnt, c; X FILE *mail; X char buffer[BUFSIZ], tmpf[L_tmpnam]; X int status = EX_OK; X char *error; X Node *node, mynode; X X newsmode = FALSE; X while ((c = getopt(argc, argv, "npvV:")) != EOF) X switch (c) X { X case 'n': X /* Set news-mode */ X newsmode = TRUE; X break; X case 'v': X /* set more verbosity */ X verbose++; X break; X case 'V': X /* allow version on command line only if test version. */ X if (*version != '%' || version[1] != 'I' || version[2] != '%') X version = optarg; X /* NOTE: Comparison cannot be with strcmp, because SCCS would also X change that comparison string. */ X break; X case 'p': X /* this is not private message */ X public = True; X break; X default: X (void) fprintf(stderr, "See manual page.\n", *argv); X exit(EX_USAGE); X } X X /* create name of temporary mail-message */ X (void) strcpy(tmpf, P_tmpdir); X (void) strcat(tmpf, mktemp("rfm.XXXXXX")); X X /* open mail-temp */ X if (mail = fopen(tmpf, "w+")) X { X /* protect mail file */ X (void) chmod(tmpf, 0600); X X /* copy mail to temp-file */ X while (fgets(buffer, BUFSIZ, stdin)) X (void) fputs(buffer, mail); X X /* update nodelist-index if needed */ X if (error = update_index()) X { X /* there was error while updating nodelist-index */ X if (*error == '$') X sendback(mail, "%s: %s", error + 1, sys_errlist[errno]); X else X sendback(mail, "%s", error); X exit(EX_SOFTWARE); X } X X mynode.zone = MY_ZONE; X mynode.net = MY_NET; X mynode.node = MY_NODE; X mynode.point = MY_POINT; X if ((node = node_entry(mynode)) == NULL) X { X (void) fprintf(stderr, "Unable to this node from nodelist\n"); X log("No %s in nodelist", ascnode(mynode)); X exit(EX_SOFTWARE); X } X this = *node; X X X /* send mail to all user's */ X for (rewind(mail), cnt = optind; cnt < argc; rewind(mail), cnt++) X /* send mail */ X if ((status = sendmail(mail, argv[cnt])) != EX_OK) X break; X X /* remove temporary file */ X (void) fclose(mail); X (void) unlink(tmpf); X } X else X { X /* opening tmp-file failed, we can't even send mail back */ X log("$Can not open %s for writing", tmpf); X exit(EX_CANTCREAT); X } X X exit(status); X /*NOTREACHED*/ X} SHAR_EOF echo "File rfmail.c is complete" chmod 0644 rfmail.c || echo "restore of rfmail.c fails" echo "x - extracting funcs.c (Text)" sed 's/^X//' << 'SHAR_EOF' > funcs.c && X#ifndef lint Xstatic char *sccsid = "@(#)%M% %I% Teemu Torma %H%"; X#endif X X/* Miscancelleus functions, logging, sequence numberic etc. X X @(#)Copyright (c) 1987 by Teemu Torma X X Permission is given to distribute this program and alter this code as X needed to adapt it to forign systems provided that this header is X included and that the original author's name is preserved. */ X X/* LINTLIBRARY */ X X#include <stdio.h> X#include <string.h> X#include <ctype.h> X#include <varargs.h> X#include <unistd.h> X#include <sys/types.h> X#include <time.h> X#include <sys/utsname.h> X#include <errno.h> X#include "hsu.h" X#include "config.h" X#include "fnet.h" X#include "sysexits.h" X#include "nodelist.h" X#include "shuffle.h" X X#define labs(n) (((n) < 0l) ? (-(n)) : (n)) X Xextern void exit(), perror(); Xextern long atol(); Xextern time_t time(); Xextern int errno; Xextern char *sys_errlist[]; X XFILE *logfp = NULL; X X/* Lock file descriptor up to the end. If yur system doesn't have lockf() X (also known as locking()), or other region or file locking function X or system call, this should be done with lock-files. */ X Xint Xlock(fd) X int fd; X{ X#ifdef LOCK_LOCKF X return lockf(fd, F_LOCK, 0l); X#else X return locking(fd, F_LOCK, 0l); X#endif X} X X/* Unlock file descriptor up to the end. Routine which calls this should X first seek to original position. */ X Xint Xunlock(fd) X int fd; X{ X#ifdef LOCK_LOCKF X return lockf(fd, F_ULOCK, 0l); X#else X return locking(fd, F_ULOCK, 0l); X#endif X} X X/* Return ascii-date in specified format. If format string is null, return X date as date(1) returns it. Format is same than date(1) has, in addition X %z, which means timezone name. Clock is the time to convert, if NULL, X we'll use current time. */ X Xchar * Xdate(fmt, clock) X char *fmt; X time_t *clock; X{ X /* names for weekdays */ X static char *weekdays[] = { X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", X }; X X /* names for months */ X static char *months[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", X }; X X static char buffer[80]; X char *bp = buffer; X time_t _clock; X struct tm *tm; X X if (!clock) X _clock = time((long *) 0); X tm = localtime(clock ? clock : &_clock); X X /* if no format string, this is default */ X if (!fmt) X fmt = "%a %h %d %T %z 19%y"; X X for (*bp = 0; *fmt; fmt++) X switch (*fmt) X { X case '%': X switch (*++fmt) X { X /* newline */ X case 'n': X *bp++ = '\n'; X break; X /* tabulator */ X case 't': X *bp++ = '\t'; X break; X /* month number 1-12 */ X case 'm': X (void) sprintf(bp, "%02d", tm->tm_mon + 1); X while (*bp) X bp++; X break; X /* day of month 1-31 */ X case 'd': X (void) sprintf(bp, "%2d", tm->tm_mday); X while (*bp) X bp++; X break; X case 'q': X (void) sprintf(bp, "%02d", tm->tm_mday); X while (*bp) X bp++; X break; X /* year 00-99 */ X case 'y': X (void) sprintf(bp, "%02d", tm->tm_year); X while (*bp) X bp++; X break; X /* date in format YY/MM/DD */ X case 'D': X (void) sprintf(bp, "%02d/%02d/%02d", tm->tm_year, X tm->tm_mon + 1, tm->tm_mday); X while (*bp) X bp++; X break; X /* hour 0-23 */ X case 'H': X (void) sprintf(bp, "%02d", tm->tm_hour); X while (*bp) X bp++; X break; X /* minutes 0-59 */ X case 'M': X (void) sprintf(bp, "%02d", tm->tm_min); X while (*bp) X bp++; X break; X /* seconds 0-59 */ X case 'S': X (void) sprintf(bp, "%02d", tm->tm_sec); X while (*bp) X bp++; X break; X /* time in format HH:MM:SS */ X case 'T': X (void) sprintf(bp, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, X tm->tm_sec); X while (*bp) X bp++; X break; X /* day of year 1-356 */ X case 'j': X (void) sprintf(bp, "%03d", tm->tm_yday + 1); X while (*bp) X bp++; X break; X /* weekday 0-6 */ X case 'w': X (void) sprintf(bp, "%d", tm->tm_wday); X while (*bp) X bp++; X break; X /* name of weekday 'Mon', 'Tue', ... , 'Sun' */ X case 'a': X (void) strcpy(bp, weekdays[tm->tm_wday]); X while (*bp) X bp++; X break; X /* name of month 'Jan', 'Feb', ... , 'Dec' */ X case 'h': X (void) strcpy(bp, months[tm->tm_mon]); X while (*bp) X bp++; X break; X /* name of timezone, e.g. EST */ X case 'z': X (void) strcpy(bp, *tzname); X while (*bp) X bp++; X break; X /* numeric time zone, e.g. +0200 */ X case 'o': X (void) sprintf(bp, "%c%02ld%02ld", (timezone <= 0l) ? '+' : '-', X (labs(timezone) / (60l * 60l)), X (labs(timezone) % (60l * 60l))); X while (*bp) X bp++; X break; X case 'l': X /* military time zone, Z = UT, A = -1, M = -12, (J not used), X N = +1, Y = +12.. */ X *bp = (timezone == 0l) ? 'Z' : ((int) (labs(timezone) / X (60l * 60l)) + X ((timezone < 0l) ? 'M' : '@')); X if (timezone > 0l && *bp >= 'J') X (*bp)++; X *++bp = 0; X break; X default: X *bp++ = *fmt; X break; X } X break; X default: X *bp++ = *fmt; X break; X } X X *bp = 0; X return buffer; X} X X/* Log to logfile. If logfile is not open, open it. X X If first character in format string is '$', print also errno. If external X variable verbose is set, logging will be done also to stderr. */ X X/* VARARGS */ Xvoid Xlog(va_alist) X va_dcl X{ X va_list args; X char *fmt; X int eno = errno; X X va_start(args); X X fmt = va_arg(args, char *); X X if (!logfp) X if ((logfp = fopen(LOGFILE, "a")) == NULL) X { X perror("Cannot open log file"); X return; X } X X (void) fprintf(logfp, "%s: ", date("%a, %d %h %y %T %o", (long *) 0)); X (void) vfprintf(logfp, *fmt == '$' ? fmt + 1 : fmt, args); X if (*fmt == '$') X (void) fprintf(logfp, " errno = %d (%s)\n", eno, sys_errlist[eno]); X else X (void) fprintf(logfp, "\n"); X (void) fflush(logfp); X X /* if verbose is set, print also to stderr */ X if (verbose) X { X (void) vfprintf(stderr, fmt + (*fmt == '$'), args); X if (*fmt == '$') X (void) fprintf(stderr, " errno = %d (%s)\n", eno, X sys_errlist[eno]); X else X (void) fprintf(stderr, "\n"); X (void) fflush(stderr); X } X X va_end(args); X} X X/* Debug output. First argument should be number, rest are used arguments X for vfprintf(3S). If external variable verbose has equal or greater X value than first number, vfprintf(3S) will be used to print other X arguments to stderr. */ X X/* VARARGS */ Xvoid Xdebug(va_alist) X va_dcl X{ X va_list args; X char *fmt; X int debug_level; X X va_start(args); X X debug_level = va_arg(args, int); X fmt = va_arg(args, char *); X X if (debug_level <= verbose) X { X if (*fmt != '>' && *fmt != '<') X (void) fprintf(stderr, "\n%ld: ", time( (long *) NULL)); X (void) vfprintf(stderr, fmt, args); X } X X va_end(args); X} X X/* Get next job number. New sequemnt number will be taken from file X LIBDIR/seq, which is in ascii-format and new number will be saved X back there. */ X Xlong Xjob_number() X{ X char seqfile[128], buffer[14]; X FILE *fp; X long seqn = 0; X X (void) sprintf(seqfile, "%s/seq", LIBDIR); X if (fp = fopen(seqfile, "r+")) X { X (void) lock(fileno(fp)); X if (fgets(buffer, 14, fp)) X seqn = atol(buffer); X seqn++; X (void) rewind(fp); X (void) fprintf(fp, "%ld\n", seqn); X (void) unlock(fileno(fp)); X (void) fclose(fp); X } X else X { X log("$Can not open seq-file %s", seqfile); X exit(EX_OSFILE); X } X return seqn; X} X X/* General sequencer */ X Xlong Xsequencer(filename) X char *filename; X{ X char seqfile[128], buffer[14]; X FILE *fp; X long seqn = 0; X X (void) sprintf(seqfile, "%s", filename); X if ((fp = fopen(seqfile, "r+")) == NULL) X { X if (errno == ENOENT) X { X if ((fp = fopen(seqfile, "w+")) == NULL) X { X log("$Can not create seq-file %s", seqfile); X exit(EX_OSFILE); X } X fputs("1", fp); X fclose(fp); X if ((fp = fopen(seqfile, "r+")) == NULL) X { X log("$Can not open new seq-file %s", seqfile); X exit(EX_OSFILE); X } X } X else X { X log("$Can not open seq-file %s", seqfile); X exit(EX_OSFILE); X } X } X X (void) lock(fileno(fp)); X if (fgets(buffer, 14, fp)) X seqn = atol(buffer); X else X seqn = 0; /* This can theoretically fail */ X X seqn++; X (void) rewind(fp); X (void) fprintf(fp, "%ld\n", seqn); X (void) unlock(fileno(fp)); X (void) fclose(fp); X return seqn; X} X X/* Returns current last sequence number */ Xlong Xgetsequencer(filename) X char *filename; X{ X char seqfile[128], buffer[14]; X FILE *fp; X long seqn = 0; X X (void) sprintf(seqfile, "%s", filename); X if ((fp = fopen(seqfile, "r+")) == NULL) X { X if (errno == ENOENT) X { X if ((fp = fopen(seqfile, "w+")) == NULL) X { X log("$Can not create seq-file %s", seqfile); X exit(EX_OSFILE); X } X fputs("1", fp); X fclose(fp); X if ((fp = fopen(seqfile, "r+")) == NULL) X { X log("$Can not open new seq-file %s", seqfile); X exit(EX_OSFILE); X } X } X else X { X log("$Can not open seq-file %s", seqfile); X exit(EX_OSFILE); X } X } X X (void) lock(fileno(fp)); X if (fgets(buffer, 14, fp)) X seqn = atol(buffer); X else X seqn = 0; /* This can theoretically fail */ X X (void) unlock(fileno(fp)); X (void) fclose(fp); X return seqn; X} X X/* Get full pathname for spoolfile with new job number. File is in X spool directory and contains prefix followed by four digit X job number. */ X Xchar * Xspoolfile(prefix) X char *prefix; X{ X static char file[BUFLEN]; X X (void) sprintf(file, "%s/%s%08ld", SPOOL, prefix, job_number()); X return file; X} X X/* Return basename of s */ X Xchar * Xbasename(s) X register char *s; X{ X register char *p = s; X X while (*s) X if (*s++ == '/') X p = s; X return p; X} X X/* Open file with directory name and filename. */ X XFILE * Xpfopen(dir, name, mode) X char *dir, *name, *mode; X{ X char filename[128]; X X (void) strcpy(filename, dir); X (void) strcat(filename, "/"); X (void) strcat(filename, name); X X return fopen(filename, mode); X} X Xchar *baseit(n) X long n; X{ X static char tab[] = X "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; X int count = 0; X X SHUFFLEBUFFERS; X X while (n) X { X tcharp[count] = tab[n % strlen(tab)]; X n = n / strlen(tab); X count++; X } X X tcharp[count] = 0; X return tcharp; X} X X/* Create packet name for node given */ X Xsprintpacketname(s, node) X char *s; X Node node; X{ X sprintf(s, "%s%s.%s.%s", node.point ? X sprintfs("%s.", baseit( (long) node.point)) : "", X baseit( (long) node.node), baseit( (long) node.net), X baseit( (long) node.zone)); X} X X/* Create packet name for inbound xx.xx.xx.xx.num. If point X number is 0, don't include it. All numbers are in ~63-base to squeeze X them to as small space as possible. It could be more sensible solution X to make them directory trees but I would need more time for that. This X trick makes finding packets difficult. X */ X Xsprintipacketname(s, node) X char *s; X Node node; X{ X sprintf(s, "%s%s.%s.%s.%s", node.point ? X sprintfs("%s.", baseit( (long) node.point)) : "", X baseit( (long) node.node), baseit( (long) node.net), X baseit( (long) node.zone), baseit(sequencer(IPACKETSEQUENCE))); X} X X/* Get line from config file. If *-character is in first column, report X EOF, and otherwise return line with comments stripped. This causes X effect, that each section in configuration file looks like it's own X file. Arguments and return value are the same than with fgets(3S). */ X Xchar * Xgetcl(buffer, len, fp) X char *buffer; X int len; X FILE *fp; X{ X char *cp; X X while (fgets(buffer, len, fp)) X { X buffer[strlen(buffer) - 1] = 0; X if (*buffer == '*') X return (char *) NULL; X /* everything after #-sign is comment */ X if (cp = strchr(buffer, '#')) X *cp = 0; X /* if there's something left, return it */ X if (*buffer) X return buffer; X } X return (char *) NULL; X} X X/* Scan config file to specified section. This mechanism is not very X effective, but otherwise it would get too complicated. */ X Xvoid Xsection(number, config) X int number; X FILE *config; X{ X char buffer[BUFLEN]; X X (void) rewind(config); X while (--number) X while (getcl(buffer, BUFLEN, config)) X /* skip section */; X} X X/* Get header field from file. */ X X#define MAX_HEADER_LEN 256 X Xchar *mheader(fp, headername) X FILE *fp; X char *headername; X{ X static char header[MAX_HEADER_LEN]; X long position; X X position = ftell(fp); X X rewind(fp); X X /* Blank line terminates also, there shouldn't be any headers X after it any more */ X X while (fgets(header, MAX_HEADER_LEN, fp) && *header != '\n') X if (!strncmp(header, headername, strlen(headername))) X { X /* Remove \n at end */ X header[strlen(header) - 1] = 0; X fseek(fp, position, 0); X return header + strlen(headername); X } X X /* Not found, return empty string */ X X fseek(fp, position, 0); X return ""; X} X X X SHAR_EOF chmod 0644 funcs.c || echo "restore of funcs.c fails" echo "x - extracting rfnews.c (Text)" sed 's/^X//' << 'SHAR_EOF' > rfnews.c && X#ifndef lint Xstatic char *sccsid = "@(#)%M% %I% Teemu Torma %H%"; X#endif X X/* Spool Usenet news articles to fidonet. Currently there is no news-system X in fido, it's done by software after and before mail-slots. Echomail X is most popular one, and this program does about the same thing: X it sends news-articles as fido-mail containing suitable information X for echomail. Echomail relies on normal fido-mail, we send news thru X rfmail, because this mechanism requires nothing different from normal X mail. X X @(#)Copyright (c) 1987 by Teemu Torma X X Permission is given to distribute this program and alter this code as X needed to adapt it to forign systems provided that this header is X included and that the original author's name is preserved. */ X X#include <stdio.h> X#include <ctype.h> X#include <string.h> X#include <sys/types.h> X#include "hsu.h" X#include "config.h" X#include "fnet.h" X#include "fnews.h" X#include "nodelist.h" X Xextern void exit(); Xextern void perror(); X Xint verbose = INIT_VERBOSE; X X/* Get header field. Rewind header file and scan forward until we find X desired field. Not effective, but... Return NULL if field not found. */ X Xchar * Xget_field(field, hdr) X char *field; X FILE *hdr; X{ X static char buffer[BUFLEN]; X int conditional = FALSE; X char *p; X X if (*field == '?') X { X conditional = TRUE; X for (p = field; *p = *(p + 1); p++); X } X X (void) rewind(hdr); X while (fgets(buffer, BUFLEN, hdr)) X if (!strncmp(field, buffer, strlen(field))) X { X if (conditional) X { X /* Note: <= is ok, because there is one blank after header which X wouldn't be included in Fidonet message header */ X if (!strncmp(field, "Subject:", 8)) X if (strlen(buffer + 8) <= 72) return NULL; X if (!strncmp(field, "From:", 5)) X if (strlen(buffer + 5) <= 36) return NULL; X if (!strncmp(field, "Reply-To:", 9)) X if (strlen(buffer + 9) <= 36) return NULL; X if (!strncmp(field, "To:", 3)) X if (strlen(buffer + 3) <= 36) return NULL; X } X X buffer[strlen(buffer) - 1] = 0; X return buffer + strlen(field); X } X X return (char *) NULL; X} X X/* Open stream which is associated with fido-mailers starndard input. X We could use also popen(3S), but it invokes also sh(1) which slows X down opening and gets more memory. This routine exists if something X goes wrong, because this is quite essential part of program and X we can not continue without mailer. */ X XFILE * Xopen_mailer(to) X char *to; X{ X FILE *fp; X int fd[2]; X char mailer[128]; X X (void) sprintf(mailer, "%s/rfmail", LIBDIR); X X /* create pipe */ X if (pipe(fd) == -1) X { X perror("rfnews: pipe"); X exit(1); X } X X switch (fork()) X { X case -1: X perror("rfnews: fork failed"); X exit(1); X case 0: X (void) close(0); X /* duplicate pipe and close now unused fd's */ X if (dup(fd[0]) == 0) X { X (void) close(fd[0]); X (void) close(fd[1]); X (void) execlp(mailer, "rfmail", "-p", to, (char *) NULL); X perror(mailer); X } X else X perror("rfnews: dup"); X exit(0); X default: X (void) close(fd[0]); X /* open the other end of pipe as buffered */ X if ((fp = fdopen(fd[1], "w")) == NULL) X { X perror("rfnews: fdopen"); X exit(1); X } X } X return fp; X} X X/* Save news header for future processing and return file pointer to it. */ X XFILE * Xsave_hdr() X{ X char buffer[BUFLEN]; X FILE *hdr = tmpfile(); X X while (fgets(buffer, BUFLEN, stdin) && *buffer != '\n') X (void) fputs(buffer, hdr); X return hdr; X} X X/* Open rfmail. Send mail to net/node in config file, receiver will be X All, because Usenet News articles don't have To: field. Net/node X exraction is not very reliable of there's something wrong in X configuration file. */ X XFILE * Xinvoke_rfmail(config) X FILE *config; X{ X char buffer[BUFLEN], *cp; X int net, node; X X section(SECT_NETNODE, config); X if (getcl(buffer, BUFLEN, config)) X { X /* get net number */ X for (cp = buffer, net = 0; *cp && isdigit(*cp) && *cp != '/'; cp++) X net = net * 10 + *cp - '0'; X debug(1, "Got net %d from config file", net); X if (*cp) X { X /* get node number */ X for (cp++, node = 0; *cp && isdigit(*cp); cp++) X node = node * 10 + *cp - '0'; X debug(1, "Got node %d from config file", node); X } X else X { X log("Missing node in config file"); X exit(1); X } X (void) sprintf(buffer, "All@%d.%d.FidoNet", node, net); X return open_mailer(buffer); X } X else X { X log("Missing net/node in config file"); X exit(1); X } X /* NOTREACHED */ X} X X/* Try to get area for some of the newsgroups in newsgroup list. Method X is not very sophisticated, but it should work. */ X Xchar * Xget_area(config, groups) X FILE *config; X char *groups; X{ X static char conv[BUFLEN]; X char buffer[BUFLEN]; X char *cp, *gr, *area; X X debug(2, "Checking grouplist '%s'", groups); X X section(SECT_NG_AREA, config); X while (getcl(conv, BUFLEN, config)) X { X for (cp = conv; !isspace(*cp); cp++) X /* find the end of newsgroup name */; X if (isspace(*cp)) X { X *cp++ = 0; X (void) strcpy(buffer, groups); X debug(3, "Config line '%s'", conv); X /* get group by group from newsgroup-list */ X for (gr = strtok(buffer, " ,"); gr; gr = strtok((char *) NULL, " ,")) X { X debug(3, "Comparing '%s' and '%s'", gr, conv); X if (!strcmp(gr, conv)) X { X while (isspace(*cp)) X cp++; X area = cp; X while (*cp && !isspace(*cp)) X cp++; X *cp = 0; X debug(3, "Match, return area '%s'", area); X return area; X } X } X debug(3, "No match"); X } X else X log("Invalid line in config: '%s'", conv); X } X log("No area found for any group in '%s'", groups); X return (char *) NULL; X} X X/* Do actual processing of article. This will copy article, get newsgoroups X and save them and then send all rest of article. */ X X/* ARGSUSED */ Xint Xmain(argc, argv, envp) X int argc; X char **argv, **envp; X{ X FILE *config, *rfmail, *hdr; X char buffer[BUFLEN], seenbys[BUFLEN]; X int c; X char *cp, *errorstr; X X /* get options */ X while ((c = getopt(argc, argv, "v")) != EOF) X switch (c) X { X case 'v': X verbose++; X break; X default: X (void) fprintf(stderr, "Usage: %s [-v]\n", *argv); X exit(1); X } X X /* try to update nodelist-index */ X if (errorstr = update_index()) X { X if (*errorstr == '$') X log("$Cannot update nodelist-index: %s", errorstr + 1); X else X log("Cannot update nodelist-index: %s", errorstr); X exit(EX_OSERR); X } X X /* try to open config file */ X if ((config = pfopen(LIBDIR, "fnews.cf", "r")) == NULL) X { X log("$Unable to open config file"); X exit(1); X } X X /* open rfmail to send news */ X rfmail = invoke_rfmail(config); X X /* save news header */ X hdr = save_hdr(); X X /* try to get area to which this article is going */ X if (cp = get_field("Newsgroups: ", hdr)) X if (cp = get_area(config, cp)) X { X (void) fprintf(rfmail, "AREA:%s\n", cp); X debug(2, "send area %s", cp); X } X else X { X log("Unable to get area"); X#ifdef JUNK_AREA X (void) fprintf(rfmail, "AREA:\n", JUNK_AREA); X log("Sending article to area %s", JUNK_AREA); X#else X exit(1); X#endif X } X else X { X log("Missing Newsgroups: line in article"); X exit(1); X } X X /* now send news header */ X section(SECT_HEADERS, config); X while (getcl(buffer, BUFLEN, config)) X if (cp = get_field(buffer, hdr)) X (void) fprintf(rfmail,"%s%s\n", buffer, cp); X (void) putc('\n', rfmail); X X /* send rest of the article */ X while (fgets(buffer, BUFLEN, stdin)) X (void) fputs(buffer, rfmail); X X /* send last information */ X fprintf(rfmail, "--- %s\n", PROGRAMNAME); X section(SECT_ENDMSG, config); X if (!getcl(buffer, BUFLEN, config)) X strcpy(buffer, "No Origin"); X else if (!getcl(seenbys, BUFLEN, config)) X *seenbys = 0; X X (void) fprintf(rfmail, " * Origin: %s (%d:%d/%d.%d)\n", buffer, X MY_ZONE, MY_NET, MY_NODE, MY_POINT); X fprintf(rfmail, "SEEN-BY: %s \n", seenbys); X fprintf(rfmail, "\001PATH: %d/%d \n", MY_NET, MY_NODE); X X /* I think that that was all... */ X (void) fclose(rfmail); X (void) wait((int *) NULL); X X exit(0); X /* NOTREACHED */ X} SHAR_EOF chmod 0644 rfnews.c || echo "restore of rfnews.c fails" echo "x - extracting rmail.c (Text)" sed 's/^X//' << 'SHAR_EOF' > rmail.c && X#ifndef lint Xstatic char *sccsid = "@(#)%M% %I% Teemu Torma %H%"; X#endif X X/* Replacement for rmail. This looks out, if destination system X name is numeric, and if it is, feeds mail to fidonet mail X server. If systemname is not numeric, this executes real rmail. X If this site is not fidonet gateway, define GATEWAY in rmail.h X to path to that host. X X This is just a temporary replacement, MUCH MORE better is to use X sendmail for gatewaying or smail 2.3 version that also can invoke X other mailer. X X @(#)Copyright (c) 1987 by Teemu Torma X X Permission is given to distribute this program and alter this code as X needed to adapt it to forign systems provided that this header is X included and that the original author's name is preserved. */ X X#include <stdio.h> X#include <string.h> X#include <ctype.h> X#include <sys/types.h> X#include "hsu.h" X#include "rmail.h" X#include "config.h" X#include "sysexits.h" X#include "nodelist.h" X#include "fnet.h" X Xextern char *malloc(); Xextern void exit(); Xextern void perror(); Xextern char *regex(); X X/* verbosity */ Xint verbose = INIT_VERBOSE; X X#ifndef GATEWAY 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 XFILE * Xopen_mailer(program, args, pid) X char *program, **args; X int *pid; X{ X FILE *fp; X int fd[2]; X X /* create pipe */ X if (pipe(fd) == -1) X { X perror("rmail: pipe"); X exit(EX_OSERR); X } X X switch (*pid = fork()) X { X case -1: X perror("rmail: fork failed"); X exit(EX_OSERR); X case 0: X (void) close(0); X if (dup(fd[0]) == 0) X { X (void) close(fd[0]); X (void) close(fd[1]); X (void) execvp(program, args); X perror(program); X } X else X perror("rmail: dup"); X exit(EX_OSERR); X default: X (void) close(fd[0]); X if ((fp = fdopen(fd[1], "w")) == NULL) X { X perror("rmail: fdopen"); X exit(EX_OSERR); X } X } X return fp; X} X X#endif X X X/* ARGSUSED */ Xint Xmain(argc, argv, envp) X int argc; X char **argv, **envp; X{ X int cnt; X X#ifdef GATEWAY X X char *path; X X#else X X char **rargs = marray(argc + 1), **fargs = marray(argc + 1); X int rrec = 0, frec = 0, rargc = 1, fargc = 1; X int status = EX_OK; X char dummyname[100]; X Node dummynode; X X#endif X X#ifdef GATEWAY X X /* If GATEWAY is defined, send all mail going to fidonet to there. X If domain .FidoNet is known by other mahcines, routing is not X really problem and this rmail is not needed elsewhere than in X real gateway if there's no sendmail or other intelligent system X in use. */ X X for (cnt = 1; cnt < argc; cnt++) X if (parse_address(argv[cnt], dummyname, &dummynet, &dummynode) == NULL) X { X#ifdef DEBUG X (void) printf("Routing %s to %s.\n", argv[cnt], GATEWAY); X#endif X path = malloc((unsigned) (strlen(GATEWAY) + X strlen(argv[cnt]) + 2)); X (void) strcpy(path, GATEWAY); X (void) strcat(path, "!"); X argv[cnt] = strcat(path, argv[cnt]); X } X X (void) execvp(RMAIL, argv); X perror(RMAIL); X X#else X X *rargs = RMAIL; X *fargs = FIDOMAILER; X X /* Scan thru receiver list and put all receivers in fidonet in fido- X mailer's receiver-list and all others in real rmails one. No X options can be passed to fidomailer thru this, because it would X be too difficult to determine which one goes to which one and X there might be same options also. Somehow it's good that fidomailer X is well hidden under this... */ X X for (cnt = 1; cnt < argc; cnt++) X if (*argv[cnt] == '-') X rargs[rargc++] = strsave(argv[cnt]); X else X { X if (parse_address(argv[cnt], dummyname, &dummynode) == NULL) X { X#ifdef DEBUG X (void) printf("Argument %d (receiver %d) in fidomailer: %s\n", X fargc, frec + 1, argv[cnt]); X#endif X fargs[fargc++] = strsave(argv[cnt]); X frec++; X } X else X { X#ifdef DEBUG X (void) printf("Argument %d (receiver %d) in rmail: %s\n", X rargc, rrec + 1, argv[cnt]); X#endif X rargs[rargc++] = strsave(argv[cnt]); X rrec++; X } X } X X /* NULL terminate arument lists */ X rargs[rargc] = NULL; X fargs[fargc] = NULL; X X /* If there is mail only to rmail or fidomail, execute that mailer X with our original argument list. */ X X if (!frec) X { X#ifdef DEBUG X (void) printf("No mail to fidonet, executing %s\n", RMAIL); X#endif X (void) execvp(RMAIL, argv); X perror(RMAIL); X exit(1); X } X else X if (!rrec) X { X#ifdef DEBUG X (void) printf("No mail to UUCP, executing %s\n", FIDOMAILER); X#endif X (void) execvp(FIDOMAILER, argv); X perror(FIDOMAILER); X exit(1); X } X else X { X X /* There's mail to both fidonet and usenet. We'll have to open X both mailers and feed this letter to them at the same time. X This is quite risky: If one of the mailer's exits for any X reason, we will get SIGPIPE and we'll fall down.... if that X happens, the other mailer got incomplete letter. */ X X int rmail_pid, fmail_pid; X register FILE *rmail = open_mailer(RMAIL, rargs, &rmail_pid); X register FILE *fmail = open_mailer(FIDOMAILER, fargs, &fmail_pid); X char buffer[BUFSIZ]; X int stat_loc, pid; X X#ifdef DEBUG X (void) printf("Mail both to fidonet and UUCP\n"); X#endif X while (fgets(buffer, BUFSIZ, stdin)) X { X (void) fputs(buffer, rmail); X (void) fputs(buffer, fmail); X } X (void) fclose(rmail); X (void) fclose(fmail); X X /* We should wait for both mailers to exit and check their exit X statuses. Then we should decide which one we'll should return, X in this case let's return status from rmail, if other than X EX_OK, otherwise from fidomail. */ X X while ((pid = wait(&stat_loc)) != -1) X if (pid == rmail_pid) X { X if ((stat_loc & 0377) == 0) X status = stat_loc >> 8; X } X else X if (pid == fmail_pid) X { X if ((stat_loc & 0377) == 0 && status == EX_OK) X status = stat_loc >> 8; X } X } X#endif X X exit(status); X /* NOTREACHED */ X} SHAR_EOF chmod 0644 rmail.c || echo "restore of rmail.c fails" echo "x - extracting fpack.c (Text)" sed 's/^X//' << 'SHAR_EOF' > fpack.c && X#ifndef lint Xstatic char *sccsid = "@(#)%M% %I% Teemu Torma %H%"; X#endif X X/* Create and update fidomail packets. Read mail messages from X spool directory and append them to packet. If packet doesn't X exist already, it will be created. Packet name in P.point.node.net.zone. X X @(#)Copyright (c) 1987 by Teemu Torma X X Permission is given to distribute this program and alter this code as X needed to adapt it to forign systems provided that this header is X included and that the original author's name is preserved. */ X X#include <stdio.h> X#include <ctype.h> X#include <errno.h> X#include <string.h> X#include <fcntl.h> X#include <sys/types.h> X#include <time.h> X#include <sys/stat.h> X#include <dirent.h> X#include "hsu.h" X#include "config.h" X#include "fnet.h" X#include "fpack.h" X#include "nodelist.h" X Xextern time_t time(); Xextern int getopt(); Xextern int optind; Xextern char *optarg; Xextern uint sleep(); Xextern void exit(); Xextern void swab(); X XNode node; Xint verbose = INIT_VERBOSE; X X/* Return True if string is numeric. */ X Xbool Xnumeric(s) X register char *s; X{ X while (*s) X if (!isdigit(*s++)) X return False; X return True; X} X X/* Return argument as 16 bit interger to msdos */ X XINT16 Xint16(value) X int value; X{ X INT16 msint, mval = (INT16) value; X X#ifdef SWAB_BYTES X swab((char *) &mval, (char *) &msint, 2); X#else X msint = mval; X#endif X return msint; X} X X/* Put string to file in null-terminated format. */ X Xvoid Xput_string(fp, s) X FILE *fp; X char *s; X{ X while (*s) X { X (void) putc(*s, fp); X s++; X } X (void) putc(0, fp); X} X X/* Return date in ascii string that is in fido-format, ie. X dd mmm yy hh:mm:ss, like 24 Jan 87 06:24:27 */ X Xchar X*fido_date(clock) X time_t clock; X{ X struct tm *tm; X static char date[20]; X X /* Literal months */ X static char *months[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", X }; X X tm = localtime(&clock); X (void) sprintf(date, "%02d %3s %02d %02d:%02d:%02d", tm->tm_mday, X months[tm->tm_mon], tm->tm_year, tm->tm_hour, X tm->tm_min, tm->tm_sec); X return date; X} X X/* Write int to file in msdos interger format */ X Xint Xwrite_int(fp, value) X FILE *fp; X int value; X{ X INT16 msint = int16(value); X X return fwrite((char *) &msint, sizeof(INT16), 1, fp); X} X X/* Write fido msg header for mailfile. Mailfile contains first headers X terminated by empty line, after that comes message text. X Header consists of one letter id followed by arguments if any. X X Id-letters: X N - fidonet address information of message, e.g. N 2:504/8.0. X T - to whom message is to, e.g. T Teemu Torma. X F - from whom message is from, e.g. F Foo Bar. X S - subject of message, e.g S How are you?. X P - indicates that message is private. (Here is little conflict: X rfmail uses option -p to indicate that mail to public and here X P means that mail is private. Maybe this P should mean that SHAR_EOF echo "End of part 2" echo "File fpack.c is continued in part 3" echo "3" > s2_seq_.tmp exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.