/* elmo - ELectronic Mail Operator Copyright (C) 2003, 2004 rzyjontko 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; version 2. 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. ---------------------------------------------------------------------- */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "sender.h" #include "smtp.h" #include "clock.h" #include "ask.h" #include "error.h" #include "eprintf.h" #include "rstring.h" #include "mlex.h" #include "select.h" #include "file.h" #include "xmalloc.h" #include "mailinfo.h" #include "read.h" #include "compose.h" #include "line.h" #include "misc.h" #include "cmd.h" #include "gettext.h" #include "wrapbox.h" #include "mybox.h" #include "ecurses.h" #include "interface.h" #include "color.h" #include "label.h" #include "rmime.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #define PREAMBLE do { if (message.type != BOX_SENDER) return; } while (0) typedef enum {SENDER_DROP, SENDER_DRAFT, SENDER_SENT} sent_t; /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ /* Sender window consists of select_t object, and an optional label. */ static select_t *sender_select = NULL; static elabel_t *label = NULL; /* Colors used in sender window, and in mailinfo window. */ static chtype text_color; static chtype info_color; /* Format used to display attachments. */ static char *sender_fmt = NULL; /* This object stores the message being sent. */ static mail_t message; /* Used in draw_line. */ static str_t *str_line = NULL; /* Saved directory. It is used when adding attachment. */ static char *attach_dir = NULL; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /** * this holds everything we need to know about smtp account and data * associated with it */ ask_t *sender_ask = NULL; /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * REWRITE FUNCTIONS ****************************************************************************/ static void write_references (FILE *fp, rstring_t *references) { char **iterator; fprintf (fp, "References:"); for (iterator = references->array; *iterator; iterator++){ fprintf (fp, " %s\n", *iterator); } fprintf (fp, "In-Reply-To: %s\n", references->array[references->count - 1]); } static int write_headers (FILE *fp, mime_t *mime, char *boundary, char *charset) { char *enc; char *fname; if (boundary == NULL) return 0; fprintf (fp, "\n--%s\n", boundary); if (mime->type) fprintf (fp, "Content-Type: %s\n", mime->type); else fprintf (fp, "Content-Type: text/plain; charset=%s\n", charset); if (mime->file_name){ fname = strrchr (mime->file_name, '/'); if (fname) fname++; else fname = mime->file_name; fprintf (fp, "Content-Disposition: attachment; " "filename=\"%s\"\n", fname); } else { fprintf (fp, "Content-Disposition: inline\n"); } enc = mime_encoding_str (mime); if (enc == NULL) enc = "8bit"; fprintf (fp, "Content-Transfer-Encoding: %s\n", enc); fprintf (fp, "\n"); return 0; } static int get_buffer (mime_t *mime, char **place, int *size) { int ret; FILE *fp; if (mime->file_name){ fp = fopen (mime->file_name, "r"); if (fp == NULL){ error_ (errno, "%s", mime->file_name); return 1; } ret = file_whole (fp, place, size); fclose (fp); return ret; } else { *size = mime->off_end - mime->off_start; return file_part (message.place.file_name, mime->off_start, mime->off_end, place); } } static int write_attachment (FILE *fp, mime_t *mime) { char *buf; int size; str_t *str; if (get_buffer (mime, & buf, & size)) return 1; str = mime_encode (mime, buf, size); if (fwrite (str->str, 1, str->len, fp) != str->len){ error_ (errno, "%s", _("writing attachment")); return 1; } str_destroy (str); return 0; } static int write_end (FILE *fp, char *boundary) { if (boundary) fprintf (fp, "\n--%s--\n", boundary); return 0; } static int rewrite_file (const char *where) { int i; FILE *fp; char *smtp; char *fname; char *header; char *boundary = NULL; char *charset = ask_for_default ("charset", NULL); if (charset == NULL || *charset == '\0') charset = "us-ascii"; if (where == NULL) return 1; fname = wrapbox_fetch_where (0); if (fname == NULL) return 1; fp = file_open (fname, "w", O_WRONLY | O_CREAT | O_EXCL, 0600); if (fp == NULL){ error_ (errno, "%s", fname); xfree (fname); return 1; } if (message.from && message.from->full) fprintf (fp, "From: %s\n", message.from->full); header = raddress_list (message.to, "To", 1, " "); if (header){ fprintf (fp, "%s\n", header); xfree (header); } header = raddress_list (message.cc, "Cc", 1, " "); if (header){ fprintf (fp, "%s\n", header); xfree (header); } header = raddress_list (message.bcc, "Bcc", 1, " "); if (header){ fprintf (fp, "%s\n", header); xfree (header); } if (message.subject) fprintf (fp, "Subject: %s\n", message.subject); if (message.date_str) fprintf (fp, "Date: %s\n", message.date_str); if (message.msg_id) fprintf (fp, "Message-ID: %s\n", message.msg_id); if (message.in_reply_to) write_references (fp, message.in_reply_to); smtp = ask_get_field (sender_ask, "name"); fprintf (fp, "User-Agent: %s/%s\n", PACKAGE, VERSION); fprintf (fp, "MIME-Version: 1.0\n"); if (smtp) fprintf (fp, "X-Elmo-SMTP: %s\n", smtp); if (message.headers){ for (i = 0; i < message.headers->count; i++){ fprintf (fp, "%s\n", message.headers->array[i]); } } if (message.mime->mime->parts == NULL){ if (message.mime->mime->type) fprintf (fp, "Content-Type: %s\n", message.mime->mime->type); else fprintf (fp, "Content-Type: text/plain; charset=%s\n", charset); fprintf (fp, "Content-Transfer-Encoding: 8bit\n"); fprintf (fp, "\n"); } else { boundary = compose_boundary (); fprintf (fp, "Content-Type: multipart/mixed; boundary=\"%s\"\n", boundary); fprintf (fp, "\n"); } if (message.mime->mime->parts){ for (i = 0; i < message.mime->mime->parts->count; i++){ write_headers (fp, message.mime->mime->parts->array[i], boundary, charset); write_attachment (fp, message.mime->mime->parts->array[i]); } write_end (fp, boundary); } else { write_attachment (fp, message.mime->mime); } fclose (fp); if (boundary) xfree (boundary); if (wrapbox_deliver_to (fname, where)){ error_ (0, _("couldn't store the message in %s"), where); xfree (fname); return 1; } xfree (fname); return 0; } /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static void save_dir (char *file) { int c; char *seek = strrchr (file, '/'); if (seek == NULL) return; if (attach_dir) xfree (attach_dir); seek++; c = *seek; *seek = '\0'; attach_dir = xstrdup (file); *seek = c; } static int parse_mail (char *file) { yyin = fopen (file, "r"); if (yyin == NULL){ error_ (errno, "%s", file); return 1; } if (mlex_outmail_scan () != NEXT_MAIL){ fclose (yyin); error_ (0, _("file %s has bad format"), file); return 1; } fclose (yyin); memcpy (& message, newmail, sizeof (message)); message.place.file_name = file; message.type = BOX_SENDER; if (message.from && message.from->email){ ask_change_where (sender_ask, "email", message.from->email); } return 0; } static void draw_line (WINDOW *win, int maxlen, int index, search_t *search) { mime_t *mime = (message.type == BOX_SENDER) ? mime_nth_leaf (message.mime, index) : NULL; if (str_line == NULL) str_line = str_create (); if (message.type == BOX_SENDER && mime){ eprintf_mime_str (sender_fmt, mime, str_line); maxlen -= window_addnstr (win, str_line->str, maxlen); } while (maxlen-- > 0) window_addch (win, ' '); } static int count (select_t *nothing) { return (message.type == BOX_SENDER) ? mime_leaf_count (message.mime) : 0; } static void set_focus (void) { if (label){ label_set_focus (label); } cmd_state_push (CMD_SENDER); sender_redraw (); } static void unset_focus (void) { if (label){ label_unset_focus (label); label_redraw (label); } cmd_state_pop (); } static void hide_window (void) { if (sender_ask) ask_destroy (sender_ask); sender_ask = NULL; window_hide (sender_select->win); } /* This file is generated by interface.pl script from interface.desc, and inc.in. */ static WINDOW *interface_init (void); #include "sender.inc" /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ void sender_init (void) { WINDOW *window = interface_init (); sender_select = select_open (window, 0, draw_line, count); window_set_functions (window, sender_refresh, sender_redraw, set_focus, unset_focus); message.type = BOX_INVALID; mailinfo_init (window, info_color); } void sender_free_resources (void) { if (sender_select) select_close (sender_select); sender_select = NULL; if (label) label_destroy (label); label = NULL; if (str_line) str_destroy (str_line); str_line = NULL; if (message.type == BOX_SENDER) mail_destroy (& message, BOX_SENDER); message.type = BOX_INVALID; if (sender_ask) ask_destroy (sender_ask); sender_ask = NULL; if (attach_dir) xfree (attach_dir); attach_dir = NULL; mailinfo_free_resources (); } void sender_refresh (void) { if (label) label_show (label); select_show (sender_select); mailinfo_refresh (); } void sender_redraw (void) { if (label) label_redraw (label); select_redraw (sender_select); mailinfo_show (& message); } void sender_open (char *file) { if (parse_mail (file)) return; window_show (sender_select->win); sender_redraw (); } void sender_open_new_to (char *to) { char *file; sender_ask = ask_select_default ("smtp_acc"); if (sender_ask == NULL) return; file = compose_new (to); if (file == NULL){ ask_destroy (sender_ask); sender_ask = NULL; return; } sender_open (file); } void sender_open_new (void) { sender_open_new_to (NULL); } void sender_open_reply (void) { char *file; sender_ask = ask_select_default ("smtp_acc"); if (sender_ask == NULL) return; file = compose_reply (0); if (file == NULL){ ask_destroy (sender_ask); sender_ask = NULL; return; } sender_open (file); } void sender_open_reply_all (void) { char *file; sender_ask = ask_select_default ("smtp_acc"); if (sender_ask == NULL) return; file = compose_reply (1); if (file == NULL){ ask_destroy (sender_ask); sender_ask = NULL; return; } sender_open (file); } void sender_open_fwd (void) { char *file; sender_ask = ask_select_default ("smtp_acc"); if (sender_ask == NULL) return; file = compose_fwd (NULL); if (file == NULL){ ask_destroy (sender_ask); sender_ask = NULL; return; } sender_open (file); } void sender_open_edit (void) { char *file; sender_ask = ask_select_default ("smtp_acc"); if (sender_ask == NULL) return; file = compose_edit (); if (file == NULL){ ask_destroy (sender_ask); sender_ask = NULL; } sender_open (file); } void sender_close (void) { int ret; char *box; PREAMBLE; ret = ask_if_sure ("save message as draft (y/n)? "); switch (ret){ case -1: return; case 0: break; case 1: box = mybox_draft (); if (rewrite_file (box)){ xfree (box); return; } xfree (box); break; } if (message.type == BOX_SENDER) mail_destroy (& message, BOX_SENDER); message.type = BOX_INVALID; hide_window (); } void sender_go (void) { char *box; PREAMBLE; box = mybox_outbox (); if (rewrite_file (box)){ xfree (box); return; } xfree (box); if (message.type == BOX_SENDER) mail_destroy (& message, BOX_SENDER); message.type = BOX_INVALID; hide_window (); compose_after_send (); } void sender_next (void) { PREAMBLE; select_next (sender_select); } void sender_prev (void) { PREAMBLE; select_prev (sender_select); } void sender_next_page (void) { PREAMBLE; select_next_page (sender_select); } void sender_prev_page (void) { PREAMBLE; select_prev_page (sender_select); } void sender_first (void) { PREAMBLE; select_first (sender_select); } void sender_last (void) { PREAMBLE; select_last (sender_select); } void sender_change_smtp (void) { char *name; char *email; address_t *addr; PREAMBLE; if (sender_ask == NULL) return; ask_select_different (sender_ask); email = ask_get_field (sender_ask, "email"); name = ask_get_field (sender_ask, "my_name"); addr = address_empty (); addr->name = name; addr->email = email; message.reply_to = message.from = address_complete (addr); sender_redraw (); } void sender_change_from (void) { char *from; PREAMBLE; if (message.from){ from = read_argument (_("From: "), message.from->full, COMPLETE_ADDRS, HIDE_NO); } else { from = read_argument (_("From: "), NULL, COMPLETE_ADDRS, HIDE_NO); } if (from){ message.reply_to = message.from = address_from_string (from); } mailinfo_show (& message); } void sender_change_reply_to (void) { char *reply; PREAMBLE; if (message.reply_to){ reply = read_argument (_("Reply-To: "), message.reply_to->full, COMPLETE_ADDRS, HIDE_NO); } else { reply = read_argument (_("Reply-To: "), NULL, COMPLETE_ADDRS, HIDE_NO); } if (reply){ message.reply_to = address_from_string (reply); } mailinfo_show (& message); } void sender_change_to (void) { char *to; char *newto; PREAMBLE; to = raddress_list (message.to, NULL, 0, ""); newto = read_argument (_("To: "), to, COMPLETE_ADDRS, HIDE_NO); if (newto == NULL){ xfree (to); return; } if (message.to) raddress_destroy (message.to); message.to = raddress_get_from_header (newto); if (to) xfree (to); mailinfo_show (& message); } void sender_change_cc (void) { char *cc; char *newcc; PREAMBLE; cc = raddress_list (message.cc, NULL, 0, ""); newcc = read_argument (_("Cc: "), cc, COMPLETE_ADDRS, HIDE_NO); if (newcc == NULL){ xfree (cc); return; } if (message.cc) raddress_destroy (message.cc); message.cc = raddress_get_from_header (newcc); if (cc) xfree (cc); mailinfo_show (& message); } void sender_change_bcc (void) { char *bcc; char *newbcc; PREAMBLE; bcc = raddress_list (message.bcc, NULL, 0, ""); newbcc = read_argument (_("Bcc: "), bcc, COMPLETE_ADDRS, HIDE_NO); if (newbcc == NULL){ xfree (bcc); return; } if (message.bcc) raddress_destroy (message.bcc); message.bcc = raddress_get_from_header (newbcc); if (bcc) xfree (bcc); mailinfo_show (& message); } void sender_change_subject (void) { char *subject; PREAMBLE; subject = read_argument (_("Subject: "), message.subject, COMPLETE_NONE, HIDE_NO); if (subject == NULL) return; if (message.subject) xfree (message.subject); message.subject = xstrdup (subject); mailinfo_show (& message); } void sender_add_attachment (void) { char *attachment; char *true_name; mime_t *mime; int size; PREAMBLE; attachment = read_argument (_("File: "), attach_dir, COMPLETE_FILES, HIDE_NO); true_name = file_expand_tilde (attachment); if (true_name == NULL || *true_name == '\0') return; size = file_size (true_name); if (size == -1){ error_ (errno, "%s", attachment); if (true_name != attachment) xfree (true_name); return; } save_dir (attachment); mime = mime_from_file_name (true_name); mime->off_start = 0; mime->off_end = size; if (message.mime->mime->parts == NULL){ mime_t *mime = mime_create (); mime->type = xstrdup ("multipart/mixed"); mime->parts = rmime_create_size (4); rmime_add (mime->parts, message.mime->mime); message.mime->mime = mime; } rmime_add (message.mime->mime->parts, mime); if (true_name != attachment) xfree (true_name); sender_redraw (); } void sender_delete_attachment (void) { int index = sender_select->bar_pos; mime_t *mime; PREAMBLE; if (message.mime->mime->parts == NULL || message.mime->mime->parts->count <= 1){ error_ (0, "%s", _("Can't make a message empty. Please add " "something else first.")); return; } mime = message.mime->mime->parts->array[index]; mime_destroy (mime); rmime_remove (message.mime->mime->parts, index); sender_redraw (); } void sender_change_type (void) { int index = sender_select->bar_pos; char *type; mime_t *mime; PREAMBLE; if (message.mime->mime->parts == NULL) mime = message.mime->mime; else mime = message.mime->mime->parts->array[index]; type = read_argument (_("Content-Type: "), mime->type, COMPLETE_NONE, HIDE_NO); if (type && *type){ if (mime->type) xfree (mime->type); mime->type = xstrdup (type); } sender_redraw (); } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE sender.c * ****************************************************************************/