Implement --attach option in mail; fix dead.letter functionality
* NEWS: Update. * doc/imprimatur: Upgrade. * libmailutils/mime/attachment.c (mu_message_create_attachment): Bugfixes. * mail/mail.c: New options --attach, --content-type and --encoding. * mail/mail.h (default_encoding, default_content_type): New externs. (send_attach_file): New proto. * mail/send.c (send_attach_file): New function. (save_dead_message_env): New function. (save_dead_message): Rewrite. (mail_send0): Attach files, if requested.
Showing
6 changed files
with
409 additions
and
29 deletions
1 | GNU mailutils NEWS -- history of user-visible changes. 2012-06-07 | 1 | GNU mailutils NEWS -- history of user-visible changes. 2012-07-18 |
2 | Copyright (C) 2002-2012 Free Software Foundation, Inc. | 2 | Copyright (C) 2002-2012 Free Software Foundation, Inc. |
3 | See the end of file for copying conditions. | 3 | See the end of file for copying conditions. |
4 | 4 | ||
... | @@ -82,7 +82,7 @@ are not used to avoid compromising security. | ... | @@ -82,7 +82,7 @@ are not used to avoid compromising security. |
82 | 82 | ||
83 | See <http://mailutils.org/wiki/debug_level>. | 83 | See <http://mailutils.org/wiki/debug_level>. |
84 | 84 | ||
85 | ** Imap4d undergone a lot of changes to comply to existing RFCs | 85 | ** Imap4d underwent a lot of changes to comply to existing RFCs |
86 | 86 | ||
87 | ** Pop3d and imap4d allow for mailbox-independent compulsory locking | 87 | ** Pop3d and imap4d allow for mailbox-independent compulsory locking |
88 | 88 | ||
... | @@ -114,6 +114,27 @@ header field with the given date. | ... | @@ -114,6 +114,27 @@ header field with the given date. |
114 | 114 | ||
115 | See <http://mailutils.org/wiki/Timestamp_(Sieve_test)>. | 115 | See <http://mailutils.org/wiki/Timestamp_(Sieve_test)>. |
116 | 116 | ||
117 | ** mail: sending attachments | ||
118 | |||
119 | The mail[x] utility now allows for sending attachments. Any number of | ||
120 | files can be attached to the composed letter by using the `--attach' | ||
121 | (`-A') options. The files will be attached in the same order in which | ||
122 | they appear in the command line. By default, each attachment is | ||
123 | assigned the content type "application/octet-stream" and is encoded | ||
124 | using Base64. This can be changed using the `--content-type' and | ||
125 | `--encoding' options. These options affect all attachments that | ||
126 | appear after them in the command line, until next occurrence of the | ||
127 | same option or end of command line, whichever occurs first. For | ||
128 | example: | ||
129 | |||
130 | mail -A prog --encoding quoted-printable --content-type text/c \ | ||
131 | -A main.c -A ext.h | ||
132 | |||
133 | Here, the file "prog" will be attached witg the content type | ||
134 | "application/octet-stream" and encoding base64, while the files | ||
135 | "main.c" and "ext.h" will be marked with content type "text/c" and | ||
136 | encoded using "quoted-printable" algorithm. | ||
137 | |||
117 | ** MH: improved compatibility with other implementations | 138 | ** MH: improved compatibility with other implementations |
118 | 139 | ||
119 | ** MH inc: new option --moveto | 140 | ** MH inc: new option --moveto |
... | @@ -181,12 +202,29 @@ imap4d, pop3d, comsat) will be built. Its counterpart, | ... | @@ -181,12 +202,29 @@ imap4d, pop3d, comsat) will be built. Its counterpart, |
181 | `--enable-build-clients' controls whether client utilities will be | 202 | `--enable-build-clients' controls whether client utilities will be |
182 | built. | 203 | built. |
183 | 204 | ||
205 | The effect of both options is overridden by the `--enable-build-*' | ||
206 | options for particular components. For example, to build only | ||
207 | the "mail" utility: | ||
208 | |||
209 | ./configure --disable-build-clients --enable-build-mail | ||
210 | |||
211 | ** The --with-mailbindir option | ||
212 | |||
213 | This option changes installation directory for the "mail" utility. | ||
214 | |||
184 | ** DBM options | 215 | ** DBM options |
185 | 216 | ||
186 | It is normally not needed to specify --with-gdbm, --with-berkeley-db | 217 | It is normally not needed to specify --with-gdbm, --with-berkeley-db |
187 | or --with-ndbm explicitly. Configuration will automatically pick up | 218 | or --with-ndbm explicitly. Configuration will automatically pick up |
188 | all available DBM libraries it can use. | 219 | all available DBM libraries it can use. |
189 | 220 | ||
221 | The option `--with-dbm' can be used to enable or disable building of | ||
222 | all available DBM interfaces. Its effect is overridden by `--with-*' | ||
223 | options for particular interfaces. For example, to build only GDBM | ||
224 | (even if another databases are supported by the system): | ||
225 | |||
226 | ./configure --without-dbm --with-gdbm | ||
227 | |||
190 | ** Nntp client is not yet implemented | 228 | ** Nntp client is not yet implemented |
191 | 229 | ||
192 | ** Link with GSASL by default | 230 | ** Link with GSASL by default | ... | ... |
imprimatur @ f32ef198
... | @@ -88,12 +88,15 @@ mu_message_create_attachment (const char *content_type, const char *encoding, | ... | @@ -88,12 +88,15 @@ mu_message_create_attachment (const char *content_type, const char *encoding, |
88 | "Content-Transfer-Encoding: %s\n" | 88 | "Content-Transfer-Encoding: %s\n" |
89 | "Content-Disposition: attachment; filename=%s\n\n", | 89 | "Content-Disposition: attachment; filename=%s\n\n", |
90 | content_type, name, encoding, name); | 90 | content_type, name, encoding, name); |
91 | if (ret) | 91 | if (ret == 0) |
92 | { | 92 | { |
93 | if ((ret = mu_header_create (&hdr, header, | 93 | if ((ret = mu_header_create (&hdr, header, |
94 | strlen (header))) == 0) | 94 | strlen (header))) == 0) |
95 | { | 95 | { |
96 | mu_stream_t bstr; | ||
96 | mu_message_get_body (*newmsg, &body); | 97 | mu_message_get_body (*newmsg, &body); |
98 | mu_body_get_streamref (body, &bstr); | ||
99 | |||
97 | if ((ret = mu_file_stream_create (&fstream, filename, | 100 | if ((ret = mu_file_stream_create (&fstream, filename, |
98 | MU_STREAM_READ)) == 0) | 101 | MU_STREAM_READ)) == 0) |
99 | { | 102 | { |
... | @@ -101,10 +104,12 @@ mu_message_create_attachment (const char *content_type, const char *encoding, | ... | @@ -101,10 +104,12 @@ mu_message_create_attachment (const char *content_type, const char *encoding, |
101 | MU_FILTER_ENCODE, | 104 | MU_FILTER_ENCODE, |
102 | MU_STREAM_READ)) == 0) | 105 | MU_STREAM_READ)) == 0) |
103 | { | 106 | { |
104 | mu_body_set_stream (body, tstream, *newmsg); | 107 | mu_stream_copy (bstr, tstream, 0, NULL); |
108 | mu_stream_unref (tstream); | ||
105 | mu_message_set_header (*newmsg, hdr, NULL); | 109 | mu_message_set_header (*newmsg, hdr, NULL); |
106 | } | 110 | } |
107 | } | 111 | } |
112 | mu_stream_unref (bstr); | ||
108 | free (header); | 113 | free (header); |
109 | } | 114 | } |
110 | } | 115 | } | ... | ... |
... | @@ -31,7 +31,9 @@ static char doc[] = N_("GNU mail -- process mail messages.\n" | ... | @@ -31,7 +31,9 @@ static char doc[] = N_("GNU mail -- process mail messages.\n" |
31 | "by the first argument, or the user's mbox, if no argument given.\n"); | 31 | "by the first argument, or the user's mbox, if no argument given.\n"); |
32 | static char args_doc[] = N_("[address...]\n-f [OPTION...] [file]\n--file [OPTION...] [file]\n--file=file [OPTION...]"); | 32 | static char args_doc[] = N_("[address...]\n-f [OPTION...] [file]\n--file [OPTION...] [file]\n--file=file [OPTION...]"); |
33 | 33 | ||
34 | #define F_OPTION 256 | 34 | #define F_OPTION 256 |
35 | #define F_ENCODING 257 | ||
36 | #define F_CONTENT_TYPE 258 | ||
35 | 37 | ||
36 | static struct argp_option options[] = { | 38 | static struct argp_option options[] = { |
37 | { NULL, 'f', NULL, OPTION_HIDDEN, NULL, 0 }, | 39 | { NULL, 'f', NULL, OPTION_HIDDEN, NULL, 0 }, |
... | @@ -57,6 +59,12 @@ static struct argp_option options[] = { | ... | @@ -57,6 +59,12 @@ static struct argp_option options[] = { |
57 | N_("append given header to the message being sent"), 0}, | 59 | N_("append given header to the message being sent"), 0}, |
58 | {"exec", 'E', N_("COMMAND"), 0, | 60 | {"exec", 'E', N_("COMMAND"), 0, |
59 | N_("execute COMMAND"), 0 }, | 61 | N_("execute COMMAND"), 0 }, |
62 | {"encoding", F_ENCODING, N_("NAME"), 0, | ||
63 | N_("set encoding for subsequent --attach options"), 0 }, | ||
64 | {"content-type", F_CONTENT_TYPE, N_("TYPE"), 0, | ||
65 | N_("set content type for subsequent --attach options"), 0 }, | ||
66 | {"attach", 'A', N_("FILE"), 0, | ||
67 | N_("attach FILE"), 0 }, | ||
60 | { NULL, 0, NULL, 0, NULL, 0 } | 68 | { NULL, 0, NULL, 0, NULL, 0 } |
61 | }; | 69 | }; |
62 | 70 | ||
... | @@ -84,6 +92,17 @@ parse_opt (int key, char *arg, struct argp_state *state) | ... | @@ -84,6 +92,17 @@ parse_opt (int key, char *arg, struct argp_state *state) |
84 | args->hint |= HINT_SEND_MODE; | 92 | args->hint |= HINT_SEND_MODE; |
85 | send_append_header (arg); | 93 | send_append_header (arg); |
86 | break; | 94 | break; |
95 | |||
96 | case 'A': | ||
97 | args->hint |= HINT_SEND_MODE; | ||
98 | if (send_attach_file (arg)) | ||
99 | exit (1); | ||
100 | break; | ||
101 | |||
102 | case F_CONTENT_TYPE: | ||
103 | free (default_content_type); | ||
104 | default_content_type = mu_strdup (arg); | ||
105 | break; | ||
87 | 106 | ||
88 | case 'e': | 107 | case 'e': |
89 | util_cache_command (&command_list, "setq mode=exist"); | 108 | util_cache_command (&command_list, "setq mode=exist"); |
... | @@ -139,6 +158,11 @@ parse_opt (int key, char *arg, struct argp_state *state) | ... | @@ -139,6 +158,11 @@ parse_opt (int key, char *arg, struct argp_state *state) |
139 | case 'E': | 158 | case 'E': |
140 | util_cache_command (&command_list, "%s", arg); | 159 | util_cache_command (&command_list, "%s", arg); |
141 | break; | 160 | break; |
161 | |||
162 | case F_ENCODING: | ||
163 | free (default_encoding); | ||
164 | default_encoding = mu_strdup (arg); | ||
165 | break; | ||
142 | 166 | ||
143 | case 'F': | 167 | case 'F': |
144 | util_cache_command (&command_list, "set byname"); | 168 | util_cache_command (&command_list, "set byname"); | ... | ... |
... | @@ -171,6 +171,8 @@ extern mu_mailbox_t mbox; | ... | @@ -171,6 +171,8 @@ extern mu_mailbox_t mbox; |
171 | extern size_t total; | 171 | extern size_t total; |
172 | extern int interactive; | 172 | extern int interactive; |
173 | extern const char *program_version; | 173 | extern const char *program_version; |
174 | extern char *default_encoding; | ||
175 | extern char *default_content_type; | ||
174 | 176 | ||
175 | /* Functions */ | 177 | /* Functions */ |
176 | extern int mail_alias (int argc, char **argv); | 178 | extern int mail_alias (int argc, char **argv); |
... | @@ -256,6 +258,7 @@ extern char *mail_expand_name (const char *name); | ... | @@ -256,6 +258,7 @@ extern char *mail_expand_name (const char *name); |
256 | 258 | ||
257 | extern void send_append_header (char *text); | 259 | extern void send_append_header (char *text); |
258 | extern void send_append_header2 (char *name, char *value, int mode); | 260 | extern void send_append_header2 (char *name, char *value, int mode); |
261 | extern int send_attach_file (const char *name); | ||
259 | 262 | ||
260 | extern int escape_shell (int argc, char **argv, compose_env_t *env); | 263 | extern int escape_shell (int argc, char **argv, compose_env_t *env); |
261 | extern int escape_command (int argc, char **argv, compose_env_t *env); | 264 | extern int escape_command (int argc, char **argv, compose_env_t *env); | ... | ... |
... | @@ -15,6 +15,7 @@ | ... | @@ -15,6 +15,7 @@ |
15 | along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ | 15 | along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ |
16 | 16 | ||
17 | #include "mail.h" | 17 | #include "mail.h" |
18 | #include <mailutils/mime.h> | ||
18 | #include <sys/types.h> | 19 | #include <sys/types.h> |
19 | #include <sys/stat.h> | 20 | #include <sys/stat.h> |
20 | #include <fcntl.h> | 21 | #include <fcntl.h> |
... | @@ -128,6 +129,233 @@ mail_sendheader (int argc, char **argv) | ... | @@ -128,6 +129,233 @@ mail_sendheader (int argc, char **argv) |
128 | } | 129 | } |
129 | return 0; | 130 | return 0; |
130 | } | 131 | } |
132 | |||
133 | /* Attachments */ | ||
134 | struct atchinfo | ||
135 | { | ||
136 | char *encoding; | ||
137 | char *content_type; | ||
138 | char *filename; | ||
139 | }; | ||
140 | |||
141 | static mu_list_t attlist; | ||
142 | |||
143 | char *default_encoding; | ||
144 | char *default_content_type; | ||
145 | |||
146 | static void | ||
147 | atchinfo_free (void *p) | ||
148 | { | ||
149 | struct atchinfo *ap = p; | ||
150 | free (ap->encoding); | ||
151 | free (ap->content_type); | ||
152 | free (ap->filename); | ||
153 | free (ap); | ||
154 | } | ||
155 | |||
156 | int | ||
157 | send_attach_file (const char *name) | ||
158 | { | ||
159 | int rc; | ||
160 | struct stat st; | ||
161 | struct atchinfo *aptr; | ||
162 | |||
163 | if (stat (name, &st)) | ||
164 | { | ||
165 | if (errno == ENOENT) | ||
166 | { | ||
167 | mu_error (_("%s: file does not exist"), name); | ||
168 | return 1; | ||
169 | } | ||
170 | else | ||
171 | { | ||
172 | mu_error (_("%s: cannot stat: %s"), name, mu_strerror (errno)); | ||
173 | return 1; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | if (!S_ISREG (st.st_mode)) | ||
178 | { | ||
179 | mu_error (_("%s: not a regular file"), name); | ||
180 | return 1; | ||
181 | } | ||
182 | |||
183 | if (!attlist) | ||
184 | { | ||
185 | rc = mu_list_create (&attlist); | ||
186 | if (rc) | ||
187 | { | ||
188 | mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc); | ||
189 | exit (1); | ||
190 | } | ||
191 | mu_list_set_destroy_item (attlist, atchinfo_free); | ||
192 | } | ||
193 | aptr = mu_alloc (sizeof (*aptr)); | ||
194 | aptr->encoding = mu_strdup (default_encoding ? | ||
195 | default_encoding : "base64"); | ||
196 | aptr->content_type = mu_strdup (default_content_type ? | ||
197 | default_content_type : | ||
198 | "application/octet-stream"); | ||
199 | aptr->filename = mu_strdup (name); | ||
200 | rc = mu_list_append (attlist, aptr); | ||
201 | if (rc) | ||
202 | { | ||
203 | mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", NULL, rc); | ||
204 | exit (1); | ||
205 | } | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int | ||
210 | saveatt (void *item, void *data) | ||
211 | { | ||
212 | struct atchinfo *aptr = item; | ||
213 | mu_mime_t mime = data; | ||
214 | mu_message_t part; | ||
215 | mu_header_t hdr; | ||
216 | int rc; | ||
217 | size_t nparts; | ||
218 | char *p; | ||
219 | |||
220 | rc = mu_message_create_attachment (aptr->content_type, aptr->encoding, | ||
221 | aptr->filename, &part); | ||
222 | if (rc) | ||
223 | { | ||
224 | mu_error (_("cannot attach \"%s\": %s"), aptr->filename, | ||
225 | mu_strerror (rc)); | ||
226 | return 1; | ||
227 | } | ||
228 | |||
229 | mu_mime_get_num_parts (mime, &nparts); | ||
230 | mu_message_get_header (part, &hdr); | ||
231 | mu_rfc2822_msg_id (nparts, &p); | ||
232 | mu_header_set_value (hdr, MU_HEADER_CONTENT_ID, p, 1); | ||
233 | free (p); | ||
234 | |||
235 | rc = mu_mime_add_part (mime, part); | ||
236 | mu_message_unref (part); | ||
237 | if (rc) | ||
238 | { | ||
239 | mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_add_part", aptr->filename, rc); | ||
240 | return 1; | ||
241 | } | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int | ||
247 | add_attachments (mu_message_t *pmsg, mu_mime_t *pmime) | ||
248 | { | ||
249 | mu_message_t inmsg, outmsg, part; | ||
250 | mu_body_t body; | ||
251 | mu_header_t inhdr, outhdr; | ||
252 | mu_iterator_t itr; | ||
253 | mu_mime_t mime; | ||
254 | mu_stream_t str, output; | ||
255 | int rc; | ||
256 | char *p; | ||
257 | |||
258 | if (mu_list_is_empty (attlist)) | ||
259 | { | ||
260 | *pmime = NULL; | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | inmsg = *pmsg; | ||
265 | |||
266 | /* Create a mime object */ | ||
267 | rc = mu_mime_create (&mime, NULL, 0); | ||
268 | if (rc) | ||
269 | { | ||
270 | mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create", NULL, rc); | ||
271 | return 1; | ||
272 | } | ||
273 | |||
274 | /* Add original message as its first part */ | ||
275 | /* 1. Create the part and obtain a reference to its stream */ | ||
276 | mu_message_create (&part, NULL); | ||
277 | mu_message_get_body (part, &body); | ||
278 | mu_body_get_streamref (body, &output); | ||
279 | |||
280 | /* 2. Get original body stream and copy it out to the part's body */ | ||
281 | mu_message_get_body (inmsg, &body); | ||
282 | mu_body_get_streamref (body, &str); | ||
283 | mu_stream_copy (output, str, 0, NULL); | ||
284 | |||
285 | mu_stream_close (output); | ||
286 | mu_stream_destroy (&output); | ||
287 | |||
288 | mu_message_get_header (inmsg, &inhdr); | ||
289 | mu_header_get_iterator (inhdr, &itr); | ||
290 | |||
291 | /* 3. Copy "Content-*" headers from the original message */ | ||
292 | mu_message_get_header (part, &outhdr); | ||
293 | for (mu_iterator_first (itr); !mu_iterator_is_done (itr); | ||
294 | mu_iterator_next (itr)) | ||
295 | { | ||
296 | const char *name, *value; | ||
297 | |||
298 | if (mu_iterator_current_kv (itr, (const void **)&name, | ||
299 | (void**)&value) == 0) | ||
300 | { | ||
301 | if (mu_c_strncasecmp (name, "Content-", 8) == 0) | ||
302 | mu_header_set_value (outhdr, name, value, 0); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | /* 4. Add the content type and content ID headers. */ | ||
307 | mu_header_set_value (outhdr, MU_HEADER_CONTENT_TYPE, "text/plain", 0); | ||
308 | mu_rfc2822_msg_id (0, &p); | ||
309 | mu_header_set_value (outhdr, MU_HEADER_CONTENT_ID, p, 1); | ||
310 | free (p); | ||
311 | |||
312 | /* 5. Add part to the mime object */ | ||
313 | mu_mime_add_part (mime, part); | ||
314 | mu_message_unref (part); | ||
315 | |||
316 | /* Add the respective attachments */ | ||
317 | rc = mu_list_foreach (attlist, saveatt, mime); | ||
318 | if (rc) | ||
319 | { | ||
320 | mu_mime_destroy (&mime); | ||
321 | return 1; | ||
322 | } | ||
323 | |||
324 | /* Get the resulting message */ | ||
325 | rc = mu_mime_get_message (mime, &outmsg); | ||
326 | |||
327 | if (rc) | ||
328 | { | ||
329 | mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_get_message", NULL, rc); | ||
330 | mu_mime_destroy (&mime); | ||
331 | return 1; | ||
332 | } | ||
333 | |||
334 | /* Copy rest of headers from the original message */ | ||
335 | mu_message_get_header (outmsg, &outhdr); | ||
336 | for (mu_iterator_first (itr); !mu_iterator_is_done (itr); | ||
337 | mu_iterator_next (itr)) | ||
338 | { | ||
339 | const char *name, *value; | ||
340 | |||
341 | if (mu_iterator_current_kv (itr, (const void **)&name, | ||
342 | (void**)&value) == 0) | ||
343 | { | ||
344 | if (mu_c_strcasecmp (name, MU_HEADER_MIME_VERSION) == 0 || | ||
345 | mu_c_strncasecmp (name, "Content-", 8) == 0) | ||
346 | continue; | ||
347 | mu_header_append (outhdr, name, value); | ||
348 | } | ||
349 | } | ||
350 | mu_iterator_destroy (&itr); | ||
351 | |||
352 | mu_message_unref (outmsg); | ||
353 | mu_message_unref (inmsg); | ||
354 | *pmsg = outmsg; | ||
355 | *pmime = mime; | ||
356 | return 0; | ||
357 | } | ||
358 | |||
131 | 359 | ||
132 | 360 | ||
133 | /* Send-related commands */ | 361 | /* Send-related commands */ |
... | @@ -342,17 +570,21 @@ fill_body (mu_message_t msg, mu_stream_t instr) | ... | @@ -342,17 +570,21 @@ fill_body (mu_message_t msg, mu_stream_t instr) |
342 | } | 570 | } |
343 | 571 | ||
344 | static int | 572 | static int |
345 | save_dead_message (compose_env_t *env) | 573 | save_dead_message_env (compose_env_t *env) |
346 | { | 574 | { |
347 | if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0) | 575 | if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0) |
348 | { | 576 | { |
349 | mu_stream_t dead_letter; | 577 | mu_stream_t dead_letter, str; |
350 | int rc; | 578 | int rc; |
579 | time_t t; | ||
580 | struct tm *tm; | ||
351 | const char *name = getenv ("DEAD"); | 581 | const char *name = getenv ("DEAD"); |
352 | 582 | char *sender; | |
583 | |||
353 | /* FIXME: Use MU_STREAM_APPEND if appenddeadletter, instead of the | 584 | /* FIXME: Use MU_STREAM_APPEND if appenddeadletter, instead of the |
354 | stream manipulations below */ | 585 | stream manipulations below */ |
355 | rc = mu_file_stream_create (&dead_letter, name, MU_STREAM_WRITE); | 586 | rc = mu_file_stream_create (&dead_letter, name, |
587 | MU_STREAM_CREAT|MU_STREAM_WRITE); | ||
356 | if (rc) | 588 | if (rc) |
357 | { | 589 | { |
358 | mu_error (_("Cannot open file %s: %s"), name, strerror (rc)); | 590 | mu_error (_("Cannot open file %s: %s"), name, strerror (rc)); |
... | @@ -364,8 +596,73 @@ save_dead_message (compose_env_t *env) | ... | @@ -364,8 +596,73 @@ save_dead_message (compose_env_t *env) |
364 | else | 596 | else |
365 | mu_stream_truncate (dead_letter, 0); | 597 | mu_stream_truncate (dead_letter, 0); |
366 | 598 | ||
599 | time (&t); | ||
600 | tm = gmtime (&t); | ||
601 | sender = mu_get_user_email (NULL); | ||
602 | if (!sender) | ||
603 | sender = mu_strdup ("UNKNOWN"); | ||
604 | mu_stream_printf (dead_letter, "From %s ", sender); | ||
605 | free (sender); | ||
606 | mu_c_streamftime (dead_letter, "%c%n", tm, NULL); | ||
607 | |||
608 | if (mu_header_get_streamref (env->header, &str) == 0) | ||
609 | { | ||
610 | mu_stream_copy (dead_letter, str, 0, NULL); | ||
611 | mu_stream_unref (str); | ||
612 | } | ||
613 | else | ||
614 | mu_stream_write (dead_letter, "\n", 1, NULL); | ||
615 | |||
367 | mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL); | 616 | mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL); |
368 | mu_stream_copy (dead_letter, env->compstr, 0, NULL); | 617 | mu_stream_copy (dead_letter, env->compstr, 0, NULL); |
618 | mu_stream_write (dead_letter, "\n", 1, NULL); | ||
619 | mu_stream_destroy (&dead_letter); | ||
620 | } | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | static int | ||
625 | save_dead_message (mu_message_t msg) | ||
626 | { | ||
627 | if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0) | ||
628 | { | ||
629 | mu_stream_t dead_letter, str; | ||
630 | int rc; | ||
631 | time_t t; | ||
632 | struct tm *tm; | ||
633 | const char *name = getenv ("DEAD"); | ||
634 | char *sender; | ||
635 | |||
636 | /* FIXME: Use MU_STREAM_APPEND if appenddeadletter, instead of the | ||
637 | stream manipulations below */ | ||
638 | rc = mu_file_stream_create (&dead_letter, name, | ||
639 | MU_STREAM_CREAT|MU_STREAM_WRITE); | ||
640 | if (rc) | ||
641 | { | ||
642 | mu_error (_("Cannot open file %s: %s"), name, strerror (rc)); | ||
643 | return 1; | ||
644 | } | ||
645 | if (mailvar_get (NULL, "appenddeadletter", | ||
646 | mailvar_type_boolean, 0) == 0) | ||
647 | mu_stream_seek (dead_letter, 0, MU_SEEK_END, NULL); | ||
648 | else | ||
649 | mu_stream_truncate (dead_letter, 0); | ||
650 | |||
651 | time (&t); | ||
652 | tm = gmtime (&t); | ||
653 | sender = mu_get_user_email (NULL); | ||
654 | if (!sender) | ||
655 | sender = mu_strdup ("UNKNOWN"); | ||
656 | mu_stream_printf (dead_letter, "From %s ", sender); | ||
657 | free (sender); | ||
658 | mu_c_streamftime (dead_letter, "%c%n", tm, NULL); | ||
659 | |||
660 | if (mu_message_get_streamref (msg, &str) == 0) | ||
661 | { | ||
662 | mu_stream_copy (dead_letter, str, 0, NULL); | ||
663 | mu_stream_unref (str); | ||
664 | } | ||
665 | mu_stream_write (dead_letter, "\n", 1, NULL); | ||
369 | mu_stream_destroy (&dead_letter); | 666 | mu_stream_destroy (&dead_letter); |
370 | } | 667 | } |
371 | return 0; | 668 | return 0; |
... | @@ -445,6 +742,7 @@ mail_send0 (compose_env_t *env, int save_to) | ... | @@ -445,6 +742,7 @@ mail_send0 (compose_env_t *env, int save_to) |
445 | if (rc) | 742 | if (rc) |
446 | { | 743 | { |
447 | mu_error (_("Cannot open temporary file: %s"), mu_strerror (rc)); | 744 | mu_error (_("Cannot open temporary file: %s"), mu_strerror (rc)); |
745 | mu_list_destroy (&attlist); | ||
448 | return 1; | 746 | return 1; |
449 | } | 747 | } |
450 | 748 | ||
... | @@ -541,8 +839,9 @@ mail_send0 (compose_env_t *env, int save_to) | ... | @@ -541,8 +839,9 @@ mail_send0 (compose_env_t *env, int save_to) |
541 | /* If interrupted, dump the file to dead.letter. */ | 839 | /* If interrupted, dump the file to dead.letter. */ |
542 | if (int_cnt) | 840 | if (int_cnt) |
543 | { | 841 | { |
544 | save_dead_message (env); | 842 | save_dead_message_env (env); |
545 | mu_stream_destroy (&env->compstr); | 843 | mu_stream_destroy (&env->compstr); |
844 | mu_list_destroy (&attlist); | ||
546 | return 1; | 845 | return 1; |
547 | } | 846 | } |
548 | 847 | ||
... | @@ -558,18 +857,30 @@ mail_send0 (compose_env_t *env, int save_to) | ... | @@ -558,18 +857,30 @@ mail_send0 (compose_env_t *env, int save_to) |
558 | 857 | ||
559 | if (util_header_expand (&env->header) == 0) | 858 | if (util_header_expand (&env->header) == 0) |
560 | { | 859 | { |
860 | mu_mime_t mime = NULL; | ||
561 | mu_message_t msg = NULL; | 861 | mu_message_t msg = NULL; |
562 | int rc; | ||
563 | int status = 0; | 862 | int status = 0; |
564 | 863 | int sendit = (compose_header_get (env, MU_HEADER_TO, NULL) || | |
565 | mu_message_create (&msg, NULL); | 864 | compose_header_get (env, MU_HEADER_CC, NULL) || |
566 | 865 | compose_header_get (env, MU_HEADER_BCC, NULL)); | |
567 | /* Fill the body. */ | 866 | do |
568 | mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL); | ||
569 | rc = fill_body (msg, env->compstr); | ||
570 | |||
571 | if (rc == 0) | ||
572 | { | 867 | { |
868 | status = mu_message_create (&msg, NULL); | ||
869 | if (status) | ||
870 | break; | ||
871 | /* Fill the body. */ | ||
872 | mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL); | ||
873 | status = fill_body (msg, env->compstr); | ||
874 | if (status) | ||
875 | break; | ||
876 | |||
877 | mu_message_set_header (msg, env->header, NULL); | ||
878 | env->header = NULL; | ||
879 | |||
880 | status = add_attachments (&msg, &mime); | ||
881 | if (status) | ||
882 | break; | ||
883 | |||
573 | /* Save outgoing message */ | 884 | /* Save outgoing message */ |
574 | if (save_to) | 885 | if (save_to) |
575 | { | 886 | { |
... | @@ -628,30 +939,29 @@ mail_send0 (compose_env_t *env, int save_to) | ... | @@ -628,30 +939,29 @@ mail_send0 (compose_env_t *env, int save_to) |
628 | } | 939 | } |
629 | 940 | ||
630 | /* Do we need to Send the message on the wire? */ | 941 | /* Do we need to Send the message on the wire? */ |
631 | if (status == 0 && | 942 | if (status == 0 && sendit) |
632 | (compose_header_get (env, MU_HEADER_TO, NULL) || | ||
633 | compose_header_get (env, MU_HEADER_CC, NULL) || | ||
634 | compose_header_get (env, MU_HEADER_BCC, NULL))) | ||
635 | { | 943 | { |
636 | mu_message_set_header (msg, env->header, NULL); | ||
637 | env->header = NULL; | ||
638 | status = send_message (msg); | 944 | status = send_message (msg); |
639 | if (status) | 945 | if (status) |
640 | { | 946 | { |
641 | mu_error (_("cannot send message: %s"), | 947 | mu_error (_("cannot send message: %s"), |
642 | mu_strerror (status)); | 948 | mu_strerror (status)); |
643 | save_dead_message (env); | 949 | save_dead_message (msg); |
644 | } | 950 | } |
645 | } | 951 | } |
646 | } | 952 | } |
953 | while (0); | ||
954 | |||
647 | mu_stream_destroy (&env->compstr); | 955 | mu_stream_destroy (&env->compstr); |
648 | mu_message_destroy (&msg, NULL); | 956 | mu_message_destroy (&msg, NULL); |
957 | mu_mime_destroy (&mime); | ||
649 | return status; | 958 | return status; |
650 | } | 959 | } |
651 | else | 960 | else |
652 | save_dead_message (env); | 961 | save_dead_message_env (env); |
653 | 962 | ||
654 | mu_stream_destroy (&env->compstr); | 963 | mu_stream_destroy (&env->compstr); |
964 | mu_list_destroy (&attlist); | ||
655 | return 1; | 965 | return 1; |
656 | } | 966 | } |
657 | 967 | ... | ... |
-
Please register or sign in to post a comment