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