/* -*- c -*-
   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.  

   ----------------------------------------------------------------------

   Functions resposible for preparing message headers, calling an editor, etc.
   
*/

%{

#define _GNU_SOURCE 1

/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif

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

#include "ecurses.h"
#include "xmalloc.h"
#include "mail.h"
#include "rstring.h"
#include "ask.h"
#include "error.h"
#include "file.h"
#include "folder.h"
#include "compose.h"
#include "sender.h"
#include "eprintf.h"
#include "wrapbox.h"
#include "misc.h"
#include "read.h"
#include "str.h"
#include "run.h"
#include "gettext.h"
#include "property.h"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/

#ifdef NO_OFFENSIVE_BOUNDARY
# define BOUNDARY_DELIMITER_FORMAT "--some_elmo_boundary%d"
#else
# define BOUNDARY_DELIMITER_FORMAT "--java_obsysa%d"
#endif

#define DEFAULT_REPLY_FMT "On %d, %f wrote:"
#define LINE_MAX_LEN      70

#define PREAMBLE do {if (mail == NULL) return NULL; } while (0)


#define YY_DECL static void write_mail YY_PROTO ((void)) 

typedef enum {COMPOSE_NEW, COMPOSE_REPLY, COMPOSE_FWD} compose_act_t;

/****************************************************************************
 *    IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE DATA
 ****************************************************************************/
 
/**
 * This indicates what kind of action did we take.  It's set in one of:
 * compose_new, compose_reply, compose_fwd, and used in compose_after_send.
 */
 static compose_act_t compose_act = 0;


 static char *to      = NULL;
 static char *subject = NULL;

 static int cursor_line = 0;
 static int cursor_pos  = 0;
 static int cursor_col  = 0;

 static int line = 0;
 static int pos  = 0;
 static int col  = 0;

 static char *fname = NULL;

/****************************************************************************
 *    INTERFACE DATA
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
 ****************************************************************************/

 static void       free_resources (void);
 static void       update_pos (char *str, int len);
 static char      *message_id (void);
 static char      *boundary (void);
 static char      *make_date (void);
 static rstring_t *make_references (void);
 static int        may_be (address_t *addr, address_t *first, char *email);
 static char      *make_to_reply (int all);
 static FILE      *open_file_or_pipe (const char *file);
 static void       close_file_or_pipe (const char *file, FILE *fp);
 static char      *make_subject (const char *prefix);
 static char      *make_signature (void);
 static char      *make_file_and_run (void);
 static char      *property_fallback (const char *name);

/****************************************************************************
 *    INTERFACE FUNCTIONS
 ****************************************************************************/

%} 

%option noyywrap


%%

\$[a-zA-Z_]+(\.[a-zA-Z_]+)* {
        char *str = property_get (yytext + 1);
        int   len = (str) ? strlen (str) : 0;
        int   ret;

        if (str)
                update_pos (str, len);
        
        ret = fwrite (str, 1, len, yyout);
        pos += ret;

        if (str)
                xfree (str);
}

(\$\$)+ {
        int ret;
        
        ret = fwrite (yytext, 1, yyleng / 2, yyout);
        pos += ret;
        col += ret;
}

"$=" {
        cursor_pos  = pos;
        cursor_line = line;
        cursor_col  = col;
}


\n+     line += yyleng; col = -1; pos += fwrite (yytext, 1, yyleng, yyout);


\$[^a-zA-Z\$=]? {
        error_ (0, _("%s:%d: invalid $-sequence"), fname, line);
}


[^\$\n]+ {
        int ret;
        
        ret = fwrite (yytext, 1, yyleng, yyout);
        pos += ret;
        col += ret;
}

<<EOF>> return; yyunput (0, NULL);

%%


char *
compose_new (char *to_str)
{
        free_resources ();
        
        compose_act = COMPOSE_NEW;

        if (to_str == NULL && ask_for_default_int ("ask_for_mail", NULL, 1)){
                to_str = read_argument ("To: ", NULL, COMPLETE_ADDRS,
                                        HIDE_NO);
                if (to_str == NULL)
                        return NULL;
        }

        to      = xstrdup (to_str);
        subject = read_argument ("Subject: ", NULL, COMPLETE_NONE, HIDE_NO);

        if (subject == NULL){
                xfree (to);
                return NULL;
        }

        subject = xstrdup (subject);
        
        return make_file_and_run ();
}



char *
compose_reply (int all)
{
        free_resources ();
        
        compose_act = COMPOSE_REPLY;
        to          = make_to_reply (all);
        subject     = make_subject ("Re:");

        return make_file_and_run ();
}



char *
compose_fwd (char *to_str)
{
        free_resources ();
        
        compose_act = COMPOSE_FWD;

        if (to_str == NULL && ask_for_default_int ("ask_for_mail", NULL, 1)){
                to_str = read_argument ("To: ", NULL, COMPLETE_ADDRS,
                                        HIDE_NO);
                if (to_str == NULL)
                        return NULL;
        }

        to      = xstrdup (to_str);
        subject = make_subject ("Fwd:");

        return make_file_and_run ();
}



char *
compose_edit (void)
{
        FILE   *fp;
        char   *name;
        char   *oldname;
        mail_t *mail = folder_mail_selected ();

        if (mail == NULL)
                return NULL;

        oldname = wrapbox_fetch_single (mail);

        if (oldname == NULL){
                error_ (0, "%s", _("couldn't copy selected message"));
                return NULL;
        }

        fp      = file_temp_file (& name);
        fclose (fp);

        
        if (file_rename (oldname, name)){
                xfree (oldname);
                error_ (errno, "%s", name);
                return NULL;
        }

        xfree (oldname);
        wrapbox_remove (mail);
        run_editor (name, 0, 0, 0);
        return name;
}



char *
compose_date (void)
{
        return make_date ();
}




char *
compose_msg_id (void)
{
        return message_id ();
}



rstring_t *
compose_in_reply_to (void)
{
        switch (compose_act){

                case COMPOSE_REPLY:
                        return make_references ();

                default:
                        return NULL;
        }
}



char *
compose_boundary (void)
{
        return boundary ();
}



void
compose_after_send (void)
{
        switch (compose_act){

                case COMPOSE_NEW:
                        break;

                case COMPOSE_REPLY:
                        folder_after_reply ();
                        break;

                case COMPOSE_FWD:
                        folder_after_fwd ();
                        break;

        }
}


void
compose_init (void)
{
        property_register ("template", property_fallback);
        property_register ("subject", property_fallback);
        property_register ("quote", property_fallback);
        property_register ("signature", property_fallback);
        property_register ("date", property_fallback);
        property_register ("address", property_fallback);
        property_register ("mail", property_fallback);
        property_register ("to", property_fallback);
        property_register ("action", property_fallback);
}



void
compose_free_resources (void)
{
        free_resources ();
}


/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTIONS
 ****************************************************************************/


static void
free_resources (void)
{
        if (to)
                xfree (to);
        to = NULL;

        if (subject)
                xfree (subject);
        subject = NULL;

        if (fname)
                xfree (fname);
        fname = NULL;
}



static void
update_pos (char *str, int len)
{
        char *prev = str;
        char *seek = str;

        while (1){
                seek = strchr (prev, '\n');
                if (seek == NULL)
                        break;
                line++;
                prev = seek + 1;
        }

        col = len - (prev - str + 1);
}


static char *
message_id (void)
{
        size_t  hlen  = 100;
        char   *hname = xmalloc (hlen);
        char   *result;

        while (gethostname (hname, hlen)){
                hlen  = (hlen + 1) * 2;
                hname = xrealloc (hname, hlen);
        }
        hlen = strlen (hname);

        /*                <  elmo time  pid rand  @ hostname  >  \0 */
        result = xmalloc (1 + 4 +  10 + 10 + 10 + 1 + hlen +  1 + 1);

        sprintf (result, "<elmo%d%d%d@%s>", (int) time (NULL),
                 (int) getpid (), rand (), hname);
        xfree (hname);

        return result;
}


static char *
boundary (void)
{
        char *result;

        result = xmalloc (11 + 10 + 1);

        sprintf (result, BOUNDARY_DELIMITER_FORMAT, rand ());
        return result;
}


static char *
make_date (void)
{
        char *result;
  
#ifdef HAVE_LOCALE_H
        setlocale (LC_ALL, "C");
#endif

        result = date_string ("%a, %d %b %Y %H:%M:%S %z", time (NULL));

#ifdef HAVE_LOCALE_H
        setlocale (LC_ALL, "");
#endif

        return result;
}



static rstring_t *
make_references (void)
{
        mail_t    *mail   = folder_mail_selected ();
        rstring_t *result = NULL;

        if (mail == NULL)
                return NULL;

        if (mail->in_reply_to)
                result = rstring_copy (mail->in_reply_to);
        else if (mail->msg_id){
                result = rstring_create_size (2);
                result->allocated_all = 1;
        }

        if (mail->msg_id)
                rstring_add (result, xstrdup (mail->msg_id));
        return result;
}


static int
may_be (address_t *addr, address_t *first, char *email)
{
        if (addr == NULL)
                return 0;

        if (addr->full == NULL)
                return 0;

        if (strcmp (addr->email, email) == 0)
                return 0;

        if (strcmp (addr->email, first->email) == 0)
                return 0;

        return 1;
}



static char *
make_to_reply (int all)
{
        int         i;
        char       *str;
        mail_t     *mail  = folder_mail_selected ();
        char       *email = ask_get_field (sender_ask, "email");
        address_t  *first = NULL;
        raddress_t *to    = NULL;

        if (mail == NULL)
                return NULL;
        
        if (mail->to)
                to = raddress_create_size (1 + mail->to->count + 1);
        else
                to = raddress_create_size (2);
  
        if (mail->reply_to)
                first = mail->reply_to;
        else
                first = mail->from;

        if (first)
                raddress_add (to, first);

        if (all){
                for (i = 0; i < mail->to->count; i++){
                        if (may_be (mail->to->array[i], first, email))
                                raddress_add (to, mail->to->array[i]);
                }
        }

        str = raddress_list (to, NULL, 1, " ");
        raddress_destroy (to);
        return str;
}



static char *
make_wrote (void)
{
        char   *result;
        mail_t *mail = folder_mail_selected ();
        char   *fmt  = NULL;

        if (mail == NULL)
                return NULL;
        
        if (compose_act != COMPOSE_REPLY)
                return NULL;
        
        fmt = address_wrote_format (mail->from);
        if (fmt == NULL)
                fmt = DEFAULT_REPLY_FMT;

        result = eprintf_mail (fmt, mail);
        return result;
}



static FILE *
open_file_or_pipe (const char *file)
{
        if (*file == '|')
                return popen (file + 1, "r");
        else
                return fopen (file, "r");
}



static void
close_file_or_pipe (const char *file, FILE *fp)
{
        if (*file == '|')
                pclose (fp);
        else
                fclose (fp);
}



static char *
make_signature (void)
{
        char *sigfile = ask_for_default ("sigfile", NULL);
        char *sig_dashes = ask_for_default ("sig_dashes", NULL);
        char *signature;
        int   ret;
        int   BUFSIZE = 1000;
        FILE *fp;

        if (sigfile && strcmp (sigfile, "default") == 0){
                signature = xstrdup ("\n-- \nElmo: A MUA that sucks much less.");
        }
        else if (!sigfile || sigfile == NULL){
                signature = NULL;
        }
        else {
                fp = open_file_or_pipe (sigfile);
                if (!fp){
                        error_ (errno, "%s", sigfile);
                        return NULL;
                }
                signature          = xmalloc (BUFSIZE);
                if (sig_dashes) {
                        strncpy (signature, "\n-- \n", 5);
                        ret                = fread (signature + 5, 1, BUFSIZE - 7, fp);
                        signature[ret + 5] = '\0';
                } else {
                        signature[0]       = '\n';
                        ret                = fread (signature + 1, 1, BUFSIZE - 2, fp);
                        signature[ret + 1] = '\0';
                }    
                close_file_or_pipe (sigfile, fp);
        }
        return signature;
}


static char *
make_subject (const char *prefix)
{
        mail_t *mail = folder_mail_selected ();
        char   *subj = (mail) ? mail->subject : NULL;
        str_t  *str  = str_create ();
        char   *seek;

        if (subj && *subj){
                if (prefix)
                        seek = strstr (subj, prefix);
                else
                        seek = subj;

                if (! seek)
                        str_sprintf (str, "%s %s", prefix, subj);
                else
                        str_sprintf (str, "%s", subj);
        }
        else if (prefix){
                str_sprintf (str, "%s", prefix);
        }

        return str_finished (str);
}


static void
reset_state (void)
{
        line = 1;
        col  = 0;
        pos  = 0;
}


static int
write_template (FILE *fp)
{
        YY_BUFFER_STATE buffer;
        
        yyout = fp;
        fname = property_get ("template");
        yyin  = fopen (fname, "r");

        if (yyin == NULL){
                error_ (errno, _("Couldn't open template file %s. "
                                 "Make sure, that elmo is properly "
                                 "installed."), fname);
                xfree (fname);
                fname = NULL;
                return 1;
        }

        buffer = yy_create_buffer (yyin, YY_BUF_SIZE);
        yy_switch_to_buffer (buffer);

        reset_state ();
        write_mail ();

        yy_delete_buffer (buffer);

        fclose (yyin);
        xfree (fname);

        yyin  = NULL;
        yyout = NULL;
        fname = NULL;

        return 0;
}


static char *
make_file_and_run (void)
{
        char *name;
        FILE *fp;

        fp = file_temp_file (& name);

        if (fp == NULL){
                return NULL;
        }

        if (write_template (fp)){
                fclose (fp);
                unlink (name);
                return NULL;
        }

        fclose (fp);
        run_editor (name, cursor_line, cursor_col, cursor_pos);
        return name;
}


/****************************************************************************
 *    PROPERTIES
 ****************************************************************************/


static char *
address_property (address_t *addr, char **fields)
{
        if (strcmp (*fields, "full") == 0)
                return xstrdup (addr->full);
        if (strcmp (*fields, "email") == 0)
                return xstrdup (addr->email);
        if (strcmp (*fields, "name") == 0)
                return xstrdup (addr->name);
        if (strcmp (*fields, "name_email") == 0)
                return xstrdup (address_name (addr));
        if (strcmp (*fields, "groups") == 0)
                return (addr->groups)
                        ? rstring_flatten (addr->groups, " ") : NULL;
        if (strcmp (*fields, "sex") == 0){
                switch (addr->flags.bits.sex){
                        case SEX_MALE:
                                return xstrdup ("M");
                        case SEX_FEMALE:
                                return xstrdup ("F");
                        default:
                                return NULL;
                }
        }
        if (strcmp (*fields, "official") == 0){
                if (addr->flags.bits.official)
                        return xstrdup ("yes");
                else
                        return xstrdup ("no");
        }
        if (strcmp (*fields, "foreign") == 0){
                if (addr->flags.bits.foreign)
                        return xstrdup ("yes");
                else
                        return xstrdup ("no");
        }
        if (strcmp (*fields, "abook") == 0){
                if (addr->flags.bits.abook)
                        return xstrdup ("yes");
                else
                        return xstrdup ("no");
        }

        return NULL;
}


static char *
mail_text (mail_t *mail)
{
        str_t *str;

        str = wrapbox_mail_body (mail, NULL, 0);
        return (str) ? str_finished (str) : NULL;
}


static char *
indent_string (mail_t *mail)
{
        char *result = ask_for_default ("indent_string", NULL);

        if (result)
                result = eprintf_mail (result, mail);
        else
                result = xstrdup ("> ");
        return result;
}


static void
copy (str_t *dest, str_t *src, char *indent)
{
        char *start = src->str;
        char *end   = src->str;
        int   ilen  = (indent) ? strlen (indent) : 0;

        while (1){
                end = strchr (start, '\n');
                if (end == NULL)
                        break;
                str_put_string_len (dest, indent, ilen);
                str_put_string_len (dest, start, end - start + 1);
                start = end + 1;
        }

        str_sprintf (dest, "%s%s", indent, start);
}


static char *
quoted_text (mail_t *mail)
{
        char  *indent;
        str_t *str;
        str_t *result;
        
        switch (compose_act){

                case COMPOSE_NEW:
                        return NULL;

                case COMPOSE_FWD:
                        return mail_text (mail);
                
                case COMPOSE_REPLY:
                        str = wrapbox_mail_body (mail, NULL, 0);
                        if (str == NULL)
                                return NULL;
                        result = str_create_size (str->len * 60 / 59);
                        indent = indent_string (mail);
                        copy (result, str, indent);
                        str_destroy (str);
                        xfree (indent);
                        return str_finished (result);
        }
        return NULL;
}


static char *
mail_property (mail_t *mail, char **fields)
{
        if (strcmp (*fields, "from") == 0)
                return address_property (mail->from, fields + 1);
        if (strcmp (*fields, "subject") == 0)
                return xstrdup (mail->subject);
        if (strcmp (*fields, "date") == 0)
                return xstrdup (mail->date_str);
        if (strcmp (*fields, "quoted") == 0)
                return quoted_text (mail);
        if (strcmp (*fields, "text") == 0)
                return mail_text (mail);
        if (strcmp (*fields, "to") == 0)
                return raddress_list (mail->to, NULL, 70, "");

        return NULL;
}


static char *
my_addr_property (char **fields)
{
        char      *result;
        address_t *addr = xcalloc (1, sizeof (address_t));

        addr->email = ask_get_field (sender_ask, "email");
        addr->name  = ask_get_field (sender_ask, "my_name");

        result = address_property (addr, fields);

        xfree (addr);
        return result;
}



static char *
get_template (void)
{
        return xstrdup (DATADIR "/template");
}



static char *
get_action (void)
{
        switch (compose_act){

                case COMPOSE_NEW:
                        return xstrdup ("new");

                case COMPOSE_REPLY:
                        return xstrdup ("reply");

                case COMPOSE_FWD:
                        return xstrdup ("forward");
        }

        return NULL;
}



static char *
property_fallback (const char *name)
{
        char      *result = NULL;
        char      *copy   = xstrdup (name);
        rstring_t *split  = rstring_split (copy, ".");
        
        split->allocated_first = 1;

        if (strcmp (split->array[0], "mail") == 0)
                result = mail_property (folder_mail_selected (),
                                        split->array + 1);
        else if (strcmp (split->array[0], "address") == 0)
                result = my_addr_property (split->array + 1);
        else if (strcmp (split->array[0], "date") == 0)
                result = make_date ();
        else if (strcmp (split->array[0], "template") == 0)
                result = get_template ();
        else if (strcmp (split->array[0], "subject") == 0)
                result = xstrdup (subject);
        else if (strcmp (split->array[0], "signature") == 0)
                result = make_signature ();
        else if (strcmp (split->array[0], "quote") == 0)
                result = make_wrote ();
        else if (strcmp (split->array[0], "to") == 0)
                result = xstrdup (to);
        else if (strcmp (split->array[0], "action") == 0)
                result = get_action ();

        rstring_delete (split);
        return result;
}

/****************************************************************************
 *    INTERFACE CLASS BODIES
 ****************************************************************************/
/****************************************************************************
 *
 *    END MODULE compose.l
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1