]> git.llucax.com Git - software/mutt-debian.git/blob - edit.c
informing the users of the new behavior of write_bcc
[software/mutt-debian.git] / edit.c
1 /*
2  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3  * 
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  * 
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  * 
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */ 
18
19 /* Close approximation of the mailx(1) builtin editor for sending mail. */
20
21 #if HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "mutt.h"
26 #include "mutt_curses.h"
27 #include "mutt_idna.h"
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <errno.h>
36
37 /*
38  * SLcurses_waddnstr() can't take a "const char *", so this is only
39  * declared "static" (sigh)
40  */
41 static char* EditorHelp1 = N_("\
42 ~~              insert a line begining with a single ~\n\
43 ~b users        add users to the Bcc: field\n\
44 ~c users        add users to the Cc: field\n\
45 ~f messages     include messages\n\
46 ~F messages     same as ~f, except also include headers\n\
47 ~h              edit the message header\n\
48 ~m messages     include and quote messages\n\
49 ~M messages     same as ~m, except include headers\n\
50 ~p              print the message\n");
51
52 static char* EditorHelp2 = N_("\
53 ~q              write file and quit editor\n\
54 ~r file         read a file into the editor\n\
55 ~t users        add users to the To: field\n\
56 ~u              recall the previous line\n\
57 ~v              edit message with the $visual editor\n\
58 ~w file         write message to file\n\
59 ~x              abort changes and quit editor\n\
60 ~?              this message\n\
61 .               on a line by itself ends input\n");
62
63 static char **
64 be_snarf_data (FILE *f, char **buf, int *bufmax, int *buflen, LOFF_T offset,
65                int bytes, int prefix)
66 {
67   char tmp[HUGE_STRING];
68   char *p = tmp;
69   int tmplen = sizeof (tmp);
70
71   tmp[sizeof (tmp) - 1] = 0;
72   if (prefix)
73   {
74     strfcpy (tmp, NONULL(Prefix), sizeof (tmp));
75     tmplen = mutt_strlen (tmp);
76     p = tmp + tmplen;
77     tmplen = sizeof (tmp) - tmplen;
78   }
79
80   fseeko (f, offset, 0);
81   while (bytes > 0)
82   {
83     if (fgets (p, tmplen - 1, f) == NULL) break;
84     bytes -= mutt_strlen (p);
85     if (*bufmax == *buflen)
86       safe_realloc (&buf, sizeof (char *) * (*bufmax += 25));
87     buf[(*buflen)++] = safe_strdup (tmp);
88   }
89   if (buf && *bufmax == *buflen) { /* Do not smash memory past buf */
90     safe_realloc (&buf, sizeof (char *) * (++*bufmax));
91   }
92   if (buf) buf[*buflen] = NULL;
93   return (buf);
94 }
95
96 static char **
97 be_snarf_file (const char *path, char **buf, int *max, int *len, int verbose)
98 {
99   FILE *f;
100   char tmp[LONG_STRING];
101   struct stat sb;
102   
103   if ((f = fopen (path, "r")))
104   {
105     fstat (fileno (f), &sb);
106     buf = be_snarf_data (f, buf, max, len, 0, sb.st_size, 0);
107     if (verbose)
108     {
109       snprintf(tmp, sizeof(tmp), "\"%s\" %lu bytes\n", path, (unsigned long) sb.st_size);
110       addstr(tmp);
111     }
112     safe_fclose (&f);
113   }
114   else
115   {
116     snprintf(tmp, sizeof(tmp), "%s: %s\n", path, strerror(errno));
117     addstr(tmp);
118   }
119   return (buf);
120 }
121
122 static int be_barf_file (const char *path, char **buf, int buflen)
123 {
124   FILE *f;
125   int i;
126   
127   if ((f = fopen (path, "w")) == NULL)          /* __FOPEN_CHECKED__ */
128   {
129     addstr (strerror (errno));
130     addch ('\n');
131     return (-1);
132   }
133   for (i = 0; i < buflen; i++) fputs (buf[i], f);
134   if (fclose (f) == 0) return 0;
135   printw ("fclose: %s\n", strerror (errno));
136   return (-1);
137 }
138
139 static void be_free_memory (char **buf, int buflen)
140 {
141   while (buflen-- > 0)
142     FREE (&buf[buflen]);
143   if (buf)
144     FREE (&buf);
145 }
146
147 static char **
148 be_include_messages (char *msg, char **buf, int *bufmax, int *buflen,
149                      int pfx, int inc_hdrs)
150 {
151   int offset, bytes, n;
152   char tmp[LONG_STRING];
153
154   while ((msg = strtok (msg, " ,")) != NULL)
155   {
156     if (mutt_atoi (msg, &n) == 0 && n > 0 && n <= Context->msgcount)
157     {
158       n--;
159
160       /* add the attribution */
161       if (Attribution)
162       {
163         mutt_make_string (tmp, sizeof (tmp) - 1, Attribution, Context, Context->hdrs[n]);
164         strcat (tmp, "\n");     /* __STRCAT_CHECKED__ */
165       }
166
167       if (*bufmax == *buflen)
168         safe_realloc ( &buf, sizeof (char *) * (*bufmax += 25));
169       buf[(*buflen)++] = safe_strdup (tmp);
170
171       bytes = Context->hdrs[n]->content->length;
172       if (inc_hdrs)
173       {
174         offset = Context->hdrs[n]->offset;
175         bytes += Context->hdrs[n]->content->offset - offset;
176       }
177       else
178         offset = Context->hdrs[n]->content->offset;
179       buf = be_snarf_data (Context->fp, buf, bufmax, buflen, offset, bytes,
180                            pfx);
181
182       if (*bufmax == *buflen)
183         safe_realloc (&buf, sizeof (char *) * (*bufmax += 25));
184       buf[(*buflen)++] = safe_strdup ("\n");
185     }
186     else
187       printw (_("%d: invalid message number.\n"), n);
188     msg = NULL;
189   }
190   return (buf);
191 }
192
193 static void be_print_header (ENVELOPE *env)
194 {
195   char tmp[HUGE_STRING];
196
197   if (env->to)
198   {
199     addstr ("To: ");
200     tmp[0] = 0;
201     rfc822_write_address (tmp, sizeof (tmp), env->to, 1);
202     addstr (tmp);
203     addch ('\n');
204   }
205   if (env->cc)
206   {
207     addstr ("Cc: ");
208     tmp[0] = 0;
209     rfc822_write_address (tmp, sizeof (tmp), env->cc, 1);
210     addstr (tmp);
211     addch ('\n');
212   }
213   if (env->bcc)
214   {
215     addstr ("Bcc: ");
216     tmp[0] = 0;
217     rfc822_write_address (tmp, sizeof (tmp), env->bcc, 1);
218     addstr (tmp);
219     addch ('\n');
220   }
221   if (env->subject)
222   {
223     addstr ("Subject: ");
224     addstr (env->subject);
225     addch ('\n');
226   }
227   addch ('\n');
228 }
229
230 /* args:
231  *      force   override the $ask* vars (used for the ~h command)
232  */
233 static void be_edit_header (ENVELOPE *e, int force)
234 {
235   char tmp[HUGE_STRING];
236
237   move (LINES-1, 0);
238
239   addstr ("To: ");
240   tmp[0] = 0;
241   mutt_addrlist_to_local (e->to);
242   rfc822_write_address (tmp, sizeof (tmp), e->to, 0);
243   if (!e->to || force)
244   {
245     if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 4, 0) == 0)
246     {
247       rfc822_free_address (&e->to);
248       e->to = mutt_parse_adrlist (e->to, tmp);
249       e->to = mutt_expand_aliases (e->to);
250       mutt_addrlist_to_idna (e->to, NULL);      /* XXX - IDNA error reporting? */
251       tmp[0] = 0;
252       rfc822_write_address (tmp, sizeof (tmp), e->to, 1);
253       mvaddstr (LINES - 1, 4, tmp);
254     }
255   }
256   else
257   {
258     mutt_addrlist_to_idna (e->to, NULL);        /* XXX - IDNA error reporting? */
259     addstr (tmp);
260   }
261   addch ('\n');
262
263   if (!e->subject || force)
264   {
265     addstr ("Subject: ");
266     strfcpy (tmp, e->subject ? e->subject: "", sizeof (tmp));
267     if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 9, 0) == 0)
268       mutt_str_replace (&e->subject, tmp);
269     addch ('\n');
270   }
271
272   if ((!e->cc && option (OPTASKCC)) || force)
273   {
274     addstr ("Cc: ");
275     tmp[0] = 0;
276     mutt_addrlist_to_local (e->cc);
277     rfc822_write_address (tmp, sizeof (tmp), e->cc, 0);
278     if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 4, 0) == 0)
279     {
280       rfc822_free_address (&e->cc);
281       e->cc = mutt_parse_adrlist (e->cc, tmp);
282       e->cc = mutt_expand_aliases (e->cc);
283       tmp[0] = 0;
284       mutt_addrlist_to_idna (e->cc, NULL);
285       rfc822_write_address (tmp, sizeof (tmp), e->cc, 1);
286       mvaddstr (LINES - 1, 4, tmp);
287     }
288     else
289       mutt_addrlist_to_idna (e->cc, NULL);
290     addch ('\n');
291   }
292
293   if (option (OPTASKBCC) || force)
294   {
295     addstr ("Bcc: ");
296     tmp[0] = 0;
297     mutt_addrlist_to_local (e->bcc);
298     rfc822_write_address (tmp, sizeof (tmp), e->bcc, 0);
299     if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 5, 0) == 0)
300     {
301       rfc822_free_address (&e->bcc);
302       e->bcc = mutt_parse_adrlist (e->bcc, tmp);
303       e->bcc = mutt_expand_aliases (e->bcc);
304       mutt_addrlist_to_idna (e->bcc, NULL);
305       tmp[0] = 0;
306       rfc822_write_address (tmp, sizeof (tmp), e->bcc, 1);
307       mvaddstr (LINES - 1, 5, tmp);
308     }
309     else
310       mutt_addrlist_to_idna (e->bcc, NULL);
311     addch ('\n');
312   }
313 }
314
315 int mutt_builtin_editor (const char *path, HEADER *msg, HEADER *cur)
316 {
317   char **buf = NULL;
318   int bufmax = 0, buflen = 0;
319   char tmp[LONG_STRING];
320   int abort = 0;
321   int done = 0;
322   int i;
323   char *p;
324   
325   scrollok (stdscr, TRUE);
326
327   be_edit_header (msg->env, 0);
328
329   addstr (_("(End message with a . on a line by itself)\n"));
330
331   buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
332
333   tmp[0] = 0;
334   while (!done)
335   {
336     if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 0, 0) == -1)
337     {
338       tmp[0] = 0;
339       continue;
340     }
341     addch ('\n');
342
343     if (EscChar && tmp[0] == EscChar[0] && tmp[1] != EscChar[0])
344     {
345       /* remove trailing whitespace from the line */
346       p = tmp + mutt_strlen (tmp) - 1;
347       while (p >= tmp && ISSPACE (*p))
348         *p-- = 0;
349
350       p = tmp + 2;
351       SKIPWS (p);
352
353       switch (tmp[1])
354       {
355         case '?':
356           addstr (_(EditorHelp1));
357           addstr (_(EditorHelp2));
358           break;
359         case 'b':
360           msg->env->bcc = mutt_parse_adrlist (msg->env->bcc, p);
361           msg->env->bcc = mutt_expand_aliases (msg->env->bcc);
362           break;
363         case 'c':
364           msg->env->cc = mutt_parse_adrlist (msg->env->cc, p);
365           msg->env->cc = mutt_expand_aliases (msg->env->cc);
366           break;
367         case 'h':
368           be_edit_header (msg->env, 1);
369           break;
370         case 'F':
371         case 'f':
372         case 'm':
373         case 'M':
374           if (Context)
375           {
376             if (!*p && cur)
377             {
378               /* include the current message */
379               p = tmp + mutt_strlen (tmp) + 1;
380               snprintf (tmp + mutt_strlen (tmp), sizeof (tmp) - mutt_strlen (tmp), " %d",
381                                                                 cur->msgno + 1);
382             }
383             buf = be_include_messages (p, buf, &bufmax, &buflen,
384                                        (ascii_tolower (tmp[1]) == 'm'),
385                                        (ascii_isupper ((unsigned char) tmp[1])));
386           }
387           else
388             addstr (_("No mailbox.\n"));
389           break;
390         case 'p':
391           addstr ("-----\n");
392           addstr (_("Message contains:\n"));
393           be_print_header (msg->env);
394           for (i = 0; i < buflen; i++)
395             addstr (buf[i]);
396           addstr (_("(continue)\n"));
397           break;
398         case 'q':
399           done = 1;
400           break;
401         case 'r':
402           if (*p)
403           {
404             strncpy(tmp, p, sizeof(tmp));
405             mutt_expand_path(tmp, sizeof(tmp));
406             buf = be_snarf_file (tmp, buf, &bufmax, &buflen, 1);
407           }
408           else
409             addstr (_("missing filename.\n"));
410           break;
411         case 's':
412           mutt_str_replace (&msg->env->subject, p);
413           break;
414         case 't':
415           msg->env->to = rfc822_parse_adrlist (msg->env->to, p);
416           msg->env->to = mutt_expand_aliases (msg->env->to);
417           break;
418         case 'u':
419           if (buflen)
420           {
421             buflen--;
422             strfcpy (tmp, buf[buflen], sizeof (tmp));
423             tmp[mutt_strlen (tmp)-1] = 0;
424             FREE (&buf[buflen]);
425             buf[buflen] = NULL;
426             continue;
427           }
428           else
429             addstr (_("No lines in message.\n"));
430           break;
431
432         case 'e':
433         case 'v':
434           if (be_barf_file (path, buf, buflen) == 0)
435           {
436             char *tag, *err;
437             be_free_memory (buf, buflen);
438             buf = NULL;
439             bufmax = buflen = 0;
440
441             if (option (OPTEDITHDRS))
442             {
443               mutt_env_to_local (msg->env);
444               mutt_edit_headers (NONULL(Visual), path, msg, NULL, 0);
445               if (mutt_env_to_idna (msg->env, &tag, &err))
446                 printw (_("Bad IDN in %s: '%s'\n"), tag, err);
447             }
448             else
449               mutt_edit_file (NONULL(Visual), path);
450
451             buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
452
453             addstr (_("(continue)\n"));
454           }
455           break;
456         case 'w':
457           be_barf_file (*p ? p : path, buf, buflen);
458           break;
459         case 'x':
460           abort = 1;
461           done = 1;
462           break;
463         default:
464           printw (_("%s: unknown editor command (~? for help)\n"), tmp);
465           break;
466       }
467     }
468     else if (mutt_strcmp (".", tmp) == 0)
469       done = 1;
470     else
471     {
472       safe_strcat (tmp, sizeof (tmp), "\n");
473       if (buflen == bufmax)
474         safe_realloc (&buf, sizeof (char *) * (bufmax += 25));
475       buf[buflen++] = safe_strdup (tmp[1] == '~' ? tmp + 1 : tmp);
476     }
477     
478     tmp[0] = 0;
479   }
480
481   if (!abort) be_barf_file (path, buf, buflen);
482   be_free_memory (buf, buflen);
483
484   return (abort ? -1 : 0);
485 }