/* 
   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 <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#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
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1