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

makemail.c

/* Makemail.c - Routines to form outgoing mail.
   Copyright (C) 1991 - 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., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307  USA */


#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include "af.h"
#include "makemail.h"
#include "sendmail.h"
#include "keyseq.h"
#include "functions.h"
#include "variable.h"
#include "io.h"
#include "mime.h"
#include "misc.h"
#include "complete.h"
#include "version.h"
#include STRING_HDR

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

#ifndef lint
static char *RcsId = "$Id: makemail.c,v 2.7 2003/11/27 01:45:57 malc Exp $";
#endif /* ! lint */

/****************************************************************************/
/* Global function declarations */

extern char *xmalloc(), *xrealloc(), *xstrdup(), *vstrcat();
extern char *a_strerror(), *strdate(), *strudate(), *alias();
extern char *c_contype(), *c_encoding(), *get_charset();
extern char *set_param(), *encode_header(), *decode_header();
extern char *decode_header_line(), *strseq(), *safe_filename();
extern char *unmessage(), *get_line(), *get_estr(), *get_ecstr();
extern char *get_dstr(), *get_dcstr(), *get_vtext(), *get_addr();
extern int strcasecmp(), strncasecmp(), charlen(), get_vval();
extern int confirm(), is_header(), is_fromline(), is_blank();
extern int count_addresses(), encoding_needed(), match_contype();
extern int listed();
extern void free(), emsgl(), typeout(), show_error();
extern void change_text(), free_messages(), free_seq();
extern MESSAGE *get_body_parts(), *get_submessage();
extern TEXTLINE *insert_text(), *append_text(), *replace_text();
extern TEXTLINE *decode_text_list();
extern KEYSEQ *make_seq();
extern DATEZONE *date_now();
extern CLIST *fn_complete(), *contype_complete();

#ifdef NO_MTA_ID
extern char *strid();
#endif /* NO_MTA_ID */

/* Local function declarations */

char *get_header(), *fold_header();
int check_dest_headers();
void copy_message_text(), set_header();
void autofold_headers(), free_headers();
static char *make_from(), *make_org(), *make_reply_to();
static char *make_subject(), *make_content_type();
static char *make_disposition(), *make_in_reply_to();
static char *make_refs(), *make_mailer();
static int get_dest(), get_subject(), get_contype(), get_disposition();
static int get_description(), check_sender(), set_user_header();
static void add_header(), save_header(), unsave_header();
static void prefix_text(), canonicalise_text();
static HEADER *find_header(), *find_unfound_header();

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

extern int errno;

/****************************************************************************/
/* Import the user quit flag from commands.c */

extern int user_quit;

/****************************************************************************/
void init_composition_headers(comp, to, cc, bcc, subject,
                       ctype, filnam, orig_msg)
COMPOSITION *comp;
char *to, *cc, *bcc, *subject, *ctype, *filnam;
MESSAGE *orig_msg;
{
      /* Form the basic headers for a mail message */

      int resent = FALSE;
      DEF_HDR *def;

#ifndef NO_MTA_RESENT
      /* Should we use resent headers in this message? */

      resent = (comp->bounce);
#endif /* ! NO_MTA_RESENT */

      /* Create the resent headers if required */

      for (def = def_resent; resent && def->name != NULL; def++) {
            add_header(comp, def->name, def->edit, def->show,
                     def->reqd, def->uniq, def->chk_func);
      }

      /* Now create the standard headers */

      for (def = def_headers; def->name != NULL; def++) {
            add_header(comp, def->name, def->edit, def->show,
                     def->reqd, def->uniq, def->chk_func);
      }

      /* Don't default headers when editing messages */

      if (comp->editing) {
            return;
      }

      /* Set the originator details */

      set_header(comp, (resent) ? RESENT_FROM : FROM, make_from());
      set_header(comp, (resent) ? RESENT_ORG : ORG, make_org());
        set_header(comp, (resent) ? RESENT_REPLY_TO : REPLY_TO,
               make_reply_to());

      /* Set the subject */

      set_header(comp, SUBJECT,
               make_subject(subject, orig_msg,
                        comp->forward || comp->attachments));

      /* Set the destinations */

      set_header(comp, (resent) ? RESENT_TO : TO, to);
      set_header(comp, (resent) ? RESENT_CC : CC, cc);
      set_header(comp, (resent) ? RESENT_BCC : BCC, bcc);

      /* Default the MIME headers for a message */

      set_header(comp, MIME_VERSION, VERSION_VALUE);
      set_header(comp, CONTENT_TYPE, (ctype != NULL) ?
               ctype : make_content_type(NULL, FALSE));
      set_header(comp, C_T_ENCODING, get_vtext(V_ENCODING));

      /* Set the content disposition if required */

      if (comp->body_part || filnam != NULL) {
            set_header(comp, CONTENT_DISP,
                     make_disposition(filnam, FALSE));
      }

      /* Form an In-Reply-To: header if required */

      if (comp->reply) {
            set_header(comp, IN_REPLY_TO,
                     make_in_reply_to(orig_msg->from, orig_msg->date));
      }

      /* Set up the References: header if required */

      if (comp->reply || comp->forward) {
            set_header(comp, REFERENCES, make_refs(orig_msg->refs));
      }

      /* Set the X-Mailer header */

      set_header(comp, X_MAILER, make_mailer());

      /* That's the headers sorted */

      return;
}
/****************************************************************************/
void init_body_part_headers(comp, contype, filnam, attachment)
COMPOSITION *comp;
char *contype, *filnam;
int attachment;
{
      /* Form the basic headers for a body part */

      DEF_HDR *def;

      /* Now create the standard body part headers */

      for (def = def_body_part; def->name != NULL; def++) {
            add_header(comp, def->name, def->edit, def->show,
                     def->reqd, def->uniq, def->chk_func);
      }

      /* Default the MIME headers for a message */

      set_header(comp, MIME_VERSION, VERSION_VALUE);
      set_header(comp, CONTENT_TYPE, (contype != NULL)
               ? contype : make_content_type(NULL, FALSE));
      set_header(comp, C_T_ENCODING, get_vtext(V_ENCODING));
      set_header(comp, CONTENT_DISP, make_disposition(filnam, attachment));

      /* That's the headers sorted */

      return;
}
/****************************************************************************/
void copy_message_text(message, orig_message, preface, prefix,
                   headers_to_copy, headers, textonly, quote_8bit,
                   keep_fromlines, attaching, fold_width)
MESSAGE *message, *orig_message;
char *preface, *prefix, *headers_to_copy;
int headers, textonly, quote_8bit, keep_fromlines, attaching, fold_width;
{
      /*
       * Copy the text of one message into another, either with
       * or without headers, possibly ignoring any non-text body
       * parts, possibly quoting any 8-bit characters, and possibly
       * prefacing and/or prefixing the copied body text.
       *
       * The headers listed in the headers_to_copy argument are
       * included as part of the (possibly prefixed) text to copy.
       */

      char *line, *space;
      TEXTLINE *decoded_text = NULL, *t, *u;
      MESSAGE *body_parts, *b, *submessage;

#ifdef MTA_CONTENT_LENGTH
      unsigned length;

      /* Initialise the message's length */

      length = message->length;

#endif /* ! MTA_CONTENT_LENGTH */

      /* Extract the body parts or submessage from the message */

      body_parts = (textonly) ? get_body_parts(orig_message) : NULL;
      submessage = (textonly) ? get_submessage(orig_message) : NULL;

      /* Handle any from lines in the original message */

      t = orig_message->text;
      while (t != NULL && is_fromline(t->line)) {
            if (headers && keep_fromlines) {
                  /* From lines are renamed in attachments */
                  if (attaching) {
                        /* Rename the from header */

                        space = strchr(t->line, ' ');
                        message->text =
                              append_text(message->text,
                                        vstrcat(X_FROM, " ",
                                              space + 1, NULL));
                  } else {
                        /* Insert the raw from line to the text */

                        message->text = append_text(message->text,
                                              xstrdup(t->line));
                  }
            }
            t = t->next;
      }

      /* Add a new from line to the text if required */

      if (headers && !keep_fromlines && !attaching) {
            line = vstrcat(MFROM, get_addr(), " ",
                         strudate(date_now()), "\n", NULL);
            message->text = append_text(message->text, line);
      }

      /* Copy the message's header lines if required */

      while (t != NULL && is_header(t->line)) {
            /* Add the line to the new message if required */

            if (headers
                || attaching && listed(t->line, headers_to_attach)) {
                  /* Append this header to the new message */

                  message->text =
                        append_text(message->text, xstrdup(t->line));
            }

            /* And move on to the next line */

            t = t->next;
      }

      /* Skip blank lines between header and body */

      while (t != NULL && is_blank(t->line)) {
            t = t->next;
      }

      /* Add a single blank line after the message headers */

      if (headers || attaching) {
            message->text = append_text(message->text, xstrdup("\n"));
      }

      /* Preface the text if required */

      if (preface != NULL) {
            /* Expand the format of the preface text */

            preface = unmessage(orig_message, preface, fold_width);

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

            length += strlen(preface) + strlen("\n");
#endif /* ! MTA_CONTENT_LENGTH */

            /* Add the preface to the message */

            message->text = append_text(message->text,
                                  vstrcat(preface, "\n", NULL));
      }

      /* If we have headers to copy into the body, then do so */

      for (u = orig_message->text; u != NULL && u != t; u = u->next) {
            /* If this is a listed header then copy it into the text */

            if (is_header(u->line) && listed(u->line, headers_to_copy)) {
                  /* Decode the header line */

                  line = decode_header_line(u->line, WR_FOLD);

                  /* Add the line to the message */

                  decoded_text = append_text(decoded_text,
                                       xstrdup(line));
            }
      }

      /* Now copy the message body if we don't have sub-parts */

      if (body_parts == NULL && submessage == NULL &&
          (!textonly || orig_message->textual) && t != NULL) {
            /* Decode the message body */

            decoded_text = decode_text_list(t, orig_message->encoding,
                                    orig_message->textual);

            /* Prefix the text if required */

            if (prefix != NULL) {
                  prefix_text(decoded_text, prefix);
            }

            /* Canonicalise the text if required */

            if (quote_8bit) {
                  canonicalise_text(decoded_text);
            }

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

            length += text_chars(decoded_text);
#endif /* ! MTA_CONTENT_LENGTH */

            /* And append the body text to the message */

            message->text = replace_text(message->text, NULL,
                                   decoded_text);
      }

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

      message->length = length;
#endif /* MTA_CONTENT_LENGTH */

      /* Now copy any textual body parts into the message */

      for (b = body_parts; b != NULL; b = b->next) {
            /* Recurse to copy the body parts of the message */

            copy_message_text(message, b, NULL, prefix, NULL,
                          FALSE, textonly, quote_8bit,
                          FALSE, FALSE, fold_width);
      }

      /* Now write any encapsulated message */

      if (submessage != NULL) {
            /* Recurse to write the encapsulated message */

            copy_message_text(message, submessage, NULL, prefix,
                          FALSE, NULL, textonly, quote_8bit,
                          FALSE, FALSE, fold_width);
      }

      /* Clean up any multipart information */

      free_messages(body_parts);
      free_messages(submessage);
      return;
}
/****************************************************************************/
int set_up_headers(comp)
COMPOSITION *comp;
{
      /* Form the initial headers for a mail message */

      int resent = FALSE;
      int dest_ok = FALSE;

      /* Don't ask the user questions for some types of mail */

      if (comp->silent || comp->editing) {
            return(TRUE);
      }

#ifndef NO_MTA_RESENT
      /* Should we use resent headers in this message? */

      resent = (comp->bounce);
#endif /* ! NO_MTA_RESENT */

      /* Loop until we have valid destination addresses */

      while (!comp->body_part && !dest_ok) {
            /* Find out who we're sending the mail to */

            if (!get_dest(comp, "To: ", (resent) ? RESENT_TO : TO)) {
                  return(FALSE);
            }

            /* May want to check for users to carbon-copy to */

            if (get_vval(V_ASK_CC) &&
                !get_dest(comp, "Cc: ", (resent) ? RESENT_CC : CC)) {
                  return(FALSE);
            }

            /* And maybe even ask about blind carbon copies */

            if (get_vval(V_ASK_BCC) &&
                !get_dest(comp, "Bcc: ", (resent) ? RESENT_BCC : BCC)) {
                  return(FALSE);
            }

            /* Check the user specified at least one destination */

            if (!(dest_ok = check_dest_headers(comp, resent))) {
                  /* No destinations; report the error */

                  show_error("No destination addresses specified");

                  /* And clear any erroneous text */

                  set_header(comp, TO, NULL);
                  set_header(comp, CC, NULL);
                  set_header(comp, BCC, NULL);
            }
      }

      /* Get the subject if not already specified */

      if (!comp->body_part && !get_subject(comp)) {
            return(FALSE);
      }

      /* Get the MIME details if required for this message */

      if (!get_contype(comp) || !get_disposition(comp)
          || !get_description(comp)) {
            return(FALSE);
      }

      /* Canonicalise and fold the the generated headers */

      autofold_headers(comp);
      return(TRUE);
}
/****************************************************************************/
int set_up_copying(comp, orig_msg)
COMPOSITION *comp;
MESSAGE *orig_msg;
{
      /* Ask the user whether to copy message on reply */

      int copy;

      /* If we don't have an original message then bail out */

      if (orig_msg == NULL) {
            return(TRUE);
      }

      /* If we're not replying then copy if there's an original */

      if (!comp->reply || orig_msg == NULL) {
            /* Set the copy flag and return success */

            comp->copy = (orig_msg != NULL);
            return(TRUE);
      }

      /* Get the value of the variable */

      copy = get_vval(V_COPY);

      /* Now decide if we're copying the message */

      comp->copy = (copy == V_TRUE ||
                  copy != V_FALSE && confirm("Copy message? ", FALSE));

      /* And return success unless the user quit */

      return(!user_quit);
}
/****************************************************************************/
int set_up_sigfile(comp)
COMPOSITION *comp;
{
      /* Determine and return the signature file */

      char *sigfile, *prompt;
      char *base_prompt = "Signature file: ";

      /* Get the users's default signature file */

      sigfile = get_vtext(V_SIGFILE);

      /* Check if a signature file is defined or required */

      if (sigfile == NULL || comp->bounce || comp->editing) {
            return(TRUE);
      }

      /* If ask: is not set then just use the sigfile */

      if (strncasecmp(sigfile, V_ASK_SIGFILE, strlen(V_ASK_SIGFILE))) {
            comp->sigfile = xstrdup(sigfile);
            return(TRUE);
      }

      /* Get the default signature file */

      sigfile = sigfile + strlen(V_ASK_SIGFILE);

      /* If we are mailing silently then use the default signature */

      if (comp->silent) {
            comp->sigfile = xstrdup(sigfile);
            return(TRUE);
      }

      /* Ask the user which signature file */

      prompt = xstrdup(base_prompt);
      sigfile = get_ecstr(NULL, prompt, sigfile, fn_complete, C_CAUTIOUS);
      free(prompt);

      /* Set the signature file and return status */

      comp->sigfile = (sigfile != NULL) ? xstrdup(sigfile) : NULL;
      return(!user_quit);
}
/****************************************************************************/
void sign_composition(comp)
COMPOSITION *comp;
{
      /* Append the user's signature file to the composition */

      FILE *fp;
      char *separator, *line;
      TEXTLINE *signature = NULL;

#ifdef SIGLINES
#if SIGLINES > 0
      int nlines = 0;
#endif /* SIGLINES > 0 */
#endif /* SIGLINES */

      /* Open the signature file - failure isn't an error */

      if ((fp = fopen(comp->sigfile, "r")) == NULL) {
            /* No signature file; treat as if it's empty */

            free(comp->sigfile);
            comp->sigfile = NULL;
            return;
      }

      /* If the composition's empty we'll need a blank line */

      if (comp->message->text == NULL) {
            signature = append_text(signature, xstrdup("\n"));
      }

      /* Append the user's signature separation string */

      if ((separator = get_vtext(V_SEPARATOR)) != NULL) {
            separator = vstrcat(separator, "\n", NULL);
            signature = append_text(signature, separator);
      }

      while ((line = get_line(fp)) != NULL) {

#ifdef SIGCOLS
#if SIGCOLS > 0
            /* Truncate the line if it's too long */

            if (strlen(line) > SIGCOLS) {
                  line[SIGCOLS - 1] = '\n';
                  line[SIGCOLS] = '\0';
            }
#endif /* SIGCOLS > 0 */
#endif /* SIGCOLS */

            /* Add the line to the signature */

            signature = append_text(signature, line);
            
#ifdef SIGLINES
#if SIGLINES > 0
            /* Abort if we've reached maximum lines allowed */

            if (++nlines >= SIGLINES) {
                  break;
            }
#endif /* SIGLINES > 0 */
#endif /* SIGLINES */
      }

      /* Done; close the file and update the composition */

      (void) fclose(fp);
      comp->message->text = replace_text(comp->message->text,
                                 NULL, signature);
      comp->body = (comp->body != NULL) ? comp->body : signature;
      free(comp->sigfile);
      comp->sigfile = NULL;

      return;
}
/****************************************************************************/
void delete_headers(comp)
COMPOSITION *comp;
{
      /* Unset any headers that the user deleted */

      HEADER *h;

      /* Loop over the headers checking if we found them */

      for (h = comp->headers; h != NULL; h = h->next) {
            /* Is this header expected but missing? */

            if (h->edit && h->show && !h->found) {
                  /* Check and delete the header */

                  if (h->reqd) {
                        /* We can't delete this header */

                        typeout("Can't delete ");
                        typeout(h->name);
                        typeout(" header\n");
                  } else if (h->text != NULL) {
                        /* Update the header in the list */

                        set_header(comp, h->name, NULL);
                  }
            }
      }

      /* All done */

      return;
}
/****************************************************************************/
void check_mime_headers(comp, verbose)
COMPOSITION *comp;
int verbose;
{
      char *ctype, *cset, *saved_cset;
      char *slash, *enc, *saved_enc;
      char *new_ctype, *new_enc;
      int textual, multipart, status;
      HEADER *ctype_hdr, *enc_hdr;
      TEXTLINE *decoded_body;

      /* Extract the relevant headers */

      ctype_hdr = find_header(comp, CONTENT_TYPE);
      enc_hdr = find_header(comp, C_T_ENCODING);

      /* Find out if the composition is textual, and how it's encoded */

      ctype = c_contype(ctype_hdr->text);
      slash = (ctype != NULL) ? strchr(ctype, '/') : NULL;
      textual = (ctype == NULL || slash == NULL ||
               !strncasecmp(ctype, TEXT_TYPE, slash - ctype));
      multipart = (ctype != NULL && slash != NULL &&
                 !strncasecmp(ctype, MULTIPART_TYPE, slash - ctype));
      cset = get_charset(ctype_hdr->text);
      saved_cset = (ctype_hdr->saved != NULL)
            ? get_charset(ctype_hdr->saved) : NULL;
      enc = c_encoding(enc_hdr->text);
      saved_enc = (enc_hdr->saved != NULL)
            ? c_encoding(enc_hdr->saved) : NULL;

      /* Decode the message body and extract details of the contents */

      decoded_body = decode_text_list(comp->body, comp->encoded,
                              comp->message->textual);
      status = encoding_needed(decoded_body);
      free(decoded_body);

      /* Seven-bit messages must be in the us-ascii charset */

      if (!(status & ENCODE_8BIT) && !(status & ENCODE_SMTP)
          && textual && cset != NULL && strcasecmp(cset, US_ASCII)) {
            /* Revert to the us-ascii character set */

            new_ctype = set_param(ctype, CHARSET_PARAM, US_ASCII);
            save_header(comp, CONTENT_TYPE, new_ctype);
            free(new_ctype);
      }

      /* Force the saved or fallback charset for non-ascii messages */

      if ((status & ENCODE_8BIT) && textual &&
          (cset == NULL || !strcasecmp(cset, US_ASCII))) {
            /* Check if we can use the saved charset */

            if (saved_cset != NULL && strcasecmp(saved_cset, US_ASCII)) {
                  /* We can (silently) use the saved character set */

                  new_ctype = set_param(ctype, CHARSET_PARAM,
                                    saved_cset);
                  set_header(comp, CONTENT_TYPE, new_ctype);
                  free(new_ctype);
            } else {
                  /* Noisily force use of the fallback character set */

                  if (verbose) {
                        /* Let the user know what's happening */

                        typeout("Forced ");
                        typeout(FALLBACK_CHARSET);
                        typeout(" charset due to non-ascii message\n");
                  }

                  /* And update the charset in the headers */

                  new_ctype = set_param(ctype, CHARSET_PARAM,
                                    FALLBACK_CHARSET);
                  save_header(comp, CONTENT_TYPE, new_ctype);
                  free(new_ctype);
            }
      }

      /* Check we're not encoding when we don't need to */

      if (!(status & ENCODE_8BIT) && !(status & ENCODE_BINARY)
          && !(status & ENCODE_SMTP) && enc != NULL && !comp->encoded
          && (!strcasecmp(enc, EIGHT_BIT) || !strcasecmp(enc, BASE64)
            || !strcasecmp(enc, QUOTED_PRINTABLE))) {
            /* Revert to the default 7bit encoding */

            save_header(comp, C_T_ENCODING, SEVEN_BIT);
      }

      /* Force encoding for allegedly 7-bit non-ascii messages */

      if ((status & ENCODE_8BIT) && !(status & ENCODE_BINARY)
          && !(status & ENCODE_SMTP) && (textual || multipart)
          && (enc == NULL || !strcasecmp(enc, SEVEN_BIT))) {
            /* Default to quoted-printable encoding */

            new_enc = QUOTED_PRINTABLE;

            /* Do we have a saved encoding we can use? */

            if (saved_enc != NULL && strcasecmp(saved_enc, US_ASCII)) {
                  /* We can use the saved encoding */

                  new_enc = saved_enc;
            } else if (verbose) {
                  /* Let the user know what's happening */

                  typeout("Forced ");
                  typeout(new_enc);
                  typeout(" encoding due to non-ascii message\n");
            }

            /* And update the encoding in the headers */

            set_header(comp, C_T_ENCODING, new_enc);
      }

      /* Force encoding of binary text messages or 8-bit body parts */

      if (textual && (status & ENCODE_BINARY)
          && (enc == NULL || !strcasecmp(enc, SEVEN_BIT) ||
            !strcasecmp(enc, EIGHT_BIT) || !strcasecmp(enc, BINARY))) {
            /* Decide which encoding we're going to use */

            new_enc = (status & ENCODE_BASE64) ? BASE64
                  : QUOTED_PRINTABLE;

            /* Noisily force use of the correct encoding */

            if (verbose && (saved_enc == NULL ||
                        strcasecmp(saved_enc, QUOTED_PRINTABLE))) {
                  /* Let the user know what's happening */

                  typeout("Forced ");
                  typeout(new_enc);
                  typeout(" encoding due to ");
                  typeout("null characters in message");
                  typeout((status & ENCODE_SMTP)
                        ? " and line length\n" : "\n");
            }

            /* And update the encoding in the headers */

            set_header(comp, C_T_ENCODING, new_enc);
      }

      /* Force encoding for other 8-bit or binary messages */

        if ((status & (ENCODE_8BIT | ENCODE_BINARY)) && !textual && !multipart
            && ((new_enc = get_header(comp, C_T_ENCODING)) == NULL
            || (strcasecmp(new_enc, QUOTED_PRINTABLE)
                && strcasecmp(new_enc, BASE64)))) {
            /* Force use of the appropriate encoding */

            set_header(comp, C_T_ENCODING, (status & ENCODE_BASE64)
                     ? BASE64 : QUOTED_PRINTABLE);
      }

      /* Force some encoding for non-textual binary messages */

      if ((status & ENCODE_BINARY) && !textual &&
          (enc == NULL || !strcasecmp(enc, SEVEN_BIT) ||
           !strcasecmp(enc, EIGHT_BIT) || !strcasecmp(enc, BINARY))) {
            /* Decide which encoding we're going to use */

            new_enc = (status & ENCODE_BASE64) ? BASE64
                  : QUOTED_PRINTABLE;

            /* Noisily force use of the appropriate encoding */

            if (verbose) {
                  /* Let the user know what's happening */

                  typeout("Forced ");
                  typeout(new_enc);
                  typeout(" encoding due to ");
                  typeout("null characters in message");
                  typeout((status & ENCODE_SMTP) ?
                        " and line length\n" : "\n");
            }

            /* And update the encoding in the headers */

            set_header(comp, C_T_ENCODING, BASE64);
      }

      /* Force encoding if the message will break the SMTP server */

      if (!(status & ENCODE_BINARY) && (status & ENCODE_SMTP) &&
          (enc == NULL || !strcasecmp(enc, SEVEN_BIT) ||
           !strcasecmp(enc, EIGHT_BIT) || !strcasecmp(enc, BINARY))) {
            /* Decide which encoding we're going to use */

            new_enc = (status & ENCODE_BASE64) ? BASE64
                  : QUOTED_PRINTABLE;

            /* Noisily force use of the correct encoding */

            if (verbose) {
                  /* Let the user know what's happening */

                  typeout("Forced ");
                  typeout(new_enc);
                  typeout(" encoding due to ");
                  typeout((status & ENCODE_8BIT) ?
                        "non-ascii message and " : "");
                  typeout("line length\n");
            }

            /* And update the encoding in the headers */

            save_header(comp, C_T_ENCODING, QUOTED_PRINTABLE);
      }

      /* Clean up and return */

      free(ctype);
      free(cset);
      free(enc);
      return;
}
/****************************************************************************/
int check_dest_headers(comp, resent)
COMPOSITION *comp;
int resent;
{
      /* Check that there is at least one destination address */

      char *addrs;

      /* Simply check the three destination headers for addresses */

      return((addrs = get_header(comp, (resent) ? RESENT_TO : TO)) != NULL
             && count_addresses(addrs) ||
             (addrs = get_header(comp, (resent) ? RESENT_CC : CC)) != NULL
             && count_addresses(addrs) ||
             (addrs = get_header(comp, (resent) ? RESENT_BCC : BCC)) != NULL
             && count_addresses(addrs));
}
/****************************************************************************/
char *get_header(comp, name)
COMPOSITION *comp;
char *name;
{
      /* Return the text associated with the named header */

      HEADER *hdr;

      /* Find the appropriate header */

      hdr = find_header(comp, name);

      /* And return the text if any? */

      return((hdr != NULL) ? hdr->text : NULL);
}
/****************************************************************************/
void free_headers(hdrs)
HEADER *hdrs;
{
      /* Free up the space used up in the list of headers */

      if (hdrs != NULL) {
            /* Free the next header first */

            free_headers(hdrs->next);

            /* Free the fields of the header */

            free(hdrs->name);
            if (hdrs->text != NULL) {
                  free(hdrs->text);
            }
            if (hdrs->saved != NULL) {
                  free(hdrs->saved);
            }

            /* And then free the header itself */

            free(hdrs);
      }

      return;
}
/****************************************************************************/
void make_composition_multipart(comp, boundary, digest)
COMPOSITION *comp;
char *boundary;
int digest;
{
      /* Set the MIME headers for a multipart message */

      char *old_contype, *new_contype;

      /* Make the composition multipart */

      comp->multipart = TRUE;

      /* Get the current content-type */

      old_contype = get_header(comp, CONTENT_TYPE);

      /* Now force multipart type or set the boundary */

      if (old_contype != NULL
          && match_contype(MULTIPART_TYPE, c_contype(old_contype))
          && (new_contype = set_param(old_contype, BOUNDARY_PARAM,
                              boundary)) != NULL) {
            /* It's already multipart; set the boundary */

            set_header(comp, CONTENT_TYPE, new_contype);
            unsave_header(comp, CONTENT_TYPE);
            free(new_contype);
      } else {
            /* Create a multipart header from scratch */

            set_header(comp, CONTENT_TYPE,
                     make_content_type(boundary, digest));
            unsave_header(comp, CONTENT_TYPE);
      }

      /* All done */

      return;
}
/****************************************************************************/
void add_posting_headers(comp)
COMPOSITION *comp;
{
      /* Set any headers required just before mail is sent */

#ifdef NO_MTA_DATE
      int resent = FALSE;
      DATEZONE *date;

      /* Check if the message is Resent- */

      resent = (get_header(comp, RESENT_FROM) != NULL ||
              get_header(comp, RESENT_SENDER) != NULL);

      /* Get the current date */

      date = date_now();

#else /* ! NO_MTA_DATE */
#ifdef NO_MTA_ID

      int resent = FALSE;
      DATEZONE *date;

      /* Check if the message is Resent- */

      resent = (get_header(comp, RESENT_FROM) != NULL ||
              get_header(comp, RESENT_SENDER) != NULL);

      /* Get the current date */

      date = date_now();
#endif /* NO_MTA_ID */
#endif /* NO_MTA_DATE */

#ifdef NO_MTA_ID
      /* Set the Id of the message */

      set_header(comp, (resent) ? RESENT_ID : MESSAGE_ID, strid(date));
#endif /* NO_MTA_ID */

#ifdef NO_MTA_DATE
      /* Set the date of the message */

      set_header(comp, (resent) ? RESENT_DATE : DATE, strdate(date, TRUE));
#endif /* NO_MTA_DATE */

      /* All done */

      return;
}
/****************************************************************************/
char *fold_header(name, text, refold, break_at_commas)
char *name, *text;
int refold, break_at_commas;
{
      /*
       * Canonicalise any newlines in the header text to folds (to
       * stop it all going horribly wrong), and, if refold is TRUE,
       * fold the header to a suitable width.
       */

      /* Fold a header so that it will fit neatly into 72 characters */

      char *new_text, *p1, *p2;
      char *break1, *break2;
      char *cbreak1, *cbreak2;
      int ok_to_break, width;
      int newline = FALSE;

      /* Can't more than double the size of the text */

      new_text = xmalloc(strlen(text) * 2 + 1);

      /* The intitial width includes the name */

      width = strlen(name) + 1;

      /* Haven't found any breaks yet */

      break1 = break2 = NULL;
      cbreak1 = cbreak2 = NULL;

      /* Can't break at the start of an address list */

      ok_to_break = !break_at_commas;

      /* Set up pointers into the old and new text */

      p1 = text;
      p2 = new_text;

      /* Copy the text, folding as required */

      while (*p1 != '\0') {
            /* Check for a newline */

            newline = IS_NEWLINE(*p1);

            /* Skip existing folds when refolding */

            while (newline && refold &&
                   (IS_NEWLINE(*p1) || IS_LWSP(*p1))) {
                  p1++;
            }

            /* Is this a possible break position? */

            if (break_at_commas && ok_to_break) {
                  cbreak1 = p1;
                  cbreak2 = p2;
            } else if (newline || IS_LWSP(*p1)) {
                  break1 = p1;
                  break2 = p2;
            }

            /* Reset the width if we have a newline */

            if (newline && !refold) {
                  width = 0;
                  break1 = break2 = NULL;
                  cbreak1 = cbreak2 = NULL;
                  ok_to_break = (!break_at_commas);
            }

            /* Check the width after this character */

            width += (newline) ? (refold) ? charlen(' ', width)
                  : 0 : charlen(*p1, width);

            /* Do we need to fold before this character? */

            if (refold && width > FOLD_WIDTH &&
                (cbreak1 != NULL || break1 != NULL)) {
                  /* Jump back to the last break */

                  p1 = (cbreak1 != NULL) ? cbreak1 : break1;
                  p2 = (cbreak2 != NULL) ? cbreak2 : break2;
                  break1 = break2 = NULL;
                  cbreak1 = cbreak2 = NULL;

                  /* Skip any white space after the fold */

                  while (IS_NEWLINE(*p1) || IS_LWSP(*p1)) {
                        p1++;
                  }

                  /* Insert a newline and tab into the text */

                  *p2++ = '\n';
                  *p2++ = '\t';

                  /* Don't do any special newline processing */

                  newline = FALSE;

                  /* And reset the width */

                  width = charlen('\t', 0);
            }

            /* Is it ok to break after this character? */

            ok_to_break = (!break_at_commas || *p1 == ',');

            /* Add the base character to the text */

            *p2++ = (newline && refold) ? ' ' : *p1++;

            /* Check for a bad fold */

            if (newline && !refold && !IS_LWSP(*p1)) {
                  /* Force a tab after the newline */

                  *p2++ = '\t';
                  width += charlen('\t', width);
            }
      }

      /* Add any newline needed to the text and terminate it */

      if (newline) {
            *p2++ = '\n';
      }
      *p2 = '\0';

      /* Resize and return the new text */

      new_text = xrealloc(new_text, (p2 - new_text) + 1);
      return(new_text);
}
/****************************************************************************/
void autofold_headers(comp)
COMPOSITION *comp;
{
      /* Canonicalise and fold newlines in the composition's headers */

      char *new_text;
      int refold;
      HEADER *h;

      /* Check if we should add folds as required */

      refold = (get_vval(V_AUTOFOLD));

      /* Loop through each header in the list */

      for (h = comp->headers; h != NULL; h = h->next) {
            /* Fold headers with text defined */

            if (h->text != NULL) {
                  /* Generate the header's new text */

                  new_text = fold_header(h->name, h->text, refold,
                                     h->chk_func == alias);

                  /* Replace the original text with the new */

                  free(h->text);
                  h->text = new_text;
            }
      }

      /* Done */

      return;
}
/****************************************************************************/
void reset_headers(comp)
COMPOSITION *comp;
{
      /*
       * Reset the composition's headers' found flags before an
       * update.  These are used to detect headers deleted by the
       * user.
       */

      HEADER *h;

      /* Clear the found flag for each header */

      for (h = comp->headers; h != NULL; h = h->next) {
            /* Reset this header's details */

            h->found = FALSE;
      }

      /* That's all folks */

      return;
}
/****************************************************************************/
HEADER *find_dest_header(comp, resent)
COMPOSITION *comp;
int resent;
{
      /* Return the header which contains the composition's destinations */

      char *dest_hdr;
      HEADER *hdr;

      /* Which destination header do we use? */

      dest_hdr = xstrdup((resent) ? RESENT_TO : TO);

      /* Get the destination */

      if ((hdr = find_header(comp, dest_hdr)) == NULL
          || !count_addresses(hdr->text)) {
            /* No To: addresses, try Cc: instead */

            free(dest_hdr);
            dest_hdr = xstrdup((resent) ? RESENT_CC : CC);
            if ((hdr = find_header(comp, dest_hdr)) == NULL
                || !count_addresses(hdr->text)) {
                  /* No Cc: addresses, try Bcc: instead */

                  free(dest_hdr);
                  dest_hdr = xstrdup((resent) ? RESENT_BCC : BCC);
                  if ((hdr = find_header(comp, dest_hdr)) != NULL
                      && !count_addresses(hdr->text)) {
                        /* Give up, there are no destinations */

                        hdr = NULL;
                  }
            }
      }

      /* Clean up and return the header */

      free(dest_hdr);
      return(hdr);
}
/****************************************************************************/
int add_user_header(comp, line, headers)
COMPOSITION *comp;
char *line;
int headers;
{
      /* Set the header given by text into hdrs with checking */

      char *name, *text;
      int status;

      /* If bouncing, ignore Resent- headers on first pass */

      if (!headers && comp->bounce &&
          !strncasecmp(line, RESENT, strlen(RESENT))) {
            return(TRUE);
      }

      /* Make the header name */
      
      text = strchr(line, ':') + 1;
      name = xmalloc(text - line + 1);
      (void) strncpy(name, line, text - line);
      name[text - line] = '\0';

      /* Remove white space before the header text */

      while (isspace(*text)) {
            text++;
      }

      /* Get the text into an allocated string */

      text = (*text != '\0') ? xstrdup(text) : NULL;

      /* Remove the newline after the text */

      if (text != NULL) {
            text[strlen(text) - 1] = '\0';
      }

      /* Add the header */

      status = set_user_header(comp, name, text, headers);

      /* Clean up and return the status */

      free(name);
      if (text != NULL) {
            free(text);
      }
      return(status);
}
/****************************************************************************/
void set_header(comp, name, value)
COMPOSITION *comp;
char *name, *value;
{
      /* Set the value of a header's text to value */

      HEADER *hdr;

      /* Does this header already exist? */

      if ((hdr = find_header(comp, name)) != NULL) {
            /* Free any old value of the header */

            if (hdr->text != NULL) {
                  free(hdr->text);
            }

            /* Set the text to the new value */

            hdr->text = (value != NULL) ? xstrdup(value) : NULL;
      }

      /* That's done */

      return;
}
/****************************************************************************/
static void prefix_text(text, prefix)
TEXTLINE *text;
char *prefix;
{
      /* Prepend the prefix to each line of text */

      char *prefixed_line;
      TEXTLINE *t;

      /* Loop over the text, prefixing each line */

      for (t = text; t != NULL; t = t->next) {
            /* Create the prefixed line */

            prefixed_line = xmalloc(strlen(prefix) + t->len + 1);
            (void) strcpy(prefixed_line, prefix);
            (void) memcpy(prefixed_line + strlen(prefix),
                        t->line, t->len);
            *(prefixed_line + strlen(prefix) + t->len) = '\0';

            /* And replace the line in the text */

            change_text(t, prefixed_line);
      }

      /* All done */

      return;
}
/****************************************************************************/
static void canonicalise_text(text)
TEXTLINE *text;
{
      /* Canonicalise the text by quoting non-ASCII characters */

      TEXTLINE *t;
      KEYSEQ *seq;

      /* Loop over the text, prefixing each line */

      for (t = text; t != NULL; t = t->next) {
            /* Generate the line's key sequence */

            seq = make_seq(t->line, t->len);

            /* And replace the text with the canonical */

            change_text(t, xstrdup(strseq(seq, SK_QUOTE)));
            free_seq(seq);
      }

      /* All done */

      return;
}
/****************************************************************************/
static int get_dest(comp, prompt, hdrname)
COMPOSITION *comp;
char *prompt, *hdrname;
{
      /* Get a list of destinations for the message */

      char *deflt, *addrs;

      /* Get and decode the default destinations from the headers */

      if ((deflt = get_header(comp, hdrname)) != NULL) {
            deflt = decode_header(hdrname, deflt, WR_UNFOLD);
      }

      /* Do we need to ask for the destinations? */

      if (deflt != NULL && (!comp->reply || !get_vval(V_EDIT_REPLY))) {
            return(TRUE);
      }

      /* Get and alias the destination(s) */

      while ((addrs = get_estr(NULL, prompt, deflt)) != NULL
             && (addrs = alias(addrs)) == NULL) {
            /* Error in destination; print a message for a while */

            show_error(a_strerror());
      }

      /* Check for a user quit */

      if (addrs == NULL && user_quit) {
            return(FALSE);
      }

      /* Update the headers and return success */

      set_header(comp, hdrname, addrs);
      return(TRUE);
}
/****************************************************************************/
static int get_subject(comp)
COMPOSITION *comp;
{
      /* Get the subject for a message */

      char *deflt, *subject;

      /* Get the default subject from the headers */

      if ((deflt = get_header(comp, SUBJECT)) != NULL) {
            deflt = decode_header(SUBJECT, deflt, WR_UNFOLD);
      }

      /* Use default for silent, bounce or send if set */

      if (comp->bounce || comp->silent
          || (!comp->reply && !comp->forward && !comp->editing
            && !comp->attachments && deflt != NULL)) {
            return(TRUE);
      }

      /* Set up the default for the subject if replying */

      if (comp->reply && deflt != NULL &&
          strncasecmp(deflt, REPLY_PFX, strlen(REPLY_PFX))) {
            /* Prepend the reply prefix to the subject */

            deflt = vstrcat(REPLY_PFX, " ", deflt, NULL);
      } else {
            /* Just copy the default subject */

            deflt = (deflt != NULL) ? xstrdup(deflt) : NULL;
      }

      /* Get the subject and set the header */

      if ((subject = get_estr(NULL, "Subject: ", deflt)) == NULL
          && user_quit) {
            /* User quit; clean up and fail */

            return(FALSE);
      }

      /* Free space if required */

      if (deflt != NULL) {
            free(deflt);
      }

      /* Set the header and return success */

      set_header(comp, SUBJECT, subject);
      return(TRUE);
}
/****************************************************************************/
static int get_contype(comp)
COMPOSITION *comp;
{
      /* Get the content-type of a message */

      char *deflt, *ctype;

      /* We normally want to use the default content-type */

      if (!comp->mime && !comp->body_part) {
            return(TRUE);
      }

      /* Get the default content-type from the headers */

      if ((deflt = get_header(comp, CONTENT_TYPE)) != NULL) {
            deflt = decode_header(CONTENT_TYPE, deflt, WR_UNFOLD);
      }

      /* Get the content-type and check for errors */

      while ((ctype = get_dcstr(NULL, "Content-Type: ", deflt,
                          contype_complete, C_PERMISSIVE)) != NULL
             && (ctype = contype(ctype)) == NULL) {
            /* Error in content-type; print a message for a while */

            show_error(a_strerror());
      }

      /* Check for a user quit */

      if (ctype == NULL && user_quit) {
            return(FALSE);
      }

      /* Update the header and return success */

      set_header(comp, CONTENT_TYPE, ctype);
      return(TRUE);
}
/****************************************************************************/
static int get_disposition(comp)
COMPOSITION *comp;
{
      /* Get the content-disposition of a message */

      char *deflt, *cdisp;

      /* We normally don't want a content-disposition */

      if (!comp->body_part && (!comp->mime || !comp->file)) {
            return(TRUE);
      }

      /* Get the default content-disposition from the headers */

      if ((deflt = get_header(comp, CONTENT_DISP)) != NULL) {
            deflt = decode_header(CONTENT_DISP, deflt, WR_UNFOLD);
      }

      /* Get the content-disposition and check for errors */

      while ((cdisp = get_estr(NULL, "Content-Disposition: ",
                         deflt)) != NULL
             && (cdisp = disposition(cdisp)) == NULL) {
            /* Error in content-disposition; print a message */

            show_error(a_strerror());
      }

      /* Check for a user quit */

      if (cdisp == NULL && user_quit) {
            return(FALSE);
      }

      /* Update the header and return success */

      set_header(comp, CONTENT_DISP, cdisp);
      return(TRUE);
}
/****************************************************************************/
static int get_description(comp)
COMPOSITION *comp;
{
      /* Get the content-description for a message */

      char *deflt, *cdesc;

      /* We normally don't want a content-description */

      if (!comp->body_part && (!comp->mime || !comp->file)) {
            return(TRUE);
      }

      /* Get the default content-description from the headers */

      if ((deflt = get_header(comp, CONTENT_DESC)) != NULL) {
            deflt = decode_header(CONTENT_DESC, deflt, WR_UNFOLD);
      }

      /* Get the content-description */

      if ((cdesc = get_dstr(NULL, "Content-Description: ", deflt)) == NULL
          && user_quit) {
            return(FALSE);
      }

      /* Update the header and return success */

      set_header(comp, CONTENT_DESC, cdesc);
      return(TRUE);
}
/****************************************************************************/
static int set_user_header(comp, name, text, headers)
COMPOSITION *comp;
char *name, *text;
int headers;
{
      /* Set a header value from user-supplied data */

      char *ctext = NULL;
      unsigned found = FALSE;
      HEADER *hdr, *dup;

      /* Find the header if it already exists */

      if ((hdr = find_unfound_header(comp, name)) == NULL) {
            /* Do we have any other header of this type? */

            if ((dup = find_header(comp, name)) != NULL) {
                  /* Copy the flags for the new header */

                  add_header(comp, name, dup->edit, dup->show,
                           dup->reqd, dup->uniq, dup->chk_func);
            } else {
                  /* Create a user-defined header */

                  add_header(comp, name, TRUE, TRUE, FALSE,
                           FALSE, NULL);
            }

            /* Now find the header we've just created */

            hdr = find_unfound_header(comp, name);
      }

      /* Save the found flag for the header and mark it as found */

      found = hdr->found;
      hdr->found = TRUE;

      /* Check if the value has changed */

      if (hdr->text == NULL && text == NULL
          || hdr->text != NULL && text != NULL
          && !strcmp(hdr->text, text)) {
            return(TRUE);
      }

      /* Ignore uneditable headers in initial bounced mail */

      if (!comp->updated && comp->bounce && !hdr->edit) {
            return(TRUE);
      }

      /* Check if we're allowed to edit the header */

      if (!hdr->edit && (!comp->editing || comp->updated)) {
            /* Tell the user about the error */

            if (!comp->silent) {
                  typeout("Can't modify ");
                  typeout(hdr->name);
                  typeout(" header\n");
            }

            /* And return failure */

            return(FALSE);
      }

      /* Check if this is a duplicate header */

      if (hdr->uniq && found) {
            /* Tell the user about the error */

            if (!comp->silent) {
                  typeout("Duplicate ");
                  typeout(hdr->name);
                  typeout(" header ignored\n");
            }

            /* And return failure */

            return(FALSE);
      }

      /* Check the header if required */

      if (text != NULL && hdr->chk_func != NULL) {
            /* Run the check function */

            if ((ctext = hdr->chk_func(text)) == NULL
               && (!comp->editing || comp->updated)) {
                  /* Tell the user about the error */

                  if (!comp->silent) {
                        typeout(a_strerror());
                        typeout(" in ");
                        typeout(name);
                        typeout(" header\n");
                  }

                  /* And return failure */

                  return(FALSE);
            }

            /* Update the header text if required */

            text = (ctext != NULL) ? ctext : text;
      } else if (hdr->reqd && text == NULL) {
            /* Tell the user about the error unless editing a message */

            if (!comp->silent && (!comp->editing || comp->updated)) {
                  typeout("Can't delete ");
                  typeout(hdr->name);
                  typeout(" header\n");
            }

            /* Free any checked text */

            if (ctext != NULL) {
                  free(ctext);
            }

            /* And return status */

            return((comp->editing && !comp->updated) ? TRUE : FALSE);
      }

      /* Free any old value of the header */

      if (hdr->text != NULL) {
            free(hdr->text);
            hdr->text = NULL;
      }

      /* Encode the text of the header as required */

      text = (text == NULL || !strlen(text)) ? NULL :
            encode_header(hdr->name, text, WR_FOLD);

      /* Set the text to the new value */

      hdr->text = (text != NULL) ? xstrdup(text) : NULL;

      /* Clear any saved value of the header */

      if (hdr->saved != NULL) {
            free(hdr->saved);
            hdr->saved = NULL;
      }

      /* Free the checked text if required */

      if (ctext != NULL) {
            free(ctext);
      }

      /* Check if we need to add a Sender: header */

      check_sender(comp, name, text);

      /* Return success */

      return(TRUE);
}
/****************************************************************************/
static int check_sender(comp, name, text)
COMPOSITION *comp;
char *name, *text;
{
      /*
       * Node has been modified, check if we need to add a Sender:
       * or Resent-Sender: header as a result of this.
       */

      /* We don't need to add a Sender: header when editing */

      if (comp->editing) {
            return(TRUE);
      }

      /* Check if it was a (Resent-) From: header */

      if (comp->bounce && strcasecmp(name, RESENT_FROM)
          || !comp->bounce && strcasecmp(name, FROM)) {
            return(TRUE);
      }

      /* Check the user didn't try to delete the header */

      if (text == NULL) {
            /* Report the error? */

            if (!comp->silent) {
                  typeout("Can't delete ");
                  typeout(name);
                  typeout("header.\n");
            }

            /* Repair the damage */

            set_header(comp, name, make_from());
            return(FALSE);
      }

      /* Add a (Resent-)Sender: header and return success */

      set_header(comp, (comp->bounce) ? RESENT_SENDER : SENDER,
               make_from());
      return(TRUE);
}
/****************************************************************************/
void add_header(comp, name, edit, show, reqd, uniq, chk_func)
COMPOSITION *comp;
char *name;
unsigned edit, show, reqd, uniq;
char *(*chk_func)();
{
      /*
       * Add a header to the composition, setting the 
       * flags and checking function appropiately.
       */

      HEADER *node, *h;

      /* Generate the node */

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

      /* Set the header's name and parameters */

      node->name = xstrdup(name);
      node->edit = edit;
      node->show = show;
      node->reqd = reqd;
      node->uniq = uniq;
      node->found = FALSE;
      node->chk_func = chk_func;

      /* Initialise text, saved, and next to NULL */ 

      node->text = node->saved = NULL;
      node->next = NULL;

      /* If we're creating the first header just do it */

      if (comp->headers == NULL) {
            comp->headers = node;
            return;
      }

      /* Append the node after duplicates or at the end of the list */

      for (h = comp->headers; h != NULL; h = h->next) {
            /* Append the node after this header? */

            if (h->next == NULL || !strcasecmp(h->name, node->name)
                && strcasecmp(h->next->name, node->name)) {
                  /* Insert the node after this header */

                  node->next = h->next;
                  h->next = node;
                  return;
            }
      }
      /*NOTREACHED*/
}
/****************************************************************************/
static void save_header(comp, name, value)
COMPOSITION *comp;
char *name, *value;
{
      /* Set a value, while saving the current value */

      HEADER *h;

      /* Does this header already exist? */

      if ((h = find_header(comp, name)) != NULL) {
            /* Free any previous saved value */

            if (h->saved != NULL) {
                  free(h->saved);
            }

            /* And save the current value */

            h->saved = (h->text != NULL) ? xstrdup(h->text) : NULL;
      }

      /* Now set the header and return */

      set_header(comp, name, value);
      return;
}
/****************************************************************************/
static void unsave_header(comp, name)
COMPOSITION *comp;
char *name;
{
      /* Clear any saved value of a header */

      HEADER *hdr;

      /* Find the appropriate header */

      hdr = find_header(comp, name);

      /* And delete any saved value */

      if (hdr->saved != NULL) {
            free(hdr->saved);
      }
      hdr->saved = NULL;

      return;
}
/****************************************************************************/
static HEADER *find_header(comp, name)
COMPOSITION *comp;
char *name;
{
      /* Find a header from the composition by name */

      HEADER *hdr;

      /* Search the list for the header */

      for (hdr = comp->headers; hdr != NULL; hdr = hdr->next) {
            /* Is this the header we're looking for? */

            if (!strcasecmp(hdr->name, name)) {
                  return(hdr);
            }
      }

      /* The header wasn't found in the list */

      return(NULL);
}
/****************************************************************************/
static HEADER *find_unfound_header(comp, name)
COMPOSITION *comp;
char *name;
{
      /* Find an unfound header from the list by name */

      HEADER *h;

      /* Search the list for the header */

      for (h = comp->headers; h != NULL; h = h->next) {
            /* Is this the header we're looking for? */

            if ((h->uniq || !h->found)
                && !strcasecmp(h->name, name)) {
                  return(h);
            }
      }

      /* The header wasn't found in the list */

      return(NULL);
}
/****************************************************************************/
static char *make_from()
{
      /* Return a static buffer containing From: details */

      static char *frombuf = NULL;
      char *rname;

      /* Free any previous return buffer */

      if (frombuf != NULL) {
            free(frombuf);
      }

      /* Get the user's real name */

      rname = get_vtext(V_REALNAME);

      /* Now generate an appropriate from address */

      frombuf = (rname == NULL) ? xstrdup(get_addr()) :
            vstrcat(rname, " <", get_addr(), ">", NULL);

      /* And return the buffer */

      return(frombuf);
}
/****************************************************************************/
static char *make_org()
{
      /* Return the Organization: details */

      return(get_vtext(V_ORG));
}
/****************************************************************************/
static char *make_reply_to()
{
      /* Return the Reply-To: details */

      return(get_vtext(V_REPLY));
}
/****************************************************************************/
static char *make_subject(subject, orig_msg, forwarding)
char *subject;
MESSAGE *orig_msg;
int forwarding;
{
      /* Make a subject from a format string or the default */

      char *format, *ftext;

      /* Use the subject forward string if possible */

      if (forwarding && (format = get_vtext(V_FWD_SUBJECT)) != NULL &&
          (ftext = unmessage(orig_msg, format, FOLD_WIDTH)) != NULL) {
            /* Return the formatted text as the subject */

            return(ftext);
      }

      /* Simply return the original subject */

      return(subject);
}
/****************************************************************************/
static char *make_content_type(boundary, digest)
char *boundary;
int digest;
{
      /* Return the Content-Type: details in a static buffer */

      static char *ctstr = NULL;

      /* Free any previous return value */

      if (ctstr != NULL) {
            free(ctstr);
      }

      /* Form and return the details; multipart if there's a boundary */

      if (boundary != NULL) {
            ctstr = vstrcat(MULTIPART_TYPE, "/", (digest)
                        ? DIGEST_SUBTYPE : MIXED_SUBTYPE, "; ",
                        BOUNDARY_PARAM, "=", "\"", boundary, "\"",
                        NULL);
      } else {
            ctstr = vstrcat(TEXT_TYPE, "/", PLAIN_SUBTYPE, "; ",
                        CHARSET_PARAM, "=", get_vtext(V_CHARSET),
                        NULL);
      }
      return(ctstr);
}
/****************************************************************************/
static char *make_disposition(filnam, attachment)
char *filnam;
int attachment;
{
      /* Return the Content-Disposition: details */

      static char *cdstr = NULL;

      /* Free any previous return value */

      if (cdstr != NULL) {
            free(cdstr);
      }

      /* Form and return the details */

      if (filnam != NULL) {
            cdstr = vstrcat((attachment) ? ATTACHMENT_DISP : INLINE_DISP,
                        "; ", FILENAME_PARAM, "=\"",
                        safe_filename(filnam), "\"", NULL);
      } else {
            cdstr = xstrdup((attachment) ? ATTACHMENT_DISP : INLINE_DISP);
      }           

      return(cdstr);
}
/****************************************************************************/
static char *make_in_reply_to(from, date)
char *from;
DATEZONE *date;
{
      /* Return the In-Reply-To: details in a static buffer */

      static char *irstr = NULL;
      char *dstr;

      /* Free any previous return value */

      if (irstr != NULL) {
            free(irstr);
      }

      /* Get the date details */

      dstr = strdate(date, FALSE);

      /* Form and return the details */

      irstr = vstrcat(INREPLY_TEXT1, from, INREPLY_TEXT2, dstr, NULL);
      return(irstr);
}
/****************************************************************************/
static char *make_refs(refs)
char **refs;
{
      /* Return the References: details in a static buffer */

      static char *rstr = NULL;
      int ref;

      /* Free any previous return value */

      if (rstr != NULL) {
            free(rstr);
      }

      /* Initialise the text to NULL */

      rstr = NULL;

      /* Copy any available references into the text */

      for (ref = 0; refs != NULL && ref < NO_REFERENCES; ref++) {
            /* Is this reference available? */

            if (refs[ref] == NULL) {
                  continue;
            }

            /* Append the reference */

            if (rstr == NULL) {
                  rstr = xstrdup(refs[ref]);
            } else {
                  rstr = xrealloc(rstr, strlen(rstr) +
                              strlen(refs[ref]) + 2);
                  (void) strcat(rstr, " ");
                  (void) strcat(rstr, refs[ref]);
            }
      }

      /* Return the details */

      return(rstr);
}
/****************************************************************************/
static char *make_mailer()
{
      /* Return the X-Mailer: definition in a static buffer */

      static char *mstr = NULL;

      /* Don't bother if the return text has already been set */

      if (mstr == NULL) {
            /* Copy the program name and version into the text */

            mstr = vstrcat(PROGNAME, " v", VERSION, NULL);
      }
      return(mstr);
}
/****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index