Logo Search packages:      
Sourcecode: af version File versions  Download package

parsemail.c

/* Parsemail.c - Mailfile parser for af.
   Copyright (C) 1992 - 2003 Malc Arnold.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */


#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "af.h"
#include "atom.h"
#include "parsemail.h"
#include "keyseq.h"
#include "functions.h"
#include "variable.h"
#include "tags.h"
#include "mime.h"
#include STRING_HDR

/****************************************************************************/
/* RCS info */

#ifndef lint
static char *RcsId = "$Id: parsemail.c,v 2.7 2003/10/27 23:16:20 malc Exp $";
#endif /* ! lint */

/****************************************************************************/
/* External function declarations */

extern char *xmalloc(), *xrealloc(), *xstrdup(), *vstrcat();
extern char *strerror(), *canonical(), *mail_addresses();
extern char *mailboxes(), *nonnull_groups(), *addrnames();
extern char *get_addr(), *strudate(), *atext(), *get_fline();
extern char *get_binline(), *get_fbinline(), *c_contype();
extern char *c_encoding(), *disponly(), *utos();
extern char *get_charset(), *get_disp_param(), *safe_filename();
extern char *encode_header_line(), *decode_header_line();
extern int strncasecmp(), get_vval(), close_folder();
extern int empty_folder(), set_mime_flags(), set_tags();
extern int mmdf_form(), is_textual(), is_blank(), is_header();
extern int is_mime_header(), is_fromline(), extract_references();
extern unsigned count_messages();
extern void free(), afree(), free_messages(), msgl();
extern void emsgl(), set_sys_tags(), mask_tags();
extern void free_tlist();
extern size_t trim_text(), text_size();
extern ATOM *wtokenise();
extern DATEZONE *date_now(), *parse_date();
extern FILE *open_folder();
extern TAG_LIST *taglist();
extern TEXTLINE *add_text(), *add_bintext();
extern TEXTLINE *insert_text(), *change_text();

#ifdef READ_VIA_POP3
extern char *read_pop3();
extern int close_pop3();
extern void netid_pop3();
extern FILE *open_pop3();
#endif /* READ_VIA_POP3 */

#ifndef atol
extern long atol();
#endif /* !atol */

/* Local function declarations */

MESSAGE *null_msg(), *update_message_from_text();
TEXTLINE *find_hdr_line();
static int process_header();
static void parse_header(), parse_header_text();
static void init_body_part();
static MESSAGE *parse_mail(), *new_msg();
static MESSAGE *add_mail(), *add_msg();
static MESSAGE *msg_error();

/****************************************************************************/
/* Import the system error number */

extern int errno;

/****************************************************************************/
/* The error status of the last folder read */

static int last_read_failed = FALSE;

/****************************************************************************/
/* A static to tell us if we've seen a Reply-To header yet */

static int reply_found = FALSE;

/* Another to say if we've seen a Message-ID yet */

static int id_found = FALSE;

/* Another to say if we've seen a Mime-Version yet */

static int version_found = FALSE;

/* And another to indicate only MIME errors were found */

static int mime_errors_only = FALSE;

/****************************************************************************/
MESSAGE *get_messages(folder, old_list, offset)
char *folder;
MESSAGE *old_list;
long offset;
{
      /*
       * This function reads the contents of a file into a
       * doubly-linked list of messages.
       *
       * If old_list is non-null, then append the entries to
       * it.  If offset is non-zero then skip that many bytes
       * in the file before reading.
       *
       */

      int mmdf;
      FILE *fp;
      MESSAGE *new_list = NULL;

      /* No errors so far */

      last_read_failed = FALSE;

#ifdef READ_VIA_POP3
      /* Get the contents of a POP3 folder */

      if (POP3_MBOX(folder)) {
            /* Open a connection to the POP3 folder */

            if ((fp = open_pop3(folder, offset, TRUE)) == NULL) {
                  last_read_failed = TRUE;
                  return(NULL);
            }

            /* Parse the contents of the folder */

            new_list = parse_mail(NULL, folder, old_list,
                              read_pop3, FALSE);

            /* Close the folder and check status */

            if (close_pop3(FALSE)) {
                  last_read_failed = TRUE;
                  free_messages(new_list);
                  return(NULL);
            }

            /* And set the UIDL IDs for each message */

            netid_pop3(new_list);

            /* Add the terminating message if not updating */

            if (old_list == NULL) {
                  new_list = null_msg(new_list);
            }

            /* And return the modified list */

            return(new_list);
      }
#endif /* READ_VIA_POP3 */

      /* Read the folder if it isn't empty */

      if (!empty_folder(folder)) {
            /* Check if the file is in MMDF format */

            mmdf = mmdf_form(folder);

            /* Open the folder */

            if ((fp = open_folder(folder, FALSE, offset)) == NULL) {
                  last_read_failed = TRUE;
                  return(NULL);
            }

            /* Parse the contents of the folder */

            new_list = parse_mail(fp, folder, old_list, get_fline, mmdf);

            /* Close the folder and check status */

            if (close_folder(folder, fp)) {
                  last_read_failed = TRUE;
                  free_messages(new_list);
                  return(NULL);
            }
      }

      /* Add the terminating message if not updating */

      if (old_list == NULL) {
            new_list = null_msg(new_list);
      }

      /* And return the modified list */

      return(new_list);
}
/****************************************************************************/
MESSAGE *read_body_part(message, start, end, escape, position, what)
MESSAGE *message;
TEXTLINE *start, *end;
char *escape;
int position, what;
{
      /*
       * Create a new message from the text of message between start
       * and end, including a copy of the original message's headers.
       */

      char *header, *text;
      int fromlines, headers;
      MESSAGE *node = NULL;
      TEXTLINE *t;

      /* First we need to initialise the new message */

      node = new_msg(NULL, position, TRUE);
      node->prev = node->next = NULL;
      
      /* Preserve some details from the original message */

      init_body_part(message, node, what);

      /* Body parts have an implicit MIME-version */

      version_found = TRUE;

      /* Are we to handle From lines or headers? */

      fromlines = (what == BE_BODY_PART);
      headers = TRUE;

      /* Loop over the message's text, copying headers */

      for (t = start; t != end && t != NULL; t = t->next) {
            /* Do any special handling this line needs */

            if (fromlines && is_fromline(t->line)) {
                  /* Skip this line */
            } else if (headers && is_header(t->line)) {
                  /* Copy the original header */

                  header = xstrdup(t->line);

                  /* Merge the entire (folded) header line */

                  while (t->next != NULL && (*t->next->line == ' ' ||
                                       *t->next->line == '\t')) {
                        /* Add this line to the folded header */

                        header = xrealloc(header, strlen(header) +
                                      strlen(t->next->line) + 1);
                        (void) strcat(header, t->next->line);

                        /* And skip the folded header line */

                        t = t->next;
                  }

                  /* Parse the header into the body part */

                  parse_header(node, header, what == BE_NORMAL);

                  /* And add the line to the text if appropriate */

                  if (what != BE_BODY_PART || is_mime_header(header)) {
                        node->text = add_text(node->text, header);
                  }

                  /* We won't be processing From lines now */

                  fromlines = FALSE;
            } else if (headers && !is_blank(t->line)) {
                  /* Insert a blank line before the body */

                  node->text = add_text(node->text, xstrdup("\n"));

                  /* And add this line to the body */

                  text = (escape != NULL &&
                        !strncmp(t->line, escape, strlen(escape)))
                        ? t->line + strlen(escape) : t->line;
                  node->text = add_text(node->text, xstrdup(text));

                  /* Stop processing From lines and headers */

                  fromlines = FALSE;
                  headers = FALSE;
            } else {
                  /* Add the line to the text */

                  text = (escape != NULL &&
                        !strncmp(t->line, escape, strlen(escape)))
                        ? t->line + strlen(escape) : t->line;
                  node->text = add_text(node->text, xstrdup(text));

                  /* Stop processing From lines and headers */

                  fromlines = FALSE;
                  headers = FALSE;
            }
      }

      /* Set up the body part's global data */

      node = add_mail(node, NULL);

      /* And return the body-part */

      return(node);
}
/****************************************************************************/
MESSAGE *composition_message()
{
      /* Return a blank message suitable for use in a composition */

      MESSAGE *node;

      /* Initialise the message */

      node = new_msg(NULL, 0, FALSE);
      node->prev = node->next = NULL;

      /* And return it */

      return(node);
}
/****************************************************************************/
MESSAGE *read_message(fp, message, composing, editing, mmdf)
FILE *fp;
MESSAGE *message;
int composing, editing, mmdf;
{
      /*
       * Read the text of a single message from fp, and create a
       * new message initialised from this message.  If message is
       * non-null then copy it's headers instead of using any in the
       * file.  If we're not composing and the headers in the file
       * aren't valid, then return NULL instead.
       */

      char *line, *new_line;
      int fromlines;
      size_t len = 0;
      MESSAGE *node = NULL;
      TEXTLINE *t;

#ifdef MTA_CONTENT_LENGTH
      unsigned length = 0;
#else /* ! MTA_CONTENT_LENGTH */
      char *delim;

      /* What is the delimiter for this file? */

      delim = (!composing) ? xstrdup((mmdf) ? MMDF_DELIM : MFROM) : NULL;
#endif /* ! MTA_CONTENT_LENGTH*/

      /* Set the global flags back to their defaults */

      version_found = mime_errors_only = FALSE;

      /* Are we allowing from lines in the message? */

      fromlines = (!composing || editing);

      /* Now copy the headers in the message, if required */

      for (t = (message != NULL) ? message->text : NULL;
           t != NULL; t = t->next) {
            /* Initialise the message if required */

            if (node == NULL) {
                  /* Initialise the new message */

                  node = new_msg(t->line, 0, !composing);
                  node->prev = node->next = NULL;
            }

            /* Are we processing from lines? */

            fromlines = (fromlines && is_fromline(t->line));

            /* Check if we've run out of headers */

            if (!fromlines && !is_header(t->line)) {
                  break;
            }

            /* Process and check this header line */

            if (!process_header(node, t->line, composing)) {
                  /* Error processing the header */

                  free_messages(node);
                  return(NULL);
            }
      }

      /* Read the first line from the file */

      line = get_binline(fp, &len);

      /* Initialise the message if required */

      if (node == NULL) {
            /* Initialise the new message */

            node = new_msg(line, 0, !composing);
            node->prev = node->next = NULL;
      }

      /* Now parse the headers in the file if required */

      while (message == NULL && line != NULL) {
            /* Are we processing from lines? */

            fromlines = (fromlines && is_fromline(line));

            /* Check if we've run out of headers */

            if (!fromlines && (!is_textual(line, len)
                           || !is_header(line))) {
                  break;
            }

            /* Process and check this header line */

            if (!process_header(node, line, composing)) {
                  /* Error processing the header */

                  free_messages(node);
                  free(line);
                  return(NULL);
            }

            /* Get the next line from the file */

            line = get_fbinline(fp, &len, TRUE);
      }

      /* Skip any blank lines after the headers */

      while (line != NULL && is_blank(line)) {
            free(line);
            line = get_binline(fp, &len);
      }

      /* Add a blank line to the message */

      node->text = add_text(node->text, xstrdup("\n"));

      /* Messages may "validly" have some invalid MIME headers */

      node->bad = (node->bad && (version_found || !mime_errors_only));

      /* Loop over each line in the file */

      while (line != NULL) {
            /* Ensure the line ends in a newline */

            if (*(line + len - 1) != '\n') {
                  line = xrealloc(line, len + 2);
                  *(line + len++) = '\n';
                  *(line + len) = '\0';
            }

#ifdef MTA_CONTENT_LENGTH
            /* Update the length of the message body */

            length += len;
#else /* ! MTA_CONTENT_LENGTH */
            /* Quote any delimiter within the message */
            
            if (delim != NULL && len > strlen(delim)
                && !strncmp(line, delim, strlen(delim))) {
                  new_line = vstrcat(DELIM_PFX, line, NULL);
                  free(line);
                  line = new_line;
            }
#endif /* ! MTA_CONTENT_LENGTH */

            /* Add the line to the message's text */

            node->text = add_bintext(node->text, line, len);

            /* And read the next line from the file */

            line = get_binline(fp, &len);
      }

#ifdef MTA_CONTENT_LENGTH
      length -= trim_text(node->text);
#else /* ! MTA_CONTENT_LENGTH */
      (void) trim_text(node->text);
#endif /* ! MTA_CONTENT_LENGTH */

#ifdef MTA_CONTENT_LENGTH
      /* Update the message's content-length */

      node->length = length;
#else /* ! MTA_CONTENT_LENGTH */
      /* Clean up the delimiter */

      free(delim);
#endif /* ! MTA_CONTENT_LENGTH */

      /* Set up the new message's global data */

      node = add_mail(node, NULL);

      /* And return the new message */

      return(node);
}
/****************************************************************************/
MESSAGE *update_message_from_text(message)
MESSAGE *message;
{
      /*
       * Process the text of a message, and update the message's
       * flags from any headers in the text.  This is used when
       * composing, to handle updating the message's values after
       * the text has been modified.
       */

      int fromlines = TRUE;
      TEXTLINE *t;

      /* Set the global flags back to their defaults */

      version_found = mime_errors_only = FALSE;

      /* Now parse the contents of the file */

      for (t = message->text; t != NULL; t = t->next) {
            /* Are we processing from lines? */

            fromlines = (fromlines && is_fromline(t->line));

            /* Check if we've run out of headers */

            if (!fromlines && !is_header(t->line)) {
                  break;
            }

            /* Parse any header into the message */

            if (is_header(t->line)) {
                  parse_header(message, t->line, FALSE);
            }
      }
#if 0
      /* In a digest, default the content type */

      if (message->contype == NULL && digest) {
            /* Set the content-type to message/rfc822 */

            message->contype = vstrcat(MESSAGE_TYPE, "/",
                                 RFC822_SUBTYPE, NULL);
      }
#endif
      /* Update the message's global data */

      message = add_mail(message, NULL);

      /* Messages may "validly" have some invalid MIME headers */

      message->bad = (message->bad && (version_found || !mime_errors_only));

      /* And return the message */

      return(message);
}
/****************************************************************************/
MESSAGE *null_msg(list)
MESSAGE *list;
{
      /* Append a null message to list */

      MESSAGE *node;

      /* Form the null message */

      node = new_msg(NULL, 0, FALSE);
      node->prev = node->next = NULL;

      /* Append the node to the list and return it */

      return(add_msg(node, list));
}
/****************************************************************************/
int read_failed()
{
      /* Return whether the last folder read failed */

      return(last_read_failed);
}
/****************************************************************************/
void insert_headers(message, body_parts, what)
MESSAGE *message, *body_parts;
int what;
{
      /* Copy or generate the headers of message into the body parts */

      char **hname, *htext;
      MESSAGE *m;
      TEXTLINE *hline, *text;

      /* Loop over the messages adding any required from lines */

      for (m = body_parts; m != NULL && m->text != NULL; m = m->next) {
            /* Store the insert point for the message */

            text = (!strncmp(m->text->line, MFROM, strlen(MFROM)))
                  ? m->text->next : m->text;

            /* Try to build a From line for the message */

            if (strncmp(m->text->line, MFROM, strlen(MFROM))
                && m->addr != NULL && m->date != NULL) {
                  /* Build and add the message's From line */

                  htext = vstrcat(MFROM, m->addr, " ",
                               strudate(m->date), "\n", NULL);
                  m->text = insert_text(m->text, text, htext);
            }

            /* Now add any preserved headers we require */

            for (hname = preserved_hdrs; *hname != NULL; hname++) {
                  /* Check if we need to add the header */

                  if ((hline = find_hdr_line(message, *hname)) != NULL
                      && find_hdr_line(m, *hname) == NULL) {
                        /* Prepend this header to the list */

                        htext = xstrdup(hline->line);
                        m->text = insert_text(m->text, text, htext);
                  }
            }

            /* Now add any multipart-only headers we require */

            for (hname = multipart_hdrs; what == BE_NORMAL
                 && *hname != NULL; hname++) {
                  /* Check if we need to add the header */

                  if ((hline = find_hdr_line(message, *hname)) != NULL
                      && find_hdr_line(m, *hname) == NULL) {
                        /* Prepend this header to the list */

                        htext = xstrdup(hline->line);
                        m->text = insert_text(m->text, text, htext);
                  }
            }
      }

      /* That's that done */

      return;
}
/****************************************************************************/
TEXTLINE *find_hdr_line(message, name)
MESSAGE *message;
char *name;
{
      /* Find the named header in the message's text */

      TEXTLINE *t;

      /* Loop through the text looking for name */

      for (t = message->text; t != NULL &&
           !is_blank(t->line); t = t->next) {
            /* Is this the header we're looking for? */

            if (!strncasecmp(t->line, name, strlen(name))) {
                  return(t);
            }
      }

      /* No match for the header in the list */

      return(NULL);
}
/****************************************************************************/
static MESSAGE *parse_mail(fp, folder, list, readfunc, mmdf)
FILE *fp;
char *folder;
MESSAGE *list;
char *(*readfunc)();
int mmdf;
{
      /*
       * Actually parse a mailbox, appending the messages read
       * to list.
       */

      char *delim, *line;
      int fromlines = TRUE;
      int headers = TRUE;
      int added_msg, update;
      unsigned msg_no;
      MESSAGE *node = NULL;

#ifdef MTA_CONTENT_LENGTH
      unsigned length = 0;
#endif /* MTA_CONTENT_LENGTH */
      
#ifdef MTA_BLANK_SEPARATED
      int last_line_blank = TRUE;
#endif /* MTA_BLANK_SEPARATED */

      /* Set the initial message number */

      msg_no = count_messages(list, TRUE) + 1;

      /* How often should we update the message count? */

      update = (folder != NULL) ? get_vval(V_MSG_UPDATE) : 0;

      /* What is the delimiter for this folder? */

      delim = xstrdup((mmdf) ? MMDF_DELIM : MFROM);

      /* Process each line as it is read */

      while ((line = readfunc(fp, headers)) != NULL) {
            /* Check for a new message */

#ifdef MTA_CONTENT_LENGTH
            if (node == NULL || length + strlen(line) >= node->length
                && !strncmp(line, delim, strlen(delim))) {
#else /* ! MTA_CONTENT_LENGTH */
#ifdef MTA_BLANK_SEPARATED
            if (node == NULL || last_line_blank &&
                !strncmp(line, delim, strlen(delim))) {
#else /* ! MTA_BLANK_SEPARATED */
            if (node == NULL || !strncmp(line, delim, strlen(delim))) {
#endif /* ! MTA_BLANK_SEPARATED */
#endif /* ! MTA_CONTENT_LENGTH */
                  /* Update the message count for the first message */

                  if (node == NULL && update) {
                        msgl("Reading ", folder, "; message ",
                             utos(msg_no), "...", NULL);
                  }

                  /* Is this message not empty? */

                  added_msg = (node != NULL && node->text != NULL);

                  if (node != NULL) {
#ifdef MTA_CONTENT_LENGTH
                        /* Trim any blank lines in the node */

                        length -= trim_text(node->text);

                        /* Update the node's content-length */

                        node->length = length;
                        length = 0;
#else /* ! MTA_CONTENT_LENGTH */
                        /* Trim any blank lines in the node */

                        (void) trim_text(node->text);
#endif /* ! MTA_CONTENT_LENGTH */
                  }

                  /* Add any previous message to the list */

                  list = add_mail(node, list, TRUE);

                  /* Skip MMDF delimiter line(s) */

                  while (mmdf && line != NULL &&
                        !strncmp(line, delim, strlen(delim))) {
                        line = readfunc(fp, TRUE);
                  }

                  /* Update the message number as required */

                  msg_no = (added_msg) ? msg_no + 1 : msg_no;

                  /* Create a new node for the message */

                  node = new_msg(line, msg_no, TRUE);
                  fromlines = headers = TRUE;

                  /* Update the message count if required */

                  if (update && added_msg && (msg_no % update) == 0) {
                        msgl("Reading ", folder, "; message ",
                             utos(msg_no), "...", NULL);
                  }
            } else if (headers && is_header(line)) {
                  /* Parse header lines */

                  parse_header(node, line, FALSE);
                  fromlines = FALSE;
            } else if (headers && (!fromlines || !is_fromline(line))) {
                  /* Headers must be followed by a blank line */

                  if (!is_blank(line)) {
                        node->text =
                              add_text(node->text, xstrdup("\n"));
                  }
                  fromlines = headers = FALSE;
#ifdef MTA_CONTENT_LENGTH
                  /* We need to update the message length */

                  length += strlen(line);
            } else {
                  /* Update the length of the message body */

                  length += strlen(line);
#endif /* MTA_CONTENT_LENGTH */
            }

            /* Add non-null lines to the message */

            if (line != NULL) {
                  node->text = add_text(node->text, line);
            }

#ifdef MTA_BLANK_SEPARATED
            /* Store whether this line was blank */

            last_line_blank = !headers && is_blank(line);
#endif /* MTA_BLANK_SEPARATED */
      }

      if (node != NULL) {
#ifdef MTA_CONTENT_LENGTH
            /* Trim any blank lines in the node */

            length -= trim_text(node->text);

            /* Update the node's content-length */

            node->length = length;
#else /* ! MTA_CONTENT_LENGTH */
            /* Trim any blank lines in the node */

            (void) trim_text(node->text);
#endif /* ! MTA_CONTENT_LENGTH */
      }

      /* Add any last message to the list */
            
      list = add_mail(node, list);

      /* Clean up and return the list of messages */

      free(delim);
      return(list);
}
/****************************************************************************/
static MESSAGE *new_msg(line, position, need_fromline)
char *line;
unsigned position;
int need_fromline;
{
      /*
       * Set up the node structure for a new message.  If line is
       * NULL them the message is a dummy, otherwise the from,
       * group, reply and date fields are set from the details
       * specified in line, if available.  All other fields are
       * set to default values.
       */

      char *sender, *date;
      int ref;
      MESSAGE *node;

      /* Get the storage for the new node */

      node = (MESSAGE *) xmalloc(sizeof(MESSAGE));

      /* Set the default values for the fields */

      node->from = xstrdup(ERRUSER);
      node->addr = xstrdup(ERRUSER);
      node->subject = xstrdup(DEFSUBJECT);

      node->group = node->reply = node->cc = NULL;
      node->contype = node->encoding = node->charset = NULL;
      node->description = node->filename = NULL;
      for (ref = 0; ref < NO_REFERENCES; ref++) {
            node->refs[ref] = NULL;
      }
      node->date = NULL;
      node->text = NULL;
      node->pos = position;
      node->id = NULL;

#ifdef MTA_CONTENT_LENGTH
      node->length = 0;
#endif /* MTA_CONTENT_LENGTH */
      
      node->sys_tags = node->user_tags = NULL;
      node->visible = node->new = TRUE;
      node->processed = node->bad = node->deleted = FALSE;
      node->textual = node->viewable = TRUE;
      node->decodable = TRUE;
      node->multipart = node->alternative = FALSE;
      node->parallel = node->attachment = FALSE;
      node->read = node->replied = FALSE;
      node->forwarded = node->saved = FALSE;
      node->printed = FALSE;
      
      /* Check if line implies an erroneous message */

      if (line == NULL || strncmp(line, MFROM, strlen(MFROM))) {
            return((line == NULL || !need_fromline)
                   ? node : msg_error(node, FALSE));
      }

      /* Now find the sender and date in the line */

      sender = strchr(line, ' ') + 1;
      if ((date = strchr(sender, ' ')) != NULL) {
            *date++ = '\0';
      }

      /* Set the from and reply fields */

      node = msg_from(node, sender);

      /* Set the date field if possible */

      if (date != NULL) {
            node = msg_date(node, date);
            *(date - 1) = ' ';
      }

      return(node);
}
/****************************************************************************/
static MESSAGE *add_mail(node, list)
MESSAGE *node, *list;
{
      /* Handle the global processing after each message is formed */

      char *group;

      /* Check there was a message */

      if (node == NULL) {
            return(list);
      } else if (node->text == NULL) {
            free(node);
            return(list);
      }

      /* Set the group reply address for the message */

      if (node->group != NULL && node->reply != NULL) {
            group = xmalloc(strlen(node->reply) +
                        strlen(node->group) + 3);
            (void) sprintf(group, "%s, %s", node->reply, node->group);
            free(node->group);
            node->group = canonical(group);
            free(group);
      } else if (node->group == NULL && node->reply != NULL) {
            node->group = xstrdup(node->reply);
      }

      /* Update the error and mime values if no mime-version found */

      if (!version_found && node->bad && mime_errors_only) {
            /* Unset the message's MIME values */

            if (node->contype != NULL) {
                  free(node->contype);
                  node->contype = NULL;
            }
            if (node->encoding != NULL) {
                  free(node->encoding);
                  node->encoding = NULL;
            }
            if (node->charset != NULL) {
                  free(node->charset);
                  node->charset = NULL;
            }

            /* We don't count this node as invalid */

            node->bad = FALSE;
      }

      /* Update the MIME flags for the message */

      (void) set_mime_flags(node);

      /* Set the system tags for the message */

      set_sys_tags(node);

      /* Append the node to the list */

      list = add_msg(node, list);

      /* Set the global flags back to their defaults */

      reply_found = id_found = FALSE;
      version_found = mime_errors_only = FALSE;

      return(list);
}
/****************************************************************************/
static void init_body_part(message, node, what)
MESSAGE *message, *node;
int what;
{
      /*
       * Initialise a body-part from the original message.
       * We preserve some of the header details of the
       * original message in the body-part so that the user
       * can (for example) reply to the body-part.
       *
       * This routine assumes it is working on a message
       * returned from new_msg().
       */

      int ref;

      /* Free the redundant message details */

      free(node->from);
      free(node->addr);
      free(node->subject);

      /* Copy the basic message information */

      node->from = xstrdup(message->from);
      node->addr = xstrdup(message->addr);
      node->subject = xstrdup(message->subject);

      /* Now copy any reply information */

      node->reply = (message->reply != NULL)
            ? xstrdup(message->reply) : NULL;
      node->group = (message->group != NULL)
            ? xstrdup(message->group) : NULL;
      node->cc = (message->cc != NULL)
            ? xstrdup(message->cc) : NULL;

      /* Default the MIME headers depending on what we're doing */

      if (what == BE_DIGEST) {
            /* Digest entries default to message/rfc822 */

            node->contype = vstrcat(MESSAGE_TYPE, "/",
                              RFC822_SUBTYPE, NULL);
      } else if (what == BE_BODY_PART) {
            /* Preserve the original message's MIME headers */

            node->contype = (message->contype != NULL)
                  ? xstrdup(message->contype) : NULL;
            node->encoding = (message->encoding != NULL)
                  ? xstrdup(message->encoding) : NULL;
      }

      /* Default the disposition details */

      node->filename = (message->filename != NULL)
            ? xstrdup(message->filename) : NULL;
      node->attachment = message->attachment;

      /* Copy the date if there is one */

      if (message->date != NULL) {
            node->date = (DATEZONE *) xmalloc(sizeof(DATEZONE));
            node->date->d_date = message->date->d_date;
            node->date->d_zone = message->date->d_zone;
      } else {
            node->date = NULL;
      }

      /* Copy the references (except within a message or digest) */

      for (ref = 0; what == BE_NORMAL && ref < NO_REFERENCES; ref++) {
            node->refs[ref] = (message->refs[ref] != NULL)
                  ? xstrdup(message->refs[ref]) : NULL;
      }

      /* That's all we need */

      return;
}
/****************************************************************************/
static int process_header(node, line, composing)
MESSAGE *node;
char *line;
int composing;
{
      /* Check and process a line as a message's header or from line */

      char *eline, *sep;

      /* Process the line as a header if required */

      if (!is_header(line)) {
            /* Just add the line to the text */

            node->text = add_text(node->text, xstrdup(line));
            return(TRUE);
      }

      /* Encode the header line */

      eline = encode_header_line(line, WR_FOLD);
      line = xstrdup(eline);

      /* Parse the header into the new message if required */

      if (!composing || !is_blank(strchr(line, ':') + 1)) {
            parse_header(node, line, FALSE);
      }

      /* Check for an error in the header */

      if (!composing && node->bad && (version_found || !mime_errors_only)) {
            /* Sort out the header we got the error on */

            if ((sep = strchr(line, ':')) != NULL) {
                  *(sep + 1) = '\0';
            }
            if ((sep = strchr(line, ' ')) != NULL) {
                  *sep = '\0';
            }

            /* Give the user an error message */

            emsgl("Error reading file: ", line,
                  " header invalid", NULL);

            /* Clean up and fail */

            free(line);
            return(FALSE);
      }

      /* Add this line to the text and return success */

      node->text = add_text(node->text, line);
      return(TRUE);
}
/****************************************************************************/
static MESSAGE *add_msg(node, list)
MESSAGE *node, *list;
{
      /* This function simply adds a node to the list of headers */

      MESSAGE *m;

      /* Handle adding to a null list */

      if (list == NULL) {
            /* The new list is the node */

            node->prev = node->next = NULL;
            return(node);
      } else if (list->text == NULL) {
            /* We need to insert the entry before this null message */

            node->prev = NULL;
            node->next = list;
            list->prev = node;

            return(node);
      } else {
            /*
             * Find the last entry in the list.  We don't use
             * recursion 'cos it's too slow on large lists.
             */

            for (m = list; m->next != NULL &&
                 m->next->text != NULL; m = m->next) {
                  /* NULL LOOP */
            }

            /* Append the new node */

            node->prev = m;
            if ((node->next = m->next) != NULL) {
                  m->next->prev = node;
            }
            m->next = node;

            return(list);
      }
      /*NOTREACHED*/
}
/****************************************************************************/
static void parse_header(node, line, body_part)
MESSAGE *node;
char *line;
int body_part;
{
      /* Process the header if it requires it */

      char *colon;

      /* Find the colon in the line */

      colon = strchr(line, ':');

      /* Process the header if required */

      parse_header_text(node, line, colon + 1, colon - line + 1, body_part);
      return;
}
/****************************************************************************/
static void parse_header_text(node, name, text, namelen, body_part)
MESSAGE *node;
char *name, *text;
int namelen, body_part;
{
      /*
       * Process the named header's text using namelen to delimit
       * the name.  Add the results to node.
       */

      int i;
      HDR_PTABLE *ptab;

      /* Which parsing table will we use? */

      ptab = (body_part) ? b_ptab : h_ptab;

      /* Look for a handling function */

      for (i = 0; ptab[i].name != NULL; i++) {
            if (!strncasecmp(ptab[i].name, name, namelen)) {
                  /* Call the function to handle the header */

                  node = ptab[i].func(node, text);
                  break;
            }
      }

      return;
}
/****************************************************************************/
static MESSAGE *msg_date(node, date)
MESSAGE *node;
char *date;
{
      /* Set the date field of a message from a Date: header */

      DATEZONE *date_val;

      /* Parse the date and handle failure */

      if ((date_val = parse_date(date)) != NULL) {
            /* Set the node's date to the value */

            if (node->date != NULL) {
                  free(node->date);
            }
            node->date = date_val;
      } else {
            /* Error in the date header */

            node = msg_error(node, FALSE);
      }

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_from(node, addrs)
MESSAGE *node;
char *addrs;
{
      /*
       * Set the from, group and reply fields of a message from
       * a From: header, if not already specified by a To: or
       * Reply-To: header respectively.
       */

      char *group;

      /* Get the canonical form of the address list */

      if ((group = mailboxes(addrs)) == NULL) {
            return(msg_error(node, FALSE));
      }

      /* Set the names associated with the addresses */

      if (node->from != NULL) {
            free(node->from);
      }
      node->from = xstrdup(addrnames());
      if (node->addr != NULL) {
            free(node->addr);
      }
      node->addr = mail_addresses(group);

      /* Set the reply address if not set via Reply-To */

      if (!reply_found) {
            /* Update the node's reply address */

            if (node->reply != NULL) {
                  free(node->reply);
            }
            node->reply = mailboxes(group);
      } else {
            /* Free the unwanted group */

            free(group);
      }

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_reply(node, addrs)
MESSAGE *node;
char *addrs;
{
      /* Set the reply field of a message from a Reply-To: header */

      char *reply;

      /* Get the canonical form of the address */

      if ((reply = canonical(addrs)) == NULL) {
            return(msg_error(node, FALSE));
      }

      /* Update the node's reply address */

      if (node->reply != NULL) {
            free(node->reply);
      }
      node->reply = reply;

      /* Note that we've found a Reply-To header */

      reply_found = TRUE;
      return(node);
}
/****************************************************************************/
static MESSAGE *msg_subject(node, subject)
MESSAGE *node;
char *subject;
{
      /* Set the subject of a message from a Subject: header */

      ATOM *alist;

      /* Form an atom list from the subject */

      if (!is_blank(subject) && (alist = wtokenise(subject)) != NULL) {
            /* Update the message's subject */

            if (node->subject != NULL) {
                  free(node->subject);
            }
            node->subject = atext(NULL, alist, AC_UNFOLD);
            afree(alist);
      }

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_to(node, addrs)
MESSAGE *node;
char *addrs;
{
      /* Set the group field of a message from a To: header */

      char *addresses, *group;

      /* Canonicalise the address list */

      if ((addresses = canonical(addrs)) == NULL) {
            return(msg_error(node, FALSE));
      }

      /* Strip empty groups from the address list */

      group = nonnull_groups(addresses);
      free(addresses);

      /* And update the node's group addresses */

      if (node->group != NULL) {
            free(node->group);
      }
      node->group = group;

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_cc(node, addrs)
MESSAGE *node;
char *addrs;
{
      /* Set the cc field of a message from a Cc: header */

      char *addresses, *cc;

      /* Canonicalise the address list */

      if ((addresses = canonical(addrs)) == NULL) {
            return(msg_error(node, FALSE));
      }

      /* Strip empty groups from the address list */

      cc = nonnull_groups(addresses);
      free(addresses);

      /* And update the node's cc addresses */

      if (node->cc != NULL) {
            free(node->cc);
      }
      node->cc = cc;

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
/*ARGSUSED*/
static MESSAGE *msg_version(node, version)
MESSAGE *node;
char *version;
{
      /* Check that the message's MIME-Version header is correct */

      version_found = TRUE;
      return(node);
}
/****************************************************************************/
static MESSAGE *msg_type(node, ctype)
MESSAGE *node;
char *ctype;
{
      /* Set the type of a message from a Content-Type: header */

      char *type;

      /* Canonicalise the content type */

      if ((type = c_contype(ctype)) == NULL) {
            return(msg_error(node, TRUE));
      }

      /* And update the node's content type and charset */

      if (node->contype != NULL) {
            free(node->contype);
      }
      if (node->charset != NULL) {
            free(node->charset);
      }
      node->contype = type;
      node->charset = get_charset(type);

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_encoding(node, cte)
MESSAGE *node;
char *cte;
{
      /*
       * Set the encoding of a message from a
       * Content-Transfer-Encoding: header.
       */

      char *enc;

      /* Canonicalise the encoding */

      if ((enc = c_encoding(cte)) == NULL) {
            return(msg_error(node, TRUE));
      }

      /* Update the node's encoding */

      if (node->encoding != NULL) {
            free(node->encoding);
      }
      node->encoding = enc;

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_disposition(node, cdisp)
MESSAGE *node;
char *cdisp;
{
      /*
       * Set the disposition of a message from
       * a Content-Disposition: header.
       */

      char *disp, *filename;

      /* Canonicalise the encoding */

      if ((disp = disponly(cdisp)) == NULL) {
            return(msg_error(node, TRUE));
      }

      /* Is the node an attachment? */

      node->attachment = (strcasecmp(disp, INLINE_DISP) != 0);
      free(disp);

      /* Get any suggested file name */

      if ((filename = get_disp_param(cdisp, FILENAME_PARAM)) != NULL) {
            /* Update the node's file name */

            if (node->filename != NULL) {
                  free(node->filename);
            }
            node->filename = safe_filename(filename);
            free(filename);
      }

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_description(node, cdesc)
MESSAGE *node;
char *cdesc;
{
      /* Set a message's description from a Content-Description: header */

      ATOM *alist;

      /* Form an atom list from the description */

      if (!is_blank(cdesc) && (alist = wtokenise(cdesc)) != NULL) {
            /* Update the message's description */

            if (node->description != NULL) {
                  free(node->description);
            }
            node->description = atext(NULL, alist, AC_UNFOLD);
            afree(alist);
      }

      /* And return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_id(node, id)
MESSAGE *node;
char *id;
{
      /* Set the references of a message from a Message-ID header */

      int refs_found;

      /* Extract and check any references in the header */

      if (!(refs_found = extract_references(node->refs, id, TRUE))) {
            /* Bad Message-ID header */

            (void) msg_error(node, FALSE);
      }

      /* Update the Message-ID found flag and return the node */

      id_found = (id_found || refs_found);
      return(node);
}
/****************************************************************************/
static MESSAGE *msg_inreply(node, id)
MESSAGE *node;
char *id;
{
      /* Set the references of a message from an In-Reply-To header */

      (void) extract_references(node->refs, id, FALSE);
      return(node);
}
/****************************************************************************/
static MESSAGE *msg_refs(node, refs)
MESSAGE *node;
char *refs;
{
      /* Set the references of a message from a References header */

      (void) extract_references(node->refs, refs, FALSE);
      return(node);
}
/****************************************************************************/
#ifdef MTA_CONTENT_LENGTH
static MESSAGE *msg_length(node, length)
MESSAGE *node;
char *length;
{
      /* Set the length of the body of the current message */

      node->length = atol(length);
      return(node);
}
#endif /* MTA_CONTENT_LENGTH */
/****************************************************************************/
static MESSAGE *msg_status(node, status)
MESSAGE *node;
char *status;
{
      /* Set the status fields of a message */

      char *p;

      /* Handle each option set in the status list */

      for (p = status; *p != '\0'; p++) {
            switch(*p) {
            case ST_NEW:
                  node->new = TRUE;
                  node->read = FALSE;
                  break;
            case ST_OLD:
                  node->new = FALSE;
                  break;
            case ST_READ:
                  node->read = TRUE;
                  break;
            case ST_UNREAD:
            case ST_PRESERVED:
                  node->read = FALSE;
                  break;
            case ST_SAVED:
                  node->saved = TRUE;
                  break;
            case ST_PRINTED:
                  node->printed = TRUE;
                  break;
            case ST_REPLIED:
                  node->replied = TRUE;
                  break;
            case ST_FORWARDED:
                  node->forwarded = TRUE;
                  break;
            default:
                  break;
            }
      }

      /* Return the updated node */

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_tags(node, tags)
MESSAGE *node;
char *tags;
{
      /* Set the tags of a message, as modified by persistent-tags */

      TAG_LIST *tlist;

      /* Make a tag list from the tags and mask it */

      tlist = taglist(tags, TL_SET);
      mask_tags(tlist);

      /* Set the remaining tags on the message */

      (void) set_tags(node, tlist);
      free_tlist(tlist);

      return(node);
}
/****************************************************************************/
static MESSAGE *msg_error(node, mime)
MESSAGE *node;
int mime;
{
      /* Handle an error in the headers of the current message */

      mime_errors_only = (mime && (mime_errors_only || !(node->bad)));
      node->bad = TRUE;
      return(node);
}
/****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index