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