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

message.c

/* Message.c - Message handling commands for af.
   Copyright (C) 1997 - 2002 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 "keyseq.h"
#include "functions.h"
#include "commands.h"
#include "sendmail.h"
#include "variable.h"
#include "mode.h"
#include "tags.h"
#include "complete.h"
#include "io.h"
#include "mailcap.h"
#include "mime.h"
#include STRING_HDR

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

#ifndef lint
static char *RcsId = "$Id: message.c,v 1.4 2002/09/08 21:26:22 malc Exp $";
#endif /* ! lint */

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

extern char *xmalloc(), *xstrdup(), *vstrcat(), *tempnam();
extern char *strerror(), *expand(), *typeonly(), *get_vtext();
extern char *decode_header_line(), *get_str(), *get_cstr();
extern char *get_dcstr(), *utos();
extern int unlink(), access(), active(), is_blank(), mmdf_form();
extern int write_text(), get_vval(), confirm(), long_confirm();
extern int select_key(), listed(), edit_file(), tagset();
extern int mailcap_view(), mailcap_print(), mailcap_edit();
extern int match_contype(), viewable_ctype(), viewable_charset();
extern int known_charset(), to_sections(), close_pipe();
extern int vtupdate(), wait_for_children();
extern int remove_mime_tempfiles(), edit_composition();
extern long filesize();
extern void free(), free_messages(), free_message_parts();
extern void insert_headers(), read_msg_body(), set_sys_tags();
extern void typeout(), sectioned_typeout(), msg(), cmsg();
extern void msgl(), emsg(), emsgl(), show_buffer(), alldisplay();
extern void redisplay(), show_decoded_text(), show_bad_message_msg();
extern void show_bad_encoding_msg(), show_bad_cset_msg();
extern void show_mailcap_msg(), show_attachment_msg();
extern void free_composition();
extern FILE *open_pipe();
extern MESSAGE *read_msg_hdrs(), *read_hdrs_from_text();
extern MESSAGE *copy_one_message(), *null_msg();
extern MESSAGE *get_body_parts(), *get_submessage();
extern MESSAGE *get_alternative(), *get_digest_parts();
extern MESSAGE *rebuild_message();
extern MESSAGE_PART *get_message_parts();
extern MAILCAP *find_mailcap();
extern CLIST *fn_complete();
extern COMPOSITION *init_composition();

/* Local function declarations */

int msg_touched(), msg_status_changed();
static int msg_parts_touched(), page_one_message();
static int write_body_parts(), confirm_view();
static int need_confirm_mailcap_view();
static int save_attachment(), pipe_attachment();
static int msg_warnings(), print_message_error();
static void show_typeout(), show_pager();
static void replace_message(), clear_processed_flag();

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

extern int errno;

/****************************************************************************/
/* A flag to indicate that message statuses have changed */

static int status_changed = FALSE;

/****************************************************************************/
/* Import the current windown and user quit flag from commands.c */

extern int user_quit;

/****************************************************************************/
int page_messages(win, first, last, have_prev, have_next, cmd, fmt,
              hdrlist, how, parallel, part_no, no_sections)
WINDOW *win;
MESSAGE *first, *last;
char *cmd, *hdrlist;
int have_prev, have_next, fmt, how;
int parallel, part_no, no_sections;
{
      /* Display a list of messages */

      int sub_part_no;
      MESSAGE *message;
      MESSAGE *prev, *next;

      /* Set up sectioned typeout in case we need it */

      sectioned_typeout(TRUE, FALSE, FALSE);

      /* Initialise the message to display */

      message = first;
      sub_part_no = (IS_BODY_PART(how)) ? 1 : 0;

      /* Find the last message if required */

      while (no_sections < 0 && message->next != last) {
            /* Increment the message and the part number */

            message = message->next;
            sub_part_no = (IS_BODY_PART(how)) ? sub_part_no + 1 : 0;
      }

      /* Page each message in the list as suggested */

      while (!user_quit && message != NULL) {
            /* Ascertain the previous and next message */

            prev = (message != first) ? message->prev : NULL;
            next = (message->next != last) ? message->next : NULL;

            /* Page the message as required */

            no_sections = page_one_message(win, message,
                                     have_prev || prev != NULL,
                                     have_next || next != NULL,
                                     cmd, fmt, hdrlist, how,
                                     parallel, sub_part_no,
                                     no_sections);

            /* Don't allow movement past the first message */

            no_sections = (prev == NULL && !have_prev &&
                         no_sections < 0) ? 0 : no_sections;

            /* Update the current message and sub part number */

            message = (!no_sections) ? message :
                  (no_sections < 0) ? prev : next;
            sub_part_no += (!IS_BODY_PART(how) || !no_sections)
                  ? 0 : (no_sections < 0) ? -1 : 1;
      }

      /* Turn off sectioned typeout now? */

      sectioned_typeout(IS_BODY_PART(how), FALSE, FALSE);

      /* Return the number of sections left to move */

      return(no_sections);
}
/****************************************************************************/
static int page_one_message(win, message, have_prev, have_next, cmd, fmt,
                      hdrlist, how, parallel, part_no, no_sections)
WINDOW *win;
MESSAGE *message;
char *cmd, *hdrlist;
int have_prev, have_next, fmt, how;
int parallel, part_no, no_sections;
{
      /* Display a single message or body part via typeout or a pager */

      int msg_fmt, mc_fmt, msg_how;
      int orig_sections, show_top_level;
      MESSAGE *body_parts, *submessage;
      MESSAGE *alternative;
      MESSAGE_PART *msg_parts;
      MAILCAP *mcap;

      /* Check for a mailcap entry for the message */

      mcap = (!message->viewable && message->decodable && how != PG_RAW)
            ? find_mailcap(message->contype, message, MCAP_VIEW) : NULL;

      /* Extract the body parts or any encapsulated message */

      body_parts = (how != PG_RAW && how != PG_HEADERS && mcap == NULL)
            ? get_body_parts(message) : NULL;
      submessage = (how != PG_RAW && how != PG_HEADERS && mcap == NULL)
            ? get_submessage(message) : NULL;
      msg_parts = (how != PG_RAW && how != PG_HEADERS && mcap == NULL)
            ? get_message_parts(win->buf->messages, message) : NULL;

      /* If we have a partial message then rebuild it */

      submessage = (msg_parts == NULL) ? submessage
            : rebuild_message(msg_parts);

      /* Extract one body_part from a multipart/alternative */

      if ((alternative = get_alternative(message, body_parts,
                                 MCAP_VIEW)) != NULL) {
            /* Replace the body parts with the alternative */

            free_messages(body_parts);
            body_parts = alternative;
      }

      /* Save the original section count */

      orig_sections = no_sections;

      /* We have a message; check and update the section count */

      no_sections = (body_parts != NULL || no_sections < 0 && have_prev
                   || no_sections > 0 && have_next) ? no_sections : 0;
      no_sections += (submessage != NULL) ? 0 : (no_sections > 0) ? -1 :
            (no_sections < 0 && body_parts == NULL) ? 1 : 0;

      /* Will we be displaying the top level of the message? */

      show_top_level = (!no_sections && orig_sections >= 0);

      /* What output format do we need for the top level? */

      msg_fmt = (body_parts != NULL || mcap != NULL)
            ? fmt | WF_NOBODY : fmt;

      /* Add any warning flags to the output format */

      if (how != PG_RAW) {
            msg_fmt = (msg_fmt | msg_warnings(message, mcap, fmt,
                                      IS_BODY_PART(how)));
      }

      /* And what format do we use for mailcap viewing? */

      mc_fmt = WF_BODY | WF_SHOW | WF_NOBLANK | WF_DECODE;

      /* Check if we want to see the body part */

      if ((show_top_level || mcap != NULL) && how != PG_RAW &&
          !confirm_view(message, body_parts, submessage, mcap, part_no)) {
            /* We don't want to show the body part */

            free_messages(body_parts);
            free_messages(submessage);
            free_message_parts(msg_parts);

            /* Return the next section we want to view */

            return((orig_sections < 0 && have_prev) ? -1 : 1);
      }

      /* Reset the available typeout sections */

      sectioned_typeout(TRUE, have_prev, have_next || body_parts != NULL);

      /* Page the top level of the message if required */

      if (show_top_level && submessage == NULL &&
          (mcap == NULL || !IS_BODY_PART(how))
          && !strcmp(cmd, V_USE_TYPEOUT)) {
            /* Display the body part to typeout */

            show_typeout(message, msg_fmt, how == PG_BODY_PART,
                       part_no, hdrlist);

            /* Gather the section count and handle implicit moves */

            no_sections = to_sections();
            no_sections = (no_sections || mcap != NULL) ? no_sections : 1;
      } else if (show_top_level && submessage == NULL
               && (mcap == NULL || !IS_BODY_PART(how))) {
            /* Show the body part and set the section count */

            show_pager(message, cmd, msg_fmt, how == PG_BODY_PART,
                     part_no, hdrlist);
            no_sections = (orig_sections <= 0 && have_prev)
                  ? -1 : (mcap != NULL) ? 0 : 1;
      }

      /* Show the message body via mailcap if required */

      if (!no_sections && !user_quit && mcap != NULL) {
            /* Show the body part and set the section count */

            (void) mailcap_view(message, mcap, cmd, mc_fmt,
                            hdrlist, parallel);
            no_sections = (orig_sections <= 0 && have_prev) ? -1 : 1;
      }

      /* Now recurse over the body-part list */

      if ((!show_top_level || no_sections > 0)
          && !user_quit && body_parts != NULL) {
            /* How do we want to display the messages? */

            msg_how = (!IS_BODY_PART(how)) ? PG_MULTIPART : PG_BODY_PART;

            /* Now page the body parts of the message */

            no_sections = page_messages(win, body_parts, NULL, TRUE,
                                  have_next, cmd, fmt, hdrlist,
                                  msg_how, message->parallel,
                                  part_no, no_sections);

            /* Clean up any children from multipart/parallel messages */

            (void) wait_for_children("Waiting for display programs...");
            remove_mime_tempfiles();

            /* Allow for the top level message if moving backwards */

            no_sections += (no_sections < 0) ? 1 : 0;
      }

      /* And the encapsulated message */

      if (!user_quit && submessage != NULL) {
            no_sections = page_one_message(win, submessage, have_prev,
                                     have_next, cmd, fmt, hdrlist,
                                     how, FALSE, part_no,
                                     no_sections);
      }

      /* Update the message status if we're at the top level */

      if (!IS_BODY_PART(how)) {
            (void) msg_touched(win, message, ST_READ, FALSE);
      }

      /* Update the status of any message parts */

      (void) msg_parts_touched(win, msg_parts, ST_READ, FALSE);

      /* Free the body parts, encapsulated message, and message parts */

      free_messages(body_parts);
      free_messages(submessage);
      free_message_parts(msg_parts);

      /* Return the number of sections to move */

      return(no_sections);
}
/****************************************************************************/
MESSAGE *expand_message(win, message)
WINDOW *win;
MESSAGE *message;
{
      /* Return the message's body parts as a message list */
      
      MESSAGE *body_parts, *submessage, *m;
      MESSAGE_PART *msg_parts = NULL;

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

      body_parts = get_body_parts(message);
      body_parts = (body_parts != NULL) ? body_parts
            : get_submessage(message);
      msg_parts = (body_parts != NULL) ? NULL :
            get_message_parts(win->buf->messages, message);

      /* Rebuild any partial message */

      body_parts = (msg_parts == NULL) ? body_parts
            : rebuild_message(msg_parts);

      /* Automagically expand encapsulated messages at this level */

      for (m = body_parts; m != NULL; m = m->next) {
            /* Check if this is an encapsulated message */

            if ((submessage = get_submessage(m)) != NULL) {
                  /* Replace the body part with the submessage */

                  submessage->prev = m->prev;
                  submessage->next = m->next;
                  if (submessage->prev != NULL) {
                        submessage->prev->next = submessage;
                  }
                  if (submessage->next != NULL) {
                        submessage->next->prev = submessage;
                  }
                  body_parts = (body_parts == m)
                        ? submessage : body_parts;

                  /* And clean up the original body part */

                  m->prev = m->next = NULL;
                  free_messages(m);

                  /* Make sure the for loop still works */

                  m = submessage;
            }
      }

      /* Check we found some valid body parts or try a digest */

      if (body_parts == NULL &&
          (body_parts = get_digest_parts(message)) == NULL) {
            /* No submessage and not a valid digest either */

            return(NULL);
      }
            
      /* Add the header lines to the body parts */

      insert_headers(message, body_parts);

      /* Update the original message's status */

      (void) msg_touched(win, message, ST_READ, FALSE);

      /* Update the status of any message parts */

      (void) msg_parts_touched(win, msg_parts, ST_READ, FALSE);

      /* And free any message parts */

      free_message_parts(msg_parts);

      /* Return the null-terminated message list */

      return(null_msg(body_parts));
}
/****************************************************************************/
int save_selected(win, start, end, texpr, filnam, sname, fmt)
WINDOW *win;
MESSAGE *start, *end;
TAG_EXPR *texpr;
char *filnam, *sname;
int fmt;
{
      /* Save the selected messages to the file */

      int status, appending;
      MESSAGE *m;
      FILE *fp;

      /* Are we writing or appending the file? */

      appending = (filesize(filnam) > 0);

      /* Give the user a message if required */

      msgl((appending) ? "Appending " : "Writing ",
           sname, " to ", filnam, "...", NULL);

      /* Check if we need to write in MMDF format */

      fmt |= (WF_FMT(fmt) == WF_MBOX && mmdf_form(filnam)) ? WF_MMDF : 0;

      /* Open the file we're writing to */

      if ((fp = fopen(filnam, "a")) == NULL) {
            /* We can't open the file so fail */

            emsgl("Can't open ", filnam, ": ", strerror(errno), NULL);
            free(filnam);
            return(FALSE);
      }

      /* Now save the text of the messages */

      for (m = start; m->text != NULL && m != end; m = m->next) {
            /* Save the message if it's been selected */

            if (m->visible && !m->processed &&
                (texpr == NULL || tagset(m, texpr)) &&
                (status = write_body_parts(fp, win, m, ST_SAVED,
                                     fmt, 0))) {
                  /* Error saving the message */

                  emsgl("Error writing ", filnam, ": ",
                        strerror(status), NULL);
                  (void) fclose(fp);
                  clear_processed_flag(win->buf);
                  free(filnam);
                  return(FALSE);
            }
      }

      /* Close the file */

      (void) fclose(fp);

      /* Clear the processed flag on the messages */

      clear_processed_flag(win->buf);

      /* Confirm the write succeeded */

      msgl((appending) ? "Appending " : "Writing ",
           sname, " to ", filnam, "... Done", NULL);

      /* And show any changed tags */

      if (msg_status_changed()) {
            alldisplay(win->buf);
      }

      /* Free the file name and return success */

      free(filnam);
      return(TRUE);
}
/****************************************************************************/
int print_selected(win, start, end, texpr, cmd, sname, fmt)
WINDOW *win;
MESSAGE *start, *end;
TAG_EXPR *texpr;
char *cmd, *sname;
int fmt;
{
      /* Print the selected messages to a file */

      int status;
      MESSAGE *m;
      FILE *fp;

      /* Print a message for the user */

      msgl("Printing ", sname, "...", NULL);

      /* Open a pipe to the print command */

      if ((fp = open_pipe(cmd, "w", TRUE, NULL)) == NULL) {
            (void) close_pipe(fp, TRUE, FALSE);
            emsgl("Can't start process ", cmd,
                  ": ", strerror(errno), NULL);
            return(FALSE);
      }

      /* Now print the text of the messages */

      for (m = start; m->text != NULL && m != end; m = m->next) {
            /* Is this message one we should print? */

            if (m->visible && !m->processed &&
                (texpr == NULL || tagset(m, texpr)) &&
                (status = write_body_parts(fp, win, m, ST_PRINTED,
                                     fmt, 0))) {
                  /* Error writing to the printer */

                  emsgl("Error writing pipe: ",
                        strerror(status), NULL);
                  clear_processed_flag(win->buf);
                  (void) close_pipe(fp, TRUE, FALSE);
                  return(FALSE);
            }
      }

      /* Close the pipe */

      (void) close_pipe(fp, TRUE, FALSE);

      /* Clear the processed flag on the messages */

      clear_processed_flag(win->buf);

      /* Confirm the print succeeded */

      msgl("Printing ", sname, "... Done", NULL);

      /* And show any changed tags */

      if (msg_status_changed()) {
            alldisplay(win->buf);
      }
      return(TRUE);
}
/****************************************************************************/
int pipe_selected(win, start, end, texpr, cmd, sname, fmt)
WINDOW *win;
MESSAGE *start, *end;
TAG_EXPR *texpr;
char *cmd, *sname;
int fmt;
{
      /* Pipe the selected messages into a command */

      int status;
      MESSAGE *m;
      FILE *fp;

      /* Open a pipe to the command */

      if ((fp = open_pipe(cmd, "w", TRUE, NULL)) == NULL) {
            emsgl("Can't start process ", cmd, ": ",
                  strerror(errno), NULL);
            return(FALSE);
      }

      /* Actually write the text */

      for (m = start; m->text != NULL && m != end; m = m->next) {
            /* Is this message one we should pipe? */

            if (m->visible && !m->processed &&
                (texpr == NULL || tagset(m, texpr)) &&
                (status = write_body_parts(fp, win, m, ST_READ,
                                     fmt, 0))) {
                  /* Error writing the pipe */

                  emsgl("Error writing pipe: ",
                        strerror(status), NULL);
                  (void) close_pipe(fp, TRUE, FALSE);
                  clear_processed_flag(win->buf);
                  return(FALSE);
            }
      }

      /* Clear the processed flag on the messages */

      clear_processed_flag(win->buf);

      /* Close the pipe */

      (void) close_pipe(fp, TRUE, TRUE);

      /* And show any changed tags */

      if (msg_status_changed()) {
            alldisplay(win->buf);
      }
      return(TRUE);
}
/****************************************************************************/
int edit_message(win, message, body, verbose)
WINDOW *win;
MESSAGE *message;
int body, verbose;
{
      /* Handle editing and then re-reading a message */

      char *ctype;
      MAILCAP *mcap = NULL;
      COMPOSITION *comp;

      /* See if we find a mailcap command to edit the message */

      if (message->decodable && !message->viewable && !message->multipart
          && body && (mcap = find_mailcap(message->contype,
                                  message, MCAP_EDIT)) == NULL
          && !message->textual) {
            /* No way to edit this message */

            ctype = typeonly(message->contype);
            emsgl("No edit method for content type ",
                  typeonly(ctype), " found in mailcaps", NULL);
            free(ctype);
            return(FALSE);
      }

      /* Create a composition from the original message */

      if ((comp = init_composition(NULL, NULL, NULL, NULL, NULL,
                             NULL, message, SM_EDIT)) == NULL) {
            /* Failed to initialise the composition */

            return(FALSE);
      }

      /* Now edit the composition */

      if (!edit_composition(comp, !body, TRUE)) {
            /* Error editing the composition */

            free_composition(comp);
            return(FALSE);
      }

      /* Succeeded, update the message status and buffer's pointers */

      (void) msg_touched(win, comp->message, ST_READ, FALSE);
      replace_message(win, message, copy_one_message(comp->message));

      /* Clean up the composition */

      free_composition(comp);

      /* Mark the buffer as modified and update the display */

      win->buf->mod = TRUE;
      alldisplay(win->buf);

      /* Possibly confirm and return success */

      if (verbose) {
            msg("Message updated");
      }
      return(TRUE);
}
/****************************************************************************/
static void replace_message(win, old_msg, new_msg)
WINDOW *win;
MESSAGE *old_msg, *new_msg;
{
      /* Replace old_msg with new_msg in the current buffer */

      WINDOW *w;

      /* Set the message's user tags from the old message */

      if (old_msg->user_tags != NULL) {
            new_msg->user_tags = xstrdup(old_msg->user_tags);
      }

      /* Set the new message's position from the old message */

      new_msg->pos = old_msg->pos;

      /* Update the buffer's message list if required */

      if (win->buf->messages == old_msg) {
            win->buf->messages = new_msg;
      }

      /* Update the new message's pointers */

      if ((new_msg->prev = old_msg->prev) != NULL) {
            new_msg->prev->next = new_msg;
      }
      new_msg->next = old_msg->next;
      new_msg->next->prev = new_msg;

      /* Remove the old message from the list */

      old_msg->next = old_msg->prev = NULL;

      /* Update the buffer's point and mark */

      if (win->buf->point == old_msg) {
            win->buf->point = new_msg;
      }
      if (win->buf->mark == old_msg) {
            win->buf->mark = new_msg;
      }

      /* Now update all the windows' pointers */

      w = win;
      do {
            /* Update the window's first, point and mark */

            if (w->first == old_msg) {
                  w->first = new_msg;
            }
            if (w->point == old_msg) {
                  w->point = new_msg;
            }
            if (w->mark == old_msg) {
                  w->mark = new_msg;
            }

            /* Move on to the next window */

            w = w->next;
      } while (w != win);

      /* Finally, free the old message */

      free_messages(old_msg);
      return;
}
/****************************************************************************/
static int write_body_parts(fp, win, message, msg_status, fmt, part_no)
FILE *fp;
WINDOW *win;
MESSAGE *message;
int msg_status, fmt, part_no;
{
      /* Write message, or it's decoded body parts, to fp */

      char *hdrlist;
      int status, mc_fmt, what;
      int body_part_no = 0;
      MESSAGE *body_parts, *m;
      MESSAGE *submessage;
      MESSAGE *alternative;
      MESSAGE_PART *msg_parts;
      MAILCAP *mcap;

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

      body_parts = (fmt & WF_DECODE && WF_FMT(fmt) != WF_MBOX)
            ? get_body_parts(message) : NULL;
      submessage = (fmt & WF_DECODE && WF_FMT(fmt) != WF_MBOX)
            ? get_submessage(message) : NULL;
      msg_parts = (fmt & WF_DECODE && WF_FMT(fmt) != WF_MBOX)
            ? get_message_parts(win->buf->messages, message) : NULL;

      /* The mailcap format doesn't include blank lines */

      mc_fmt = WF_BODY | WF_SHOW | WF_NOBLANK | WF_DECODE;

      /* If we have a partial message then rebuild it */

      submessage = (msg_parts == NULL) ? submessage
            : rebuild_message(msg_parts);

      /* What operation are we doing to the message */

      what = (msg_status == ST_PRINTED) ? MCAP_PRINT : MCAP_VIEW;

      /* Extract one body_part from a multipart/alternative */

      if ((alternative = get_alternative(message, body_parts,
                                 what)) != NULL) {
            /* Replace the body parts with the alternative */

            free_messages(body_parts);
            body_parts = alternative;
      }

      /* Which message headers should we write? */

      hdrlist = (WF_FMT(fmt) == WF_SOME) ? get_vtext(V_NOTDISP) : NULL;

      /* Print a title before each body part */

      if (part_no && message->viewable && WF_FMT(fmt) == WF_SOME
          && (fmt & WF_SHOW) && submessage == NULL &&
          fprintf(fp, "(** Part %d of multipart message **)\n",
                part_no) == EOF) {
            /* Error writing the title */

            status = errno;
            free_messages(body_parts);
            return(status);
      }

      /* If we don't have any body parts then write the message */

      if (body_parts == NULL && submessage == NULL) {
            /* Check for a mailcap entry for printing the message */

            mcap = (!message->viewable && message->decodable
                  && msg_status == ST_PRINTED)
                  ? find_mailcap(message->contype, message,
                               MCAP_PRINT) : NULL;

            /* Check if we can print the message body */

            if ((!message->textual && !message->viewable && mcap == NULL
                 || !message->textual && message->viewable &&
                 body_parts == NULL && submessage == NULL
                 || !message->decodable) && msg_status == ST_PRINTED) {
                  /* Don't print the message body */

                  fmt |= WF_NOBODY;
            }
                
            /* Write the message and save the status */

            if (!(status = (mcap != NULL) ?
                  mailcap_print(fp, message, mcap, mc_fmt, hdrlist)
                  : write_text(fp, message, fmt, hdrlist))) {
                  /* Update the message's status */

                  (void) msg_touched(win, message, msg_status, TRUE);
            }

            /* Check if we can print the message body */

            if (!status && msg_status == ST_PRINTED &&
                (!message->textual && !message->viewable && mcap == NULL
                 || message->viewable && !message->textual &&
                 body_parts == NULL && submessage == NULL
                 || !message->decodable)) {
                  /* Print the message error */

                  status = print_message_error(fp, message, part_no);
            }
                
            /* And return the status of the write */

            return(status);
      }

      /* Show the headers of a multipart message */

      if (!part_no && body_parts != NULL &&
          (status = write_text(fp, message, fmt | WF_NOBODY, hdrlist))) {
            /* Error writing the message headers */

            free_messages(body_parts);
            return(status);
      }

      /* Loop over the body parts, writing them to fp */

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

            if (status = write_body_parts(fp, win, m, msg_status,
                                    fmt, ++body_part_no)) {
                  /* Error writing the body part */

                  free_messages(body_parts);
                  return(status);
            }
      }

      /* Now write any encapsulated message */

      if (submessage != NULL &&
          (status = write_body_parts(fp, win, submessage,
                               msg_status, fmt, part_no))) {
            /* Error writing the encapsulated message */

            free_messages(submessage);
            free_message_parts(msg_parts);
            return(status);
      }

      /* Update the message status if we're at the top level */

      if (!part_no) {
            (void) msg_touched(win, message, msg_status, TRUE);
      }

      /* Update the status of any message parts */

      (void) msg_parts_touched(win, msg_parts, msg_status, TRUE);

      /* Free the body parts, encapsulated message, and message parts */

      free_messages(body_parts);
      free_messages(submessage);
      free_message_parts(msg_parts);

      /* Now we can return success */

      return(0);
}
/****************************************************************************/
static int confirm_view(message, body_parts, submessage, mcap, part_no)
MESSAGE *message, *body_parts, *submessage;
MAILCAP *mcap;
int part_no;
{
      /* Confirm that we want to view a message */

      char *ctype, *cdesc, *enc, *entity, *object, *prompt;
      int invalid, unprintable, unknown, status = FALSE;

      /* Check for an invalid multipart or encapsulated message */

      invalid = ((!message->textual && message->viewable
                || message->multipart) && body_parts == NULL
               && submessage == NULL);

      /* Check for an unknown content-type */

      unknown = (mcap == NULL && !message->textual && !message->multipart
               && !message->viewable && body_parts == NULL
               && submessage == NULL);

      /* Check for an unknown character set */

      unprintable = (unknown && message->charset != NULL
                   && viewable_ctype(message->contype)
                   && !known_charset(message->charset));

      /* If the charset is unknown then the content-type is known */

      unknown = (unknown && !unprintable);

      /* We only need to confirm attachments or complex messages */

      if (message->decodable && !invalid && !unknown
          && !unprintable && !message->attachment
          && (mcap == NULL || !need_confirm_mailcap_view(message))) {
            return(TRUE);
      }

      /* Build the prompt for the confirmation */

      ctype = (message->contype != NULL) ? typeonly(message->contype)
            : vstrcat(TEXT_TYPE, "/", PLAIN_SUBTYPE, NULL);
      cdesc = (message->description == NULL || invalid) ? xstrdup("")
            : vstrcat("\"", message->description, "\"", NULL);
      entity = xstrdup((part_no) ? "body part" : "message");
      object = xstrdup((invalid || unprintable) ? entity : "attachment");
      enc = message->encoding;

      /* Show some helpful text about the confirmation */

      if (mcap != NULL) {
            /* Show the message about mailcap messages */

            show_mailcap_msg(ctype, cdesc, entity,
                         mcap->desc, mcap->view);
      } else if (!message->decodable) {
            /* Show the message about unknown encodings */

            show_bad_encoding_msg(ctype, cdesc, enc, entity);
      } else if (invalid && message->multipart) {
            /* Show the message about invalid multipart messages */

            show_bad_message_msg(ctype, cdesc, entity, D_BAD_MULTIPART);
      } else if (invalid) {
            /* Show the message about invalid partial messages */

            show_bad_message_msg(ctype, cdesc, entity, D_BAD_MESSAGE);
      } else if (unknown) {
            /* Show the message about unknown content-types */

            show_bad_message_msg(ctype, cdesc, entity, D_BAD_CONTENT);
      } else if (unprintable) {
            /* Show the message about unknown character sets */

            show_bad_cset_msg(ctype, message->charset, cdesc, entity);
      } else {
            /* Show the message about viewing attachments */

            show_attachment_msg(ctype, cdesc, entity);
      }

      /* The prompt varies for invalid messages or attachments */

      prompt = vstrcat((invalid) ? "View invalid or incomplete "
                   : (unknown || unprintable || !message->decodable)
                   ? "View, save, or pipe " : "View or save ",
                   ctype, " ", object, "? ", NULL);

      /* Get a key from the user */

      switch (select_key(prompt, (invalid) ? BAD_MSG_OPTS :
                     (!message->decodable) ? BAD_ENC_OPTS :
                     (unknown) ? BAD_MCAP_OPTS :
                     (unprintable) ? BAD_CSET_OPTS :
                     ATTACHMENT_OPTS, TRUE)) {
      case ATTACHMENT_VIEW:
      case ATTACHMENT_YES:
            /* We'll want to view the attachment */

            status = TRUE;
            break;
      case ATTACHMENT_SAVE:
            /* Save the attachment's body */

            (void) save_attachment(message);
            break;
      case ATTACHMENT_PIPE:
            /* Pipe the attachment's body */

            (void) pipe_attachment(message);
            break;
      }

      /* Clean up and return status */

      free(ctype);
      free(cdesc);
      free(entity);
      free(object);
      free(prompt);

      return(status);
}
/****************************************************************************/
static int need_confirm_mailcap_view(message)
MESSAGE *message;
{
      /* Check if we need to confirm viewing a message via mailcap */

      char *entry, *end, *ctype;
      size_t len;

      /* Is the content-type listed as needing confirmation? */

      entry = get_vtext(V_CONFIRM);
      while (message->contype != NULL && entry != NULL && *entry != '\0') {
            /* Find the end of the content-type */

            end = strchr(entry + 1, ':');

            /* How long is the current content-type? */

            if ((len = (end != NULL) ? end - entry : strlen(entry)) > 0) {
                  /* Build the full content-type for this entry */

                  ctype = xmalloc(len + 1);
                  (void) strncpy(ctype, entry, len);
                  ctype[len] = '\0';

                  /* Does this entry say we need confirmation? */

                  if (match_contype(ctype, message->contype)) {
                        /* We need to confirm this message */

                        free(ctype);
                        return(TRUE);
                  }

                  /* We don't need this content-type any more */

                  free(ctype);
            }

            /* Update the loop counter */

            entry = (end != NULL) ? end + 1 : NULL;
      }
  
      /* We don't need to confirm this message */
  
      return(FALSE);
}
/****************************************************************************/
static int save_attachment(message)
MESSAGE *message;
{
      /* Handle saving an attachment to a file */

      char *prompt, *deflt, *filnam = NULL;
      int fmt, status;
      FILE *fp;

      /* Which format should we save in? */

      fmt = WF_BODY | WF_DECODE | WF_SHOW | WF_NOBLANK;

      /* Form the prompt for the file name */

      prompt = vstrcat("Save attachment to file: ", NULL);

      /* Get the file to save the messages to */

      if (message->filename != NULL) {
            /* Default the file from the message */

            deflt = xstrdup(message->filename);
            filnam = get_dcstr(NULL, prompt, deflt, fn_complete,
                           C_PERMISSIVE);
            free(deflt);
      } else {
            /* No default file in this case */

            filnam = get_cstr(NULL, prompt, fn_complete, C_PERMISSIVE);
      }
      free(prompt);

      /* Check and expand the file name */

      if (filnam == NULL) {
            return(FALSE);
      }
      filnam = expand(filnam);

      /* Confirm before we append to an existing file */

      if (!access(filnam, 00)) {
            /* Make the prompt and get confirmation */

            prompt = vstrcat("Overwrite existing ", filnam, "? ", NULL);
            status = long_confirm(prompt, TRUE);
            free(prompt);

            /* Quit if we didn't get confirmation */

            if (!status) {
                  free(filnam);
                  return(FALSE);
            }
      }

      /* Give the user a message */

      msgl("Writing attachment to ", filnam, "...", NULL);

      /* Open the file we're writing to */

      if ((fp = fopen(filnam, "w")) == NULL) {
            /* We can't open the file so fail */

            emsgl("Can't open ", filnam, ": ", strerror(errno), NULL);
            free(filnam);
            return(FALSE);
      }

      /* Now save the text of the message */

      if (status = write_text(fp, message, fmt, NULL)) {
            /* Error saving the attachment */

            emsgl("Error writing ", filnam, ": ",
                  strerror(status), NULL);
            (void) fclose(fp);
            free(filnam);
            return(FALSE);
      }

      /* Close the file */

      (void) fclose(fp);

      /* Confirm we wrote the attachment ok */

      msgl("Writing attachment to ", filnam, "... Done", NULL);

      /* Free the file name and return */

      free(filnam);
      return(TRUE);
}
/****************************************************************************/
static int pipe_attachment(message)
MESSAGE *message;
{
      /* Handle piping an attachment into a command */

      char *cmd;
      int fmt, status;
      FILE *fp;

      /* Which format should we save in? */

      fmt = WF_BODY | WF_DECODE | WF_SHOW | WF_NOBLANK;

      /* Get the command to pipe into */

      if ((cmd = get_str(NULL, "Pipe attachment into command: ")) == NULL) {
            return(FALSE);
      }

      /* Open a pipe to the command */

      if ((fp = open_pipe(cmd, "w", TRUE, NULL)) == NULL) {
            emsgl("Can't start process ", cmd, ": ",
                  strerror(errno), NULL);
            return(FALSE);
      }

      /* Now write the text of the message */

      if (status = write_text(fp, message, fmt, NULL)) {
            /* Error writing the pipe */

            emsgl("Error writing pipe: ",
                  strerror(status), NULL);
            (void) close_pipe(fp, TRUE, FALSE);
            return(FALSE);
      }

      /* Close the pipe */

      (void) close_pipe(fp, TRUE, TRUE);

      /* And return success */

      return(TRUE);
}
/****************************************************************************/
static int msg_warnings(message, mcap, fmt, body_part)
MESSAGE *message;
MAILCAP *mcap;
int fmt, body_part;
{
      /* Set up the warning flags for writing viewed messages */

      int warnings = 0;

      /* Flag unknown multipart subtypes */

      if (message->multipart && !message->viewable && mcap == NULL) {
            warnings = warnings | WF_WARN_MPART;
      }

      /* Flag unknown textual subtypes */

      if (!(fmt & WF_NOBODY) && message->textual
          && message->contype != NULL && mcap == NULL
          && !viewable_ctype(message->contype)) {
            warnings = warnings | WF_WARN_TEXT;
      }

      /* And messages in character sets we can't view */

      if (!(fmt & WF_NOBODY) && message->textual
          && message->charset != NULL && mcap == NULL
          && !viewable_charset(message->charset)) {
            warnings = warnings | WF_WARN_CSET;
      }

      /* Finally, note whether the message is a body part */

      if (warnings && body_part) {
            warnings = warnings | WF_BODY_PART;
      }

      /* Now return the warning flags */

      return(warnings);
}
/****************************************************************************/
static int print_message_error(fp, message, body_part)
FILE *fp;
MESSAGE *message;
int body_part;
{
      /* Print an error message about the message or body part */

      int status = 0;
      int invalid, unknown;
      int unprintable;

      /* Check for an invalid multipart or encapsulated message */

      invalid = (!message->textual && message->viewable
               || message->multipart);

      /* Check for an unknown content-type */

      unknown = (!message->textual && !message->multipart
               && !message->viewable);

      /* Check for an unknown character set */

      unprintable = (unknown && message->charset != NULL
                   && viewable_ctype(message->contype)
                   && !known_charset(message->charset));

      /* Print a message about unknown encodings */

      if (!message->decodable &&
          (fputs("[Can't print ", fp) == EOF
           || fputs((body_part) ? "body part" : "message body", fp) == EOF
           || fputs(" encoded with the ", fp) == EOF
           || fputs(message->encoding, fp) == EOF
           || fputs(" encoding]\n", fp) == EOF)) {
            /* Failed to print, save the error status */

            status = errno;
      }
      
      /* Print a message about invalid multipart messages */

      if (invalid && message->multipart &&
          (fputs("[Can't print invalid multipart ", fp) == EOF
           || fputs((body_part) ? "body part]\n" :
                  "message body]\n", fp) == EOF)) {
            /* Failed to print, save the error status */

            status = errno;
      }

      /* Print a message about invalid partial messages */

      if (invalid && !message->multipart &&
          (fputs("[Can't print invalid partial ", fp) == EOF
           || fputs((body_part) ? "body part]\n" :
                  "message body]\n", fp) == EOF)) {
            /* Failed to print, save the error status */

            status = errno;
      }

      /* Print a message about unknown content-types */

      if (unknown && !invalid && !unprintable &&
          (fputs("[Can't print ", fp) == EOF
           || fputs((body_part) ? "body part" : "message", fp) == EOF
           || fputs(" of type ", fp) == EOF
           || fputs(message->contype, fp) == EOF
           || fputs("]\n", fp) == EOF)) {
            /* Failed to print, save the error status */

            status = errno;
      }

      /* Print a message about unknown character sets */

      if (unprintable &&
          (fputs("[Can't print ", fp) == EOF
           || fputs((body_part) ? "body part" : "message", fp) == EOF
           || fputs(" in the ", fp) == EOF
           || fputs(message->charset, fp) == EOF
           || fputs(" character set]\n", fp) == EOF)) {
            /* Failed to print, save the error status */

            status = errno;
      }

      /* Now return the status */

      return(status);
}
/****************************************************************************/
static void show_typeout(message, fmt, body_part, part_no, hdrlist)
MESSAGE *message;
int fmt, body_part, part_no;
char *hdrlist;
{
      /* Display a message via typeout */

      int found = FALSE;
      TEXTLINE *t;

      /* If this is a body part, let the user know this */

      if (part_no) {
            typeout("(** Part ");
            typeout(utos(part_no));
            typeout(" of multipart ");
            typeout((body_part) ? "body part" : "message");
            typeout(" **)\n");
      }

      /* Write the (decoded) headers of the message to typeout */

      for (t = message->text; !user_quit && t != NULL
           && !is_blank(t->line); t = t->next) {
            /* Display the line if required */

            if (WF_FMT(fmt) == WF_TEXT || WF_FMT(fmt) == WF_SOME
                && !listed(t->line, hdrlist)) {
                  /* Typeout the header and get the sections */

                  typeout((fmt & WF_DECODE) ?
                        decode_header_line(t->line, WR_SHOW | WR_FOLD)
                        : t->line);

                  /* We found a header to display */

                  found = TRUE;
            }
      }

      /* Print the blank line if any headers were displayed */

      if (found && !user_quit && t != NULL && !(fmt & WF_NOBODY)) {
            typeout(t->line);
      }
      t = (t != NULL) ? t->next : t;

      /* Warn the user about unknown multipart subtypes */

      if (fmt & WF_WARN_MPART) {
            typeout("\n(** This ");
            typeout((fmt & WF_BODY_PART) ? "body part" : "message");
            typeout(" is marked as being of content-type ");
            typeout(typeonly(message->contype));
            typeout(" **)\n");
      }

      /* Warn the user about unknown textual subtypes */

      if (fmt & WF_WARN_TEXT) {
            /* Warn about non-plain text messages */

            typeout("(** This ");
            typeout((fmt & WF_BODY_PART) ? "body part" : "message");
            typeout(" is marked as being of content-type ");
            typeout(typeonly(message->contype));
            typeout(" **)\n");
      }

      /* Warn about messages in charsets we can't display */

      if (fmt & WF_WARN_CSET) {
            typeout("(** This ");
            typeout((fmt & WF_BODY_PART) ? "body part" : "message");
            typeout(" is in the ");
            typeout(message->charset);
            typeout(" character set **)\n");
      }

      /* And separate out any warnings */

      if (WF_WARN(fmt)) {
            typeout("\n");
      }

      /* Display the (decoded) text of the message if required */

      if (!user_quit && !(fmt & WF_NOBODY)) {
            /* Write the text and update the number of sections */

            show_decoded_text(t, (fmt & WF_DECODE) ? message->encoding
                          : NULL, message->textual);
      }

      /* End the typeout and return */

      typeout(NULL);
      return;
}
/****************************************************************************/
static void show_pager(message, cmd, fmt, body_part, part_no, hdrlist)
MESSAGE *message;
char *cmd, *hdrlist;
int fmt, body_part, part_no;
{
      /* Display a message via a pager */

      FILE *fp;

      /* Start up the pager if possible */

      if ((fp = open_pipe(cmd, "w", TRUE, NULL)) == NULL) {
            return;
      }

      /* If this is a body part, let the user know this */

      if (part_no) {
            (void) fprintf(fp, "(** Part %d of multipart %s **)\n",
                         part_no, (body_part) ? "body part"
                         : "message");
      }

      /* Write the text of the message to the pipe */

      (void) write_text(fp, message, fmt, hdrlist);

      /* Close the pipe and return */

      (void) close_pipe(fp, TRUE, get_vval(V_PAUSE));
      return;
}
/****************************************************************************/
int msg_touched(win, message, operation, processed)
WINDOW *win;
MESSAGE *message;
int operation, processed;
{
      /*
       * The message has been touched, update the status and tags
       * according to the operation carried out.  If processed is
       * TRUE then set the message's processed flag too.  Return
       * TRUE if the system tags were changed.
       */

      int changed = FALSE;

      /* Check for status change */

      changed = (message->new || !message->read);

      /* Update the message's new and read flags */
      
      message->new = FALSE;
      message->read = TRUE;

      /* Handle the operation-specific settings */

      switch (operation) {
      case ST_REPLIED:
            changed = (changed || !message->replied);
            message->replied = TRUE;
            break;
      case ST_FORWARDED:
            changed = (changed || !message->forwarded);
            message->forwarded = TRUE;
            break;
      case ST_SAVED:
            changed = (changed || !message->saved);
            message->saved = TRUE;
            break;
      case ST_PRINTED:
            changed = (changed || !message->printed);
            message->printed = TRUE;
            break;
      }

      /* Update the system tags and buffer modification flag? */

      if (changed) {
            if (!active(win->buf, M_READONLY)) {
                  win->buf->st_mod = TRUE;
            }
            set_sys_tags(message);
      }

      /* Set the message's processed flag if required */

      message->processed = (processed) ? TRUE : message->processed;

      /* Now update the global "message status changed" flag */

      status_changed = (status_changed || changed);

      /* Return TRUE if message statuses have changed */

      return(changed);
}
/****************************************************************************/
int msg_parts_touched(win, msg_parts, msg_status, processed)
WINDOW *win;
MESSAGE_PART *msg_parts;
int msg_status, processed;
{
      /* Update the status of all the partial messages */

      int changed = FALSE;
      MESSAGE_PART *m;

      /* Loop over the message parts */ 

      for (m = msg_parts; m != NULL; m = m->next) {
            /* Update the status of any message in this part */

            changed = (m->parts == NULL &&
                     msg_touched(win, m->message, msg_status,
                               processed) || changed);

            /* Update the status of any sub-parts of this part */

            changed = (((m->parts == NULL) ? changed :
                      msg_parts_touched(win, m->parts, msg_status,
                                    processed)) || changed);
      }

      /* Now return whether any parts changed status */

      return(changed);
}
/****************************************************************************/
int msg_status_changed()
{
      /* Return whether statuses have changed and reset the flag */

      int changed = status_changed;

      /* Reset the flag and return the original */

      status_changed = FALSE;
      return(changed);
}
/****************************************************************************/
void clear_processed_flag(buf)
MAILBUF *buf;
{
      /* Clear the processed flag on all the buffer's messages */

      MESSAGE *m;

      /* Loop over the messages clearing the flag */

      for (m = buf->messages; m != NULL; m = m->next) {
            m->processed = FALSE;
      }

      /* That's all there is to it */

      return;
}
/****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index