Rewrite SMTP mailer.
* libproto/mailer/smtp.c: Rewrite using new SMTP API. * libproto/mailer/smtp_quit.c (mu_smtp_quit): Return immediately if already in closed state. * libmailutils/ticket.c (mu_ticket_get_cred): Return MU_ERR_FAILURE if all methods fail. * mail/send.c: Port 23321cf7 from patches-2.2 (msg_to_pipe): Return status code. (save_dead_message, send_message): New functions, extracted from mail_send0. (mail_send0): Call save_dead_message if sending failed.
Showing
4 changed files
with
387 additions
and
1350 deletions
... | @@ -211,7 +211,9 @@ mu_ticket_get_cred (mu_ticket_t ticket, mu_url_t url, const char *challenge, | ... | @@ -211,7 +211,9 @@ mu_ticket_get_cred (mu_ticket_t ticket, mu_url_t url, const char *challenge, |
211 | } | 211 | } |
212 | arg [strlen (arg) - 1] = '\0'; /* nuke the trailing line. */ | 212 | arg [strlen (arg) - 1] = '\0'; /* nuke the trailing line. */ |
213 | } | 213 | } |
214 | 214 | else | |
215 | return MU_ERR_FAILURE; | ||
216 | |||
215 | if (pplain) | 217 | if (pplain) |
216 | { | 218 | { |
217 | *pplain = strdup (arg); | 219 | *pplain = strdup (arg); | ... | ... |
... | @@ -37,24 +37,21 @@ | ... | @@ -37,24 +37,21 @@ |
37 | #include <unistd.h> | 37 | #include <unistd.h> |
38 | 38 | ||
39 | #include <mailutils/address.h> | 39 | #include <mailutils/address.h> |
40 | #include <mailutils/argcv.h> | ||
40 | #include <mailutils/debug.h> | 41 | #include <mailutils/debug.h> |
41 | #include <mailutils/errno.h> | 42 | #include <mailutils/errno.h> |
42 | #include <mailutils/header.h> | 43 | #include <mailutils/header.h> |
43 | #include <mailutils/body.h> | 44 | #include <mailutils/body.h> |
45 | #include <mailutils/iterator.h> | ||
44 | #include <mailutils/message.h> | 46 | #include <mailutils/message.h> |
45 | #include <mailutils/mime.h> | 47 | #include <mailutils/mime.h> |
46 | #include <mailutils/mutil.h> | 48 | #include <mailutils/mutil.h> |
47 | #include <mailutils/observer.h> | 49 | #include <mailutils/observer.h> |
48 | #include <mailutils/property.h> | 50 | #include <mailutils/property.h> |
49 | #include <mailutils/stream.h> | 51 | #include <mailutils/stream.h> |
50 | #include <mailutils/url.h> | 52 | #include <mailutils/smtp.h> |
51 | #include <mailutils/tls.h> | 53 | #include <mailutils/tls.h> |
52 | #include <mailutils/md5.h> | ||
53 | #include <mailutils/io.h> | ||
54 | #include <mailutils/secret.h> | ||
55 | #include <mailutils/cctype.h> | ||
56 | #include <mailutils/cstr.h> | 54 | #include <mailutils/cstr.h> |
57 | |||
58 | #include <mailutils/sys/mailer.h> | 55 | #include <mailutils/sys/mailer.h> |
59 | #include <mailutils/sys/url.h> | 56 | #include <mailutils/sys/url.h> |
60 | #include <mailutils/sys/registrar.h> | 57 | #include <mailutils/sys/registrar.h> |
... | @@ -100,1071 +97,149 @@ static struct _mu_record _smtp_record = { | ... | @@ -100,1071 +97,149 @@ static struct _mu_record _smtp_record = { |
100 | the mailbox, via the register entry/record. */ | 97 | the mailbox, via the register entry/record. */ |
101 | mu_record_t mu_smtp_record = &_smtp_record; | 98 | mu_record_t mu_smtp_record = &_smtp_record; |
102 | 99 | ||
103 | struct _smtp | 100 | struct _smtp_mailer |
104 | { | 101 | { |
105 | mu_mailer_t mailer; | 102 | mu_mailer_t mailer; |
106 | char *mailhost; | 103 | mu_smtp_t smtp; |
107 | char *localhost; | ||
108 | |||
109 | /* IO buffering. */ | ||
110 | char *buffer; /* Must be freed. */ | ||
111 | size_t buflen; | ||
112 | |||
113 | char *ptr; | ||
114 | char *nl; | ||
115 | |||
116 | enum smtp_state | ||
117 | { | ||
118 | SMTP_NO_STATE, SMTP_OPEN, SMTP_GREETINGS, SMTP_EHLO, SMTP_EHLO_ACK, | ||
119 | SMTP_HELO, SMTP_HELO_ACK, SMTP_QUIT, SMTP_QUIT_ACK, SMTP_ENV_FROM, | ||
120 | SMTP_ENV_RCPT, SMTP_MAIL_FROM, SMTP_MAIL_FROM_ACK, SMTP_RCPT_TO, | ||
121 | SMTP_RCPT_TO_ACK, SMTP_DATA, SMTP_DATA_ACK, SMTP_SEND, SMTP_SEND_ACK, | ||
122 | SMTP_SEND_DOT, SMTP_STARTTLS, SMTP_STARTTLS_ACK, SMTP_AUTH, SMTP_AUTH_ACK, | ||
123 | } | ||
124 | state; | ||
125 | |||
126 | int extended; | ||
127 | unsigned long capa; /* Server capabilities */ | ||
128 | size_t max_size; /* Maximum message size the server is willing | ||
129 | to accept */ | ||
130 | unsigned long auth_mechs; /* Available ESMTP AUTH mechanisms */ | ||
131 | 104 | ||
132 | const char *mail_from; | 105 | mu_address_t rcpt_to; |
133 | mu_address_t rcpt_to; /* Destroy this if not the same as argto below. */ | ||
134 | mu_address_t rcpt_bcc; | 106 | mu_address_t rcpt_bcc; |
135 | size_t rcpt_to_count; | ||
136 | size_t rcpt_bcc_count; | ||
137 | size_t rcpt_index; | ||
138 | size_t rcpt_count; | ||
139 | int bccing; | ||
140 | mu_message_t msg; /* Destroy this if not same argmsg. */ | ||
141 | |||
142 | /* The mu_mailer_send_message() args. */ | ||
143 | mu_message_t argmsg; | ||
144 | mu_address_t argfrom; | ||
145 | mu_address_t argto; | ||
146 | }; | 107 | }; |
147 | 108 | ||
148 | typedef struct _smtp *smtp_t; | ||
149 | |||
150 | /* ESMTP capabilities */ | ||
151 | #define CAPA_STARTTLS 0x00000001 | ||
152 | #define CAPA_8BITMIME 0x00000002 | ||
153 | #define CAPA_SIZE 0x00000004 | ||
154 | #define CAPA_AUTH 0x00000008 | ||
155 | |||
156 | /* ESMTP AUTH mechanisms */ | ||
157 | #define AUTH_LOGIN 0x00000001 | ||
158 | #define AUTH_PLAIN 0x00000002 | ||
159 | #define AUTH_CRAM_MD5 0x00000004 | ||
160 | #define AUTH_DIGEST_MD5 0x00000008 | ||
161 | #define AUTH_GSSAPI 0x00000010 | ||
162 | #define AUTH_EXTERNAL 0x00000020 | ||
163 | |||
164 | struct auth_mech_record | ||
165 | { | ||
166 | unsigned long id; | ||
167 | char *name; | ||
168 | }; | ||
169 | |||
170 | static struct auth_mech_record auth_mech_list[] = { | ||
171 | {AUTH_LOGIN, "login"}, | ||
172 | {AUTH_PLAIN, "plain"}, | ||
173 | {AUTH_CRAM_MD5, "cram-md5"}, | ||
174 | {AUTH_DIGEST_MD5, "digest-md5"}, | ||
175 | {AUTH_GSSAPI, "gssapi"}, | ||
176 | {AUTH_EXTERNAL, "external"}, | ||
177 | {0, NULL}, | ||
178 | }; | ||
179 | |||
180 | static void smtp_destroy (mu_mailer_t); | ||
181 | static int smtp_open (mu_mailer_t, int); | ||
182 | static int smtp_close (mu_mailer_t); | ||
183 | static int smtp_send_message (mu_mailer_t, mu_message_t, mu_address_t, | ||
184 | mu_address_t); | ||
185 | static int smtp_writeline (smtp_t smtp, const char *format, ...); | ||
186 | static int smtp_readline (smtp_t); | ||
187 | static int smtp_read_ack (smtp_t); | ||
188 | static int smtp_parse_ehlo_ack (smtp_t); | ||
189 | static int smtp_write (smtp_t); | ||
190 | static int smtp_starttls (smtp_t); | ||
191 | static int smtp_auth (smtp_t); | ||
192 | |||
193 | static int _smtp_set_rcpt (smtp_t, mu_message_t, mu_address_t); | ||
194 | |||
195 | /* Useful little macros, since these are very repetitive. */ | ||
196 | |||
197 | static void | ||
198 | CLEAR_STATE (smtp_t smtp) | ||
199 | { | ||
200 | smtp->ptr = smtp->buffer; | ||
201 | smtp->nl = NULL; | ||
202 | |||
203 | smtp->state = SMTP_NO_STATE; | ||
204 | |||
205 | smtp->extended = 0; | ||
206 | |||
207 | if (smtp->mail_from) | ||
208 | smtp->mail_from = NULL; | ||
209 | |||
210 | if (smtp->rcpt_to != smtp->argto) | ||
211 | mu_address_destroy (&smtp->rcpt_to); | ||
212 | |||
213 | smtp->rcpt_to = NULL; | ||
214 | |||
215 | mu_address_destroy (&smtp->rcpt_bcc); | ||
216 | |||
217 | smtp->rcpt_to_count = 0; | ||
218 | smtp->rcpt_bcc_count = 0; | ||
219 | smtp->rcpt_index = 0; | ||
220 | smtp->rcpt_count = 0; | ||
221 | smtp->bccing = 0; | ||
222 | |||
223 | if (smtp->msg != smtp->argmsg) | ||
224 | mu_message_destroy (&smtp->msg, NULL); | ||
225 | |||
226 | smtp->msg = NULL; | ||
227 | |||
228 | smtp->argmsg = NULL; | ||
229 | smtp->argfrom = NULL; | ||
230 | smtp->argto = NULL; | ||
231 | } | ||
232 | |||
233 | /* If we are resuming, we should be resuming the SAME operation | ||
234 | as that which is ongoing. Check this. */ | ||
235 | static int | ||
236 | smtp_check_send_resumption (smtp_t smtp, | ||
237 | mu_message_t msg, mu_address_t from, | ||
238 | mu_address_t to) | ||
239 | { | ||
240 | if (smtp->state == SMTP_NO_STATE) | ||
241 | return 0; | ||
242 | |||
243 | /* FIXME: state should be one of the "send" states if its not | ||
244 | "no state" */ | ||
245 | if (msg != smtp->argmsg) | ||
246 | return MU_ERR_BAD_RESUMPTION; | ||
247 | |||
248 | if (from != smtp->argfrom) | ||
249 | return MU_ERR_BAD_RESUMPTION; | ||
250 | |||
251 | if (to != smtp->argto) | ||
252 | return MU_ERR_BAD_RESUMPTION; | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | #define CHECK_SEND_RESUME(smtp, msg, from, to) \ | ||
258 | do { \ | ||
259 | if((status = smtp_check_send_resumption(smtp, msg, from, to)) != 0) \ | ||
260 | return status; \ | ||
261 | } while (0) | ||
262 | |||
263 | /* Clear the state and close the stream. */ | ||
264 | #define CHECK_ERROR_CLOSE(mailer, smtp, status) \ | ||
265 | do \ | ||
266 | { \ | ||
267 | if (status != 0) \ | ||
268 | { \ | ||
269 | mu_stream_close (mailer->stream); \ | ||
270 | CLEAR_STATE (smtp); \ | ||
271 | return status; \ | ||
272 | } \ | ||
273 | } \ | ||
274 | while (0) | ||
275 | |||
276 | /* Clear the state. */ | ||
277 | #define CHECK_ERROR(smtp, status) \ | ||
278 | do \ | ||
279 | { \ | ||
280 | if (status != 0) \ | ||
281 | { \ | ||
282 | CLEAR_STATE (smtp); \ | ||
283 | return status; \ | ||
284 | } \ | ||
285 | } \ | ||
286 | while (0) | ||
287 | |||
288 | /* Clear the state for non recoverable error. */ | ||
289 | #define CHECK_EAGAIN(smtp, status) \ | ||
290 | do \ | ||
291 | { \ | ||
292 | if (status != 0) \ | ||
293 | { \ | ||
294 | if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \ | ||
295 | { \ | ||
296 | CLEAR_STATE (smtp); \ | ||
297 | } \ | ||
298 | return status; \ | ||
299 | } \ | ||
300 | } \ | ||
301 | while (0) | ||
302 | |||
303 | static int | ||
304 | _mailer_smtp_init (mu_mailer_t mailer) | ||
305 | { | ||
306 | smtp_t smtp; | ||
307 | |||
308 | /* Allocate memory specific to smtp mailer. */ | ||
309 | smtp = mailer->data = calloc (1, sizeof (*smtp)); | ||
310 | if (mailer->data == NULL) | ||
311 | return ENOMEM; | ||
312 | |||
313 | smtp->mailer = mailer; /* Back pointer. */ | ||
314 | smtp->state = SMTP_NO_STATE; | ||
315 | |||
316 | mailer->_destroy = smtp_destroy; | ||
317 | mailer->_open = smtp_open; | ||
318 | mailer->_close = smtp_close; | ||
319 | mailer->_send_message = smtp_send_message; | ||
320 | |||
321 | /* Set our properties. */ | ||
322 | { | ||
323 | mu_property_t property = NULL; | ||
324 | |||
325 | mu_mailer_get_property (mailer, &property); | ||
326 | mu_property_set_value (property, "TYPE", "SMTP", 1); | ||
327 | } | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static void | ||
333 | smtp_destroy (mu_mailer_t mailer) | ||
334 | { | ||
335 | smtp_t smtp = mailer->data; | ||
336 | |||
337 | CLEAR_STATE (smtp); | ||
338 | |||
339 | /* Not our responsability to close. */ | ||
340 | |||
341 | if (smtp->mailhost) | ||
342 | free (smtp->mailhost); | ||
343 | if (smtp->localhost) | ||
344 | free (smtp->localhost); | ||
345 | if (smtp->buffer) | ||
346 | free (smtp->buffer); | ||
347 | |||
348 | free (smtp); | ||
349 | |||
350 | mailer->data = NULL; | ||
351 | } | ||
352 | |||
353 | /** Open an SMTP mailer. | ||
354 | An SMTP mailer must be opened before any messages can be sent. | ||
355 | @param mailer the mailer created by smtp_create() | ||
356 | @param flags the mailer flags | ||
357 | */ | ||
358 | static int | 109 | static int |
359 | smtp_open (mu_mailer_t mailer, int flags) | 110 | smtp_open (mu_mailer_t mailer, int flags) |
360 | { | 111 | { |
361 | smtp_t smtp = mailer->data; | 112 | const char *host; |
362 | int status; | 113 | long port; |
363 | long port; | 114 | struct _smtp_mailer *smtp_mailer = mailer->data; |
364 | 115 | int rc; | |
365 | /* Sanity checks. */ | 116 | size_t parmc = 0; |
366 | if (!smtp) | 117 | char **parmv = NULL; |
367 | return EINVAL; | 118 | int notls = 0; |
119 | int noauth = 0; | ||
120 | |||
121 | rc = mu_smtp_create (&smtp_mailer->smtp); | ||
122 | if (rc) | ||
123 | return rc; | ||
124 | if (mu_debug_check_level (mailer->debug, MU_DEBUG_PROT)) | ||
125 | mu_smtp_trace (smtp_mailer->smtp, MU_SMTP_TRACE_SET); | ||
126 | if (mu_debug_check_level (mailer->debug, MU_DEBUG_TRACE6)) | ||
127 | mu_smtp_trace_mask (smtp_mailer->smtp, MU_SMTP_TRACE_SET, MU_XSCRIPT_SECURE); | ||
128 | if (mu_debug_check_level (mailer->debug, MU_DEBUG_TRACE7)) | ||
129 | mu_smtp_trace_mask (smtp_mailer->smtp, MU_SMTP_TRACE_SET, MU_XSCRIPT_PAYLOAD); | ||
130 | |||
131 | mu_smtp_set_param (smtp_mailer->smtp, MU_SMTP_PARAM_URL, | ||
132 | mu_url_to_string (mailer->url)); | ||
368 | 133 | ||
369 | mailer->flags = flags; | 134 | rc = mu_url_sget_host (mailer->url, &host); |
135 | if (rc) | ||
136 | return rc; | ||
137 | if (mu_url_get_port (mailer->url, &port)) | ||
138 | port = 25; | ||
370 | 139 | ||
371 | if ((status = mu_url_get_port (mailer->url, &port)) != 0) | 140 | /* Additional information is supplied in the arguments */ |
372 | return status; | 141 | if (mu_url_sget_fvpairs (mailer->url, &parmc, &parmv) == 0) |
373 | |||
374 | switch (smtp->state) | ||
375 | { | 142 | { |
376 | case SMTP_NO_STATE: | 143 | size_t i; |
377 | if (smtp->mailhost) | ||
378 | { | ||
379 | free (smtp->mailhost); | ||
380 | smtp->mailhost = NULL; | ||
381 | } | ||
382 | |||
383 | /* Fetch the mailer server name and the port in the mu_url_t. */ | ||
384 | if ((status = mu_url_aget_host (mailer->url, &smtp->mailhost)) != 0) | ||
385 | return status; | ||
386 | |||
387 | if (smtp->localhost) | ||
388 | { | ||
389 | free (smtp->localhost); | ||
390 | smtp->localhost = NULL; | ||
391 | } | ||
392 | /* Fetch our local host name. */ | ||
393 | |||
394 | status = mu_get_host_name (&smtp->localhost); | ||
395 | |||
396 | if (status != 0) | ||
397 | { | ||
398 | /* gethostname failed, abort. */ | ||
399 | free (smtp->mailhost); | ||
400 | smtp->mailhost = NULL; | ||
401 | return status; | ||
402 | } | ||
403 | |||
404 | /* allocate a working io buffer. */ | ||
405 | if (smtp->buffer == NULL) | ||
406 | { | ||
407 | smtp->buflen = 512; /* Initial guess. */ | ||
408 | smtp->buffer = malloc (smtp->buflen + 1); | ||
409 | if (smtp->buffer == NULL) | ||
410 | { | ||
411 | CHECK_ERROR (smtp, ENOMEM); | ||
412 | } | ||
413 | smtp->ptr = smtp->buffer; | ||
414 | } | ||
415 | |||
416 | /* Create a TCP stack if one is not given. */ | ||
417 | if (mailer->stream == NULL) | ||
418 | { | ||
419 | status = | ||
420 | mu_tcp_stream_create (&mailer->stream, smtp->mailhost, port, | ||
421 | mailer->flags); | ||
422 | CHECK_ERROR (smtp, status); | ||
423 | mu_stream_set_buffer (mailer->stream, mu_buffer_line, BUFSIZ); | ||
424 | } | ||
425 | CHECK_ERROR (smtp, status); | ||
426 | smtp->state = SMTP_OPEN; | ||
427 | |||
428 | case SMTP_OPEN: | ||
429 | MU_DEBUG2 (mailer->debug, MU_DEBUG_PROT, | ||
430 | "smtp_open (host: %s port: %ld)\n", smtp->mailhost, port); | ||
431 | status = mu_stream_open (mailer->stream); | ||
432 | CHECK_EAGAIN (smtp, status); | ||
433 | smtp->state = SMTP_GREETINGS; | ||
434 | |||
435 | case SMTP_GREETINGS: | ||
436 | /* Swallow the greetings. */ | ||
437 | status = smtp_read_ack (smtp); | ||
438 | CHECK_EAGAIN (smtp, status); | ||
439 | |||
440 | if (smtp->buffer[0] != '2') | ||
441 | { | ||
442 | mu_stream_close (mailer->stream); | ||
443 | return EACCES; | ||
444 | } | ||
445 | |||
446 | ehlo: | ||
447 | status = smtp_writeline (smtp, "EHLO %s\r\n", smtp->localhost); | ||
448 | CHECK_ERROR (smtp, status); | ||
449 | |||
450 | smtp->state = SMTP_EHLO; | ||
451 | |||
452 | case SMTP_EHLO: | ||
453 | /* We first try Extended SMTP. */ | ||
454 | status = smtp_write (smtp); | ||
455 | CHECK_EAGAIN (smtp, status); | ||
456 | smtp->state = SMTP_EHLO_ACK; | ||
457 | |||
458 | case SMTP_EHLO_ACK: | ||
459 | status = smtp_parse_ehlo_ack (smtp); | ||
460 | CHECK_EAGAIN (smtp, status); | ||
461 | |||
462 | if (smtp->buffer[0] != '2') | ||
463 | { | ||
464 | smtp->extended = 0; | ||
465 | status = smtp_writeline (smtp, "HELO %s\r\n", smtp->localhost); | ||
466 | CHECK_ERROR (smtp, status); | ||
467 | smtp->state = SMTP_HELO; | ||
468 | } | ||
469 | else | ||
470 | { | ||
471 | smtp->extended = 1; | ||
472 | |||
473 | if (smtp->capa & CAPA_STARTTLS) | ||
474 | smtp->state = SMTP_STARTTLS; | ||
475 | else if (smtp->capa & CAPA_AUTH && mailer->url->user) | ||
476 | { | ||
477 | smtp->state = SMTP_AUTH; | ||
478 | } | ||
479 | else | ||
480 | break; | ||
481 | } | ||
482 | |||
483 | case SMTP_STARTTLS: | ||
484 | case SMTP_STARTTLS_ACK: | ||
485 | if ((smtp->capa & CAPA_STARTTLS) && smtp_starttls (smtp) == 0) | ||
486 | goto ehlo; | ||
487 | 144 | ||
488 | case SMTP_AUTH: | 145 | for (i = 0; i < parmc; i++) |
489 | case SMTP_AUTH_ACK: | ||
490 | if (smtp->capa & CAPA_AUTH) | ||
491 | { | ||
492 | smtp_auth (smtp); | ||
493 | break; | ||
494 | } | ||
495 | |||
496 | case SMTP_HELO: | ||
497 | if (!smtp->extended) /* FIXME: this will always be false! */ | ||
498 | { | ||
499 | status = smtp_write (smtp); | ||
500 | CHECK_EAGAIN (smtp, status); | ||
501 | } | ||
502 | smtp->state = SMTP_HELO_ACK; | ||
503 | |||
504 | case SMTP_HELO_ACK: | ||
505 | if (!smtp->extended) | ||
506 | { | 146 | { |
507 | status = smtp_read_ack (smtp); | 147 | if (strcmp (parmv[i], "notls") == 0) |
508 | CHECK_EAGAIN (smtp, status); | 148 | notls = 1; |
509 | 149 | else if (strcmp (parmv[i], "noauth") == 0) | |
510 | if (smtp->buffer[0] != '2') | 150 | noauth = 1; |
151 | else if (strncmp (parmv[i], "auth=", 5) == 0) | ||
511 | { | 152 | { |
512 | mu_stream_close (mailer->stream); | 153 | int mc, j; |
513 | CLEAR_STATE (smtp); | 154 | char **mv; |
514 | return EACCES; | 155 | |
156 | rc = mu_argcv_get_np (parmv[i] + 5, strlen (parmv[i] + 5), | ||
157 | ",", NULL, | ||
158 | 0, | ||
159 | &mc, &mv, NULL); | ||
160 | if (rc == 0) | ||
161 | for (j = 0; j < mc; j++) | ||
162 | mu_smtp_add_auth_mech (smtp_mailer->smtp, mv[j]); | ||
163 | |||
164 | free (mv); | ||
515 | } | 165 | } |
166 | /* unrecognized arguments silently ignored */ | ||
516 | } | 167 | } |
517 | |||
518 | default: | ||
519 | break; | ||
520 | } | 168 | } |
521 | |||
522 | CLEAR_STATE (smtp); | ||
523 | |||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static int | ||
528 | smtp_close (mu_mailer_t mailer) | ||
529 | { | ||
530 | smtp_t smtp = mailer->data; | ||
531 | int status; | ||
532 | |||
533 | switch (smtp->state) | ||
534 | { | ||
535 | case SMTP_NO_STATE: | ||
536 | status = smtp_writeline (smtp, "QUIT\r\n"); | ||
537 | CHECK_ERROR (smtp, status); | ||
538 | |||
539 | smtp->state = SMTP_QUIT; | ||
540 | |||
541 | case SMTP_QUIT: | ||
542 | status = smtp_write (smtp); | ||
543 | CHECK_EAGAIN (smtp, status); | ||
544 | smtp->state = SMTP_QUIT_ACK; | ||
545 | |||
546 | case SMTP_QUIT_ACK: | ||
547 | status = smtp_read_ack (smtp); | ||
548 | CHECK_EAGAIN (smtp, status); | ||
549 | |||
550 | default: | ||
551 | break; | ||
552 | } | ||
553 | smtp->state = SMTP_NO_STATE; | ||
554 | return mu_stream_close (mailer->stream); | ||
555 | } | ||
556 | |||
557 | /* | ||
558 | Client side STARTTLS support. | ||
559 | */ | ||
560 | |||
561 | static int | ||
562 | smtp_starttls (smtp_t smtp) | ||
563 | { | ||
564 | #ifdef WITH_TLS | ||
565 | int status; | ||
566 | mu_mailer_t mailer = smtp->mailer; | ||
567 | mu_stream_t newstr; | ||
568 | 169 | ||
569 | if (!mu_tls_enable || !(smtp->capa & CAPA_STARTTLS)) | 170 | if (mailer->stream == NULL) |
570 | return -1; | ||
571 | |||
572 | smtp->capa = 0; | ||
573 | smtp->auth_mechs = 0; | ||
574 | |||
575 | status = smtp_writeline (smtp, "STARTTLS\r\n"); | ||
576 | CHECK_ERROR (smtp, status); | ||
577 | status = smtp_write (smtp); | ||
578 | CHECK_EAGAIN (smtp, status); | ||
579 | status = smtp_read_ack (smtp); | ||
580 | CHECK_ERROR (smtp, status); | ||
581 | mu_stream_flush (mailer->stream); | ||
582 | status = mu_tls_client_stream_create (&newstr, mailer->stream, | ||
583 | mailer->stream, 0); | ||
584 | CHECK_ERROR (smtp, status); | ||
585 | status = mu_stream_open (newstr); | ||
586 | MU_DEBUG1 (mailer->debug, MU_DEBUG_PROT, "TLS negotiation %s\n", | ||
587 | status == 0 ? "succeeded" : "failed"); | ||
588 | CHECK_ERROR (smtp, status); | ||
589 | |||
590 | mailer->stream = newstr; | ||
591 | |||
592 | return status; | ||
593 | #else | ||
594 | return -1; | ||
595 | #endif /* WITH_TLS */ | ||
596 | } | ||
597 | |||
598 | static void | ||
599 | cram_md5 (char *secret, unsigned char *challenge, size_t challenge_len, | ||
600 | unsigned char *digest) | ||
601 | { | ||
602 | struct mu_md5_ctx context; | ||
603 | unsigned char ipad[64]; | ||
604 | unsigned char opad[64]; | ||
605 | int secret_len; | ||
606 | int i; | ||
607 | |||
608 | if (secret == 0 || challenge == 0) | ||
609 | return; | ||
610 | |||
611 | secret_len = strlen (secret); | ||
612 | memset (ipad, 0, sizeof (ipad)); | ||
613 | memset (opad, 0, sizeof (opad)); | ||
614 | |||
615 | if (secret_len > 64) | ||
616 | { | ||
617 | mu_md5_init_ctx (&context); | ||
618 | mu_md5_process_bytes ((unsigned char *) secret, secret_len, &context); | ||
619 | mu_md5_finish_ctx (&context, ipad); | ||
620 | mu_md5_finish_ctx (&context, opad); | ||
621 | } | ||
622 | else | ||
623 | { | ||
624 | memcpy (ipad, secret, secret_len); | ||
625 | memcpy (opad, secret, secret_len); | ||
626 | } | ||
627 | |||
628 | for (i = 0; i < 64; i++) | ||
629 | { | ||
630 | ipad[i] ^= 0x36; | ||
631 | opad[i] ^= 0x5c; | ||
632 | } | ||
633 | |||
634 | mu_md5_init_ctx (&context); | ||
635 | mu_md5_process_bytes (ipad, sizeof (ipad), &context); | ||
636 | mu_md5_process_bytes (challenge, challenge_len, &context); | ||
637 | mu_md5_finish_ctx (&context, digest); | ||
638 | |||
639 | mu_md5_init_ctx (&context); | ||
640 | mu_md5_process_bytes (opad, sizeof (opad), &context); | ||
641 | mu_md5_process_bytes (digest, 16, &context); | ||
642 | mu_md5_finish_ctx (&context, digest); | ||
643 | } | ||
644 | |||
645 | static int | ||
646 | smtp_auth (smtp_t smtp) | ||
647 | { | ||
648 | int status; | ||
649 | mu_mailer_t mailer = smtp->mailer; | ||
650 | struct auth_mech_record *mechs = auth_mech_list; | ||
651 | const char *chosen_mech_name = NULL; | ||
652 | int chosen_mech_id = 0; | ||
653 | |||
654 | status = mu_url_sget_auth (mailer->url, &chosen_mech_name); | ||
655 | if (status != MU_ERR_NOENT) | ||
656 | { | 171 | { |
657 | for (; mechs->name; mechs++) | 172 | rc = mu_tcp_stream_create (&mailer->stream, host, port, mailer->flags); |
658 | { | 173 | if (rc) |
659 | if (!mu_c_strcasecmp (mechs->name, chosen_mech_name)) | 174 | return rc; |
660 | { | 175 | mu_stream_set_buffer (mailer->stream, mu_buffer_line, 0); |
661 | chosen_mech_id = mechs->id; | 176 | rc = mu_stream_open (mailer->stream); |
662 | break; | 177 | if (rc) |
663 | } | 178 | return rc; |
664 | } | ||
665 | } | 179 | } |
666 | if (chosen_mech_id) | 180 | mu_smtp_set_carrier (smtp_mailer->smtp, mailer->stream); |
667 | { | 181 | /* FIXME: Unref the stream */ |
668 | if (smtp->auth_mechs & chosen_mech_id) | ||
669 | { | ||
670 | smtp->auth_mechs = 0; | ||
671 | smtp->auth_mechs |= chosen_mech_id; | ||
672 | } | ||
673 | else | ||
674 | { | ||
675 | MU_DEBUG1 (mailer->debug, MU_DEBUG_ERROR, | ||
676 | "mailer does not support AUTH '%s' mechanism\n", | ||
677 | chosen_mech_name); | ||
678 | return -1; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | #if 0 && defined(WITH_GSASL) | ||
683 | 182 | ||
684 | /* FIXME: Add GNU SASL support. */ | 183 | rc = mu_smtp_open (smtp_mailer->smtp); |
184 | if (rc) | ||
185 | return rc; | ||
685 | 186 | ||
686 | #else | 187 | rc = mu_smtp_ehlo (smtp_mailer->smtp); |
687 | 188 | if (rc) | |
688 | /* Provide basic AUTH mechanisms when GSASL is not enabled. */ | 189 | return rc; |
689 | 190 | ||
690 | if (smtp->auth_mechs & AUTH_CRAM_MD5) | 191 | if (!notls && mu_tls_enable && |
192 | mu_smtp_capa_test (smtp_mailer->smtp, "STARTTLS", NULL) == 0) | ||
691 | { | 193 | { |
692 | int i; | 194 | rc = mu_smtp_starttls (smtp_mailer->smtp); |
693 | char *p, *buf = NULL; | 195 | if (rc == 0) |
694 | const char *user = NULL; | ||
695 | mu_secret_t secret; | ||
696 | unsigned char *chl; | ||
697 | size_t chlen, buflen = 0, b64buflen = 0; | ||
698 | unsigned char *b64buf = NULL; | ||
699 | unsigned char digest[16]; | ||
700 | static char ascii_digest[33]; | ||
701 | |||
702 | memset (digest, 0, 16); | ||
703 | |||
704 | status = mu_url_sget_user (mailer->url, &user); | ||
705 | if (status == MU_ERR_NOENT) | ||
706 | return -1; | ||
707 | |||
708 | status = mu_url_get_secret (mailer->url, &secret); | ||
709 | if (status == MU_ERR_NOENT) | ||
710 | { | 196 | { |
711 | MU_DEBUG (mailer->debug, MU_DEBUG_ERROR, | 197 | rc = mu_smtp_ehlo (smtp_mailer->smtp); |
712 | "AUTH CRAM-MD5 mechanism requires giving a password\n"); | 198 | if (rc) |
713 | return -1; | 199 | return rc; |
714 | } | 200 | } |
715 | |||
716 | status = smtp_writeline (smtp, "AUTH CRAM-MD5\r\n"); | ||
717 | CHECK_ERROR (smtp, status); | ||
718 | status = smtp_write (smtp); | ||
719 | CHECK_EAGAIN (smtp, status); | ||
720 | status = smtp_read_ack (smtp); | ||
721 | CHECK_EAGAIN (smtp, status); | ||
722 | |||
723 | if (strncmp (smtp->buffer, "334 ", 4)) | ||
724 | { | ||
725 | MU_DEBUG (mailer->debug, MU_DEBUG_ERROR, | ||
726 | "mailer rejected the AUTH CRAM-MD5 command\n"); | ||
727 | return -1; | ||
728 | } | ||
729 | |||
730 | p = strchr (smtp->buffer, ' ') + 1; | ||
731 | mu_rtrim_cset (p, "\r\n"); | ||
732 | mu_base64_decode ((unsigned char*) p, strlen (p), &chl, &chlen); | ||
733 | |||
734 | cram_md5 ((char *) mu_secret_password (secret), chl, chlen, digest); | ||
735 | mu_secret_password_unref (secret); | ||
736 | free (chl); | ||
737 | |||
738 | for (i = 0; i < 16; i++) | ||
739 | sprintf (ascii_digest + 2 * i, "%02x", digest[i]); | ||
740 | |||
741 | mu_asnprintf (&buf, &buflen, "%s %s", user, ascii_digest); | ||
742 | buflen = strlen (buf); | ||
743 | mu_base64_encode ((unsigned char*) buf, buflen, &b64buf, &b64buflen); | ||
744 | free (buf); | ||
745 | |||
746 | status = smtp_writeline (smtp, "%s\r\n", b64buf); | ||
747 | CHECK_ERROR (smtp, status); | ||
748 | status = smtp_write (smtp); | ||
749 | CHECK_EAGAIN (smtp, status); | ||
750 | status = smtp_read_ack (smtp); | ||
751 | CHECK_EAGAIN (smtp, status); | ||
752 | } | 201 | } |
753 | 202 | ||
754 | else if (smtp->auth_mechs & AUTH_PLAIN) | 203 | if (!noauth && mu_smtp_capa_test (smtp_mailer->smtp, "AUTH", NULL) == 0) |
755 | { | 204 | { |
756 | int c; | 205 | rc = mu_smtp_auth (smtp_mailer->smtp); |
757 | char *buf = NULL; | 206 | if (rc) |
758 | unsigned char *b64buf = NULL; | 207 | return rc; |
759 | size_t buflen = 0, b64buflen = 0; | 208 | rc = mu_smtp_ehlo (smtp_mailer->smtp); |
760 | const char *user = NULL; | 209 | if (rc) |
761 | mu_secret_t secret; | 210 | return rc; |
762 | |||
763 | status = mu_url_sget_user (mailer->url, &user); | ||
764 | if (status == MU_ERR_NOENT) | ||
765 | return -1; | ||
766 | |||
767 | status = mu_url_get_secret (mailer->url, &secret); | ||
768 | if (status == MU_ERR_NOENT) | ||
769 | { | ||
770 | MU_DEBUG (mailer->debug, MU_DEBUG_ERROR, | ||
771 | "AUTH PLAIN mechanism requires giving a password\n"); | ||
772 | return -1; | ||
773 | } | ||
774 | |||
775 | mu_asnprintf (&buf, &buflen, "^%s^%s", | ||
776 | user, mu_secret_password (secret)); | ||
777 | mu_secret_password_unref (secret); | ||
778 | buflen = strlen (buf); | ||
779 | for (c = buflen - 1; c >= 0; c--) | ||
780 | { | ||
781 | if (buf[c] == '^') | ||
782 | buf[c] = '\0'; | ||
783 | } | ||
784 | mu_base64_encode ((unsigned char*) buf, buflen, &b64buf, &b64buflen); | ||
785 | free (buf); | ||
786 | |||
787 | status = smtp_writeline (smtp, "AUTH PLAIN %s\r\n", b64buf); | ||
788 | CHECK_ERROR (smtp, status); | ||
789 | status = smtp_write (smtp); | ||
790 | CHECK_EAGAIN (smtp, status); | ||
791 | status = smtp_read_ack (smtp); | ||
792 | CHECK_EAGAIN (smtp, status); | ||
793 | } | ||
794 | |||
795 | #endif /* not WITH_GSASL */ | ||
796 | return 0; | ||
797 | } | ||
798 | |||
799 | static int | ||
800 | message_set_header_value (mu_message_t msg, const char *field, | ||
801 | const char *value) | ||
802 | { | ||
803 | int status = 0; | ||
804 | mu_header_t hdr = NULL; | ||
805 | |||
806 | if ((status = mu_message_get_header (msg, &hdr))) | ||
807 | return status; | ||
808 | |||
809 | if ((status = mu_header_set_value (hdr, field, value, 1))) | ||
810 | return status; | ||
811 | |||
812 | return status; | ||
813 | } | ||
814 | |||
815 | static int | ||
816 | message_has_bcc (mu_message_t msg) | ||
817 | { | ||
818 | int status; | ||
819 | mu_header_t header = NULL; | ||
820 | size_t bccsz = 0; | ||
821 | |||
822 | if ((status = mu_message_get_header (msg, &header))) | ||
823 | return status; | ||
824 | |||
825 | status = mu_header_get_value (header, MU_HEADER_BCC, NULL, 0, &bccsz); | ||
826 | |||
827 | /* MU_ERR_NOENT, or there was a Bcc: field. */ | ||
828 | return status == MU_ERR_NOENT ? 0 : 1; | ||
829 | } | ||
830 | |||
831 | static int | ||
832 | send_header (smtp_t smtp, mu_stream_t stream) | ||
833 | { | ||
834 | int status; | ||
835 | int found_nl; | ||
836 | char data[256] = ""; | ||
837 | size_t n = 0; | ||
838 | |||
839 | while ((status = mu_stream_readline (stream, data, sizeof (data), | ||
840 | &n)) == 0 && n > 0) | ||
841 | { | ||
842 | int nl; | ||
843 | |||
844 | found_nl = (n == 1 && data[0] == '\n'); | ||
845 | if ((nl = (data[n - 1] == '\n'))) | ||
846 | data[n - 1] = '\0'; | ||
847 | if (data[0] == '.') | ||
848 | { | ||
849 | status = smtp_writeline (smtp, ".%s", data); | ||
850 | CHECK_ERROR (smtp, status); | ||
851 | } | ||
852 | else if (mu_c_strncasecmp (data, MU_HEADER_FCC, | ||
853 | sizeof (MU_HEADER_FCC) - 1)) | ||
854 | { | ||
855 | status = smtp_writeline (smtp, "%s", data); | ||
856 | CHECK_ERROR (smtp, status); | ||
857 | status = smtp_write (smtp); | ||
858 | CHECK_EAGAIN (smtp, status); | ||
859 | } | ||
860 | else | ||
861 | nl = 0; | ||
862 | |||
863 | if (nl) | ||
864 | { | ||
865 | status = smtp_writeline (smtp, "\r\n"); | ||
866 | CHECK_ERROR (smtp, status); | ||
867 | status = smtp_write (smtp); | ||
868 | CHECK_EAGAIN (smtp, status); | ||
869 | } | ||
870 | } | ||
871 | |||
872 | if (!found_nl) | ||
873 | { | ||
874 | status = smtp_writeline (smtp, "\r\n"); | ||
875 | CHECK_ERROR (smtp, status); | ||
876 | status = smtp_write (smtp); | ||
877 | CHECK_EAGAIN (smtp, status); | ||
878 | } | 211 | } |
212 | |||
879 | return 0; | 213 | return 0; |
880 | } | 214 | } |
881 | 215 | ||
882 | static int | 216 | static void |
883 | send_body (smtp_t smtp, mu_stream_t stream) | 217 | smtp_destroy (mu_mailer_t mailer) |
884 | { | 218 | { |
885 | int status; | 219 | struct _smtp_mailer *smp = mailer->data; |
886 | char data[256] = ""; | ||
887 | size_t n = 0; | ||
888 | |||
889 | while ((status = mu_stream_readline (stream, data, sizeof (data) - 1, | ||
890 | &n)) == 0 && n > 0) | ||
891 | { | ||
892 | if (data[n - 1] == '\n') | ||
893 | data[n - 1] = '\0'; | ||
894 | if (data[0] == '.') | ||
895 | status = smtp_writeline (smtp, ".%s\r\n", data); | ||
896 | else | ||
897 | status = smtp_writeline (smtp, "%s\r\n", data); | ||
898 | CHECK_ERROR (smtp, status); | ||
899 | status = smtp_write (smtp); | ||
900 | CHECK_EAGAIN (smtp, status); | ||
901 | } | ||
902 | |||
903 | status = smtp_writeline (smtp, ".\r\n"); | ||
904 | CHECK_ERROR (smtp, status); | ||
905 | smtp->state = SMTP_SEND_DOT; | ||
906 | return 0; | ||
907 | } | ||
908 | 220 | ||
909 | /* | 221 | mu_address_destroy (&smp->rcpt_to); |
910 | 222 | mu_address_destroy (&smp->rcpt_bcc); | |
911 | The smtp mailer doesn't deal with mail like: | 223 | mu_smtp_destroy (&smp->smtp); |
912 | |||
913 | To: public@com, pub2@com | ||
914 | Bcc: hidden@there, two@there | ||
915 | |||
916 | It just sends the message to all the addresses, making the | ||
917 | "blind" cc not particularly blind. | ||
918 | |||
919 | The correct algorithm is | ||
920 | |||
921 | - open smtp connection | ||
922 | - look as msg, figure out addrto&cc, and addrbcc | ||
923 | - deliver to the to & cc addresses: | ||
924 | - if there are bcc addrs, remove the bcc field | ||
925 | - send the message to to & cc addrs: | ||
926 | mail from: me | ||
927 | rcpt to: public@com | ||
928 | rcpt to: pub2@com | ||
929 | data | ||
930 | ... | ||
931 | 224 | ||
932 | - deliver to the bcc addrs: | 225 | free (smp); |
933 | 226 | ||
934 | for a in (bccaddrs) | 227 | mailer->data = NULL; |
935 | do | 228 | } |
936 | - add header field to msg, bcc: $a | ||
937 | - send the msg: | ||
938 | mail from: me | ||
939 | rcpt to: $a | ||
940 | data | ||
941 | ... | ||
942 | done | ||
943 | |||
944 | - quit smtp connection | ||
945 | |||
946 | */ | ||
947 | 229 | ||
948 | static int | 230 | static int |
949 | smtp_send_message (mu_mailer_t mailer, mu_message_t argmsg, | 231 | smtp_close (mu_mailer_t mailer) |
950 | mu_address_t argfrom, mu_address_t argto) | ||
951 | { | 232 | { |
952 | smtp_t smtp = NULL; | 233 | struct _smtp_mailer *smp = mailer->data; |
953 | int status; | 234 | return mu_smtp_quit (smp->smtp); |
954 | |||
955 | if (mailer == NULL) | ||
956 | return EINVAL; | ||
957 | |||
958 | smtp = mailer->data; | ||
959 | if (!smtp) | ||
960 | return EINVAL; | ||
961 | |||
962 | CHECK_SEND_RESUME (smtp, argmsg, argfrom, argto); | ||
963 | |||
964 | switch (smtp->state) | ||
965 | { | ||
966 | case SMTP_NO_STATE: | ||
967 | if (argmsg == NULL || argfrom == NULL) | ||
968 | return EINVAL; | ||
969 | |||
970 | smtp->argmsg = smtp->msg = argmsg; | ||
971 | smtp->argfrom = argfrom; | ||
972 | smtp->argto = argto; | ||
973 | |||
974 | status = mu_address_sget_email (smtp->argfrom, 1, &smtp->mail_from); | ||
975 | CHECK_ERROR (smtp, status); | ||
976 | |||
977 | status = _smtp_set_rcpt (smtp, smtp->argmsg, smtp->argto); | ||
978 | CHECK_ERROR (smtp, status); | ||
979 | |||
980 | /* Clear the Bcc: field if we found one. */ | ||
981 | if (message_has_bcc (smtp->argmsg)) | ||
982 | { | ||
983 | smtp->msg = NULL; | ||
984 | status = mu_message_create_copy (&smtp->msg, smtp->argmsg); | ||
985 | CHECK_ERROR (smtp, status); | ||
986 | |||
987 | status = message_set_header_value (smtp->msg, MU_HEADER_BCC, NULL); | ||
988 | CHECK_ERROR (smtp, status); | ||
989 | } | ||
990 | |||
991 | /* Begin bccing if there are not To: recipients. */ | ||
992 | if (smtp->rcpt_to_count == 0) | ||
993 | smtp->bccing = 1; | ||
994 | |||
995 | smtp->rcpt_index = 1; | ||
996 | |||
997 | smtp->state = SMTP_ENV_FROM; | ||
998 | |||
999 | case SMTP_ENV_FROM: | ||
1000 | ENV_FROM: | ||
1001 | { | ||
1002 | size_t size; | ||
1003 | |||
1004 | if ((smtp->capa & CAPA_SIZE) | ||
1005 | && mu_message_size (smtp->msg, &size) == 0) | ||
1006 | status = smtp_writeline (smtp, "MAIL FROM:<%s> SIZE=%lu\r\n", | ||
1007 | smtp->mail_from, size); | ||
1008 | else | ||
1009 | status = smtp_writeline (smtp, "MAIL FROM:<%s>\r\n", | ||
1010 | smtp->mail_from); | ||
1011 | CHECK_ERROR (smtp, status); | ||
1012 | smtp->state = SMTP_MAIL_FROM; | ||
1013 | } | ||
1014 | |||
1015 | /* We use a goto, since we may have multiple messages, | ||
1016 | we come back here and doit all over again ... Not pretty. */ | ||
1017 | case SMTP_MAIL_FROM: | ||
1018 | status = smtp_write (smtp); | ||
1019 | CHECK_EAGAIN (smtp, status); | ||
1020 | smtp->state = SMTP_MAIL_FROM_ACK; | ||
1021 | |||
1022 | case SMTP_MAIL_FROM_ACK: | ||
1023 | status = smtp_read_ack (smtp); | ||
1024 | CHECK_EAGAIN (smtp, status); | ||
1025 | if (smtp->buffer[0] != '2') | ||
1026 | { | ||
1027 | mu_stream_close (mailer->stream); | ||
1028 | CLEAR_STATE (smtp); | ||
1029 | return EACCES; | ||
1030 | } | ||
1031 | |||
1032 | /* We use a goto, since we may have multiple recipients, | ||
1033 | we come back here and do it all over again ... Not pretty. */ | ||
1034 | case SMTP_ENV_RCPT: | ||
1035 | ENV_RCPT: | ||
1036 | { | ||
1037 | mu_address_t addr = smtp->rcpt_to; | ||
1038 | const char *to = NULL; | ||
1039 | |||
1040 | if (smtp->bccing) | ||
1041 | addr = smtp->rcpt_bcc; | ||
1042 | status = mu_address_sget_email (addr, smtp->rcpt_index, &to); | ||
1043 | |||
1044 | CHECK_ERROR (smtp, status); | ||
1045 | |||
1046 | /* Add the Bcc: field back in for recipient. */ | ||
1047 | if (smtp->bccing) | ||
1048 | { | ||
1049 | status = message_set_header_value (smtp->msg, MU_HEADER_BCC, to); | ||
1050 | CHECK_ERROR (smtp, status); | ||
1051 | } | ||
1052 | |||
1053 | status = smtp_writeline (smtp, "RCPT TO:<%s>\r\n", to); | ||
1054 | |||
1055 | CHECK_ERROR (smtp, status); | ||
1056 | |||
1057 | smtp->state = SMTP_RCPT_TO; | ||
1058 | smtp->rcpt_index++; | ||
1059 | } | ||
1060 | |||
1061 | case SMTP_RCPT_TO: | ||
1062 | status = smtp_write (smtp); | ||
1063 | CHECK_EAGAIN (smtp, status); | ||
1064 | smtp->state = SMTP_RCPT_TO_ACK; | ||
1065 | |||
1066 | case SMTP_RCPT_TO_ACK: | ||
1067 | status = smtp_read_ack (smtp); | ||
1068 | CHECK_EAGAIN (smtp, status); | ||
1069 | if (smtp->buffer[0] != '2') | ||
1070 | { | ||
1071 | mu_stream_close (mailer->stream); | ||
1072 | CLEAR_STATE (smtp); | ||
1073 | return MU_ERR_SMTP_RCPT_FAILED; | ||
1074 | } | ||
1075 | /* Redo the receipt sequence for every To: and Cc: recipient. */ | ||
1076 | if (!smtp->bccing && smtp->rcpt_index <= smtp->rcpt_to_count) | ||
1077 | goto ENV_RCPT; | ||
1078 | |||
1079 | /* We are done with the rcpt. */ | ||
1080 | status = smtp_writeline (smtp, "DATA\r\n"); | ||
1081 | CHECK_ERROR (smtp, status); | ||
1082 | smtp->state = SMTP_DATA; | ||
1083 | |||
1084 | case SMTP_DATA: | ||
1085 | status = smtp_write (smtp); | ||
1086 | CHECK_EAGAIN (smtp, status); | ||
1087 | smtp->state = SMTP_DATA_ACK; | ||
1088 | |||
1089 | case SMTP_DATA_ACK: | ||
1090 | status = smtp_read_ack (smtp); | ||
1091 | CHECK_EAGAIN (smtp, status); | ||
1092 | if (smtp->buffer[0] != '3') | ||
1093 | { | ||
1094 | mu_stream_close (mailer->stream); | ||
1095 | CLEAR_STATE (smtp); | ||
1096 | return EACCES; | ||
1097 | } | ||
1098 | smtp->state = SMTP_SEND; | ||
1099 | |||
1100 | if ((smtp->mailer->flags & MAILER_FLAG_DEBUG_DATA) == 0) | ||
1101 | MU_DEBUG (smtp->mailer->debug, MU_DEBUG_PROT, "> (data...)\n"); | ||
1102 | |||
1103 | case SMTP_SEND: | ||
1104 | { | ||
1105 | mu_stream_t stream; | ||
1106 | mu_header_t hdr; | ||
1107 | mu_body_t body; | ||
1108 | |||
1109 | /* We may be here after an EAGAIN so check if we have something | ||
1110 | in the buffer and flush it. */ | ||
1111 | status = smtp_write (smtp); | ||
1112 | CHECK_EAGAIN (smtp, status); | ||
1113 | |||
1114 | mu_message_get_header (smtp->msg, &hdr); | ||
1115 | mu_header_get_streamref (hdr, &stream); | ||
1116 | mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); | ||
1117 | status = send_header (smtp, stream); | ||
1118 | mu_stream_destroy (&stream); | ||
1119 | if (status) | ||
1120 | return status; | ||
1121 | |||
1122 | mu_message_get_body (smtp->msg, &body); | ||
1123 | mu_body_get_streamref (body, &stream); | ||
1124 | mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); | ||
1125 | status = send_body (smtp, stream); | ||
1126 | mu_stream_destroy (&stream); | ||
1127 | } | ||
1128 | case SMTP_SEND_DOT: | ||
1129 | status = smtp_write (smtp); | ||
1130 | CHECK_EAGAIN (smtp, status); | ||
1131 | smtp->state = SMTP_SEND_ACK; | ||
1132 | |||
1133 | case SMTP_SEND_ACK: | ||
1134 | status = smtp_read_ack (smtp); | ||
1135 | CHECK_EAGAIN (smtp, status); | ||
1136 | if (smtp->buffer[0] != '2') | ||
1137 | { | ||
1138 | mu_stream_close (mailer->stream); | ||
1139 | CLEAR_STATE (smtp); | ||
1140 | return EACCES; | ||
1141 | } | ||
1142 | |||
1143 | /* Decide whether we need to loop again, to deliver to Bcc: | ||
1144 | recipients. */ | ||
1145 | if (!smtp->bccing) | ||
1146 | { | ||
1147 | smtp->bccing = 1; | ||
1148 | smtp->rcpt_index = 1; | ||
1149 | } | ||
1150 | if (smtp->rcpt_index <= smtp->rcpt_bcc_count) | ||
1151 | goto ENV_FROM; | ||
1152 | |||
1153 | mu_observable_notify (mailer->observable, MU_EVT_MAILER_MESSAGE_SENT, | ||
1154 | argmsg); | ||
1155 | |||
1156 | default: | ||
1157 | break; | ||
1158 | } | ||
1159 | CLEAR_STATE (smtp); | ||
1160 | return 0; | ||
1161 | } | 235 | } |
1162 | 236 | ||
237 | |||
1163 | int | 238 | int |
1164 | smtp_address_add (mu_address_t * paddr, const char *value) | 239 | smtp_address_add (mu_address_t *paddr, const char *value) |
1165 | { | 240 | { |
1166 | mu_address_t addr = NULL; | 241 | mu_address_t addr = NULL; |
1167 | int status; | 242 | int status; |
1168 | 243 | ||
1169 | status = mu_address_create (&addr, value); | 244 | status = mu_address_create (&addr, value); |
1170 | if (status) | 245 | if (status) |
... | @@ -1175,20 +250,19 @@ smtp_address_add (mu_address_t * paddr, const char *value) | ... | @@ -1175,20 +250,19 @@ smtp_address_add (mu_address_t * paddr, const char *value) |
1175 | } | 250 | } |
1176 | 251 | ||
1177 | static int | 252 | static int |
1178 | _smtp_property_is_set (smtp_t smtp, const char *name) | 253 | _smtp_property_is_set (struct _smtp_mailer *smp, const char *name) |
1179 | { | 254 | { |
1180 | mu_property_t property = NULL; | 255 | mu_property_t property = NULL; |
1181 | 256 | ||
1182 | mu_mailer_get_property (smtp->mailer, &property); | 257 | mu_mailer_get_property (smp->mailer, &property); |
1183 | return mu_property_is_set (property, name); | 258 | return mu_property_is_set (property, name); |
1184 | } | 259 | } |
1185 | 260 | ||
261 | |||
1186 | static int | 262 | static int |
1187 | _smtp_set_rcpt (smtp_t smtp, mu_message_t msg, mu_address_t to) | 263 | _smtp_set_rcpt (struct _smtp_mailer *smp, mu_message_t msg, mu_address_t to) |
1188 | { | 264 | { |
1189 | int status = 0; | 265 | int status = 0; |
1190 | mu_header_t header = NULL; | ||
1191 | char *value; | ||
1192 | 266 | ||
1193 | /* Get RCPT_TO from TO, or the message. */ | 267 | /* Get RCPT_TO from TO, or the message. */ |
1194 | 268 | ||
... | @@ -1197,290 +271,220 @@ _smtp_set_rcpt (smtp_t smtp, mu_message_t msg, mu_address_t to) | ... | @@ -1197,290 +271,220 @@ _smtp_set_rcpt (smtp_t smtp, mu_message_t msg, mu_address_t to) |
1197 | /* Use the specified mu_address_t. */ | 271 | /* Use the specified mu_address_t. */ |
1198 | if ((status = mu_mailer_check_to (to)) != 0) | 272 | if ((status = mu_mailer_check_to (to)) != 0) |
1199 | { | 273 | { |
1200 | MU_DEBUG (smtp->mailer->debug, MU_DEBUG_ERROR, | 274 | MU_DEBUG (smp->mailer->debug, MU_DEBUG_ERROR, |
1201 | "mu_mailer_send_message(): explicit to not valid\n"); | 275 | "mu_mailer_send_message(): explicit to not valid\n"); |
1202 | return status; | 276 | return status; |
1203 | } | 277 | } |
1204 | smtp->rcpt_to = to; | 278 | smp->rcpt_to = to; |
1205 | mu_address_get_count (smtp->rcpt_to, &smtp->rcpt_to_count); | ||
1206 | 279 | ||
1207 | if (status) | 280 | if (status) |
1208 | return status; | 281 | return status; |
1209 | } | 282 | } |
1210 | 283 | ||
1211 | if (!to || _smtp_property_is_set (smtp, "READ_RECIPIENTS")) | 284 | if (!to || _smtp_property_is_set (smp, "READ_RECIPIENTS")) |
1212 | { | 285 | { |
286 | mu_header_t header; | ||
287 | |||
1213 | if ((status = mu_message_get_header (msg, &header))) | 288 | if ((status = mu_message_get_header (msg, &header))) |
1214 | return status; | 289 | return status; |
1215 | 290 | ||
1216 | status = mu_header_aget_value (header, MU_HEADER_TO, &value); | 291 | do |
1217 | |||
1218 | if (status == 0) | ||
1219 | { | 292 | { |
1220 | smtp_address_add (&smtp->rcpt_to, value); | 293 | const char *value; |
1221 | free (value); | 294 | |
1222 | } | 295 | status = mu_header_sget_value (header, MU_HEADER_TO, &value); |
1223 | else if (status != MU_ERR_NOENT) | 296 | if (status == 0) |
1224 | goto end; | 297 | smtp_address_add (&smp->rcpt_to, value); |
1225 | 298 | else if (status != MU_ERR_NOENT) | |
1226 | status = mu_header_aget_value (header, MU_HEADER_CC, &value); | 299 | break; |
1227 | 300 | ||
1228 | if (status == 0) | 301 | status = mu_header_sget_value (header, MU_HEADER_CC, &value); |
1229 | { | 302 | if (status == 0) |
1230 | smtp_address_add (&smtp->rcpt_to, value); | 303 | smtp_address_add (&smp->rcpt_to, value); |
1231 | free (value); | 304 | else if (status != MU_ERR_NOENT) |
1232 | } | 305 | break; |
1233 | else if (status != MU_ERR_NOENT) | ||
1234 | goto end; | ||
1235 | 306 | ||
1236 | status = mu_header_aget_value (header, MU_HEADER_BCC, &value); | 307 | status = mu_header_sget_value (header, MU_HEADER_BCC, &value); |
1237 | if (status == 0) | 308 | if (status == 0) |
1238 | { | 309 | smtp_address_add (&smp->rcpt_bcc, value); |
1239 | smtp_address_add (&smtp->rcpt_bcc, value); | 310 | else if (status != MU_ERR_NOENT) |
1240 | free (value); | 311 | break; |
1241 | } | ||
1242 | else if (status != MU_ERR_NOENT) | ||
1243 | goto end; | ||
1244 | 312 | ||
1245 | /* If to or bcc is present, the must be OK. */ | 313 | if (smp->rcpt_to && (status = mu_mailer_check_to (smp->rcpt_to))) |
1246 | if (smtp->rcpt_to && (status = mu_mailer_check_to (smtp->rcpt_to))) | 314 | break; |
1247 | goto end; | ||
1248 | 315 | ||
1249 | if (smtp->rcpt_bcc && (status = mu_mailer_check_to (smtp->rcpt_bcc))) | 316 | if (smp->rcpt_bcc && (status = mu_mailer_check_to (smp->rcpt_bcc))) |
1250 | goto end; | 317 | break; |
318 | } | ||
319 | while (0); | ||
1251 | } | 320 | } |
1252 | 321 | ||
1253 | end: | ||
1254 | |||
1255 | if (status) | 322 | if (status) |
1256 | { | 323 | { |
1257 | mu_address_destroy (&smtp->rcpt_to); | 324 | mu_address_destroy (&smp->rcpt_to); |
1258 | mu_address_destroy (&smtp->rcpt_bcc); | 325 | mu_address_destroy (&smp->rcpt_bcc); |
1259 | } | 326 | } |
1260 | else | 327 | else |
1261 | { | 328 | { |
1262 | if (smtp->rcpt_to) | 329 | size_t rcpt_cnt, bcc_cnt; |
1263 | mu_address_get_count (smtp->rcpt_to, &smtp->rcpt_to_count); | 330 | |
1264 | 331 | if (smp->rcpt_to) | |
1265 | if (smtp->rcpt_bcc) | 332 | mu_address_get_count (smp->rcpt_to, &rcpt_cnt); |
1266 | mu_address_get_count (smtp->rcpt_bcc, &smtp->rcpt_bcc_count); | 333 | |
334 | if (smp->rcpt_bcc) | ||
335 | mu_address_get_count (smp->rcpt_bcc, &bcc_cnt); | ||
1267 | 336 | ||
1268 | if (smtp->rcpt_to_count + smtp->rcpt_bcc_count == 0) | 337 | if (rcpt_cnt + bcc_cnt == 0) |
1269 | status = MU_ERR_MAILER_NO_RCPT_TO; | 338 | status = MU_ERR_MAILER_NO_RCPT_TO; |
1270 | } | 339 | } |
1271 | 340 | ||
1272 | return status; | 341 | return status; |
1273 | } | 342 | } |
1274 | 343 | ||
1275 | /* C99 says that a conforming implementations of snprintf () | ||
1276 | should return the number of char that would have been call | ||
1277 | but many GNU/Linux && BSD implementations return -1 on error. | ||
1278 | Worse QNX/Neutrino actually does not put the terminal | ||
1279 | null char. So let's try to cope. */ | ||
1280 | static int | 344 | static int |
1281 | smtp_writeline (smtp_t smtp, const char *format, ...) | 345 | _rcpt_to_addr (mu_smtp_t smtp, mu_address_t addr, size_t *pcount) |
1282 | { | 346 | { |
1283 | int len; | 347 | size_t i, count, rcpt_cnt = 0; |
1284 | va_list ap; | 348 | int status; |
1285 | int done = 1; | 349 | |
1286 | 350 | status = mu_address_get_count (addr, &count); | |
1287 | va_start (ap, format); | 351 | if (status) |
1288 | do | 352 | return status; |
1289 | { | ||
1290 | len = vsnprintf (smtp->buffer, smtp->buflen - 1, format, ap); | ||
1291 | if (len < 0 || (len >= (int) smtp->buflen) | ||
1292 | || !memchr (smtp->buffer, '\0', len + 1)) | ||
1293 | { | ||
1294 | char *buffer = NULL; | ||
1295 | size_t buflen = smtp->buflen * 2; | ||
1296 | |||
1297 | buffer = realloc (smtp->buffer, buflen); | ||
1298 | if (smtp->buffer == NULL) | ||
1299 | return ENOMEM; | ||
1300 | smtp->buffer = buffer; | ||
1301 | smtp->buflen = buflen; | ||
1302 | done = 0; | ||
1303 | } | ||
1304 | else | ||
1305 | done = 1; | ||
1306 | } | ||
1307 | while (!done); | ||
1308 | |||
1309 | va_end (ap); | ||
1310 | |||
1311 | smtp->ptr = smtp->buffer + len; | ||
1312 | 353 | ||
1313 | if ((smtp->state != SMTP_SEND && smtp->state != SMTP_SEND_DOT) | 354 | for (i = 1; i <= count; i++) |
1314 | || smtp->mailer->flags & MAILER_FLAG_DEBUG_DATA) | ||
1315 | { | 355 | { |
1316 | while (len > 0 && mu_isblank (smtp->buffer[len - 1])) | 356 | const char *to = NULL; |
1317 | len--; | ||
1318 | MU_DEBUG2 (smtp->mailer->debug, MU_DEBUG_PROT, "> %.*s\n", len, | ||
1319 | smtp->buffer); | ||
1320 | } | ||
1321 | |||
1322 | return 0; | ||
1323 | } | ||
1324 | 357 | ||
1325 | static int | 358 | status = mu_address_sget_email (addr, i, &to); |
1326 | smtp_write (smtp_t smtp) | ||
1327 | { | ||
1328 | int status = 0; | ||
1329 | size_t len; | ||
1330 | |||
1331 | if (smtp->ptr > smtp->buffer) | ||
1332 | { | ||
1333 | len = smtp->ptr - smtp->buffer; | ||
1334 | status = mu_stream_write (smtp->mailer->stream, smtp->buffer, len, NULL); | ||
1335 | if (status == 0) | 359 | if (status == 0) |
1336 | memmove (smtp->buffer, smtp->buffer + len, len); | 360 | { |
1337 | } | 361 | status = mu_smtp_rcpt_basic (smtp, to, NULL); |
1338 | else | 362 | if (status == 0) |
1339 | { | 363 | rcpt_cnt++; |
1340 | smtp->ptr = smtp->buffer; | 364 | else if (status != MU_ERR_REPLY) |
1341 | len = 0; | 365 | break; |
366 | } | ||
1342 | } | 367 | } |
368 | *pcount = rcpt_cnt; | ||
1343 | return status; | 369 | return status; |
1344 | } | 370 | } |
1345 | 371 | ||
1346 | static int | 372 | static int |
1347 | smtp_read_ack (smtp_t smtp) | 373 | smtp_send_message (mu_mailer_t mailer, mu_message_t msg, |
374 | mu_address_t argfrom, mu_address_t argto) | ||
1348 | { | 375 | { |
1349 | int status; | 376 | struct _smtp_mailer *smp = mailer->data; |
1350 | int multi; | 377 | mu_smtp_t smtp = smp->smtp; |
1351 | 378 | int status; | |
1352 | do | 379 | size_t size, lines, count; |
1353 | { | 380 | const char *mail_from; |
1354 | multi = 0; | 381 | mu_header_t header; |
1355 | status = smtp_readline (smtp); | 382 | |
1356 | if ((smtp->ptr - smtp->buffer) > 4 && smtp->buffer[3] == '-') | 383 | if (mailer == NULL) |
1357 | multi = 1; | 384 | return EINVAL; |
1358 | if (status == 0) | 385 | |
1359 | smtp->ptr = smtp->buffer; | 386 | smp = mailer->data; |
1360 | } | 387 | if (!smp) |
1361 | while (multi && status == 0); | 388 | return EINVAL; |
1362 | 389 | ||
1363 | if (status == 0) | 390 | if ((status = mu_message_get_header (msg, &header))) |
1364 | smtp->ptr = smtp->buffer; | 391 | return status; |
1365 | return status; | 392 | |
1366 | } | 393 | status = _smtp_set_rcpt (smp, msg, argto); |
394 | if (status) | ||
395 | return status; | ||
1367 | 396 | ||
1368 | static int | 397 | status = mu_address_sget_email (argfrom, 1, &mail_from); |
1369 | smtp_parse_ehlo_ack (smtp_t smtp) | 398 | if (status) |
1370 | { | 399 | return status; |
1371 | int status; | 400 | |
1372 | int multi; | 401 | if (mu_smtp_capa_test (smp->smtp, "SIZE", NULL) == 0 && |
402 | mu_message_size (msg, &size) == 0 && | ||
403 | mu_message_lines (msg, &lines) == 0) | ||
404 | status = mu_smtp_mail_basic (smp->smtp, mail_from, | ||
405 | "SIZE=%lu", | ||
406 | (unsigned long) (size + lines)); | ||
407 | else | ||
408 | status = mu_smtp_mail_basic (smp->smtp, mail_from, NULL); | ||
409 | if (status) | ||
410 | return status; | ||
1373 | 411 | ||
1374 | smtp->ptr = smtp->buffer; | 412 | status = _rcpt_to_addr (smtp, smp->rcpt_to, &count); |
413 | if (status && count == 0) | ||
414 | return status; | ||
415 | status = _rcpt_to_addr (smtp, smp->rcpt_bcc, &count); | ||
416 | if (status && count == 0) | ||
417 | return status; | ||
1375 | 418 | ||
1376 | do | 419 | if (mu_header_sget_value (header, MU_HEADER_BCC, NULL)) |
1377 | { | 420 | { |
1378 | multi = 0; | 421 | mu_iterator_t itr; |
1379 | status = smtp_readline (smtp); | 422 | mu_body_t body; |
1380 | if ((smtp->ptr - smtp->buffer) > 4 && smtp->buffer[3] == '-') | 423 | mu_stream_t ostr, bstr; |
1381 | multi = 1; | ||
1382 | if (status == 0 && memcmp (smtp->buffer, "250", 3) == 0) | ||
1383 | { | ||
1384 | char *capa_str = smtp->buffer + 4; | ||
1385 | |||
1386 | smtp->ptr = smtp->buffer; | ||
1387 | 424 | ||
1388 | if (!mu_c_strncasecmp (capa_str, "STARTTLS", 8)) | 425 | status = mu_smtp_data (smtp, &ostr); |
1389 | smtp->capa |= CAPA_STARTTLS; | 426 | if (status) |
1390 | else if (!mu_c_strncasecmp (capa_str, "SIZE", 4)) | 427 | return status; |
1391 | { | 428 | mu_header_get_iterator (header, &itr); |
1392 | char *p; | 429 | for (mu_iterator_first (itr); !mu_iterator_is_done (itr); |
1393 | size_t n; | 430 | mu_iterator_next (itr)) |
1394 | 431 | { | |
1395 | smtp->capa |= CAPA_SIZE; | 432 | const char *name; |
1396 | 433 | void *value; | |
1397 | n = strtoul (capa_str + 5, &p, 10); | ||
1398 | if (*p != '\n') | ||
1399 | MU_DEBUG1 (smtp->mailer->debug, MU_DEBUG_ERROR, | ||
1400 | "suspicious size capability: %s", | ||
1401 | smtp->buffer); | ||
1402 | else | ||
1403 | smtp->max_size = n; | ||
1404 | } | ||
1405 | else if (!mu_c_strncasecmp (capa_str, "AUTH", 4)) | ||
1406 | { | ||
1407 | char *name, *s; | ||
1408 | |||
1409 | smtp->capa |= CAPA_AUTH; | ||
1410 | |||
1411 | for (name = strtok_r (capa_str + 5, " ", &s); name; | ||
1412 | name = strtok_r (NULL, " ", &s)) | ||
1413 | { | ||
1414 | struct auth_mech_record *mechs = auth_mech_list; | ||
1415 | |||
1416 | mu_rtrim_cset (name, "\r\n"); | ||
1417 | for (; mechs->name; mechs++) | ||
1418 | { | ||
1419 | if (!mu_c_strcasecmp (mechs->name, name)) | ||
1420 | { | ||
1421 | smtp->auth_mechs |= mechs->id; | ||
1422 | break; | ||
1423 | } | ||
1424 | } | ||
1425 | } | ||
1426 | } | ||
1427 | 434 | ||
435 | mu_iterator_current_kv (itr, (void*) &name, &value); | ||
436 | if (mu_c_strcasecmp (name, MU_HEADER_BCC) == 0) | ||
437 | continue; | ||
438 | mu_stream_printf (ostr, "%s: %s\n", name, (char*)value); | ||
1428 | } | 439 | } |
440 | mu_iterator_destroy (&itr); | ||
441 | mu_stream_write (ostr, "\n", 1, NULL); | ||
442 | |||
443 | mu_message_get_body (msg, &body); | ||
444 | mu_body_get_streamref (body, &bstr); | ||
445 | mu_stream_copy (ostr, bstr, 0, NULL); | ||
446 | mu_stream_destroy (&bstr); | ||
447 | mu_stream_close (ostr); | ||
448 | mu_stream_destroy (&ostr); | ||
1429 | } | 449 | } |
1430 | while (multi && status == 0); | 450 | else |
1431 | 451 | { | |
1432 | if (status == 0) | 452 | mu_stream_t str; |
1433 | smtp->ptr = smtp->buffer; | 453 | mu_message_get_streamref (msg, &str); |
454 | status = mu_smtp_send_stream (smtp, str); | ||
455 | mu_stream_destroy (&str); | ||
456 | } | ||
457 | mu_smtp_quit (smtp); | ||
458 | mu_address_destroy (&smp->rcpt_to); | ||
459 | mu_address_destroy (&smp->rcpt_bcc); | ||
1434 | return status; | 460 | return status; |
1435 | } | 461 | } |
1436 | 462 | ||
1437 | /* Read a complete line form the pop server. Transform CRLF to LF, | 463 | |
1438 | put a null in the buffer when done. */ | ||
1439 | static int | 464 | static int |
1440 | smtp_readline (smtp_t smtp) | 465 | _mailer_smtp_init (mu_mailer_t mailer) |
1441 | { | 466 | { |
1442 | size_t n = 0; | 467 | struct _smtp_mailer *smp; |
1443 | size_t total = smtp->ptr - smtp->buffer; | ||
1444 | int status; | ||
1445 | 468 | ||
1446 | /* Must get a full line before bailing out. */ | 469 | /* Allocate memory specific to smtp mailer. */ |
1447 | do | 470 | smp = mailer->data = calloc (1, sizeof (*smp)); |
1448 | { | 471 | if (mailer->data == NULL) |
1449 | status = mu_stream_readline (smtp->mailer->stream, smtp->buffer + total, | 472 | return ENOMEM; |
1450 | smtp->buflen - total, &n); | ||
1451 | if (status != 0) | ||
1452 | return status; | ||
1453 | 473 | ||
1454 | /* Server went away, consider this like an error. */ | 474 | smp->mailer = mailer; /* Back pointer. */ |
1455 | if (n == 0) | ||
1456 | return EIO; | ||
1457 | 475 | ||
1458 | total += n; | 476 | mailer->_destroy = smtp_destroy; |
1459 | smtp->nl = memchr (smtp->buffer, '\n', total); | 477 | mailer->_open = smtp_open; |
1460 | if (smtp->nl == NULL) /* Do we have a full line. */ | 478 | mailer->_close = smtp_close; |
1461 | { | 479 | mailer->_send_message = smtp_send_message; |
1462 | /* Allocate a bigger buffer ? */ | ||
1463 | if (total >= smtp->buflen - 1) | ||
1464 | { | ||
1465 | smtp->buflen *= 2; | ||
1466 | smtp->buffer = realloc (smtp->buffer, smtp->buflen + 1); | ||
1467 | if (smtp->buffer == NULL) | ||
1468 | return ENOMEM; | ||
1469 | } | ||
1470 | } | ||
1471 | smtp->ptr = smtp->buffer + total; | ||
1472 | } | ||
1473 | while (smtp->nl == NULL); | ||
1474 | 480 | ||
1475 | /* \r\n --> \n\0 */ | 481 | /* Set our properties. */ |
1476 | if (smtp->nl > smtp->buffer) | 482 | { |
1477 | { | 483 | mu_property_t property = NULL; |
1478 | *(smtp->nl - 1) = '\n'; | ||
1479 | *(smtp->nl) = '\0'; | ||
1480 | smtp->ptr = smtp->nl; | ||
1481 | } | ||
1482 | 484 | ||
1483 | MU_DEBUG1 (smtp->mailer->debug, MU_DEBUG_PROT, "< %s", smtp->buffer); | 485 | mu_mailer_get_property (mailer, &property); |
486 | mu_property_set_value (property, "TYPE", "SMTP", 1); | ||
487 | } | ||
1484 | 488 | ||
1485 | return 0; | 489 | return 0; |
1486 | } | 490 | } | ... | ... |
... | @@ -37,6 +37,8 @@ mu_smtp_quit (mu_smtp_t smtp) | ... | @@ -37,6 +37,8 @@ mu_smtp_quit (mu_smtp_t smtp) |
37 | return EINVAL; | 37 | return EINVAL; |
38 | if (MU_SMTP_FISSET (smtp, _MU_SMTP_ERR)) | 38 | if (MU_SMTP_FISSET (smtp, _MU_SMTP_ERR)) |
39 | return MU_ERR_FAILURE; | 39 | return MU_ERR_FAILURE; |
40 | if (smtp->state == MU_SMTP_CLOS) | ||
41 | return 0; | ||
40 | status = mu_smtp_write (smtp, "QUIT\r\n"); | 42 | status = mu_smtp_write (smtp, "QUIT\r\n"); |
41 | MU_SMTP_CHECK_ERROR (smtp, status); | 43 | MU_SMTP_CHECK_ERROR (smtp, status); |
42 | status = mu_smtp_response (smtp); | 44 | status = mu_smtp_response (smtp); | ... | ... |
... | @@ -23,7 +23,7 @@ | ... | @@ -23,7 +23,7 @@ |
23 | #include <fcntl.h> | 23 | #include <fcntl.h> |
24 | 24 | ||
25 | static int isfilename (const char *); | 25 | static int isfilename (const char *); |
26 | static void msg_to_pipe (const char *cmd, mu_message_t msg); | 26 | static int msg_to_pipe (const char *cmd, mu_message_t msg); |
27 | 27 | ||
28 | 28 | ||
29 | /* Additional message headers */ | 29 | /* Additional message headers */ |
... | @@ -338,6 +338,82 @@ fill_body (mu_message_t msg, FILE *file) | ... | @@ -338,6 +338,82 @@ fill_body (mu_message_t msg, FILE *file) |
338 | return 0; | 338 | return 0; |
339 | } | 339 | } |
340 | 340 | ||
341 | static int | ||
342 | save_dead_message (compose_env_t *env) | ||
343 | { | ||
344 | if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0) | ||
345 | { | ||
346 | FILE *fp = fopen (getenv ("DEAD"), | ||
347 | mailvar_get (NULL, "appenddeadletter", | ||
348 | mailvar_type_boolean, 0) == 0 ? | ||
349 | "a" : "w"); | ||
350 | |||
351 | if (!fp) | ||
352 | { | ||
353 | util_error (_("Cannot open file %s: %s"), getenv ("DEAD"), | ||
354 | strerror (errno)); | ||
355 | return 1; | ||
356 | } | ||
357 | else | ||
358 | { | ||
359 | char *buf = NULL; | ||
360 | size_t n; | ||
361 | rewind (env->file); | ||
362 | while (getline (&buf, &n, env->file) > 0) | ||
363 | fputs (buf, fp); | ||
364 | fclose (fp); | ||
365 | free (buf); | ||
366 | } | ||
367 | } | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static int | ||
372 | send_message (mu_message_t msg) | ||
373 | { | ||
374 | char *sendmail; | ||
375 | int status; | ||
376 | |||
377 | if (mailvar_get (&sendmail, "sendmail", mailvar_type_string, 0) == 0) | ||
378 | { | ||
379 | if (sendmail[0] == '/') | ||
380 | status = msg_to_pipe (sendmail, msg); | ||
381 | else | ||
382 | { | ||
383 | mu_mailer_t mailer; | ||
384 | |||
385 | status = mu_mailer_create (&mailer, sendmail); | ||
386 | if (status == 0) | ||
387 | { | ||
388 | if (mailvar_get (NULL, "verbose", mailvar_type_boolean, 0) == 0) | ||
389 | { | ||
390 | mu_debug_t debug = NULL; | ||
391 | mu_mailer_get_debug (mailer, &debug); | ||
392 | mu_debug_set_level (debug, | ||
393 | MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT)); | ||
394 | } | ||
395 | status = mu_mailer_open (mailer, MU_STREAM_RDWR); | ||
396 | if (status == 0) | ||
397 | { | ||
398 | status = mu_mailer_send_message (mailer, msg, NULL, NULL); | ||
399 | mu_mailer_close (mailer); | ||
400 | } | ||
401 | else | ||
402 | util_error (_("Cannot open mailer: %s"), mu_strerror (status)); | ||
403 | mu_mailer_destroy (&mailer); | ||
404 | } | ||
405 | else | ||
406 | util_error (_("Cannot create mailer: %s"), | ||
407 | mu_strerror (status)); | ||
408 | } | ||
409 | } | ||
410 | else | ||
411 | { | ||
412 | util_error (_("Variable sendmail not set: no mailer")); | ||
413 | status = ENOSYS; | ||
414 | } | ||
415 | return status; | ||
416 | } | ||
341 | 417 | ||
342 | /* mail_send0(): shared between mail_send() and mail_reply(); | 418 | /* mail_send0(): shared between mail_send() and mail_reply(); |
343 | 419 | ||
... | @@ -471,41 +547,16 @@ mail_send0 (compose_env_t * env, int save_to) | ... | @@ -471,41 +547,16 @@ mail_send0 (compose_env_t * env, int save_to) |
471 | free (buf); | 547 | free (buf); |
472 | } | 548 | } |
473 | 549 | ||
474 | /* If interrupted dump the file to dead.letter. */ | 550 | /* If interrupted, dump the file to dead.letter. */ |
475 | if (int_cnt) | 551 | if (int_cnt) |
476 | { | 552 | { |
477 | if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0) | 553 | save_dead_message (env); |
478 | { | ||
479 | FILE *fp = fopen (getenv ("DEAD"), | ||
480 | mailvar_get (NULL, "appenddeadletter", | ||
481 | mailvar_type_boolean, 0) == 0 ? | ||
482 | "a" : "w"); | ||
483 | |||
484 | if (!fp) | ||
485 | { | ||
486 | util_error (_("Cannot open file %s: %s"), getenv ("DEAD"), | ||
487 | strerror (errno)); | ||
488 | } | ||
489 | else | ||
490 | { | ||
491 | char *buf = NULL; | ||
492 | size_t n; | ||
493 | rewind (env->file); | ||
494 | while (getline (&buf, &n, env->file) > 0) | ||
495 | fputs (buf, fp); | ||
496 | fclose (fp); | ||
497 | free (buf); | ||
498 | } | ||
499 | } | ||
500 | |||
501 | fclose (env->file); | 554 | fclose (env->file); |
502 | remove (filename); | 555 | remove (filename); |
503 | free (filename); | 556 | free (filename); |
504 | return 1; | 557 | return 1; |
505 | } | 558 | } |
506 | 559 | ||
507 | fclose (env->file); /* FIXME: freopen would be better */ | ||
508 | |||
509 | /* In mailx compatibility mode, ask for Cc and Bcc after editing | 560 | /* In mailx compatibility mode, ask for Cc and Bcc after editing |
510 | the body of the message */ | 561 | the body of the message */ |
511 | if (mailvar_get (NULL, "mailx", mailvar_type_boolean, 0) == 0) | 562 | if (mailvar_get (NULL, "mailx", mailvar_type_boolean, 0) == 0) |
... | @@ -521,12 +572,12 @@ mail_send0 (compose_env_t * env, int save_to) | ... | @@ -521,12 +572,12 @@ mail_send0 (compose_env_t * env, int save_to) |
521 | file = fopen (filename, "r"); | 572 | file = fopen (filename, "r"); |
522 | if (file != NULL) | 573 | if (file != NULL) |
523 | { | 574 | { |
524 | mu_mailer_t mailer; | ||
525 | mu_message_t msg = NULL; | 575 | mu_message_t msg = NULL; |
526 | int rc; | 576 | int rc; |
577 | int status = 0; | ||
527 | 578 | ||
528 | mu_message_create (&msg, NULL); | 579 | mu_message_create (&msg, NULL); |
529 | 580 | ||
530 | /* Fill the body. */ | 581 | /* Fill the body. */ |
531 | rc = fill_body (msg, file); | 582 | rc = fill_body (msg, file); |
532 | fclose (file); | 583 | fclose (file); |
... | @@ -561,11 +612,10 @@ mail_send0 (compose_env_t * env, int save_to) | ... | @@ -561,11 +612,10 @@ mail_send0 (compose_env_t * env, int save_to) |
561 | { | 612 | { |
562 | /* Pipe to a cmd. */ | 613 | /* Pipe to a cmd. */ |
563 | if (env->outfiles[i][0] == '|') | 614 | if (env->outfiles[i][0] == '|') |
564 | msg_to_pipe (&(env->outfiles[i][1]), msg); | 615 | status = msg_to_pipe (env->outfiles[i] + 1, msg); |
565 | /* Save to a file. */ | 616 | /* Save to a file. */ |
566 | else | 617 | else |
567 | { | 618 | { |
568 | int status; | ||
569 | mu_mailbox_t mbx = NULL; | 619 | mu_mailbox_t mbx = NULL; |
570 | status = mu_mailbox_create_default (&mbx, | 620 | status = mu_mailbox_create_default (&mbx, |
571 | env->outfiles[i]); | 621 | env->outfiles[i]); |
... | @@ -585,64 +635,36 @@ mail_send0 (compose_env_t * env, int save_to) | ... | @@ -585,64 +635,36 @@ mail_send0 (compose_env_t * env, int save_to) |
585 | } | 635 | } |
586 | if (status) | 636 | if (status) |
587 | util_error (_("Cannot create mailbox %s: %s"), | 637 | util_error (_("Cannot create mailbox %s: %s"), |
588 | env->outfiles[i], mu_strerror (status)); | 638 | env->outfiles[i], |
639 | mu_strerror (status)); | ||
589 | } | 640 | } |
590 | } | 641 | } |
591 | } | 642 | } |
592 | 643 | ||
593 | /* Do we need to Send the message on the wire? */ | 644 | /* Do we need to Send the message on the wire? */ |
594 | if (compose_header_get (env, MU_HEADER_TO, NULL) | 645 | if (status == 0 && |
595 | || compose_header_get (env, MU_HEADER_CC, NULL) | 646 | (compose_header_get (env, MU_HEADER_TO, NULL) || |
596 | || compose_header_get (env, MU_HEADER_BCC, NULL)) | 647 | compose_header_get (env, MU_HEADER_CC, NULL) || |
648 | compose_header_get (env, MU_HEADER_BCC, NULL))) | ||
597 | { | 649 | { |
598 | char *sendmail; | 650 | mu_message_set_header (msg, env->header, NULL); |
599 | if (mailvar_get (&sendmail, "sendmail", | 651 | env->header = NULL; |
600 | mailvar_type_string, 0) == 0) | 652 | status = send_message (msg); |
601 | { | 653 | if (status) |
602 | mu_message_set_header (msg, env->header, NULL); | 654 | save_dead_message (env); |
603 | env->header = NULL; | ||
604 | if (sendmail[0] == '/') | ||
605 | msg_to_pipe (sendmail, msg); | ||
606 | else | ||
607 | { | ||
608 | int status = mu_mailer_create (&mailer, sendmail); | ||
609 | if (status == 0) | ||
610 | { | ||
611 | if (mailvar_get (NULL, "verbose", | ||
612 | mailvar_type_boolean, 0) == 0) | ||
613 | { | ||
614 | mu_debug_t debug = NULL; | ||
615 | mu_mailer_get_debug (mailer, &debug); | ||
616 | mu_debug_set_level (debug, | ||
617 | MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT)); | ||
618 | } | ||
619 | status = mu_mailer_open (mailer, MU_STREAM_RDWR); | ||
620 | if (status == 0) | ||
621 | { | ||
622 | mu_mailer_send_message (mailer, msg, | ||
623 | NULL, NULL); | ||
624 | mu_mailer_close (mailer); | ||
625 | } | ||
626 | else | ||
627 | util_error (_("Cannot open mailer: %s"), | ||
628 | mu_strerror (status)); | ||
629 | mu_mailer_destroy (&mailer); | ||
630 | } | ||
631 | else | ||
632 | util_error (_("Cannot create mailer: %s"), | ||
633 | mu_strerror (status)); | ||
634 | } | ||
635 | } | ||
636 | else | ||
637 | util_error (_("Variable sendmail not set: no mailer")); | ||
638 | } | 655 | } |
639 | } | 656 | } |
657 | fclose (env->file); | ||
640 | mu_message_destroy (&msg, NULL); | 658 | mu_message_destroy (&msg, NULL); |
641 | remove (filename); | 659 | remove (filename); |
642 | free (filename); | 660 | free (filename); |
643 | return 0; | 661 | return status; |
644 | } | 662 | } |
645 | } | 663 | } |
664 | else | ||
665 | save_dead_message (env); | ||
666 | |||
667 | fclose (env->file); | ||
646 | 668 | ||
647 | remove (filename); | 669 | remove (filename); |
648 | free (filename); | 670 | free (filename); |
... | @@ -662,26 +684,33 @@ isfilename (const char *p) | ... | @@ -662,26 +684,33 @@ isfilename (const char *p) |
662 | 684 | ||
663 | /* FIXME: Should probably be in util.c. */ | 685 | /* FIXME: Should probably be in util.c. */ |
664 | /* Call popen(cmd) and write the message to it. */ | 686 | /* Call popen(cmd) and write the message to it. */ |
665 | static void | 687 | static int |
666 | msg_to_pipe (const char *cmd, mu_message_t msg) | 688 | msg_to_pipe (const char *cmd, mu_message_t msg) |
667 | { | 689 | { |
668 | FILE *fp = popen (cmd, "w"); | 690 | mu_stream_t progstream, msgstream; |
669 | if (fp) | 691 | int status, rc; |
692 | |||
693 | status = mu_prog_stream_create (&progstream, cmd, MU_STREAM_WRITE); | ||
694 | if (status) | ||
670 | { | 695 | { |
671 | mu_stream_t stream = NULL; | 696 | util_error (_("Cannot pipe to %s: %s"), cmd, mu_strerror (status)); |
672 | char buffer[512]; | 697 | return status; |
673 | size_t n = 0; | ||
674 | /* FIXME: Use mu_stream_copy */ | ||
675 | mu_message_get_streamref (msg, &stream); | ||
676 | while (mu_stream_read (stream, buffer, sizeof buffer - 1, &n) == 0 | ||
677 | && n != 0) | ||
678 | { | ||
679 | buffer[n] = '\0'; | ||
680 | fprintf (fp, "%s", buffer); | ||
681 | } | ||
682 | mu_stream_destroy (&stream); | ||
683 | pclose (fp); | ||
684 | } | 698 | } |
685 | else | 699 | |
686 | util_error (_("Piping %s failed"), cmd); | 700 | mu_message_get_streamref (msg, &msgstream); |
701 | status = mu_stream_copy (progstream, msgstream, 0, NULL); | ||
702 | rc = mu_stream_close (progstream); | ||
703 | |||
704 | if (status == 0 && rc) | ||
705 | status = rc; | ||
706 | |||
707 | mu_stream_destroy (&progstream); | ||
708 | mu_stream_destroy (&msgstream); | ||
709 | |||
710 | if (status) | ||
711 | { | ||
712 | util_error (_("Sending data to %s failed: %s"), cmd, | ||
713 | mu_strerror (status)); | ||
714 | } | ||
715 | return status; | ||
687 | } | 716 | } | ... | ... |
-
Please register or sign in to post a comment