Commit 89ea7423 89ea7423fc240601e018851dbb219a07d476e1d4 by Sergey Poznyakoff

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.
1 parent 7b5902b4
...@@ -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 }
......