From decwrl!elroy.jpl.nasa.gov!usc!cs.utexas.edu!uunet!allbery Tue Jan 30 08:53:08 PST 1990 Article 1309 of comp.sources.misc: Path: decwrl!elroy.jpl.nasa.gov!usc!cs.utexas.edu!uunet!allbery From: wswietse@lso.win.tue.nl (Wietse Venema) Newsgroups: comp.sources.misc Subject: v10i038: PC-MAIL release 2, 6/11 Message-ID: <77513@uunet.UU.NET> Date: 25 Jan 90 00:44:46 GMT Sender: allbery@uunet.UU.NET Lines: 2268 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 10, Issue 38 Submitted-by: wswietse@lso.win.tue.nl (Wietse Venema) Archive-name: pcmail2/part06 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh main/connect.c <<'END_OF_main/connect.c' X/*++ X/* NAME X/* connect 3 X/* SUMMARY X/* pre- and post protocol host access X/* PROJECT X/* pc-mail X/* PACKAGE X/* cico X/* SYNOPSIS X/* int connect() X/* X/* int disconnect() X/* DESCRIPTION X/* connect() tries to make a connection to the remote host X/* and to log on, using the dial-up script and login-name X/* entries in the communications parameter file, and the password X/* provided as command-line parameter to the cico program. X/* A UUCP-like send/expect script facility is used. Thus a login X/* sequence might look like: X/* X/* send expect send expect ... X/* X/* The program will send the first "send" string, then expect the X/* first "expect" string, and so on. X/* X/* Alternative expect/send sequences can be specified in the usual manner: X/* X/* expect-send-expect-send-expect... X/* X/* If the first expect string fails, the alternative send string is X/* transmitted and the alternative expect is tried, and so on, until X/* an expect string succeeds, or until the list of alternatives is X/* exhausted. X/* X/* After the dial-up script has completed the program X/* proceeds with the following build-in send/expect sequence: X/* X/* ogin: your_login_name\\r ssword: your_password\\r X/* X/* disconnect() tries to break a connection, using the disconnect X/* entry in the communications parameter file. Unlike connect() X/* this function is not driven by a send-expect script. X/* X/* The following escape sequences are recognized in send or expect X/* strings: X/* X/* .nf X/* \\b backspace X/* \\r carriage return X/* \\n newline X/* \\t tab X/* \\s space X/* \\f form feed X/* \\nnn octal character value X/* \\\\ a real backslash X/* .fi X/* X/* In addition, the following "send" strings are given special X/* treatment: X/* X/* .nf X/* BREAK send a null character X/* EOT send Control-D X/* FUNCTIONS AND MACROS X/* xwrite(), xgetc(), trap(), debug(4)(), log(), split() X/* FILES X/* $MAILDIR/s00000 communications parameter file X/* $MAILDIR/LOGFILE system logfile X/* SEE ALSO X/* params(5) communications parameter file entries X/* DIAGNOSTICS X/* connect() returns a status E_BADSETUP if the systems parameter X/* file contains bad data, and E_NOLINE if the login script fails. X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Fri Mar 27 17:11:12 GMT+1:00 1987 X/* LAST MODIFICATION X/* 90/01/22 13:01:26 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include X#include X X#include "defs.h" X#include "params.h" X#include "status.h" X#include "comm.h" X#include "logs.h" X#include "sysdep.h" X Xhidden char *blnk = " \t"; /* send/expect separators */ X X/* forward declarations */ X Xhidden void conn_send(); Xhidden void conn_xpct(); Xhidden char *escape(); X X/* connect - connect to remote system; simple script processing with retries */ X Xpublic int connect() X{ X int *savetrap = systrap; /* save exception handler */ X jmp_buf mytrap; /* our exception handler */ X int retval; /* completion code */ X char *seq = DIAL_SEQUENCE; X register char *cp; X X /* set up exception handler */ X X if (retval = setjmp(systrap = mytrap)) { /* get here if expect fails */ X systrap = savetrap; /* it just happened */ X return (retval); X } X /* optional dial-up sequence */ X X for (cp = split(&seq, blnk); cp; cp = split(&seq, blnk)) { X conn_send(escape(cp)); X if (cp = split(&seq, blnk)) X conn_xpct(escape(cp)); X } X X /* mandatory login sequence; hack this for non-UNIX hosts */ X X conn_xpct("ogin:"); X conn_send(strcons("%s\r", LOGIN_NAME)); X conn_xpct("ssword:"); X conn_send(strcons("%s\r", password)); X X /* restore exception handler */ X X systrap = savetrap; /* get here if expect wins */ X return (0); /* say no problems... */ X} X X/* disconnect - disconnect line */ X Xpublic int disconnect() X{ X conn_send(escape(DISC_SEQUENCE)); /* send disconnect sequence */ X return (0); /* always succeeds... */ X} X X/* conn_send - send BREAK, EOT or string literal */ X Xhidden void conn_send(s) Xregister char *s; X{ X static char null = '\0'; X static char eot = '\04'; X X sleep(1); X X if (*s) { X debug(4) ("Sending: %S\n", s); X if (strcmp(s, "BREAK") == 0) { X xwrite(ttfd, &null, 1); X } else if (strcmp(s, "EOT") == 0) { X xwrite(ttfd, &eot, 1); X } else { X while (*s) { X delay(); X xwrite(ttfd, s++, 1); X } X } X } X} X X/* conn_xpct - pattern matching without meta characters */ X Xhidden void conn_xpct(s) Xchar *s; X{ X int c; X int i; X int n; X char *xp; X char *sp; X X /* X * Keep listening until we time out or until we receive the expected X * string (thus, if the other end keeps sending garbage we will never X * terminate). Make sure that we do not overrun our buffer. Parity bits X * are ignored. If we do not succeed, try alternative sequences if they X * are specified. X */ X X for (xp = split(&s, "-"); xp; xp = split(&s, "-")) { X X debug(4) ("Expecting: %S\nReceiving: ", xp); X X if (((n = strlen(xp)) > MSGBUF)) X n = MSGBUF; X for (i = 0; (c = xgetc()) != EOF; /* void */ ) { X msgin[i++] = (c &= 0177); X debug(4) ("%C", c); X if (i >= n && strncmp(xp, &msgin[i - n], n) == 0) { X debug(4) (" ok!\n"); X return; X } else if (i >= MSGBUF) { X strncpy(msgin, &msgin[i - (n - 1)], n - 1); X i = n - 1; X } X } X debug(4) (" failed!\n"); X X /* try alternative sequence, if specified, else fail */ X X if (sp = split(&s, "-")) { X conn_send(sp); X } else { X trap(E_NOLINE, "LOGIN FAILED (at \"%S\")", xp); X } X } X} X X/* escape - interpret backslash sequences */ X Xhidden char *escape(s) Xregister char *s; X{ X static char buf[BUFSIZ]; X register char *cp = buf; X register char ch; X int c; X int i; X X while (*s && cp < buf + sizeof(buf) - 1) { /* don't overflow the buffer */ X X if (*s != '\\') { /* ordinary character */ X *cp++ = *s++; X } else if (isdigit(*++s) && *s < '8') { /* \nnn octal code */ X sscanf(s, "%3o", &c); X *cp++ = c; X i = 1; X s++; X while (i++ < 3 && isdigit(*s) && *s < '8') X s++; X } else if ((ch = *s++) == 0) { /* at string terminator */ X break; X } else if (ch == 'b') { /* \b becomes backspace */ X *cp++ = '\b'; X } else if (ch == 'f') { /* \f becomes formfeed */ X *cp++ = '\f'; X } else if (ch == 'n') { /* \n becomes newline */ X *cp++ = '\n'; X } else if (ch == 'r') { /* \r becomes carriage ret */ X *cp++ = '\r'; X } else if (ch == 's') { /* \s becomes blank */ X *cp++ = ' '; X } else if (ch == 't') { /* \t becomes tab */ X *cp++ = '\t'; X } else { /* \any becomes any */ X *cp++ = ch; X } X } X *cp = '\0'; /* terminate the result */ X return (buf); X} END_OF_main/connect.c if test 6857 -ne `wc -c
main/email.c <<'END_OF_main/email.c' X/*++ X/* NAME X/* email 3 X/* SUMMARY X/* manipulate one message in preparation X/* PROJECT X/* pc-mail X/* PACKAGE X/* mail X/* SYNOPSIS X/* #include "mail.h" X/* X/* int work() X/* X/* int work_disp(def_addr) X/* char *def_addr; X/* DESCRIPTION X/* The functions in this module are responsible for manipulations X/* on mail messages in preparation. X/* X/* work() should be invoked when the user has selected an existing X/* message in preparation. It does some initializations and invokes X/* the work_disp() function. X/* X/* work_disp() allows the user to specify the disposition of a X/* mail message in preparation. It should be used after the user has X/* created a message, or after the user has selected a message in X/* preparation. X/* X/* The message file is displayed on the screen and user the X/* can choose to print, mail, edit or delete etc. the message. X/* X/* The def_addr argument contains a default mail destination: for example, X/* an address extracted from a message being replied to. It should be an X/* e-mail address or an empty string. X/* X/* The code in this module is a little tricky, to avoid "orphan" work X/* files (message file without a metafile). X/* COMMANDS X/* The program specified in the EDITOR environment variable, X/* or a system-dependent default. X/* FILES X/* mail.msg, file being edited in the current directory X/* $MAILDIR/ennnnn, message file (body) X/* $MAILDIR/cnnnnn, meta file (summary) X/* $MAILDIR/header, template mail header file X/* $MAILDIR/trailer, template signature file X/* SEE ALSO X/* pager(3), pager(5), kbdinp(3), edit(3) X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Tue May 12 15:35:20 GMT+1:00 1987 X/* LAST MODIFICATION X/* 90/01/22 13:01:35 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include X#include X X#include X#include "defs.h" X#include "path.h" X#include "pager.h" X#include "screen.h" X#include "mail.h" X#include "status.h" X X/* forward declarations */ X Xhidden void junk_work(); Xhidden int edit_work(); Xhidden int show_work(); Xhidden int hold_work(); Xhidden int send_work(); Xhidden int queue_work(); X Xpublic char address[MAXLINE]; /* default destination */ Xhidden File *workfile = 0; /* pager file */ X X/* work - user selected message in preparation */ X Xpublic int work() X{ X return (work_disp("")); X} X X/* work_disp - ask disposition of a message in preparation */ X Xpublic int work_disp(def_addr) Xchar *def_addr; X{ X static Screen screen[] = { X 'C', "Close",hold_work,"Send message later, return to message-selection menu", X#ifdef ATTACH X 'A', "Attach",attach", "Attach file to message", X#endif X 'D', "Delete",delete, delcurr, X 'E', "Edit", edit_work,"Edit this message", X 'M', "Mail", send_work,"Send this message to destination", X 'P', "Print",print, printcurr, X PGUP, PgUp, pu_pager, pageup, X PGDN, PgDn, pd_pager, pagedn, X UP, "Up", up_pager, csrup, X DOWN, "Down", dn_pager, csrdn, X 0, 0, show_work, X "(Reading a message in preparation)", X }; X struct stat s; X X strcpy(address, def_addr); /* set up default address */ X kbdinp(screen); /* ask disposition */ X junk_work(); /* destroy mail pager file */ X return (S_REDRAW); /* say screen was changed */ X} X X/* show_work - show message in preparation or error message in middle window */ X Xhidden int show_work() X{ X if (workfile) { /* check pager file exists */ X set_pager(workfile); /* select existing display */ X } else if (rd_pager(workfile = open_pager(), message)) { X mesg_pager(workfile, m_msgread); /* cannot display message */ X } X ds_pager(); /* (re)draw display */ X return (0); /* screen is up-to-date */ X} X X/* junk_work - destroy message in preparation display */ X Xhidden void junk_work() X{ X if (workfile) { /* no-op if no display */ X close_pager(workfile); /* release memory */ X workfile = 0; /* say it is gone */ X } X} X X/* edit_work - edit a message in preparation */ X Xhidden int edit_work() X{ X register int stat; X X if (stat = edit(message, MAILFILE)) /* try to edit the message */ X errdisp(stat); /* edit() had a problem */ X junk_work(); /* force new message display */ X return (S_REDRAW); /* say screen has changed */ X} X Xhidden int label_work(); X X/* hold_work - stop editing but do not yet mail a message in preparation */ X Xhidden int hold_work() X{ X static Screen screen[] = { X STRING, 0, label_work, int_error, X 0, 0, 0, X getsummary, X }; X struct stat s; X X /* X * The user does not yet want to send the message off. The purpose of the X * following code is to ask for a one-line summary, but only if such a X * comment does not yet exist. The summary is used to identify the X * message in preparation in the message- selection display. X */ X X if (stat(message, &s) || !stat(comment, &s)) { X return (S_BREAK); /* we are done here */ X } else { X return (kbdinp(screen) | S_REDRAW); /* ask for a summary */ X } X} X X/* label_work - save summary line to meta file */ X Xhidden label_work(string) Xchar *string; X{ X register int stat; X X if (stat = metafile(comment, string, (char *) 0)) { X errdisp(stat); /* oops, notify the user */ X return (S_REDRAW); /* say screen has changed */ X } else { X chmod(comment, 0444); /* make comments read-only */ X junk_desk(); /* say mail box has changed */ X return (S_BREAK); /* say no more work */ X } X} X X/* send_work - user wants to send message in preparation, ask for destination */ X Xhidden int send_work() X{ X static Screen screen[] = { X EDIT, 0, queue_work, address, X 0, 0, when, X "Press ESC to cancel. Send message to:", X }; X X return (kbdinp(screen) | S_REDRAW); X} X X/* queue_work - spool mail, delete message in preparation and meta file */ X Xhidden int queue_work(to) Xchar *to; X{ X register int stat; X X if (stat = submit(message, to)) { X errdisp(stat); /* cannot queue message */ X return (S_REDRAW); /* say screen has changed */ X } else { X return (unspool() | S_BREAK); /* remove work and meta file */ X } X} END_OF_main/email.c if test 6160 -ne `wc -c
main/gmail.c <<'END_OF_main/gmail.c' X/*++ X/* NAME X/* gmail 1 X/* SUMMARY X/* deliver unsent mail via gnuucp X/* PROJECT X/* pc-mail X/* PACKAGE X/* gnu X/* SYNOPSIS X/* gmail [-d debuglevel] X/* DESCRIPTION X/* This program replaces the sending function of the pc-mail "cico" X/* program, on systems that use GNUUCP for message transport. X/* X/* gmail searches the pc-mail message data base for unsent mail X/* (with the "Out" status) and queues it for transmission via GNUUCP. X/* When a message has been queued it is renamed to reflect X/* the "Sent" status. X/* X/* This program is intended to be called via the MAILCMD environment X/* variable, so that it is invoked upon exit from the mail user X/* interface program. X/* X/* In order to avoid corruption of the message data base, control-c X/* interrupts are disabled while this program executes. X/* ENVIRONMENT X/* MAILDIR, path to pc-mail message data base X/* COMMANDS X/* rmail, the gnuucp mailer X/* FILES X/* In the spool directory: X/* d unsent mail, message body X/* x unsent mail, destination and subject X/* q sent mail, message body X/* r sent mail, destination and subject X/* SEE ALSO X/* path(5) spool directory, file name formats X/* DIAGNOSTICS X/* Problems are reported on the standard error output, and cause the X/* program to terminate with a nonzero exit status. X/* BUGS X/* It is left up to GNUUCP to determine what to do with undeliverable mail. X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Wed Jan 3 22:16:28 MET 1990 X/* LAST MODIFICATION X/* 90/01/22 13:01:40 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include X#include X#include X X#include "defs.h" X#include "ndir.h" X#include "path.h" X X#ifndef RMAIL X#define RMAIL "rmail" X#endif X X/* Forward declarations */ X Xhidden char *get_dest(); Xhidden int uuqueue(); Xhidden void error(); Xhidden void frename(); Xhidden void parse_args(); Xhidden void scanmail(); Xhidden void usage(); X X#define debug if (dflag) (void) printf X Xhidden int dflag = 0; /* debugging option */ X Xpublic char *progname = "gmail"; /* for diagnostics */ X X/* .. */ X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X (void) signal(SIGINT, SIG_IGN); /* disable ctrl-c */ X if (pathinit()) /* get path info */ X error("no mail directory or MAILDIR environment variable not set"); X parse_args(argc, argv); /* parse command args */ X scanmail(); /* search for unsent mail */ X exit(0); X /* NOTREACHED */ X} X X/* parse_args - process command-line arguments */ X Xhidden void parse_args(argc, argv) Xint argc; Xchar **argv; X{ X while (--argc && *++argv && **argv == '-') {/* process options */ X switch (*++*argv) { X case 'd': /* turn debugging on */ X dflag++; X break; X default: /* unknown option */ X usage("invalid option: -%c", **argv); X break; X } X } X X /* check for extraneous arguments */ X X if (argc > 0) X usage("unexpected argument: %s", *argv); X} X X/* scan for unsent mail */ X Xhidden void scanmail() X{ X unsigned msgno; /* message sequence number */ X register DIR *dp; X struct direct *de; X char *dest; X X debug("directory: \"%s\"\n", maildir); X X /* X * Scan the spool directory for unsent mail. After the message has been X * piped through rmail, rename it to reflect the "Sent" status. Do not give X * up if a file cannot be opened; just proceed with the next message. X */ X X if ((dp = opendir(maildir)) == 0) X error("cannot read the mail directory: %s", maildir); X X while (de = readdir(dp)) { X debug("file: \"%s\"\n", de->d_name); X if (de->d_name[0] == OUT_META && (msgno = seqno(de->d_name)) X && (dest = get_dest(msgno)) && uuqueue(msgno, dest)) { X frename(out_mesg(msgno), sent_mesg(msgno)); X frename(out_meta(msgno), sent_meta(msgno)); X } X } X closedir(dp); X} X X/* uuqueue - queue one message */ X Xhidden int uuqueue(msgno, dest) Xunsigned msgno; Xchar *dest; X{ X char cmd[BUFSIZ]; X char *path; X char *rcpt; X static char sep[] = " \t\r\n"; X X if (access(path = out_mesg(msgno), 04)) { X debug("%s: cannot read message file: %s\n", path, sys_errlist[errno]); X return (0); X } else { X X /* X * The GNUUCP rmail program has to invoked for each recipient. rmail X * will have to deal with undeliverable mail anyway, so we ignore X * that class of errors. X */ X X for (rcpt = strtok(dest, sep); rcpt; rcpt = strtok((char *) 0, sep)) { X (void) sprintf(cmd, "%s %s <%s", RMAIL, rcpt, path); X debug("command: %s\n", cmd); X if (system(cmd) == 127) X error("could not invoke the shell"); X } X return (1); X } X} X X/* get_dest - extract recipients */ X Xhidden char *get_dest(msgno) Xunsigned msgno; X{ X static char buf[MAXLINE]; X FILE *fp; X char *ret; X char *path; X X if ((fp = fopen(path = out_meta(msgno), "r")) == 0) { X debug("%s: cannot open: %s\n", path, sys_errlist[errno]); X return (0); X } else { X if ((ret = fgets(buf, sizeof(buf), fp)) == 0) X debug("%s: no recipients found\n", path); X (void) fclose(fp); X return (ret); X } X} X X/* frename - forcibly change the name of a file */ X Xhidden void frename(from, to) Xchar *from; Xchar *to; X{ X debug("rename: %s -> %s\n", from, to); X X if (chmod(to, 0600) == 0) X (void) unlink(to); X if (rename(from, to)) X error("cannot rename %s to %s: %s", from, to, sys_errlist[errno]); X} X X/* error - complain */ X X/* VARARGS */ X Xhidden void error(va_alist) Xva_dcl X{ X va_list ap; X char *fmt; X X (void) fprintf(stderr, "%s: ", progname); X va_start(ap); X fmt = va_arg(ap, char *); X (void) vfprintf(stderr, fmt, ap); X va_end(ap); X (void) putc('\n', stderr); X exit(2); X} X X/* usage - explain what is wrong */ X X/* VARARGS */ X Xhidden void usage(va_alist) Xva_dcl X{ X va_list ap; X char *fmt; X X (void) fprintf(stderr, "%s: ", progname); X va_start(ap); X fmt = va_arg(ap, char *); X (void) vfprintf(stderr, fmt, ap); X va_end(ap); X (void) fprintf(stderr, "\nusage: gmail [-d]\n"); X exit(2); X} END_OF_main/gmail.c if test 6153 -ne `wc -c
main/gphys.c <<'END_OF_main/gphys.c' X/*++ X/* NAME X/* gphys 3 X/* SUMMARY X/* g protocol packet input/output X/* PROJECT X/* pc-mail X/* PACKAGE X/* cico X/* SYNOPSIS X/* #include "gp.h" X/* X/* void gsctrl(fd,c) X/* int fd,c; X/* X/* void gsdata(fd,pk,c) X/* int fd,c; X/* Packet *pk; X/* X/* int grpack(fd,pk) X/* int fd; X/* Packet *pk; X/* DESCRIPTION X/* The functions in this module send and receive packets. Interfacing X/* is based on Packet structures. Messages are interpreted elsewhere. X/* X/* gsctrl() sends a control packet to the remote receiver (no data X/* segment). X/* X/* gsdata() sends a data packet to the remote receiver. X/* The Packet structure is completed with a 16-bit checksum. X/* This function expects read/write sequence information in X/* the c parameter. X/* X/* grpack() receives a packet from the remote transmitter and checks X/* its integrity. It fills in the k, c, len and check fields of the X/* Packet structure and returns the type of message (DATA, SHORT, X/* CLOSE, RJ, RR, etcetera). X/* DIAGNOSTICS X/* grpack() returns FAIL if a corrupted packet was received, and X/* TIME if no packet was received within the time-out interval. X/* BUGS X/* No data re-reading in case of transmission errors. X/* Some parts of the code rely on 8-bit bytes, 16-bit short integers. X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Sun Apr 19 11:39:27 GMT+1:00 1987 X/* LAST MODIFICATION X/* 90/01/22 13:01:42 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include X#include "gp.h" X X/* local and forward declarations */ X Xstatic jmp_buf timebuf; Xstatic int chksum(),readhead(),readdata(),clkint(); X X#define READ(fd,cp,n) { if (read(fd,cp,n) != n) clkint(); } X X/* X* "A six byte framing envelope is constructed using the control" X* "byte C of a packet and five other bytes as depicted below." X* X* "The symbol denotes the ASCII ctrl/P character. If the" X* "envelope is to be followed by a data segment, has the" X* "value log2(size)-4; i.e. 1 <= k < 8. If k is 9, then the" X* "envelope represents a control packet. The and " X* "bytes are the low-order and high-order bytes respectively of" X* "0xAAA minus a 16-bit checksum. For control packets, this" X* "16-bit checksum is the same as the control byte C. For data" X* "packets, the checksum is calculated by the program below." X* "The byte is the exclusive-or of . Error" X* "control is accomplished by checking a received framing" X* "envelope for compliance with the definition, and comparing a" X* "checksum function of the data segment with ." X*/ X X/* gsctrl - send control packet (no data segment) */ X Xvoid gsctrl(fd,c) Xint fd,c; X{ X char header[6]; X register char chkhdr; X register char *cp = header; X int cksm = MAGIC-c; /* do checksum */ X X *cp++ = CTRL('P'); /* start of header */ X chkhdr = *cp++ = KCTRL; /* k byte (control) */ X chkhdr ^= *cp++ = cksm; /* c0 byte (checksum lsb) */ X chkhdr ^= *cp++ = cksm>>8; /* c1 byte (checksum msb) */ X chkhdr ^= *cp++ = c; /* message|sequence info */ X *cp = chkhdr; /* header checksum */ X X write(fd,header,sizeof(header)); /* send header */ X DEBUG(9,"xmt: %o\n",c&0377); /* show header */ X} X X/* gsdata - send data packet */ X Xvoid gsdata(fd,pk,c) Xint fd,c; Xregister Packet *pk; X{ X char header[6]; X register char chkhdr; X register char *cp = header; X int cval = pk->c|(c&077); /* fold in sequence info */ X X pk->chk = MAGIC-(chksum(pk->data,pk->len)^(0377&cval)); X X *cp++ = CTRL('P'); /* start of header */ X chkhdr = *cp++ = pk->k; /* k byte (message length) */ X chkhdr ^= *cp++ = pk->chk; /* c0 byte (checksum lsb) */ X chkhdr ^= *cp++ = pk->chk>>8; /* c1 byte (checksum msb) */ X chkhdr ^= *cp++ = cval; /* data|sequence info */ X *cp = chkhdr; /* header checksum */ X X write(fd,header,sizeof(header)); /* send header */ X DEBUG(9,"xmt: %o\n",cval&0377); /* show header */ X X write(fd,pk->data,pk->len); /* send data segment */ X DEBUG(9,"xmt: data %d bytes\n",pk->segl); /* show data */ X} X X/* grpack - receive one data or control packet; return packet type info */ X Xint grpack(fd,pk) Xint fd; Xregister Packet *pk; X{ X if (setjmp(timebuf)) /* in case we time out */ X return(TIME); /* it just happened */ X signal(SIGALRM,clkint); /* alarm clock response */ X alarm(ALARM); /* set alarm clock */ X X if (readhead(fd,pk)) { /* read packet header */ X DEBUG(7,"rcv: bad header\n",""); X alarm(0); /* turn timer off */ X return(FAIL); /* header checksum error */ X } else if (pk->k == KCTRL) { X alarm(0); /* turn timer off */ X return(MESG(pk->c)); /* CLOSE | RJ | RR etcetera */ X } else if (readdata(fd,pk)) { X DEBUG(7,"rcv: bad data\n",""); X alarm(0); /* turn timer off */ X return(FAIL); /* data checksum error */ X } else { X alarm(0); /* turn timer off */ X return(TYPE(pk->c)); /* DATA | SHORT */ X } X} X X/* readhead - read header and check header checksum */ X Xstatic int readhead(fd,pk) Xint fd; Xregister Packet *pk; X{ X char header[5]; X int ok; X register char chkhdr; X register char *cp = header; X X do { /* start reading */ X READ(fd,cp,1); /* skip all garbage */ X } while (*cp != CTRL('P')); /* up to packet header */ X X READ(fd,header,sizeof(header)); /* read packet header */ X X chkhdr = pk->k = *cp++; /* data length or control */ X chkhdr ^= pk->chk = *cp++&0377; /* data checksum lsb */ X chkhdr ^= *cp; X pk->chk |= (*cp++&0377)<<8; /* data checksum msb */ X chkhdr ^= pk->c = *cp++; /* control packet or data */ X if (ok = (chkhdr == *cp)) X DEBUG(9,"rcv: %o\n",pk->c&0377); X return(!ok); /* check the checksum */ X} X X/* readdata - read data segment and check data checksum */ X Xstatic int readdata(fd,pk) Xint fd; Xregister Packet *pk; X{ X if (seglen[pk->k] > pk->len) { X DEBUG(7,"rcv: data %d bytes too big\n",seglen[pk->k]); X return(1); X } else { X register int i; X DEBUG(9,"rcv: data %d bytes\n",pk->len = seglen[pk->k]); X for (i = 0; i < pk->len; i++) { X READ(fd,&pk->data[i],1); X } X return(pk->chk+(chksum(pk->data,pk->len)^(pk->c&0377)) != MAGIC); X } X} X X/* clkint - tiny time-out routine */ X Xstatic int clkint() X{ X DEBUG(9,"rcv: timed out\n",""); X longjmp(timebuf,1); X /* NOTREACHED */ X} X X/* chksum - unix packet driver checksum algorithm */ X Xstatic int chksum(s,n) Xregister char *s; Xregister n; X{ X register short sum; X register unsigned short t; X register short x; X X sum = -1; X x = 0; X do { X if (sum < 0) { X sum <<= 1; X sum++; X } else X sum <<= 1; X t = sum; X sum += (unsigned)*s++ & 0377; X x += sum ^ n; X if ((unsigned short)sum <= t) { X sum ^= x; X } X } while (--n > 0); X X return(sum); X} END_OF_main/gphys.c if test 6844 -ne `wc -c
main/gpres.c <<'END_OF_main/gpres.c' X/*++ X/* NAME X/* gpres.c 3 X/* SUMMARY X/* g-protocol general interface X/* PROJECT X/* pc-mail X/* PACKAGE X/* cico X/* SYNOPSIS X/* int gopen(fd); X/* int fd; X/* X/* int gwrite(fd,buf,len) X/* int fd,len; X/* char *buf; X/* X/* int gread(fd,buf,len) X/* int fd,len; X/* char *buf; X/* X/* int gclose(fd) X/* int fd; X/* DESCRIPTION X/* The functions in this module present an interface that closely X/* resembles the unix kernel i/o interface. X/* X/* gopen() handles the initial message exchange. fd should be X/* connected to a tty line. gopen() normally returns a zero value. X/* X/* gwrite() returns the number of bytes `written' to the remote system. X/* It should be considered an error if this is not equal to the number X/* of bytes requested. X/* A zero-length write should be used to indicate EOF during file transfer. X/* X/* gread() returns the requested number of bytes or the number of X/* bytes sent by the remote system, whichever is smaller. X/* A zero-length read indicates EOF during file transfer. X/* X/* gclose() shuts the protocol down, but does not otherwise change X/* communications line parameters. It normally returns a zero value. X/* FUNCTIONS AND MACROS X/* galloc(), gfree(), gsproto(), grproto() X/* DIAGNOSTICS X/* All functions return -1 in case of unrecoverable problems. X/* BUGS X/* All g protocol routines assume that the XON/XOFF flow control X/* has been turned off. X/* Some parts of the code rely on 8-bit bytes, 16-bit short integers. X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Sun Apr 19 12:41:37 GMT+1:00 1987 X/* LAST MODIFICATION X/* 90/01/22 13:01:43 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include "gp.h" X X/* local and forward declarations */ X Xstatic jmp_buf failbuf; Xstatic void gpeek(),gpoke(),memcpy(); X X/* gfail - exception handling */ X Xvoid gfail() X{ X longjmp(failbuf,1); X} X X/* gopen - not quite an analogon of unix open(2) */ X Xint gopen(fd) Xint fd; X{ X return(ginit(fd)); /* do packet stuff elsewhere */ X} X X/* gwrite - g-protocol analogon of unix write(2) */ X Xgwrite(fd,data,len) Xint fd,len; Xchar *data; X{ X /* set up exception handling */ X X if (setjmp(failbuf)) /* in case gsproto fails */ X return(FAIL); /* it just did */ X X /* handle special case of zero-length writes separately */ X X if (len <= 0) { /* end-of-file message */ X register Packet *pk = galloc(); /* allocate output packet */ X gpoke(pk,data,len); /* make null-data packet */ X gsproto(fd,pk); /* send to other side */ X } else { /* true data message */ X register int shot; /* quantum size */ X register int rest; /* amount left to do */ X for (rest = len; rest > 0; rest -= shot,data += shot) { X register Packet *pk = galloc(); /* allocate output packet */ X gpoke(pk,data,shot = MIN(pk->len,rest));/* fill the packet */ X gsproto(fd,pk); X } X } X return(len); /* no problems detected */ X} X X/* gread - g-protocol analogon of unix read(2) */ X Xgread(fd,data,len) Xint fd,len; Xchar *data; X{ X static Packet *pk; /* our byte stock */ X register int igot; /* our return value */ X X /* set up exception handling */ X X if (setjmp(failbuf)) /* in case grproto fails */ X return(FAIL); /* it just did */ X X /* if no bytes in stock, get some fresh ones and see how much we got */ X X if (pk == 0 || pk->segl <= 0) /* we are out of data */ X gpeek(pk = grproto(fd)); /* get fresh packet */ X X /* return as many bytes as asked, or as in stock, whichever is less */ X X if ((igot = MIN(len,pk->segl)) > 0) { X memcpy(data,pk->segp,igot); /* copy to caller's buffer */ X pk->segp += igot; /* update stock pointer */ X pk->segl -= igot; /* update stock count */ X } X if (pk->segl <= 0) /* if we exhausted the stock */ X gfree(pk); /* release packet */ X return(igot); /* no problems detected */ X} X X/* gclose - turn g protocol off */ X Xgclose(fd) Xint fd; X{ X return(gfinit(fd)); /* not here! */ X} X X/* X* "Each transmitter is constrained to observe the maximum data segment" X* "size established during initial synchronization by the receiver that" X* "it sends to. (...) `short' packets have zero or more data bytes but less" X* "than the maximum. The first one or two bytes of the data segment of" X* "a short packet are `count' bytes that indicate the difference between" X* "the maximum size and the number of bytes in the short segment. If the" X* "difference is less than 127, one count byte is used. If the difference" X* "exceeds 127, then the low-order seven bits of the difference are put" X* "in the first data byte and the remaining high-order bit is set as an" X* "indication that the remaining bits of the difference are in the second" X* "byte. X*/ X X/* gpoke - prepare packet for transmission */ X Xstatic void gpoke(pk,data,len) Xregister Packet *pk; Xint len; Xchar *data; X{ X register int diff = pk->len-len; /* packet/data size mismatch */ X X pk->segp = pk->data; /* set up write pointer */ X pk->segl = len; /* actual segment length */ X if (diff < 0 || len < 0) { X DEBUG(7,"gpoke: trouble\n",""); /* something very wrong */ X gfail(); X /* NOTREACHED */ X } else if (diff == 0) { X pk->c = DATA; /* long data segment */ X } else if (diff <= 127) { X pk->c = SHORT; /* short data segment */ X *pk->segp++ = diff; /* one difference byte */ X } else if (diff > 127) { X pk->c = SHORT; /* tiny data segment */ X *pk->segp++ = diff|0200; /* two difference bytes */ X *pk->segp++ = diff>>7; X } X memcpy(pk->segp,data,pk->segl); /* copy data into packet */ X} X X/* gpeek - prepare newly packet for reading */ X Xstatic void gpeek(pk) Xregister Packet *pk; X{ X register int diff; X X pk->segp = pk->data; /* set up read pointer */ X if (TYPE(pk->c) == DATA) { X diff = 0; /* long data segment */ X } else if (TYPE(pk->c) != SHORT) { X DEBUG(7,"gread: trouble\n",""); /* something funny */ X gfail(); X /* NOTREACHED */ X } else if ((diff = *pk->segp++&0377)&0200) {/* short data segment */ X diff = (diff&0177)|((*pk->segp++&0377)<<7); X } X pk->segl = pk->len-diff; /* actual segment size */ X DEBUG(9,"rcv: data %d bytes\n",pk->segl); X} X X/* memcpy - not-so-efficient implementation */ X Xstatic void memcpy(dst,src,len) Xregister char *dst,*src; Xregister int len; X{ X while (len-- > 0) X *dst++ = *src++; X} END_OF_main/gpres.c if test 6380 -ne `wc -c
main/mbox.c <<'END_OF_main/mbox.c' X/*++ X/* NAME X/* mbox 3 X/* SUMMARY X/* display and manipulate one non-work message X/* PROJECT X/* pc-mail X/* PACKAGE X/* mail X/* SYNOPSIS X/* int mbox(meta,msgid) X/* int meta; X/* unsigned msgid; X/* DESCRIPTION X/* mbox() is invoked when the user has selected a non-work mail message. X/* It instructs the pager to display the selected mail message. X/* The user has the usual options for manipulating the message X/* being displayed. X/* X/* The meta parameter indicates the message type, X/* and msgid is the numerical message id. If the message file is being X/* read for the first time (meta == NEW_META), it will be marked as read X/* by renaming the meta file. X/* X/* Message header lines can be suppressed selectively (see setup). X/* FILES X/* $MAILDIR/?nnnnn, message and meta files X/* SEE ALSO X/* pager(3), pager(5), kbdinp(3) X/* DIAGNOSTICS X/* If a selected mail message could not be found an error message X/* is displayed instead. X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Sun Apr 5 13:01:12 GMT+1:00 1987 X/* LAST MODIFICATION X/* 90/01/22 13:02:11 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include X X#include "defs.h" X#include "path.h" X#include "pager.h" X#include "screen.h" X#include "mail.h" X#include "ascf.h" X#include "params.h" X#include "ms_parse.h" X X /* X * Storage for header lines to be ignored. Both the strings with header X * names, and the pointers to these strings, are kept together. X */ X Xstruct ignore { X char strs[BUFSIZ]; /* null-terminated strings */ X char *ptrs[BUFSIZ / 2]; /* null-terminated list of pointers */ X}; X Xhidden void ign_init(); /* forward declarations */ Xhidden int ign_header(); Xhidden int mbox_filter(); X Xhidden File *letter = 0; /* pager file */ X X/* show_letter - display selected mail message */ X Xhidden int show_letter() X{ X set_pager(letter); /* select message display */ X ds_pager(); /* put it on the screen */ X return (0); /* say screen is ok */ X} X X/* mbox - user has selected a message file */ X Xpublic int mbox(meta, id) Xint meta; Xunsigned id; X{ X static Screen screen[] = { X 'C', "Close",0, initscreen, X 'D', "Delete",delete, delcurr, X 'M', "Mail", mailfile, "Mail a copy of this message", X 'P', "Print",print, printcurr, X 'R', "Reply",reply, "Create reply to sender", X 'S', "Save", save, "Save this message to ordinary file", X 'W', "Work", makework, "Save this message to work file", X '|', "|", filter, "Filter this message through command", X PGUP, PgUp, pu_pager, pageup, X PGDN, PgDn, pd_pager, pagedn, X UP, "Up", up_pager, csrup, X DOWN, "Down", dn_pager, csrdn, X 0, 0, show_letter, "(Reading a mail message)", X }; X char *seen; X X /* X * Mail being read for the first time is renamed to reflect the X * status change. X */ X X if (mbox_filter(letter = open_pager(), message)) { X mesg_pager(letter, m_msgread); /* no file or read error */ X } else if (meta != NEW_META) { /* unread message? */ X /* void */ ; /* no */ X } else if (rename(comment, seen = old_meta(id)) == 0) { X strcpy(comment, seen); /* mark message as read */ X junk_desk(); /* say desk-top outdated */ X } X kbdinp(screen); /* look at the screen */ X close_pager(letter), letter = 0; /* destroy the display */ X return (S_REDRAW); /* force screen redrawing */ X} X X/* mbox_filter - suppress some message-header lines */ X Xhidden int mbox_filter(pp, path) XFile *pp; Xchar *path; X{ X#if (defined(lint) && defined(iAPX286)) X static X#endif X struct ignore ignore; X FILE *fp; X X if ((fp = ascopen(path, "r")) == 0) { X return (1); X } else { X char buf[BUFSIZ]; X int ig_flag = 0; X int ret; X int context = MS_UUCP; X X ign_init(&ignore); /* initialize filter */ X X /* X * The header-line suppression algorithm is effective for RFC822-like X * headers lines only. Its main use is to get rid of the "Received:" X * lines that frequently show up in non-local mail. X */ X X while (ascgets(buf, sizeof(buf), fp)) { X switch (context = ms_parse(context, buf)) { X case MS_UUCP: X app_pager(pp, buf); X break; X case MS_HEADER: X if ((ig_flag = (ign_header(buf, &ignore))) == 0) X app_pager(pp, buf); X break; X case MS_CONT: X if (ig_flag == 0) X app_pager(pp, buf); X break; X case MS_BODY: X app_pager(pp, buf); X break; X } X } X ret = ferror(fp); X ascclose(fp); X return (ret); X } X} X X/* ign_init - setup header lines to be ignored */ X Xhidden void ign_init(ig) Xstruct ignore *ig; X{ X Info *ip = getparams() + P_IGNORE;/* what to ignore */ X char *sp = ":, "; /* string separators */ X char **lp = ig->ptrs; /* separated strings */ X X /* X * This function takes, from the pc-mail setup file, a (blank or X * comma)-separated list with names of mail headers to be ignored when a X * message is displayed. The list of names is broken up into separate X * strings. The result, a null-terminated list of string pointers, is X * stored in the ig argument. We use strtok() for string splitting. Since X * that function destroys its input, and since the user may change the X * setup at any time, we keep in the ig argument a copy of the relevant X * setup information. X */ X X if (ip->strval == 0) { /* nothing to ignore */ X *lp = 0; X } else { /* copy, then split */ X (void) strncpy(ig->strs, ip->strval, sizeof(ig->strs)); X for (*lp = strtok(ig->strs, sp); *lp; *lp = strtok((char *) 0, sp)) X lp++; X } X} X X/* ign_header - do we ignore this header line */ X Xhidden int ign_header(buf, ig) Xregister char *buf; Xstruct ignore *ig; X{ X register int l; /* header name length */ X register char **list; /* ptr to ignored header names */ X X /* Make sure that the header name is followed by a colon */ X X for (list = ig->ptrs; *list; list++) { X if (buf[l = strlen(*list)] == ':' && istrncmp(buf, *list, l) == 0) X return (1); X } X return (0); X} END_OF_main/mbox.c if test 6112 -ne `wc -c
main/nmail.c <<'END_OF_main/nmail.c' X/*++ X/* NAME X/* nmail X/* SUMMARY X/* extract originator and subject from new mail received by cico X/* PROJECT X/* pc-mail X/* PACKAGE X/* nmail X/* SYNOPSIS X/* nmail [-d debuglevel] X/* DESCRIPTION X/* nmail searches for new mail files received by cico and extracts X/* the originator's name, and message subject for later use by the X/* mail visual shell. X/* X/* Return address formats we understand (in order of preference): X/* .nf X/* X/* From: address (full_name) (take full_name) X/* From: full_name
(take full_name) X/* From: address (take address) X/* >From address (take address) X/* From address (take address) X/* X/* .fi X/* To avoid tampering, new files will have read-only permission. X/* X/* In order to avoid corruption, control-c interrupts are disabled X/* while this program executes. X/* FILES X/* In the spool directory: X/* n received mail message X/* h extracted originator name, subject X/* SEE ALSO X/* path(5) spool directory, file names X/* cico(1) network process X/* mail(1) visual mail shell X/* DIAGNOSTICS X/* Exit status zero when no errors were detected, nonzero in case of file X/* access errors. See status(5) for error codes. X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Tue Mar 31 20:14:11 GMT+1:00 1987 X/* LAST MODIFICATION X/* 90/01/22 13:02:20 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include X#include X#include X#include X X#include "defs.h" X#include "ndir.h" X#include "path.h" X#include "status.h" X#include "ms_parse.h" X Xextern struct tm *localtime(); /* system functions */ X Xhidden void parse_args(); /* forward declarations */ Xhidden void newmail(); Xhidden void extract(); Xhidden void usage(); X Xhidden int dflag = 0; /* debugging option */ X X#define debug if (dflag) printf X Xpublic char *progname = "nmail"; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X signal(SIGINT, SIG_IGN); /* disable ctrl-c */ X parse_args(argc, argv); /* parse command args */ X if (pathinit()) /* get path info */ X exit(E_NOSPOOL); /* bad MAILDIR variable */ X umask(0222); /* make files read-only */ X newmail(); /* get headers from new mail */ X exit(0); X /* NOTREACHED */ X} X X/* parse_args - process command-line arguments */ X Xhidden void parse_args(argc, argv) Xint argc; Xchar **argv; X{ X while (--argc && *++argv && **argv == '-') {/* process options */ X switch (*++*argv) { X case 'd': /* turn debugging on */ X if (--argc == 0) X usage("missing debugging level argument"); X if ((dflag = atoi(*++argv)) < 0 || dflag > 9) X dflag = 0; X break; X default: /* unknown option */ X usage("invalid option: -%c", **argv); X break; X } X } X X /* check for extraneous arguments */ X X if (argc > 0) X usage("unexpected argument: %s", *argv); X} X X/* scan for new mail that hasn't gotten yet a metafile */ X Xhidden void newmail() X{ X register DIR *dp; X struct direct *de; X unsigned msgno; X X debug("directory: \"%s\"\n", maildir); X X /* X * Scan the spool directory for newly-arrived mail. X * X * Incoming mail message files have a name of "n". The originator X * name is normally present in files with names "h" or "o". X * The presence of an "o" file implies that the file "n" has been X * read by the user. An "h" file means that the user has not yet read the X * message file. X * X * If a message file has no corresponding "h" or "o" file we assume it is a X * new mail message and create an "h" file with the name of the X * originator and the subject of the message. X */ X X if ((dp = opendir(maildir)) == 0) X exit(E_NOSPOOL); X X while (de = readdir(dp)) { X debug("nmail: file \"%s\"\n", de->d_name); X if (de->d_name[0] == NEW_MESG X && (msgno = seqno(de->d_name))) { X if (access(old_meta(msgno), 4) == 0) { X /* already marked as read */ ; X } else if (access(new_meta(msgno), 4) == 0) { X /* already marked as unread */ ; X } else { /* create meta file */ X extract(new_mesg(msgno), new_meta(msgno)); X } X } X } X closedir(dp); X} X X/* extract - extract originator and subject info from mail file to meta file */ X Xhidden void extract(mail, meta) Xchar *mail; Xchar *meta; X{ X FILE *mesgfp, X *metafp; X char line[MAXLINE]; X char from[MAXLINE]; /* name of sender */ X char subj[MAXLINE]; /* message subject */ X int context = MS_UUCP; X X debug("-- \"%s\" -> \"%s\"\n", mail, meta); X X if ((mesgfp = fopen(mail, "r")) == NULL) /* cannot open existing file */ X exit(E_SYSFAIL); X X strcpy(from, "Somewhere"); /* default originator */ X subj[0] = '\0'; /* initialize subject */ X X /* X * Some mailers generate real headers, separated from the message body by X * an empty line. So we stop when we find an empty line. Other mailers X * have no headers, so we stop when we see no header line. The following X * algorithm tries to extract the real user name if possible, otherwise X * it takes whatever it can get. X */ X X while ((context != MS_BODY) && fgets(line, sizeof(line), mesgfp)) { X switch (context = ms_parse(context, line)) { X case MS_UUCP: X if (sscanf(line, "%*[>] From %s", from) != 1) X (void) sscanf(line, "From %s", from); X break; X case MS_HEADER: X if (hscanf(line, "Subject:", " %[^\n]", subj) == 0 X && hscanf(line, "From:", " %*s ( %[^)] )", from) == 0) X (void) hscanf(line, "From:", " %[^<] <", from); X break; X } X } X X /* carefully check all went well */ X X if (ferror(mesgfp)) /* sorry, read problem */ X exit(E_READERR); X if ((metafp = fopen(meta, "w")) == NULL) /* cannot create metafile */ X exit(E_WRITERR); X fprintf(metafp, "%s\n%s\n", from, subj); /* write originator, subject */ X if (ferror(metafp)) { X (void) fclose(metafp); /* ms-dog needs this! */ X (void) chmod(meta, 0666); /* sorry, write problem */ X (void) unlink(meta); /* delete metafile */ X exit(E_WRITERR); X } X (void) fclose(mesgfp); X (void) fclose(metafp); X} X X/* usage - explain what is wrong */ X X/* VARARGS */ X Xhidden void usage(va_alist) Xva_dcl X{ X va_list ap; X char *fmt; X X va_start(ap); X fmt = va_arg(ap, char *); X vfprintf(stderr, fmt, ap); X va_end(ap); X fprintf(stderr, "\nusage: nmail [-d debugging_level]\n"); X exit(2); X} END_OF_main/nmail.c if test 6579 -ne `wc -c
main/startup.c <<'END_OF_main/startup.c' X/*++ X/* NAME X/* startup 3 X/* SUMMARY X/* startup/terminate network protocol X/* PROJECT X/* pc-mail X/* PACKAGE X/* cico X/* SYNOPSIS X/* startproto() X/* X/* endproto() X/* DESCRIPTION X/* startproto() should be called after a successfull login on a remote X/* host. It performs the primary handshake with the other system X/* (call accepted/locked) and negotiates a communications protocol. X/* It then sets the function pointers Close/Read/Write to the X/* appropriate values. Until endproto() is called, all i/o to the X/* remote host should proceed through the functions pointed to by X/* Read/Write. X/* X/* endproto() turns the protocol off, and sends the silly "OO" message X/* to the remote system. It does not disconnect, nor does it change X/* the state of the communications port. X/* FUNCTIONS AND MACROS X/* xgetc(), xwrite(), trap() X/* DIAGNOSTICS X/* The process of negotiation is shown when debugging is enabled. X/* startproto() and endproto() return 0 in case of success, E_LOST X/* if no response arrived and E_REJECT if the response differed X/* from the expected response. X/* BUGS X/* startproto() assumes that the local system is the calling system. X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Fri Mar 27 13:43:00 GMT+1:00 1987 X/* LAST MODIFICATION X/* 90/01/22 13:02:41 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include X X#include "defs.h" X#include "params.h" X#include "comm.h" X#include "logs.h" X#include "status.h" X#include "sysdep.h" X X/* forward declarations */ X Xhidden char *xpct(); /* expect a string */ Xhidden char *send(); /* send a string */ X X/* the functions that inplement the various protocols */ X Xextern kopen(), kclose(), kread(), kwrite(); /* k protocol */ Xextern gopen(), gclose(), gread(), gwrite(); /* g protocol */ X Xtypedef struct proto { X char name; /* name of the protocol */ X int (*open) (); /* the open function */ X int (*close) (); /* the close function */ X int (*read) (); /* the read function */ X int (*write) (); /* the write function */ X}; X X/* the order of protocols is significant! */ X Xhidden struct proto ptbl[] = { X 'k', kopen, kclose, kread, kwrite, /* try this first */ X 'g', gopen, gclose, gread, gwrite, /* then this one */ X /* add your protocols at the appropriate place */ X 0, /* terminator! */ X}; X X/* startproto - do primary handshake, establish protocol and turn it on */ X Xpublic startproto() X{ X int *savetrap = systrap; X jmp_buf mytrap; X register struct proto *pp; X register char *cp; X int status; X X if (status = setjmp(systrap = mytrap)) { /* get here if expect fails */ X systrap = savetrap; X return (status); X } X /* the primary handshake: who are we and is it ok we call right now */ X X sscanf(xpct("Shere"), "Shere=%s", rmthost); /* try to get host name */ X log("SUCCEEDED (call to %s)", rmthost); X X /* some uucico implementations seem to have problems with debug level 0 */ X X send(strcons("S%s -x%d", LOGIN_NAME, MAX(dflag, 1))); X xpct("ROK"); /* we're accepted or rejected */ X X /* choose a protocol from the list offered by the other side */ X X for (cp = xpct("P") + 1, pp = ptbl; pp->name && !index(cp, pp->name); pp++) X /* void */ ; X if (pp->name == 0) { /* no common protocol */ X send("UN"); X trap(E_REJECT, "FAILED (no common protocol in \"%s\")", cp); X /* NOTREACHED */ X } X send(strcons("U%c", pp->name)); /* my choice of protocol */ X X /* install protocol */ X X Close = pp->close; /* for endproto() */ X Read = pp->read; X Write = pp->write; X if (pp->open && CALL(pp->open) (ttfd)) /* start up a protocol */ X trap(E_LOST, "FAILED (startup)"); X X /* finish up */ X X log("OK (startup)"); X systrap = savetrap; /* get here if expect wins */ X return (0); X} X X/* endproto - terminate protocol */ X Xpublic endproto() X{ X int *savetrap = systrap; /* save exception handler */ X jmp_buf mytrap; X int status; X X if (Close) /* check there is one */ X CALL(Close) (ttfd); /* turn protocol off */ X send("OOOOOO"); /* byebye */ X X /* Don\'t wait for the other side\'s OOOOOO, just sleep and quit. */ X X (void) sleep(1); X log("OK (conversation complete)"); X return (0); X} X X/* send - write message to remote host and return pointer to message */ X Xhidden char *send(str) Xchar *str; X{ X xwrite(ttfd, "\020", 1); /* message header */ X xwrite(ttfd, str, strlen(str) + 1); /* include trailing null */ X debug(4) ("send: %S\n", str); X return (str); /* return the message */ X} X X/* xpct - read message from host in "^Pmessage[\0\n]" format; trap on errors */ X Xhidden char *xpct(pat) Xchar *pat; X{ X register int c; X register char *p = msgin; X register int inmsg = 0; X X /* X * Keep reading until we time out, or until a complete message has been X * received. Consider the link as lost in case of time out. Assume we are X * rejected if the received message differs from what was expected. X */ X X debug(4) ("xpct: %S\n", pat); X X for (;;) { X if ((c = xgetc()) == EOF) { X trap(E_LOST, "FAILED (protocol handshake)"); X /* NOTREACHED */ X } else if ((c &= 0177) == '\020') { X debug(4) (" got sync\n"); /* got start of message */ X p = msgin; /* reset */ X inmsg = 1; X } else if (inmsg == 0) { X debug(4) ("%C", c); /* don\'t store, just debug */ X } else if (*p++ = ((c == '\n') ? '\0' : c)) { X debug(4) ("%C", c); /* store and debug */ X if (p >= msgin + MSGBUF) { /* spurious Ctrl-P seen? */ X p = msgin; /* reset */ X inmsg = 0; /* reset */ X } X } else if ((debug(4) ("\n")), strncmp(pat, msgin, strlen(pat)) == 0) { X return (msgin); /* expect succeeded */ X } else { X msgin[30] = '\0'; /* truncate to 30 */ X trap(E_REJECT, "FAILED (%S)", msgin); X /* NOTREACHED */ X } X } X} END_OF_main/startup.c if test 6115 -ne `wc -c
main/str.c <<'END_OF_main/str.c' X/*++ X/* NAME X/* strcons,istrcmp,strvec,vecstr,split 3 X/* SUMMARY X/* string utility routines X/* PROJECT X/* pc-mail X/* PACKAGE X/* general stuff X/* SYNOPSIS X/* #include "defs.h" X/* X/* char *strcons(format,args) X/* char *format; X/* X/* char *split(cpp, sep) X/* char **cpp; X/* char *sep; X/* X/* int istrncmp(s1,s2,n) X/* char *s1,s2; X/* int n; X/* X/* int istrcmp(s1,s2) X/* char *s1,s2; X/* X/* char **strvec(string,separ) X/* char *string; X/* char *separ; X/* X/* freevec(vec) X/* char **vec; X/* X/* char *vecstr(vector,separ) X/* char **vector; X/* char *separ; X/* DESCRIPTION X/* strcons() produces a formatted string, using printf()-like X/* arguments. Basically it is an sprintf() that returns a X/* pointer to the result. memory for the result is taken from X/* a small memory pool that is recycled upon successive calls. X/* X/* split() searches the string pointed to by cpp for the occurrance X/* of the text token (a string not containing any of the characters X/* given in the "sep" argument). *cpp is updated if a token is X/* found; a null pointer is returned otherwise. This function X/* is an attempt to improve upon the strtok() function, which X/* can parse only one string at a time. It still modifies its X/* arguments, however. X/* X/* istrcmp() is a case-insensitive version of the strcmp() function. X/* X/* istrncmp() is a case-insensitive version of the strncmp() function. X/* X/* strvec() breaks a null-terminated string using the separators given X/* in separ, and returns a null-terminated vector of pointers to the X/* resulting substrings. Memory for the vector and substrings are X/* allocated in dynamic memory. The original string is not modified. X/* X/* freevec() frees storage allocated by strvec(). X/* X/* vecstr() takes a null-terminated vector of string pointers X/* and builds a string from the strings pointed to by the vector X/* argument, separated by the string in the separ argument. X/* Memory for the result is allocated in dynamic memory. X/* FUNCTIONS AND MACROS X/* strtok(), malloc(), memcpy(), sprintf() X/* DIAGNOSTICS X/* strvec(), vecstr() return a null pointer if there was not enough memory X/* avaliable to hold the result. X/* BUGS X/* strcons() does not do smart garbage collection; it just uses X/* a circular buffer. The present implementation is not portable X/* to machines that pass arguments via registers. X/* X/* strvec() cannot handle strings with more than BUFSIZ words. X/* strvec() uses strtok(), which may have side effects. X/* AUTHOR(S) X/* W.Z. Venema X/* Eindhoven University of Technology X/* Department of Mathematics and Computer Science X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands X/* CREATION DATE X/* Tue Apr 5 20:59:29 MET 1988 X/* LAST MODIFICATION X/* 90/01/22 13:02:43 X/* VERSION/RELEASE X/* 2.1 X/*--*/ X X#include X#include X#include X X#include "defs.h" X X#define NBUF 4 X X/* strcons - quick-and-dirty string constructor */ X X/* VARARGS */ X Xchar *strcons(va_alist) Xva_dcl X{ X va_list ap; X static char strbuf[NBUF][BUFSIZ]; X static int where = 0; X register char *cp; X char *fmt; X X va_start(ap); X fmt = va_arg(ap, char *); X (void) vsprintf(cp = strbuf[where = (where + 1) % NBUF], fmt, ap); X va_end(ap); X return (cp); X} X X/* istrcmp - case-insensitive string comparison */ X X#define LOW(c) (isascii(c)&&isupper(c)?tolower(c):(c)) X Xint istrcmp(s1, s2) Xregister char *s1; Xregister char *s2; X{ X while (*s1 && (LOW(*s1) == LOW(*s2))) X s1++, s2++; X return (LOW(*s1) - LOW(*s2)); X} X X/* istrncmp - case-insensitive string comparison */ X X#define LOW(c) (isascii(c)&&isupper(c)?tolower(c):(c)) X Xint istrncmp(s1, s2, n) Xregister char *s1; Xregister char *s2; Xregister int n; X{ X while (n > 0 && *s1 && (LOW(*s1) == LOW(*s2))) X n--, s1++, s2++; X return (n > 0 ? LOW(*s1) - LOW(*s2) : 0); X} X X/* strvec - make vector of substring pointers */ X Xchar **strvec(str, sep) Xchar *str; Xchar *sep; X{ X#ifdef lint X static X#endif X char *tmp[BUFSIZ]; /* scratch substring pointer storage */ X register char **cpp = tmp; X char *sp; /* ptr to private copy of original */ X register int bytec; X X /* make a copy of the original string */ X X if ((sp = malloc(strlen(str) + 1)) == 0) X return (0); X (void) strcpy(sp, str); X X /* chop our copy at sequences of one or more separators */ X X for (*cpp = strtok(sp, sep); *cpp; *++cpp = strtok((char *) 0, sep)) X /* void */ ; X X /* now construct the vector of pointers to the substrings */ X X if ((cpp = (char **) malloc(bytec = (cpp - tmp + 1) * sizeof(*cpp))) == 0) X return (0); X return ((char **) memcpy((char *) cpp, (char *) tmp, bytec)); X} X X/* freevec - release storage allocated by strvec() */ X Xfreevec(vec) Xchar **vec; X{ X free(vec[0]); X free((char *) vec); X} X X/* vecstr - from null-terminated vector of string pointers to one flat string */ X Xpublic char *vecstr(vec, sep) Xchar **vec; Xchar *sep; X{ X register char **cpp; X register int len = 0; /* length of final string */ X register char *cp; X register int flen = strlen(sep); /* filler between substrings */ X X /* find out how big the resulting string will be */ X X for (cpp = vec; *cpp; cpp++) X len += strlen(*cpp) + flen; X X /* allocate and initialize the result string */ X X if ((cp = malloc(len + 1)) == 0) X return (0); X *cp = '\0'; X X /* fill the resulting string */ X X for (cpp = vec; *cpp; cpp++) { X (void) strcat(cp, *cpp); X (void) strcat(cp, sep); X } X return (cp); X} X X/* split - return next token in *cpp, update cpp */ X Xpublic char *split(cpp, sep) Xregister char **cpp; Xregister char *sep; X{ X register char *start; X char *end; X char *strpbrk(); X X /* X * Find the beginning of the first token. If none is found, just return a X * null value. Otherwise, if there is a separator that follows the token, X * nullify it and advance *cpp to the first character after the nullified X * separator. If the token is not followed by a separator advance *cpp to X * the null byte that follows the token. X */ X X start = *cpp + strspn(*cpp, sep); X X if (start[0] == 0) { X return (0); /* no token */ X } else if (end = strpbrk(start, sep)) { /* look for separator */ X *end = '\0'; /* nullify */ X *cpp = end + 1; /* advance beyond end */ X return (start); /* return token */ X } else { X *cpp = start + strlen(start); /* advance to terminator */ X return (start); /* return token */ X } X} END_OF_main/str.c if test 6415 -ne `wc -c