XENIX mail frontend (source)

Scotty root at ozdaltx.UUCP
Tue Jun 7 02:21:58 AEST 1988


After years of hating the supplied mailer, I finially got
off my duff and wrote this.  My BBS users seem to like it as
it is considerablely more friendly to a non-*NIX, BBS type
person.

It compiles without anything fancy in the command line and
should run on any XENIX system that has the standard
features.  Plenty of comments are scattered in the code so
if you get the urge to hack, you should have an idea of what
I am doing.  I appreciate knowing of changes or
modifications.  This is not overly elegant code not chock
full of features. It is simple and it works which was the
main intention to begin with.  This post is not shared, so
just pop it into the editor and strip this header and
signature lines.

Hope you find it useful.
============================== CUT HERE ==============================
/*  bbsmail.c    June 5, 1988
**  A user friendly frontend to the standard XENIX mail program.
**  Should fly on any SCO XENIX system than hasn't had the supplied
**  mail program tinkered with.
**  Compile:  cc -s -O bbsmail.c -o bbsmail
**
**  Author:    Robert Scott   (ihnp4!killer!ozdaltx!root)
**          AIDS Information Exchange
**          PO BOX 35141
**          Dallas, Tx 75235
**
**  Use the code as you wish, if you do, at least be kind enough
**  to give me credit for my time.  I'd also appreciate knowing of
**  changes/modifications.
*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#define MAXLET    200        /* Number of message the program will handle */
#define TBUF    TBUF        /* Size for the buffers */

/* define the header structure */
struct    let    {
    long    adr;            /* offset in file */
    char    chg;            /* deleted, etc */
    int        llns;            /* lines in letter */
    char    send[24];        /* display sender (e.g., !login) */
    char    subj[33];        /* subject of letter */
    char    post[25];        /* date letter was received */
} let[MAXLET];

char *getlogin(), *getenv();
FILE *popen(), *pipefp, *fopen();
int pclose(), atoi();
int stat();

main(argc, argv)
char *argv[];
int argc;
{

    FILE *mfp;
    char *iam, tmpline[TBUF], lsend[TBUF];
    char *progname, *point;
    static char mailspool[32];
    char answer[6];
    int lastcount, i, j, count, mesno, curmsg, ianswer;
    long ftell(), lastsize;

    struct stat stbuf;

    progname = *argv;
    iam=getlogin();        /* find out who i am */

/* Use the mailspool by default, otherwise use the filename
   provided as an argument */
    if(argc == 1){
        sprintf(mailspool,"/usr/spool/mail/%s",iam);
    } else {
        sprintf(mailspool,"%s",*++argv);
        printf("Accessing: %s\n",mailspool);
    }
/* Initialize the structure, special case for changes for restarts */
    for(i=0; i <= MAXLET; i++)
        let[i].chg = ' ';

again:
    /* continue setting or resetting on restart the structure */
    for(i=0; i <= MAXLET; i++){
        let[i].adr = 0;
        let[i].llns = 0;
    }
/* get the mail file headers */
    count = lastcount = 0;

/* This code is faily straight forward.  It assumes the mail has already
** been processed by a standard mailer and dosn't look for anything
** special.  Should work on any mail header that uses the 
** From user date  syntax as the first line
*/
       if((mfp=fopen(mailspool,"r")) == NULL){
        printf("%s can't open %s\n",progname, mailspool);
        exit(1);
    } else {
        while((fgets(tmpline, TBUF, mfp)) != NULL){
            if(strncmp("From ", tmpline, 5) == 0){
                count++;
                let[count].adr=ftell(mfp) - strlen(tmpline);  /* get the adress */
                for(j=0,i=5; tmpline[i] != ' '; i++,j++){
                    lsend[j] = tmpline[i];  /* skip "From " to determine the sender */
                }
                lsend[j++] = '\0';
                i += 5;
                for(j=0; j < 7; i++,j++)    /* get a shotened date Mmm Dd */
                    let[count].post[j]=tmpline[i];
                let[count].post[j]='\0';
                
/* If the senders address has a !, get just !user as a sender, else
** we assume the sender was local. Display only.
*/
                if((point=strrchr(lsend,'!')) != NULL){
                    strcpy(let[count].send,point);
                } else {
                    strcpy(let[count].send,lsend);
                }
            }
            if(strncmp("Subject: ", tmpline, 9) == 0){
                for(j=0,i=9; j < 32; i++,j++)
                    if(tmpline[i] == '\n'){
                        break;
                    } else {
                        let[count].subj[j]=tmpline[i];
                    }
                let[count].subj[j]='\0';
            }
            let[count].llns++;
        }
    }
    fclose(mfp);
    pheader(count);  /* display the header */
    curmsg = 1;
    stat(mailspool, &stbuf);  /* get the current size of the spool */
    lastsize = stbuf.st_size;
    while(1){
        stat(mailspool, &stbuf);
        if(stbuf.st_size != lastsize){    /* has the spool size changed */
            printf("\n\007New mail has arrived, type 'R' to restart\n");
            lastsize = stbuf.st_size;
            continue;
        }
        if(curmsg - 1 == count){
        printf("\nLast Message: A)ns  D)el  M)box  L)ist  Q)uit  H)elp: ",curmsg);
        } else {
        printf("\n[ Next: %d ] A)ns  D)el  M)box  L)ist  Q)uit  H)elp: ",curmsg);
        }
        fflush(stdout);
/* this part could be beefed up to include a letter-number command */
        fgets(answer,5,stdin);  /* get a command from the KB */
        fflush(stdin);
        mesno = atoi(answer);
        if(mesno > count){
            printf("Only %d messages available\n",count);
            continue;
        }
        if(mesno >= 1){
            curmsg = mesno;
            readmsg(mesno, mailspool);
            curmsg++;
            continue;
        }
        if(mesno == 0){
            ianswer = answer[0];
            switch(tolower(ianswer)){
                case 'r':
                        goto again;
                        continue;
                case 'b':
                case '-':
                        if(curmsg == 1){
                            printf("Can't go past first message\n");
                            continue;
                        }
                        if(curmsg == 2){
                            --curmsg;
                        } else {
                            curmsg -= 2;
                        }
                        readmsg(curmsg, mailspool);
                        curmsg++;
                        continue;
                case '\n':
                case '+':
                    if(curmsg <= count)
                        readmsg(curmsg++, mailspool);
                        continue;
                case 'a':
                        if(curmsg  - 1 == 0)
                            continue;
                        answermsg(curmsg -1, mailspool);
                        continue;
                case 'd':
                        if(curmsg -1 == 0)
                            continue;
                        let[curmsg -1].chg='D';
                        printf("Message %d marked for deletion\n",curmsg -1);
                        continue;
                case 'u':
                        if(let[curmsg -1].chg == 'D'){
                            let[curmsg -1].chg = ' ';
                            printf("Message %d restored\n",curmsg -1);
                        }
                        continue;
                case 'm':
                        if(let[curmsg -1].chg == 'M'){
                            printf("Message %d already moved to mbox\n",curmsg -1);
                            continue;
                        }
                        if(curmsg -1 == 0)
                            continue;
                        mvmbox(curmsg -1, mailspool);
                        let[curmsg -1].chg = 'M';
                        printf("Message %d appended to mbox\n",curmsg -1);
                        continue;
                case 'l':
                        pheader(count);
                        continue;
                case 'q':
                        cleanup(count,mailspool);
                        exit(0);
                case 'h':
                case '?':
                        printhelp();
                        continue;
                default:
                        continue;
            }  /* end switch */
        }
        continue;
    }
}
/* Print the mail header - Use your favorite pager program
** Again this could be fancied up to check the envron
** for a PAGER variable
 */
pheader(c)
int c;
{
    int i, j;
    j = c;
    if((pipefp = popen("more","w")) == NULL){
        printf("Header can't pipe to MORE\n");
        exit(1);
    } else {
        fprintf(pipefp, "%d Messages:\n",j);
        fprintf(pipefp, "Stat #  From      Date      Subject                Lines\n");
        fprintf(pipefp, "========================================================\n");
        for(i=1; i <= j; i++)
            fprintf(pipefp, "%c  %3d %-10.10s (%-6.6s)  %-24.24s %d\n",
let[i].chg,i, let[i].send,let[i].post, let[i].subj, let[i].llns);
    }
    fflush(pipefp);
    pclose(pipefp);
}
/* get the current message ,cmsg, from the file, ms
** and run it through the pager  */
readmsg(cmsg, ms)
int cmsg;
char *ms;
{
    int c, i;
    char *m, mbuf[TBUF];
    FILE *mfp;

    m = ms;
    if((mfp=fopen(m,"r")) == NULL){
        printf("readmsg can't reopen %s\n",m);
        exit(1);
    }
    if((fseek(mfp, let[cmsg].adr, 0)) < 0){
        printf("readmsg can't fseek to message\n");
        exit(1);
    }
    if((pipefp =popen("more","w")) == NULL){
        printf("readmsg can't pipe to MORE\n");
        exit(1);
    } else {
        if(let[cmsg].chg == 'A')
            fprintf(pipefp," ==>  ANSWERED MESSAGE NO: %d  <==\n",cmsg);
        if(let[cmsg].chg == 'D')
            fprintf(pipefp," ==>  DELETED MESSAGE NO: %d  <==\n",cmsg);
        if(let[cmsg].chg == 'M')
            fprintf(pipefp," ==> MOVED MESSAGE NO: %d  <==\n",cmsg);
        if(let[cmsg].chg == ' ')
            fprintf(pipefp," ==>  MESSAGE NO: %d  <==\n",cmsg);
        for(i=0; i < let[cmsg].llns; ++i){
            fgets(mbuf, TBUF, mfp);
            fputs(mbuf, pipefp);
        }
    }
    fclose(mfp);
    pclose(pipefp);
}
/* Move the message to the users private mbox */
mvmbox(cmsg, ms)
int cmsg;
char *ms;
{
    int i;
    char  mymbox[48], mbuf[TBUF];
    FILE *sfp, *mbfp;

    sprintf(mymbox,"%s/mbox", getenv("HOME"));
    if((sfp=fopen(ms,"r")) == NULL){
        printf("mvmbox can't reopen %s\n",ms);
        exit(1);
    }
    if((fseek(sfp, let[cmsg].adr, 0)) < 0){
        printf("readmsg can't fseek to message\n");
        exit(1);
    }
    if((mbfp = fopen(mymbox,"a")) == NULL){
        printf("mvmbox can't append to %s\n", mymbox);
        exit(1);
    } 
    for(i=0; i < let[cmsg].llns; ++i){
        fgets(mbuf, TBUF, sfp);
        fputs(mbuf, mbfp);
    }
    fflush(mbfp);
    fflush(sfp);
    fclose(sfp);
    fclose(mbfp);
}
/* Answer the current message. Here we do check for the envron variable
** EDITOR. The default is vedit, but you can subsitiute your flavor
** of choice.  Uses mail -e to actually send a message.
*/
answermsg(cmsg, spool)
int cmsg;
char *spool;
{
    char *editor, tmpfile[32];
    char from[TBUF], tbuf[TBUF], cbuf1[64], cbuf2[TBUF];
    char c[2];
    int i,j;
    long tsize;
    FILE *tmpfp, *sfp;

    struct stat stbuf;

    if((editor = getenv("EDITOR")) == NULL)
            editor="vedit";
    sprintf(tmpfile,"/tmp/Am%06d",getpid());

    if((sfp = fopen(spool,"r")) == NULL){
        fprintf(stderr," Answer can't reopen %s\n",spool);
        return;
    }
    fseek(sfp, let[cmsg].adr, 0);  /* who is the message from */
    fgets(tbuf, TBUF, sfp);
    for(i=0; tbuf[i] != ' '; i++)
        ;
    i++;
    for(j=0; tbuf[i] != ' '; i++, j++)
        from[j] = tbuf[i];
    from[j++] = '\0';
    fprintf(stderr, "Answering Message %d To: %s\n",cmsg, from);
    fprintf(stderr, "Include copy of message in your reply? ");
    fflush(stderr);
    fgets(c,2,stdin);
    fflush(stdin);
    if((tmpfp = fopen(tmpfile, "w")) == NULL){
        printf("Answer can't open temp file\n");
        fclose(sfp);
        return;
    }
/* set up the header in the temp file */
    fprintf(tmpfp,"~s Re: %s\n",let[cmsg].subj);
    fprintf(tmpfp, "In-Reply-To: Your message of %s\n\n",let[cmsg].post);

/* put a copy of the senders letter in the temp file */
    if(c[0] == 'Y' || c[0] == 'y'){
        fprintf(tmpfp, "> %s", tbuf);
        for(i=1; i < let[cmsg].llns; ++i){
            fgets(tbuf, TBUF, sfp);
            fprintf(tmpfp, "> %s", tbuf);
        }
        fflush(sfp);
    }
    fflush(tmpfp);
    fclose(tmpfp);
    fclose(sfp);
/* check the size of the temp file */
    stat(tmpfile, &stbuf);
    tsize = stbuf.st_size;

    sprintf(cbuf1,"%s %s",editor, tmpfile);

/* Some editors, like my home-brew, issues an exit(1) on an
** aborted message
*/
    if((system(cbuf1)) != 0){
        fprintf(stderr,"Answer aborted\n");
        unlink(tmpfile);
        return;
    }

/*  Check the temp file size again, if the file size didn't change
**  then just return and forget it
*/
    stat(tmpfile, &stbuf);
    if(stbuf.st_size == tsize){
        fprintf(stderr,"File unchanged, no message sent\n");
        unlink(tmpfile);
        return;
    }

/* for some reason cat seems to work better than using
**  mail -e user < file
*/
    sprintf(cbuf2, "cat %s | mail -e %s ",tmpfile, from);
    system(cbuf2);
    unlink(tmpfile);  /* get rid of the temp file */
    let[cmsg].chg='A';  /* mark the message as answered */
}
/*  Simple cleanup.  Removes deleted, moved or answered messages
**  from the mailspool into a temp file and copys that file
**  back to the mailspool.
*/
cleanup(n,sfile)
int n;
char *sfile;
{
    int i, j, number, nosaved = 0;
    char buf[TBUF], *tmpfile, *mktemp();
    FILE *spool, *tmp;

    number = n;
    if((tmpfile = mktemp("/tmp/CMXXXXXX")) == NULL){
        printf("Cleanup can't make tempfile\n");
        return;
    }
    if((tmp = fopen(tmpfile, "a")) == NULL){
        printf("Cleanup can't open tempfile\n");
        return;
    }
    if((spool = fopen(sfile, "r")) == NULL){
        printf("Cleanup can't open mail spool\n");
        return;
    }
    for(i=1; i <= number; i++){
        if(let[i].chg == 'D' || let[i].chg == 'M' || let[i].chg == 'A')
            continue;
        if((fseek(spool, let[i].adr, 0)) < 0){
            printf("Cleanup can't seek spoolfile\n");
            return;
        }
        for(j =0; j < let[i].llns; ++j){
            fgets(buf, TBUF, spool);
            fputs(buf, tmp);
        }
        nosaved++;
        fflush(spool);
        fflush(tmp);
    }
    fclose(spool);
    fclose(tmp);
    if((tmp = fopen(tmpfile, "r")) == NULL){
        printf("Cleanup can't open tempfile for update\n");
        return;
    }
    if((spool = fopen(sfile, "w+")) == NULL){
        printf("Cleanup can't open mail spool for update\n");
        return;
    }
    while((fgets(buf, TBUF, tmp)) != NULL)
        fputs(buf, spool);
    fflush(spool);
    fflush(tmp);
    fclose(spool);
    fclose(tmp);
    fclose(tmp);
    unlink(tmpfile);
    printf("Saved %d messages in %s\n", nosaved, sfile);
    return;
}
/* the help file - what else can be said? */
printhelp()
{
printf("\n MAIL HELP\n===================\n");
printf("ENTER/RETURN or + advances to the next message\n");
printf("    or type the message number.\n");
printf("-  or B  moves back to the previous message\n");
printf("A  Lets you send a reply to the message you just read.\n");
printf("   Answered mail is also deleted\n");
printf("D  That message will be deleted when you exit mail.\n");
printf("M  Transfers the message to your private mailbox (mbox).\n");
printf("   Also deletes message upon exiting mail.\n");
printf("U  Undeletes messages.\n");
printf("L  Lists the messages in order received.\n");
printf("H or ? prints this help.\n");
printf("Q  Exits mail.\n\n");
printf("Commands may be upper or lower case.\n\n");
}

-- 
 Scotty
 AIDS INFORMATION EXCHANGE BBS
 (214) 247-2367/247-5609
 ihnp4!killer!ozdaltx!sysop 



More information about the Comp.unix.xenix mailing list