/* -*- 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 #endif #ifdef HAVE_LOCALE_H # include #endif #include #include #include #include #include #include #include #include #include #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; } <> 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, "", (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 * ****************************************************************************/