home *** CD-ROM | disk | FTP | other *** search
- /*
- * W-MAIL MicroWalt Extended Mail Agent.
- * This module handles the interactive part of the mailer
- * program, when the user wants to read or send mail.
- *
- * Authors: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
- * Message lists inspired by "clp", The Crouton Man.
- * Copyright 1988-1992 MicroWalt Corporation
- */
- #include "wmail.h"
- #include <sys/stat.h>
- #include <setjmp.h>
- #include <signal.h>
-
-
- #define NR_MSGS 20 /* #messages per summary display */
-
-
- typedef struct _mlist_ {
- struct _mlist_ *next; /* next message in list */
- MSG *msg; /* pointer to referenced message */
- } MLIST;
-
-
- static jmp_buf sig_jmp; /* for quitting out of letters */
-
-
- static _PROTOTYPE( void sig_catch, (int) );
- static _PROTOTYPE( void ml_quit, (MLIST *) );
- static _PROTOTYPE( void ml_add, (MLIST **, int) );
- static _PROTOTYPE( void ml_all, (MBOX *, MLIST **) );
- static _PROTOTYPE( void ml_range, (MSG *, MLIST **, int) );
- static _PROTOTYPE( MLIST *ml_scan, (MBOX *, MSG *, char **) );
- static _PROTOTYPE( void do_help, (void) );
- static _PROTOTYPE( void do_save, (MSG *, char *, int) );
- static _PROTOTYPE( void do_prmsg, (MSG *) );
- static _PROTOTYPE( void do_summary, (MBOX *, int) );
- static _PROTOTYPE( void do_mail, (int, char *, MSG *, int) );
-
-
- /* Catch the SIGINT interrupt for interrupting the printing of letters. */
- static void sig_catch(sig)
- int sig;
- {
- (void) sig;
- (void) signal(SIGINT, sig_catch);
- longjmp(sig_jmp, 1);
- }
-
-
- /* Free memory used by do_list(). Sets list to MESG_NIL. */
- static void ml_quit(list)
- register MLIST *list;
- {
- register MLIST *temp;
-
- if (list == (MLIST *)NULL) return;
- while(list != (MLIST *)NULL) {
- temp = list->next;
- free(list);
- list = temp;
- }
- }
-
-
- /* Add a letter to a message list. */
- static void ml_add(listp, msgnum)
- MLIST **listp;
- int msgnum;
- {
- register MLIST *temp, *list;
- MSG *msg;
-
- if ((msg = mb_select(msgnum)) == (MSG *)NULL) {
- fprintf(stderr, "%d: no such letter!\n", msgnum);
- ml_quit(*listp);
- *listp = (MLIST *)NULL;
- return;
- }
-
- /* Allocate an MLIST frame for this letter. */
- if ((temp = (MLIST *)malloc(sizeof(MLIST))) == (MLIST *)NULL) {
- fprintf(stderr, "%d: Out of memory!\n", msgnum);
- ml_quit(*listp);
- *listp = (MLIST *)NULL;
- return;
- }
-
- /* Add to the MLIST chain. */
- if (*listp == (MLIST *)NULL) {
- *listp = temp;
- } else {
- list = *listp;
- while(list->next != (MLIST *)NULL) list = list->next;
- list->next = temp;
- }
- temp->next = (MLIST *)NULL;
- temp->msg = msg;
- }
-
-
- /* Create a list of ALL messages. */
- static void ml_all(mbox, listp)
- MBOX *mbox;
- register MLIST **listp;
- {
- register MSG *msg;
-
- msg = mbox->first;
- while(msg != (MSG *)NULL) {
- ml_add(listp, msg->seq);
- msg = msg->next;
- }
- }
-
-
- /* Create a range of messagesin a list. */
- static void ml_range(curmsg, listp, endnum)
- MSG *curmsg;
- MLIST **listp;
- int endnum;
- {
- register MSG *msg;
-
- /* If no starting number was given, use the current letter. */
- if (*listp == (MLIST *)NULL) ml_add(listp, curmsg->seq);
-
- msg = (*listp)->msg;
- if (msg->seq > endnum) {
- fprintf(stderr, "%d: Smaller than starting number (%d) !\n",
- endnum, msg->seq);
- ml_quit(*listp);
- *listp = (MLIST *)NULL;
- return;
- }
- msg = msg->next;
- while((msg != (MSG *)NULL) && (msg->seq <= endnum)) {
- ml_add(listp, msg->seq);
- msg = msg->next;
- }
- }
-
-
- /*
- * Take a mesg list as imput and create a useful list of messages.
- * It is up to the calling function to figure out if let deleted
- * or not. Input syntax:
- *
- * number mesg numbers.
- * '-' range. If a field is null, use current position
- * '*' all messages
- * '$' last message
- * ' ', '\t', ',' field seprators
- * other next non-list field
- */
- static MLIST *ml_scan(mbox, msg, spp)
- MBOX *mbox;
- MSG *msg;
- char **spp;
- {
- MLIST *list = (MLIST *)NULL;
- register char *sp;
- int range, num;
-
- /* Save headache now, bound check. Are there messages? */
- if (mbox->first == (MSG *)NULL) return((MLIST *)NULL);
-
- /* Quick check for "all messages". */
- sp = *spp;
- while((*sp == ' ') || (*sp == '\t')) sp++;
- if (*sp == '*') {
- ml_all(mbox, &list);
- sp++;
- while ((*sp == ' ') || (*sp == '\t')) sp++;
- *spp = sp;
- return(list);
- }
-
- /* Scan the input text for list entries. */
- range = 0;
- while (sp != (char *)NULL) switch(*sp) {
- case '\0': /* end of text */
- if (range == 1) ml_range(msg, &list, msg->seq);
- while ((*sp == ' ') || (*sp == '\t')) sp++;
- *spp = sp;
- sp = (char *)NULL;
- continue;
- case ' ':
- case '\t': /* whitespace */
- while ((*sp == ' ') || (*sp == '\t')) sp++;
- continue;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9': /* simple number */
- if ((num = atoi(sp)) == 0) num = 1;
- if (range == 1) {
- ml_range(msg, &list, num);
- range = 0;
- } else ml_add(&list, num);
- while((*sp >= '0') && (*sp <= '9')) sp++;
- continue;
- case '-': /* range */
- range = 1;
- sp++;
- continue;
- case ',': /* list */
- sp++;
- continue;
- case '$': /* last message */
- if (mbox->last != (MSG *)NULL) num = mbox->last->seq;
- else if (mbox->first != (MSG *)NULL) num = mbox->first->seq;
- else num = msg->seq;
- if (range == 1) ml_range(msg, &list, num);
- else ml_add(&list, num);
- sp++;
- continue;
- default: /* invalid field, assume next non-list.. */
- if (range == 1) ml_range(msg, &list, msg->seq);
- while ((*sp == ' ') || (*sp == '\t')) sp++;
- *spp = sp;
- sp = (char *)NULL;
- break;
- }
- return(list);
- }
-
-
- /* Give a list of possible commands. */
- static void do_help()
- {
- printf("\n ** W-MAIL Commands **\n\n");
- printf(
- "? This help ! Shell Command Escape\n");
- printf("- Previous letter + Next letter\n");
- printf(
- "<ENTER> Show next letter d Delete current letter\n");
- printf(
- "f Forward a letter F Forward a letter (quoted)\n");
- printf("h Display letter summary m Send mail\n");
- printf("n Same as ENTER p Print a letter again\n");
- printf("q Quit, update mailbox t Type a letter\n");
- printf("s Save current letter u Un-delete a letter\n");
- printf("w Save letter (no header) x Exit, no mbox update\n");
- printf("z Display next summary page\n\n");
- printf("dp Delete current letter, and show next\n");
- printf("r Reply to the current letter (with Reply-To:)\n");
- printf("R Reply to SENDER of current letter\n");
- printf("\n");
- }
-
-
- /* Save the current letter to a disk-file. */
- static void do_save(msg, fname, header)
- MSG *msg;
- char *fname;
- int header;
- {
- char buff[1024];
- char path[PATH_MAX];
- long chars;
- int lines, i;
- FILE *fp;
- #if _UNIX
- int mymask;
- #endif
-
- /* Is this a valid message? */
- if (msg->status == MS_DELETED) {
- fprintf(stderr, "%d: inapropriate message!\n", msg->seq);
- return;
- }
-
- /* Did we get any save-file name? */
- if (*fname == '\0') fname = rc_mbox;
- if (fname == (char *)NULL) fname = SAVEFILE;
-
- /* Generate the complete path name of the save file. */
- #ifdef MSDOS
- if (rc_folder != (char *)NULL) {
- if (*fname == '\\' || *fname == '.') strcpy(path, fname);
- else sprintf(path, "%s\\%s\\%s", u_home, rc_folder,
- (*fname == '+') ? fname+1 : fname);
- } else {
- if (*fname == '\\' || *fname == '.') strcpy(path, fname);
- else sprintf(path, "%s\\%s", u_home, fname);
- }
- #else
- if (rc_folder != (char *)NULL) {
- if (*fname == '/' || *fname == '.') strcpy(path, fname);
- else sprintf(path, "%s/%s", rc_folder,
- (*fname == '+') ? fname+1 : fname);
- } else {
- if (*fname == '/' || *fname == '.') strcpy(path, fname);
- else sprintf(path, "%s/%s", u_home, fname);
- }
- #endif
- if (opt_v == 1) fprintf(stderr, "do_save(\"%s\") --> \"%s\"\n",
- fname, path);
-
- /* Save the current file creation mask. */
- #if _UNIX
- mymask = umask(u_mask);
- #endif
-
- /* Create (or append to) the save file. */
- if ((fp = fopen(path, "a")) == (FILE *)NULL) {
- perror(path);
- #if _UNIX
- (void) umask(mymask);
- #endif
- return;
- }
- msg->curr = msg->start;
-
- /* Skip the header if needed. */
- if (header == 0) {
- do {
- if (mb_rdline(msg, buff) <= 0) break;
- if (opt_v == 1) fprintf(stderr, "SAVE: skip \"%s\"\n", buff);
- if (buff[0] == '\0') break;
- } while(1);
- }
-
- /* Write the message body (poss. with header) to the save file. */
- chars = 0L;
- lines = 0;
- while((i = mb_rdline(msg, buff)) > 0) {
- chars += (long) i;
- lines++;
- if (opt_v == 1) fprintf(stderr, "SAVE: \"%s\"\n", buff);
- fprintf(fp, "%s\n", buff);
-
- }
- (void) fflush(fp);
- if (fclose(fp) < 0) perror(path);
-
- #if _UNIX
- (void) umask(mymask);
- #endif
- }
-
-
- /* Show the contents of a message. */
- static void do_prmsg(msg)
- MSG *msg;
- {
- char buff[1024];
-
- /* It this a valid message? */
- if (msg->status == MS_DELETED) {
- fprintf(stderr, "%d: inapropriate message!\n", msg->seq);
- return;
- } else printf("Message %d:\n", msg->seq);
- msg->curr = msg->start;
-
- /* Yes. Do we have to use the pager? */
- if (opt_p == 0) {
- pg_msg(msg);
- return;
- }
-
- while(mb_rdline(msg, buff) > 0) printf("%s\n", buff);
- (void) fflush(stdout);
- }
-
-
- /* Give a summary of letters. */
- static void do_summary(mbox, nextmsg)
- MBOX *mbox;
- int nextmsg;
- {
- register MSG *msg;
- register int start;
-
- start = (((nextmsg - 1) / NR_MSGS) * NR_MSGS) + 1;
- for (msg = mbox->first; msg->seq < start; msg = msg->next)
- ;
- while ((msg != (MSG *)NULL) &&
- (msg->seq < start + NR_MSGS) && (msg->seq > 0)) {
- printf("%c%c%-2d %-17.17s %-12.12s %4d/%-7ld %-.30s\n",
- (nextmsg == msg->seq) ? '>' : ' ',
- (msg->status == MS_DELETED) ? 'D': ' ',
- msg->seq, msg->realname, msg->date,
- msg->lines, msg->chars, msg->subject);
- msg = msg->next;
- }
- if (mbox->entries >= (start + NR_MSGS))
- printf("%d more message(s)\n", (mbox->entries - start - (NR_MSGS - 1)));
- }
-
-
- /* Send a reply, forward a message or send mail. */
- static void do_mail(what, addr, msg, quote)
- int what;
- char *addr;
- MSG *msg;
- int quote;
- {
- char buff[1024];
- char *rcpts[2];
- DRAFT *draft;
- register FILE *fp;
- register char *sp;
-
- /* See if this can be done at all. */
- if (msg != (MSG *)NULL) {
- if (msg->status == MS_DELETED) {
- fprintf(stderr, "%d: inapropriate message!\n", msg->seq);
- return;
- }
- }
-
- /* First of all, allocate a DRAFT selector. */
- draft = (DRAFT *)NULL;
- rcpts[0] = addr;
- rcpts[1] = (char *)NULL;
-
- /* Now, see what we have to do. */
- switch(what) {
- case 0: /* send mail */
- if (*addr == '\0') {
- do {
- fprintf(stderr, "To: ");
- (void) fflush(stderr);
- (void) fgets(buff, 1024, stdin);
- sp = strchr(buff, '\n');
- if (sp != (char *)NULL) *sp = '\0';
- sp = buff;
- } while (*sp == '\0');
- } else sp = addr;
- draft = mk_draft(1, (char *)NULL, rcpts, "", "", "");
- if (draft == (DRAFT *)NULL) return;
- strncpy(draft->to, sp, 1024);
- break;
- case 1: /* reply to sender */
- draft = mk_draft(0, (char *)NULL, rcpts, "", "", "");
- if (draft == (DRAFT *)NULL) return;
- if (!strncmp(msg->subject, "Re:", 3) ||
- !strncmp(msg->subject, "re:", 3) ||
- !strncmp(msg->subject, "RE:", 3) )
- strcpy(draft->subject, msg->subject);
- else sprintf(draft->subject, "Re: %s", msg->subject);
- break;
- case 2: /* forward a message */
- draft = mk_draft(0, (char *)NULL, rcpts, "", "", "");
- if (draft == (DRAFT *)NULL) return;
- sprintf(draft->subject, "%s (fwd)", msg->subject);
- break;
- }
-
- /* For the "reply" and "forward" modes, copy the message body. */
- if (msg != (MSG *)NULL) {
- /* Create the temporary file. */
- if ((fp = fopen(draft->dpath, "w")) == (FILE *)NULL) {
- perror(draft->dpath);
- rm_draft(draft);
- return;
- }
-
- /* Write the message to the temporary file. */
- msg->curr = msg->start;
- if (what == 2) fprintf(fp, "Forwarded message #1:\n----\n");
- while(mb_rdline(msg, buff) > 0) {
- if (quote || !strncmp(buff, "From ", 5))
- fprintf(fp, ">%s\n", buff);
- else fprintf(fp, "%s\n", buff);
- }
- if (what == 2) fprintf(fp, "----\n\nEnd of forwarded messages.\n");
- (void) fflush(fp);
- (void) fclose(fp);
- }
-
- /* Allow the user to edit the message. */
- if (ed_draft(draft) != 0) {
- rm_draft(draft);
- return;
- }
-
- /* All is well. Send it off... */
- (void) do_send(draft);
-
- /* Clearnup. */
- rm_draft(draft);
- }
-
-
- /* Interactively process the mail-box. */
- int do_cmd(mbox)
- register MBOX *mbox;
- {
- char input[512], *sp;
- MSG *msg, *xmsg;
- MLIST *list, *xlist;
- int intr, first, tmp;
- register char c;
-
- if (mbox->first == (MSG *)NULL) {
- fprintf(stderr, "Corrupted mailbox.\n");
- return(-1);
- }
-
- printf("W-MAIL %s. Type ? for Help.\n", VERSION);
- printf("\"%s\": %d message(s)\n", mbox->path, mbox->entries);
-
- msg = mbox->first;
- first = 1;
- intr = 0;
-
- do_summary(mbox, 1);
-
- while(1) {
- if (opt_q == 0) {
- intr = setjmp(sig_jmp);
- (void) signal(SIGINT, sig_catch);
- }
- if (intr == 1) printf("\n");
- printf(rc_prompt, msg->seq);
- (void) fflush(stdout);
- if (fgets(input, 512, stdin) == (char *)NULL) break;
- #ifdef MSDOS
- if ((sp = strchr(input, '\r')) != (char *)NULL) *sp = '\0';
- #endif
- if ((sp = strchr(input, '\n')) != (char *)NULL) *sp = '\0';
- if (opt_q == 0) (void) signal(SIGINT, SIG_IGN);
-
- /* Look at the first valid character of the command line. */
- sp = input;
- while ((*sp == ' ') || (*sp == '\t')) sp++;
- c = *sp++;
- while ((*sp == ' ') || (*sp == '\t')) sp++;
- switch(c) {
- case '!': /* shell escape */
- (void) do_shell(sp);
- break;
- case '?': /* type some help */
- do_help();
- break;
- case '-': /* show previous message */
- if (msg->prev != (MSG *)NULL) msg = msg->prev;
- else printf("Top of mailbox\n");
- break;
- case '+': /* show next message */
- if (msg->next != (MSG *)NULL) msg = msg->next;
- else printf("At EOF\n");
- break;
- case '\0': /* move to next message and show current */
- case 'n':
- if (first == 0) {
- if (msg->next == (MSG *)NULL) {
- printf(rc_prompt, msg->seq);
- printf("At EOF\n");
- } else msg = msg->next;
- } else first = 0;
- if (msg->status == MS_DELETED) {
- fprintf(stderr, "%d: inapropriate message!\n",
- msg->seq);
- break;
- }
- if (intr == 0) {
- do_prmsg(msg);
- msg->status = MS_READ;
- }
- break;
- case 'd': /* delete a range of letters */
- mbox->update = 1;
- list = ml_scan(mbox, msg, &sp);
- if ((xlist = list) == (MLIST *)NULL) {
- msg->status = MS_DELETED;
- printf("Message %d deleted.\n", msg->seq);
- break;
- }
- while(list != (MLIST *)NULL) {
- list->msg->status = MS_DELETED;
- printf("Message %d deleted.\n",
- list->msg->seq);
- list = list->next;
- }
- if (intr == 0) {
- if ((*sp == 'p') || (*sp == 't')) {
- if (msg->next == (MSG *)NULL) {
- printf("EOF\n");
- break;
- } else msg = msg->next;
- }
- if (*sp == 'p') do_prmsg(msg);
- if (*sp == 't') {
- tmp = opt_p;
- opt_p = 1;
- do_prmsg(msg);
- opt_p = tmp;
- }
- }
- ml_quit(xlist);
- break;
- case 'f': /* forward current letter */
- do_mail(2, sp, msg, 0);
- break;
- case 'F': /* forward current letter (quoted) */
- do_mail(2, sp, msg, 0);
- break;
- case 'h': /* show letter summary */
- list = ml_scan(mbox, msg, &sp);
- if (list != (MLIST *)NULL) {
- do_summary(mbox, list->msg->seq);
- msg = list->msg;
- } else do_summary(mbox, msg->seq);
- ml_quit(list);
- break;
- case 'm': /* send mail */
- do_mail(0, sp, (MSG *)NULL, 0);
- break;
- case 'p': /* print a letter */
- case '.': /* print a letter */
- if (intr == 0) {
- list = ml_scan(mbox, msg, &sp);
- if (list == (MLIST *)NULL) {
- do_prmsg(msg);
- break;
- }
- xlist = list;
- while (list != (MLIST *)NULL) {
- do_prmsg(list->msg);
- list = list->next;
- }
- ml_quit(xlist);
- }
- break;
- case 'q': /* update mailbox and quit */
- return(0);
- /*NOTREACHED*/
- case 'r': /* reply to a message (use Reply-To:) */
- list = ml_scan(mbox, msg, &sp);
- if (list == (MLIST *)NULL)
- do_mail(1, msg->reply, msg, 1);
- else
- do_mail(1, msg->reply, msg, 0);
- ml_quit(list);
- break;
- case 'R': /* reply to SENDER of a message */
- list = ml_scan(mbox, msg, &sp);
- if (list == (MLIST *)NULL)
- do_mail(1, msg->reply, msg, 1);
- else
- do_mail(1, msg->reply, msg, 1);
- ml_quit(list);
- break;
- case 's': /* save message to disk */
- list = ml_scan(mbox, msg, &sp);
- if (list == (MLIST *)NULL) {
- do_save(msg, sp, 1);
- break;
- }
- xlist = list;
- while (list != (MLIST *)NULL) {
- do_save(list->msg, sp, 1);
- list = list->next;
- }
- ml_quit(xlist);
- break;
- case 't': /* type (no paging) current letter */
- if (intr == 0) {
- tmp = opt_p;
- opt_p = 1;
- list = ml_scan(mbox, msg, &sp);
- if (list == (MLIST *)NULL) {
- do_prmsg(msg);
- opt_p = tmp;
- break;
- }
- xlist = list;
- while (list != (MLIST *)NULL) {
- do_prmsg(list->msg);
- list = list->next;
- }
- ml_quit(xlist);
- opt_p = tmp;
- }
- break;
- case 'u': /* un-delete a letter */
- list = ml_scan(mbox, msg, &sp);
- if ((xlist = list) == (MLIST *)NULL) break;
- while(list != (MLIST *)NULL) {
- list->msg->status &= ~MS_DELETED;
- printf("Message %d un-deleted.\n",
- list->msg->seq);
- list = list->next;
- }
- ml_quit(xlist);
- break;
- case 'w': /* write (without header) message to disk */
- list = ml_scan(mbox, msg, &sp);
- if (list == (MLIST *)NULL) {
- do_save(msg, sp, 0);
- break;
- }
- xlist = list;
- while (list != (MLIST *)NULL) {
- do_save(list->msg, sp, 0);
- list = list->next;
- }
- ml_quit(xlist);
- break;
- case 'x': /* abort, do not update mailbox */
- exit(0);
- /*NOTREACHED*/
- case 'z': /* display next summary page */
- if (msg->next == (MSG *)NULL) tmp = msg->seq - 1;
- else tmp = msg->next->seq;
- tmp = ((tmp / NR_MSGS) * NR_MSGS) + NR_MSGS + 1;
- if (tmp > mbox->entries) msg = mbox->first;
- else while (msg->seq != tmp) msg = msg->next;
- do_summary(mbox, msg->seq);
- break;
- default:
- if (isdigit(*--sp)) {
- xmsg = mb_select(atoi(sp));
- if (xmsg == (MSG *)NULL) {
- printf("No message number %s\n", sp);
- break;
- }
- msg = xmsg;
- do_prmsg(msg);
- } else printf("Unknown command\n");
- break;
- }
- }
- /*NOTREACHED*/
- return(0);
- }
-
-
- /* Print all letters and quit. */
- int do_prall(mbox)
- register MBOX *mbox;
- {
- register MSG *msg;
-
- if (mbox->first == (MSG *)NULL) {
- fprintf(stderr, "Corrupted mailbox.\n");
- return(-1);
- }
-
- msg = mbox->first;
- if (msg == (MSG *)NULL) return(1);
- while(msg != (MSG *)NULL) {
- do_prmsg(msg);
- msg = msg->next;
- }
- return(0);
- }
-