Moved from ../../mailbox
Showing
2 changed files
with
1633 additions
and
0 deletions
libproto/mailer/sendmail.c
0 → 100644
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001, 2004, 2005, | ||
3 | 2006, 2007 Free Software Foundation, Inc. | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 3 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General | ||
16 | Public License along with this library; if not, write to the | ||
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301 USA */ | ||
19 | |||
20 | #ifdef HAVE_CONFIG_H | ||
21 | # include <config.h> | ||
22 | #endif | ||
23 | |||
24 | #ifdef ENABLE_SENDMAIL | ||
25 | |||
26 | #include <assert.h> | ||
27 | #include <errno.h> | ||
28 | #include <stdio.h> | ||
29 | #include <stdlib.h> | ||
30 | #include <string.h> | ||
31 | #include <unistd.h> | ||
32 | |||
33 | #include <sys/types.h> | ||
34 | #include <sys/wait.h> | ||
35 | |||
36 | #include <mailutils/address.h> | ||
37 | #include <mailutils/debug.h> | ||
38 | #include <mailutils/message.h> | ||
39 | #include <mailutils/observer.h> | ||
40 | #include <mailutils/property.h> | ||
41 | #include <mailutils/stream.h> | ||
42 | #include <mailutils/url.h> | ||
43 | #include <mailutils/header.h> | ||
44 | #include <mailutils/body.h> | ||
45 | #include <mailutils/errno.h> | ||
46 | |||
47 | #include <mailer0.h> | ||
48 | #include <registrar0.h> | ||
49 | |||
50 | static struct _mu_record _sendmail_record = | ||
51 | { | ||
52 | MU_SENDMAIL_PRIO, | ||
53 | MU_SENDMAIL_SCHEME, | ||
54 | _url_sendmail_init, /* url init. */ | ||
55 | NULL, /* Mailbox entry. */ | ||
56 | _mailer_sendmail_init, /* Mailer entry. */ | ||
57 | NULL, /* Folder entry. */ | ||
58 | NULL, /* No need for a back pointer. */ | ||
59 | NULL, /* _is_scheme method. */ | ||
60 | NULL, /* _get_url method. */ | ||
61 | NULL, /* _get_mailbox method. */ | ||
62 | NULL, /* _get_mailer method. */ | ||
63 | NULL /* _get_folder method. */ | ||
64 | }; | ||
65 | /* We export, url parsing and the initialisation of | ||
66 | the mailbox, via the register entry/record. */ | ||
67 | mu_record_t mu_sendmail_record = &_sendmail_record; | ||
68 | |||
69 | struct _sendmail | ||
70 | { | ||
71 | int dsn; | ||
72 | char *path; | ||
73 | pid_t pid; | ||
74 | off_t offset; | ||
75 | int fd; | ||
76 | enum sendmail_state { SENDMAIL_CLOSED, SENDMAIL_OPEN, SENDMAIL_SEND } state; | ||
77 | }; | ||
78 | |||
79 | typedef struct _sendmail * sendmail_t; | ||
80 | |||
81 | static void sendmail_destroy (mu_mailer_t); | ||
82 | static int sendmail_open (mu_mailer_t, int); | ||
83 | static int sendmail_close (mu_mailer_t); | ||
84 | static int sendmail_send_message (mu_mailer_t, mu_message_t, mu_address_t, mu_address_t); | ||
85 | |||
86 | int | ||
87 | _mailer_sendmail_init (mu_mailer_t mailer) | ||
88 | { | ||
89 | sendmail_t sendmail; | ||
90 | |||
91 | /* Allocate memory specific to sendmail mailer. */ | ||
92 | sendmail = mailer->data = calloc (1, sizeof (*sendmail)); | ||
93 | if (mailer->data == NULL) | ||
94 | return ENOMEM; | ||
95 | sendmail->state = SENDMAIL_CLOSED; | ||
96 | mailer->_destroy = sendmail_destroy; | ||
97 | mailer->_open = sendmail_open; | ||
98 | mailer->_close = sendmail_close; | ||
99 | mailer->_send_message = sendmail_send_message; | ||
100 | |||
101 | /* Set our properties. */ | ||
102 | { | ||
103 | mu_property_t property = NULL; | ||
104 | mu_mailer_get_property (mailer, &property); | ||
105 | mu_property_set_value (property, "TYPE", "SENDMAIL", 1); | ||
106 | } | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static void | ||
111 | sendmail_destroy(mu_mailer_t mailer) | ||
112 | { | ||
113 | sendmail_t sendmail = mailer->data; | ||
114 | if (sendmail) | ||
115 | { | ||
116 | if (sendmail->path) | ||
117 | free (sendmail->path); | ||
118 | free (sendmail); | ||
119 | mailer->data = NULL; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | static int | ||
124 | sendmail_open (mu_mailer_t mailer, int flags) | ||
125 | { | ||
126 | sendmail_t sendmail = mailer->data; | ||
127 | int status; | ||
128 | char *path; | ||
129 | |||
130 | /* Sanity checks. */ | ||
131 | if (sendmail == NULL) | ||
132 | return EINVAL; | ||
133 | |||
134 | mailer->flags = flags; | ||
135 | |||
136 | if ((status = mu_url_aget_path (mailer->url, &path))) | ||
137 | return status; | ||
138 | |||
139 | if (access (path, X_OK) == -1) | ||
140 | { | ||
141 | free (path); | ||
142 | return errno; | ||
143 | } | ||
144 | sendmail->path = path; | ||
145 | sendmail->state = SENDMAIL_OPEN; | ||
146 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, "sendmail (%s)\n", sendmail->path); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int | ||
151 | sendmail_close (mu_mailer_t mailer) | ||
152 | { | ||
153 | sendmail_t sendmail = mailer->data; | ||
154 | |||
155 | /* Sanity checks. */ | ||
156 | if (sendmail == NULL) | ||
157 | return EINVAL; | ||
158 | |||
159 | if(sendmail->path) | ||
160 | free(sendmail->path); | ||
161 | |||
162 | sendmail->path = NULL; | ||
163 | sendmail->state = SENDMAIL_CLOSED; | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static int | ||
169 | mailer_property_is_set (mu_mailer_t mailer, const char *name) | ||
170 | { | ||
171 | mu_property_t property = NULL; | ||
172 | |||
173 | mu_mailer_get_property (mailer, &property); | ||
174 | return mu_property_is_set (property, name); | ||
175 | } | ||
176 | |||
177 | |||
178 | /* Close FD unless it is part of pipe P */ | ||
179 | #define SCLOSE(fd,p) if (p[0]!=fd&&p[1]!=fd) close(fd) | ||
180 | |||
181 | static int | ||
182 | sendmail_send_message (mu_mailer_t mailer, mu_message_t msg, mu_address_t from, | ||
183 | mu_address_t to) | ||
184 | { | ||
185 | sendmail_t sendmail = mailer->data; | ||
186 | int status = 0; | ||
187 | |||
188 | if (sendmail == NULL || msg == NULL) | ||
189 | return EINVAL; | ||
190 | |||
191 | switch (sendmail->state) | ||
192 | { | ||
193 | case SENDMAIL_CLOSED: | ||
194 | return EINVAL; | ||
195 | case SENDMAIL_OPEN: | ||
196 | { | ||
197 | int tunnel[2]; | ||
198 | int argc = 0; | ||
199 | const char **argvec = NULL; | ||
200 | size_t tocount = 0; | ||
201 | const char *emailfrom = NULL; | ||
202 | |||
203 | /* Count the length of the arg vec: */ | ||
204 | |||
205 | argc++; /* terminating NULL */ | ||
206 | argc++; /* sendmail */ | ||
207 | argc++; /* -oi (do not treat '.' as message | ||
208 | terminator) */ | ||
209 | |||
210 | if (from) | ||
211 | { | ||
212 | if ((status = mu_address_sget_email (from, 1, &emailfrom)) != 0) | ||
213 | goto OPEN_STATE_CLEANUP; | ||
214 | |||
215 | if (!emailfrom) | ||
216 | { | ||
217 | /* the address wasn't fully qualified, choke (for now) */ | ||
218 | status = EINVAL; | ||
219 | |||
220 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, | ||
221 | "envelope from (%s) not fully qualifed\n", | ||
222 | emailfrom); | ||
223 | |||
224 | goto OPEN_STATE_CLEANUP; | ||
225 | } | ||
226 | |||
227 | argc += 2; /* -f from */ | ||
228 | } | ||
229 | |||
230 | if (to) | ||
231 | { | ||
232 | status = mu_address_get_email_count (to, &tocount); | ||
233 | |||
234 | assert (!status); | ||
235 | assert (tocount); | ||
236 | |||
237 | argc += tocount; /* 1 per to address */ | ||
238 | } | ||
239 | |||
240 | argc++; /* -t */ | ||
241 | |||
242 | /* Allocate arg vec: */ | ||
243 | if ((argvec = calloc (argc, sizeof (*argvec))) == 0) | ||
244 | { | ||
245 | status = ENOMEM; | ||
246 | goto OPEN_STATE_CLEANUP; | ||
247 | } | ||
248 | |||
249 | argc = 0; | ||
250 | |||
251 | argvec[argc++] = sendmail->path; | ||
252 | argvec[argc++] = "-oi"; | ||
253 | |||
254 | if (from) | ||
255 | { | ||
256 | argvec[argc++] = "-f"; | ||
257 | argvec[argc++] = emailfrom; | ||
258 | } | ||
259 | |||
260 | if (!to || mailer_property_is_set (mailer, "READ_RECIPIENTS")) | ||
261 | { | ||
262 | argvec[argc++] = "-t"; | ||
263 | } | ||
264 | else | ||
265 | { | ||
266 | int i = 1; | ||
267 | size_t count = 0; | ||
268 | |||
269 | mu_address_get_count (to, &count); | ||
270 | |||
271 | for (; i <= count; i++) | ||
272 | { | ||
273 | const char *email; | ||
274 | if ((status = mu_address_sget_email (to, i, &email)) != 0) | ||
275 | goto OPEN_STATE_CLEANUP; | ||
276 | if (!email) | ||
277 | { | ||
278 | /* the address wasn't fully qualified, choke (for now) */ | ||
279 | status = EINVAL; | ||
280 | |||
281 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, | ||
282 | "envelope to (%s) not fully qualifed\n", | ||
283 | email); | ||
284 | |||
285 | goto OPEN_STATE_CLEANUP; | ||
286 | } | ||
287 | argvec[argc++] = email; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | assert (argvec[argc] == NULL); | ||
292 | |||
293 | if (pipe (tunnel) == 0) | ||
294 | { | ||
295 | sendmail->fd = tunnel[1]; | ||
296 | sendmail->pid = vfork (); | ||
297 | if (sendmail->pid == 0) /* Child. */ | ||
298 | { | ||
299 | SCLOSE (STDIN_FILENO, tunnel); | ||
300 | SCLOSE (STDOUT_FILENO, tunnel); | ||
301 | SCLOSE (STDERR_FILENO, tunnel); | ||
302 | close (tunnel[1]); | ||
303 | dup2 (tunnel[0], STDIN_FILENO); | ||
304 | execv (sendmail->path, (char**) argvec); | ||
305 | exit (errno); | ||
306 | } | ||
307 | else if (sendmail->pid == -1) | ||
308 | { | ||
309 | status = errno; | ||
310 | |||
311 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, | ||
312 | "vfork() failed: %s\n", strerror (status)); | ||
313 | } | ||
314 | } | ||
315 | else | ||
316 | { | ||
317 | status = errno; | ||
318 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, | ||
319 | "pipe() failed: %s\n", strerror (status)); | ||
320 | } | ||
321 | |||
322 | OPEN_STATE_CLEANUP: | ||
323 | MAILER_DEBUG0 (mailer, MU_DEBUG_TRACE, "exec argv:"); | ||
324 | for (argc = 0; argvec && argvec[argc]; argc++) | ||
325 | { | ||
326 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, " %s", argvec[argc]); | ||
327 | } | ||
328 | MAILER_DEBUG0 (mailer, MU_DEBUG_TRACE, "\n"); | ||
329 | free (argvec); | ||
330 | close (tunnel[0]); | ||
331 | |||
332 | if (status != 0) | ||
333 | { | ||
334 | close (sendmail->fd); | ||
335 | break; | ||
336 | } | ||
337 | sendmail->state = SENDMAIL_SEND; | ||
338 | } | ||
339 | |||
340 | case SENDMAIL_SEND: | ||
341 | { | ||
342 | mu_stream_t stream = NULL; | ||
343 | char buffer[512]; | ||
344 | size_t len = 0; | ||
345 | int rc; | ||
346 | size_t offset = 0; | ||
347 | mu_header_t hdr; | ||
348 | mu_body_t body; | ||
349 | int found_nl = 0; | ||
350 | int exit_status; | ||
351 | |||
352 | mu_message_get_header (msg, &hdr); | ||
353 | mu_header_get_stream (hdr, &stream); | ||
354 | |||
355 | MAILER_DEBUG0 (mailer, MU_DEBUG_TRACE, "Sending headers...\n"); | ||
356 | while ((status = mu_stream_readline (stream, buffer, sizeof (buffer), | ||
357 | offset, &len)) == 0 | ||
358 | && len != 0) | ||
359 | { | ||
360 | if (strncasecmp (buffer, MU_HEADER_FCC, | ||
361 | sizeof (MU_HEADER_FCC) - 1)) | ||
362 | { | ||
363 | MAILER_DEBUG1 (mailer, MU_DEBUG_PROT, | ||
364 | "Header: %s", buffer); | ||
365 | if (write (sendmail->fd, buffer, len) == -1) | ||
366 | { | ||
367 | status = errno; | ||
368 | |||
369 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, | ||
370 | "write() failed: %s\n", strerror (status)); | ||
371 | |||
372 | break; | ||
373 | } | ||
374 | } | ||
375 | found_nl = (len == 1 && buffer[0] == '\n'); | ||
376 | |||
377 | offset += len; | ||
378 | sendmail->offset += len; | ||
379 | } | ||
380 | |||
381 | if (!found_nl) | ||
382 | { | ||
383 | if (write (sendmail->fd, "\n", 1) == -1) | ||
384 | { | ||
385 | status = errno; | ||
386 | |||
387 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, | ||
388 | "write() failed: %s\n", strerror (status)); | ||
389 | } | ||
390 | } | ||
391 | |||
392 | mu_message_get_body (msg, &body); | ||
393 | mu_body_get_stream (body, &stream); | ||
394 | |||
395 | MAILER_DEBUG0 (mailer, MU_DEBUG_TRACE, "Sending body...\n"); | ||
396 | offset = 0; | ||
397 | while ((status = mu_stream_read (stream, buffer, sizeof (buffer), | ||
398 | offset, &len)) == 0 | ||
399 | && len != 0) | ||
400 | { | ||
401 | if (write (sendmail->fd, buffer, len) == -1) | ||
402 | { | ||
403 | status = errno; | ||
404 | |||
405 | MAILER_DEBUG1 (mailer, MU_DEBUG_TRACE, | ||
406 | "write() failed: %s\n", strerror (status)); | ||
407 | |||
408 | break; | ||
409 | } | ||
410 | offset += len; | ||
411 | sendmail->offset += len; | ||
412 | } | ||
413 | if (status == EAGAIN) | ||
414 | return status; | ||
415 | |||
416 | close (sendmail->fd); | ||
417 | |||
418 | rc = waitpid (sendmail->pid, &exit_status, 0); | ||
419 | |||
420 | if (rc < 0) | ||
421 | { | ||
422 | if (errno == ECHILD) | ||
423 | status = 0; | ||
424 | else | ||
425 | { | ||
426 | status = errno; | ||
427 | MAILER_DEBUG2 (mailer, MU_DEBUG_TRACE, | ||
428 | "waitpid(%d) failed: %s\n", | ||
429 | sendmail->pid, strerror (status)); | ||
430 | } | ||
431 | } | ||
432 | else if (WIFEXITED (exit_status)) | ||
433 | { | ||
434 | exit_status = WEXITSTATUS (exit_status); | ||
435 | MAILER_DEBUG2 (mailer, MU_DEBUG_TRACE, | ||
436 | "%s exited with: %d\n", | ||
437 | sendmail->path, exit_status); | ||
438 | status = (exit_status == 0) ? 0 : MU_ERR_PROCESS_EXITED; | ||
439 | } | ||
440 | else if (WIFSIGNALED (exit_status)) | ||
441 | status = MU_ERR_PROCESS_SIGNALED; | ||
442 | else | ||
443 | status = MU_ERR_PROCESS_UNKNOWN_FAILURE; | ||
444 | |||
445 | /* Shouldn't this notification only happen on success? */ | ||
446 | mu_observable_notify (mailer->observable, MU_EVT_MAILER_MESSAGE_SENT); | ||
447 | } | ||
448 | default: | ||
449 | break; | ||
450 | } | ||
451 | |||
452 | sendmail->state = (status == 0) ? SENDMAIL_OPEN : SENDMAIL_CLOSED; | ||
453 | |||
454 | return status; | ||
455 | } | ||
456 | |||
457 | #else | ||
458 | #include <stdio.h> | ||
459 | #include <registrar0.h> | ||
460 | mu_record_t mu_sendmail_record = NULL; | ||
461 | #endif |
libproto/mailer/smtp.c
0 → 100644
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001, 2004, 2005, | ||
3 | 2006, 2007 Free Software Foundation, Inc. | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 3 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General | ||
16 | Public License along with this library; if not, write to the | ||
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301 USA */ | ||
19 | |||
20 | /** @file smtp.c | ||
21 | @brief an SMTP mailer | ||
22 | */ | ||
23 | |||
24 | #ifdef HAVE_CONFIG_H | ||
25 | # include <config.h> | ||
26 | #endif | ||
27 | |||
28 | #ifdef ENABLE_SMTP | ||
29 | |||
30 | #include <errno.h> | ||
31 | #include <ctype.h> | ||
32 | #include <netdb.h> | ||
33 | #include <pwd.h> | ||
34 | #include <stdarg.h> | ||
35 | #include <stdio.h> | ||
36 | #include <stdlib.h> | ||
37 | #include <string.h> | ||
38 | #include <unistd.h> | ||
39 | |||
40 | #include <mailutils/address.h> | ||
41 | #include <mailutils/debug.h> | ||
42 | #include <mailutils/errno.h> | ||
43 | #include <mailutils/header.h> | ||
44 | #include <mailutils/body.h> | ||
45 | #include <mailutils/message.h> | ||
46 | #include <mailutils/mutil.h> | ||
47 | #include <mailutils/observer.h> | ||
48 | #include <mailutils/property.h> | ||
49 | #include <mailutils/stream.h> | ||
50 | #include <mailutils/url.h> | ||
51 | #include <mailutils/tls.h> | ||
52 | |||
53 | #include <mailer0.h> | ||
54 | #include <registrar0.h> | ||
55 | |||
56 | static struct _mu_record _smtp_record = { | ||
57 | MU_SMTP_PRIO, | ||
58 | MU_SMTP_SCHEME, | ||
59 | _url_smtp_init, /* url init. */ | ||
60 | NULL, /* Mailbox init. */ | ||
61 | &_mailer_smtp_init, /* Mailer init. */ | ||
62 | NULL, /* Folder init. */ | ||
63 | NULL, /* No need for a back pointer. */ | ||
64 | NULL, /* _is_scheme method. */ | ||
65 | NULL, /* _get_url method. */ | ||
66 | NULL, /* _get_mailbox method. */ | ||
67 | NULL, /* _get_mailer method. */ | ||
68 | NULL /* _get_folder method. */ | ||
69 | }; | ||
70 | /* We export : url parsing and the initialisation of | ||
71 | the mailbox, via the register entry/record. */ | ||
72 | mu_record_t mu_smtp_record = &_smtp_record; | ||
73 | |||
74 | struct _smtp | ||
75 | { | ||
76 | mu_mailer_t mailer; | ||
77 | char *mailhost; | ||
78 | char *localhost; | ||
79 | |||
80 | /* IO buffering. */ | ||
81 | char *buffer; /* Must be freed. */ | ||
82 | size_t buflen; | ||
83 | |||
84 | char *ptr; | ||
85 | char *nl; | ||
86 | off_t s_offset; | ||
87 | |||
88 | enum smtp_state | ||
89 | { | ||
90 | SMTP_NO_STATE, SMTP_OPEN, SMTP_GREETINGS, SMTP_EHLO, SMTP_EHLO_ACK, | ||
91 | SMTP_HELO, SMTP_HELO_ACK, SMTP_QUIT, SMTP_QUIT_ACK, SMTP_ENV_FROM, | ||
92 | SMTP_ENV_RCPT, SMTP_MAIL_FROM, SMTP_MAIL_FROM_ACK, SMTP_RCPT_TO, | ||
93 | SMTP_RCPT_TO_ACK, SMTP_DATA, SMTP_DATA_ACK, SMTP_SEND, SMTP_SEND_ACK, | ||
94 | SMTP_SEND_DOT, SMTP_STARTTLS, SMTP_STARTTLS_ACK | ||
95 | } | ||
96 | state; | ||
97 | |||
98 | int extended; | ||
99 | unsigned long capa; /* Server capabilities */ | ||
100 | |||
101 | const char *mail_from; | ||
102 | mu_address_t rcpt_to; /* Destroy this if not the same as argto below. */ | ||
103 | mu_address_t rcpt_bcc; | ||
104 | size_t rcpt_to_count; | ||
105 | size_t rcpt_bcc_count; | ||
106 | size_t rcpt_index; | ||
107 | size_t rcpt_count; | ||
108 | int bccing; | ||
109 | mu_message_t msg; /* Destroy this if not same argmsg. */ | ||
110 | |||
111 | off_t offset; | ||
112 | |||
113 | /* The mu_mailer_send_message() args. */ | ||
114 | mu_message_t argmsg; | ||
115 | mu_address_t argfrom; | ||
116 | mu_address_t argto; | ||
117 | }; | ||
118 | |||
119 | typedef struct _smtp *smtp_t; | ||
120 | |||
121 | /* ESMTP capabilities */ | ||
122 | #define CAPA_STARTTLS 0x00000001 | ||
123 | #define CAPA_8BITMIME 0x00000002 | ||
124 | |||
125 | static void smtp_destroy (mu_mailer_t); | ||
126 | static int smtp_open (mu_mailer_t, int); | ||
127 | static int smtp_close (mu_mailer_t); | ||
128 | static int smtp_send_message (mu_mailer_t, mu_message_t, mu_address_t, mu_address_t); | ||
129 | static int smtp_writeline (smtp_t smtp, const char *format, ...); | ||
130 | static int smtp_readline (smtp_t); | ||
131 | static int smtp_read_ack (smtp_t); | ||
132 | static int smtp_parse_ehlo_ack (smtp_t); | ||
133 | static int smtp_write (smtp_t); | ||
134 | static int smtp_starttls (smtp_t); | ||
135 | |||
136 | static int _smtp_set_rcpt (smtp_t, mu_message_t, mu_address_t); | ||
137 | |||
138 | /* Useful little macros, since these are very repetitive. */ | ||
139 | |||
140 | static void | ||
141 | CLEAR_STATE (smtp_t smtp) | ||
142 | { | ||
143 | smtp->ptr = NULL; | ||
144 | smtp->nl = NULL; | ||
145 | smtp->s_offset = 0; | ||
146 | |||
147 | smtp->state = SMTP_NO_STATE; | ||
148 | |||
149 | smtp->extended = 0; | ||
150 | |||
151 | if (smtp->mail_from) | ||
152 | smtp->mail_from = NULL; | ||
153 | |||
154 | if (smtp->rcpt_to != smtp->argto) | ||
155 | mu_address_destroy (&smtp->rcpt_to); | ||
156 | |||
157 | smtp->rcpt_to = NULL; | ||
158 | |||
159 | mu_address_destroy (&smtp->rcpt_bcc); | ||
160 | |||
161 | smtp->rcpt_to_count = 0; | ||
162 | smtp->rcpt_bcc_count = 0; | ||
163 | smtp->rcpt_index = 0; | ||
164 | smtp->rcpt_count = 0; | ||
165 | smtp->bccing = 0; | ||
166 | |||
167 | if (smtp->msg != smtp->argmsg) | ||
168 | mu_message_destroy (&smtp->msg, NULL); | ||
169 | |||
170 | smtp->msg = NULL; | ||
171 | |||
172 | smtp->offset = 0; | ||
173 | |||
174 | smtp->argmsg = NULL; | ||
175 | smtp->argfrom = NULL; | ||
176 | smtp->argto = NULL; | ||
177 | } | ||
178 | |||
179 | /* If we are resuming, we should be resuming the SAME operation | ||
180 | as that which is ongoing. Check this. */ | ||
181 | static int | ||
182 | smtp_check_send_resumption (smtp_t smtp, | ||
183 | mu_message_t msg, mu_address_t from, mu_address_t to) | ||
184 | { | ||
185 | if (smtp->state == SMTP_NO_STATE) | ||
186 | return 0; | ||
187 | |||
188 | /* FIXME: state should be one of the "send" states if its not | ||
189 | "no state" */ | ||
190 | if (msg != smtp->argmsg) | ||
191 | return MU_ERR_BAD_RESUMPTION; | ||
192 | |||
193 | if (from != smtp->argfrom) | ||
194 | return MU_ERR_BAD_RESUMPTION; | ||
195 | |||
196 | if (to != smtp->argto) | ||
197 | return MU_ERR_BAD_RESUMPTION; | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | #define CHECK_SEND_RESUME(smtp, msg, from, to) \ | ||
203 | do { \ | ||
204 | if((status = smtp_check_send_resumption(smtp, msg, from, to)) != 0) \ | ||
205 | return status; \ | ||
206 | } while (0) | ||
207 | |||
208 | /* Clear the state and close the stream. */ | ||
209 | #define CHECK_ERROR_CLOSE(mailer, smtp, status) \ | ||
210 | do \ | ||
211 | { \ | ||
212 | if (status != 0) \ | ||
213 | { \ | ||
214 | mu_stream_close (mailer->stream); \ | ||
215 | CLEAR_STATE (smtp); \ | ||
216 | return status; \ | ||
217 | } \ | ||
218 | } \ | ||
219 | while (0) | ||
220 | |||
221 | /* Clear the state. */ | ||
222 | #define CHECK_ERROR(smtp, status) \ | ||
223 | do \ | ||
224 | { \ | ||
225 | if (status != 0) \ | ||
226 | { \ | ||
227 | CLEAR_STATE (smtp); \ | ||
228 | return status; \ | ||
229 | } \ | ||
230 | } \ | ||
231 | while (0) | ||
232 | |||
233 | /* Clear the state for non recoverable error. */ | ||
234 | #define CHECK_EAGAIN(smtp, status) \ | ||
235 | do \ | ||
236 | { \ | ||
237 | if (status != 0) \ | ||
238 | { \ | ||
239 | if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \ | ||
240 | { \ | ||
241 | CLEAR_STATE (smtp); \ | ||
242 | } \ | ||
243 | return status; \ | ||
244 | } \ | ||
245 | } \ | ||
246 | while (0) | ||
247 | |||
248 | int | ||
249 | _mailer_smtp_init (mu_mailer_t mailer) | ||
250 | { | ||
251 | smtp_t smtp; | ||
252 | |||
253 | /* Allocate memory specific to smtp mailer. */ | ||
254 | smtp = mailer->data = calloc (1, sizeof (*smtp)); | ||
255 | if (mailer->data == NULL) | ||
256 | return ENOMEM; | ||
257 | |||
258 | smtp->mailer = mailer; /* Back pointer. */ | ||
259 | smtp->state = SMTP_NO_STATE; | ||
260 | |||
261 | mailer->_destroy = smtp_destroy; | ||
262 | mailer->_open = smtp_open; | ||
263 | mailer->_close = smtp_close; | ||
264 | mailer->_send_message = smtp_send_message; | ||
265 | |||
266 | /* Set our properties. */ | ||
267 | { | ||
268 | mu_property_t property = NULL; | ||
269 | mu_mailer_get_property (mailer, &property); | ||
270 | mu_property_set_value (property, "TYPE", "SMTP", 1); | ||
271 | } | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static void | ||
277 | smtp_destroy (mu_mailer_t mailer) | ||
278 | { | ||
279 | smtp_t smtp = mailer->data; | ||
280 | |||
281 | CLEAR_STATE (smtp); | ||
282 | |||
283 | /* Not our responsability to close. */ | ||
284 | |||
285 | if (smtp->mailhost) | ||
286 | free (smtp->mailhost); | ||
287 | if (smtp->localhost) | ||
288 | free (smtp->localhost); | ||
289 | if (smtp->buffer) | ||
290 | free (smtp->buffer); | ||
291 | |||
292 | free (smtp); | ||
293 | |||
294 | mailer->data = NULL; | ||
295 | } | ||
296 | |||
297 | /** Open an SMTP mailer. | ||
298 | An SMTP mailer must be opened before any messages can be sent. | ||
299 | @param mailer the mailer created by smtp_create() | ||
300 | @param flags the mailer flags | ||
301 | */ | ||
302 | static int | ||
303 | smtp_open (mu_mailer_t mailer, int flags) | ||
304 | { | ||
305 | smtp_t smtp = mailer->data; | ||
306 | int status; | ||
307 | long port; | ||
308 | |||
309 | /* Sanity checks. */ | ||
310 | if (!smtp) | ||
311 | return EINVAL; | ||
312 | |||
313 | mailer->flags = flags; | ||
314 | |||
315 | if ((status = mu_url_get_port (mailer->url, &port)) != 0) | ||
316 | return status; | ||
317 | |||
318 | switch (smtp->state) | ||
319 | { | ||
320 | case SMTP_NO_STATE: | ||
321 | if (smtp->mailhost) | ||
322 | { | ||
323 | free (smtp->mailhost); | ||
324 | smtp->mailhost = NULL; | ||
325 | } | ||
326 | |||
327 | /* Fetch the mailer server name and the port in the mu_url_t. */ | ||
328 | if ((status = mu_url_aget_host (mailer->url, &smtp->mailhost)) != 0) | ||
329 | return status; | ||
330 | |||
331 | if (smtp->localhost) | ||
332 | { | ||
333 | free (smtp->localhost); | ||
334 | smtp->localhost = NULL; | ||
335 | } | ||
336 | /* Fetch our local host name. */ | ||
337 | |||
338 | status = mu_get_host_name (&smtp->localhost); | ||
339 | |||
340 | if (status != 0) | ||
341 | { | ||
342 | /* gethostname failed, abort. */ | ||
343 | free (smtp->mailhost); | ||
344 | smtp->mailhost = NULL; | ||
345 | return status; | ||
346 | } | ||
347 | |||
348 | /* allocate a working io buffer. */ | ||
349 | if (smtp->buffer == NULL) | ||
350 | { | ||
351 | smtp->buflen = 512; /* Initial guess. */ | ||
352 | smtp->buffer = malloc (smtp->buflen + 1); | ||
353 | if (smtp->buffer == NULL) | ||
354 | { | ||
355 | CHECK_ERROR (smtp, ENOMEM); | ||
356 | } | ||
357 | smtp->ptr = smtp->buffer; | ||
358 | } | ||
359 | |||
360 | /* Create a TCP stack if one is not given. */ | ||
361 | if (mailer->stream == NULL) | ||
362 | { | ||
363 | status = | ||
364 | mu_tcp_stream_create (&mailer->stream, smtp->mailhost, port, | ||
365 | mailer->flags); | ||
366 | CHECK_ERROR (smtp, status); | ||
367 | mu_stream_setbufsiz (mailer->stream, BUFSIZ); | ||
368 | } | ||
369 | CHECK_ERROR (smtp, status); | ||
370 | smtp->state = SMTP_OPEN; | ||
371 | |||
372 | case SMTP_OPEN: | ||
373 | MAILER_DEBUG2 (mailer, MU_DEBUG_PROT, "smtp_open (host: %s port: %ld)\n", | ||
374 | smtp->mailhost, port); | ||
375 | status = mu_stream_open (mailer->stream); | ||
376 | CHECK_EAGAIN (smtp, status); | ||
377 | smtp->state = SMTP_GREETINGS; | ||
378 | |||
379 | case SMTP_GREETINGS: | ||
380 | /* Swallow the greetings. */ | ||
381 | status = smtp_read_ack (smtp); | ||
382 | CHECK_EAGAIN (smtp, status); | ||
383 | |||
384 | if (smtp->buffer[0] != '2') | ||
385 | { | ||
386 | mu_stream_close (mailer->stream); | ||
387 | return EACCES; | ||
388 | } | ||
389 | status = smtp_writeline (smtp, "EHLO %s\r\n", smtp->localhost); | ||
390 | CHECK_ERROR (smtp, status); | ||
391 | |||
392 | smtp->state = SMTP_EHLO; | ||
393 | |||
394 | case SMTP_EHLO: | ||
395 | /* We first try Extended SMTP. */ | ||
396 | status = smtp_write (smtp); | ||
397 | CHECK_EAGAIN (smtp, status); | ||
398 | smtp->state = SMTP_EHLO_ACK; | ||
399 | |||
400 | case SMTP_EHLO_ACK: | ||
401 | status = smtp_parse_ehlo_ack (smtp); | ||
402 | CHECK_EAGAIN (smtp, status); | ||
403 | |||
404 | if (smtp->buffer[0] != '2') | ||
405 | { | ||
406 | smtp->extended = 0; | ||
407 | status = smtp_writeline (smtp, "HELO %s\r\n", smtp->localhost); | ||
408 | CHECK_ERROR (smtp, status); | ||
409 | smtp->state = SMTP_HELO; | ||
410 | } | ||
411 | else | ||
412 | { | ||
413 | smtp->extended = 1; | ||
414 | |||
415 | if (smtp->capa & CAPA_STARTTLS) | ||
416 | smtp->state = SMTP_STARTTLS; | ||
417 | else | ||
418 | break; | ||
419 | } | ||
420 | |||
421 | case SMTP_STARTTLS: | ||
422 | case SMTP_STARTTLS_ACK: | ||
423 | smtp_starttls (smtp); | ||
424 | break; | ||
425 | |||
426 | case SMTP_HELO: | ||
427 | if (!smtp->extended) /* FIXME: this will always be false! */ | ||
428 | { | ||
429 | status = smtp_write (smtp); | ||
430 | CHECK_EAGAIN (smtp, status); | ||
431 | } | ||
432 | smtp->state = SMTP_HELO_ACK; | ||
433 | |||
434 | case SMTP_HELO_ACK: | ||
435 | if (!smtp->extended) | ||
436 | { | ||
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 | CLEAR_STATE (smtp); | ||
444 | return EACCES; | ||
445 | } | ||
446 | } | ||
447 | |||
448 | default: | ||
449 | break; | ||
450 | } | ||
451 | |||
452 | CLEAR_STATE (smtp); | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static int | ||
458 | smtp_close (mu_mailer_t mailer) | ||
459 | { | ||
460 | smtp_t smtp = mailer->data; | ||
461 | int status; | ||
462 | switch (smtp->state) | ||
463 | { | ||
464 | case SMTP_NO_STATE: | ||
465 | status = smtp_writeline (smtp, "QUIT\r\n"); | ||
466 | CHECK_ERROR (smtp, status); | ||
467 | |||
468 | smtp->state = SMTP_QUIT; | ||
469 | |||
470 | case SMTP_QUIT: | ||
471 | status = smtp_write (smtp); | ||
472 | CHECK_EAGAIN (smtp, status); | ||
473 | smtp->state = SMTP_QUIT_ACK; | ||
474 | |||
475 | case SMTP_QUIT_ACK: | ||
476 | status = smtp_read_ack (smtp); | ||
477 | CHECK_EAGAIN (smtp, status); | ||
478 | |||
479 | default: | ||
480 | break; | ||
481 | } | ||
482 | return mu_stream_close (mailer->stream); | ||
483 | } | ||
484 | |||
485 | /* | ||
486 | Client side STARTTLS support. | ||
487 | */ | ||
488 | |||
489 | static int | ||
490 | smtp_reader (void *iodata) | ||
491 | { | ||
492 | int status = 0; | ||
493 | smtp_t iop = iodata; | ||
494 | status = smtp_read_ack (iop); | ||
495 | CHECK_EAGAIN (iop, status); | ||
496 | return status; | ||
497 | } | ||
498 | |||
499 | static int | ||
500 | smtp_writer (void *iodata, char *buf) | ||
501 | { | ||
502 | smtp_t iop = iodata; | ||
503 | int status; | ||
504 | if (strncasecmp (buf, "EHLO", 4) == 0) | ||
505 | status = smtp_writeline (iop, "%s %s\r\n", buf, iop->localhost); | ||
506 | else | ||
507 | status = smtp_writeline (iop, "%s\r\n", buf); | ||
508 | CHECK_ERROR (iop, status); | ||
509 | status = smtp_write (iop); | ||
510 | CHECK_EAGAIN (iop, status); | ||
511 | return status; | ||
512 | } | ||
513 | |||
514 | static void | ||
515 | smtp_stream_ctl (void *iodata, mu_stream_t *pold, mu_stream_t new) | ||
516 | { | ||
517 | smtp_t iop = iodata; | ||
518 | if (pold) | ||
519 | *pold = iop->mailer->stream; | ||
520 | if (new) | ||
521 | iop->mailer->stream = new; | ||
522 | } | ||
523 | |||
524 | static int | ||
525 | smtp_starttls (smtp_t smtp) | ||
526 | { | ||
527 | #ifdef WITH_TLS | ||
528 | int status; | ||
529 | mu_mailer_t mailer = smtp->mailer; | ||
530 | char *keywords[] = { "STARTTLS", "EHLO", NULL }; | ||
531 | |||
532 | if (!mu_tls_enable || !(smtp->capa & CAPA_STARTTLS)) | ||
533 | return -1; | ||
534 | |||
535 | smtp->capa = 0; | ||
536 | status = mu_tls_begin (smtp, smtp_reader, smtp_writer, | ||
537 | smtp_stream_ctl, keywords); | ||
538 | |||
539 | MAILER_DEBUG1 (mailer, MU_DEBUG_PROT, "TLS negotiation %s\n", | ||
540 | status == 0 ? "succeeded" : "failed"); | ||
541 | |||
542 | return status; | ||
543 | #else | ||
544 | return -1; | ||
545 | #endif /* WITH_TLS */ | ||
546 | } | ||
547 | |||
548 | static int | ||
549 | message_set_header_value (mu_message_t msg, const char *field, const char *value) | ||
550 | { | ||
551 | int status = 0; | ||
552 | mu_header_t hdr = NULL; | ||
553 | |||
554 | if ((status = mu_message_get_header (msg, &hdr))) | ||
555 | return status; | ||
556 | |||
557 | if ((status = mu_header_set_value (hdr, field, value, 1))) | ||
558 | return status; | ||
559 | |||
560 | return status; | ||
561 | } | ||
562 | |||
563 | static int | ||
564 | message_has_bcc (mu_message_t msg) | ||
565 | { | ||
566 | int status; | ||
567 | mu_header_t header = NULL; | ||
568 | size_t bccsz = 0; | ||
569 | |||
570 | if ((status = mu_message_get_header (msg, &header))) | ||
571 | return status; | ||
572 | |||
573 | status = mu_header_get_value (header, MU_HEADER_BCC, NULL, 0, &bccsz); | ||
574 | |||
575 | /* MU_ERR_NOENT, or there was a Bcc: field. */ | ||
576 | return status == MU_ERR_NOENT ? 0 : 1; | ||
577 | } | ||
578 | |||
579 | /* | ||
580 | |||
581 | The smtp mailer doesn't deal with mail like: | ||
582 | |||
583 | To: public@com, pub2@com | ||
584 | Bcc: hidden@there, two@there | ||
585 | |||
586 | It just sends the message to all the addresses, making the | ||
587 | "blind" cc not particularly blind. | ||
588 | |||
589 | The correct algorithm is | ||
590 | |||
591 | - open smtp connection | ||
592 | - look as msg, figure out addrto&cc, and addrbcc | ||
593 | - deliver to the to & cc addresses: | ||
594 | - if there are bcc addrs, remove the bcc field | ||
595 | - send the message to to & cc addrs: | ||
596 | mail from: me | ||
597 | rcpt to: public@com | ||
598 | rcpt to: pub2@com | ||
599 | data | ||
600 | ... | ||
601 | |||
602 | - deliver to the bcc addrs: | ||
603 | |||
604 | for a in (bccaddrs) | ||
605 | do | ||
606 | - add header field to msg, bcc: $a | ||
607 | - send the msg: | ||
608 | mail from: me | ||
609 | rcpt to: $a | ||
610 | data | ||
611 | ... | ||
612 | done | ||
613 | |||
614 | - quit smtp connection | ||
615 | |||
616 | */ | ||
617 | |||
618 | static int | ||
619 | smtp_send_message (mu_mailer_t mailer, mu_message_t argmsg, mu_address_t argfrom, | ||
620 | mu_address_t argto) | ||
621 | { | ||
622 | smtp_t smtp = NULL; | ||
623 | int status; | ||
624 | |||
625 | if (mailer == NULL) | ||
626 | return EINVAL; | ||
627 | |||
628 | smtp = mailer->data; | ||
629 | if (!smtp) | ||
630 | return EINVAL; | ||
631 | |||
632 | CHECK_SEND_RESUME (smtp, argmsg, argfrom, argto); | ||
633 | |||
634 | switch (smtp->state) | ||
635 | { | ||
636 | case SMTP_NO_STATE: | ||
637 | if (argmsg == NULL || argfrom == NULL) | ||
638 | return EINVAL; | ||
639 | |||
640 | smtp->argmsg = smtp->msg = argmsg; | ||
641 | smtp->argfrom = argfrom; | ||
642 | smtp->argto = argto; | ||
643 | |||
644 | status = mu_address_sget_email (smtp->argfrom, 1, &smtp->mail_from); | ||
645 | CHECK_ERROR (smtp, status); | ||
646 | |||
647 | status = _smtp_set_rcpt (smtp, smtp->argmsg, smtp->argto); | ||
648 | CHECK_ERROR (smtp, status); | ||
649 | |||
650 | /* Clear the Bcc: field if we found one. */ | ||
651 | if (message_has_bcc (smtp->argmsg)) | ||
652 | { | ||
653 | smtp->msg = NULL; | ||
654 | status = mu_message_create_copy (&smtp->msg, smtp->argmsg); | ||
655 | CHECK_ERROR (smtp, status); | ||
656 | |||
657 | status = message_set_header_value (smtp->msg, MU_HEADER_BCC, NULL); | ||
658 | CHECK_ERROR (smtp, status); | ||
659 | } | ||
660 | |||
661 | /* Begin bccing if there are not To: recipients. */ | ||
662 | if (smtp->rcpt_to_count == 0) | ||
663 | smtp->bccing = 1; | ||
664 | |||
665 | smtp->rcpt_index = 1; | ||
666 | |||
667 | smtp->state = SMTP_ENV_FROM; | ||
668 | |||
669 | case SMTP_ENV_FROM: | ||
670 | ENV_FROM: | ||
671 | { | ||
672 | status = smtp_writeline (smtp, "MAIL FROM: <%s>\r\n", smtp->mail_from); | ||
673 | CHECK_ERROR (smtp, status); | ||
674 | smtp->state = SMTP_MAIL_FROM; | ||
675 | } | ||
676 | |||
677 | /* We use a goto, since we may have multiple messages, | ||
678 | we come back here and doit all over again ... Not pretty. */ | ||
679 | case SMTP_MAIL_FROM: | ||
680 | status = smtp_write (smtp); | ||
681 | CHECK_EAGAIN (smtp, status); | ||
682 | smtp->state = SMTP_MAIL_FROM_ACK; | ||
683 | |||
684 | case SMTP_MAIL_FROM_ACK: | ||
685 | status = smtp_read_ack (smtp); | ||
686 | CHECK_EAGAIN (smtp, status); | ||
687 | if (smtp->buffer[0] != '2') | ||
688 | { | ||
689 | mu_stream_close (mailer->stream); | ||
690 | CLEAR_STATE (smtp); | ||
691 | return EACCES; | ||
692 | } | ||
693 | |||
694 | /* We use a goto, since we may have multiple recipients, | ||
695 | we come back here and do it all over again ... Not pretty. */ | ||
696 | case SMTP_ENV_RCPT: | ||
697 | ENV_RCPT: | ||
698 | { | ||
699 | mu_address_t addr = smtp->rcpt_to; | ||
700 | const char *to = NULL; | ||
701 | |||
702 | if (smtp->bccing) | ||
703 | addr = smtp->rcpt_bcc; | ||
704 | status = mu_address_sget_email (addr, smtp->rcpt_index, &to); | ||
705 | |||
706 | CHECK_ERROR (smtp, status); | ||
707 | |||
708 | /* Add the Bcc: field back in for recipient. */ | ||
709 | if (smtp->bccing) | ||
710 | { | ||
711 | status = message_set_header_value (smtp->msg, MU_HEADER_BCC, to); | ||
712 | CHECK_ERROR (smtp, status); | ||
713 | } | ||
714 | |||
715 | status = smtp_writeline (smtp, "RCPT TO: <%s>\r\n", to); | ||
716 | |||
717 | CHECK_ERROR (smtp, status); | ||
718 | |||
719 | smtp->state = SMTP_RCPT_TO; | ||
720 | smtp->rcpt_index++; | ||
721 | } | ||
722 | |||
723 | case SMTP_RCPT_TO: | ||
724 | status = smtp_write (smtp); | ||
725 | CHECK_EAGAIN (smtp, status); | ||
726 | smtp->state = SMTP_RCPT_TO_ACK; | ||
727 | |||
728 | case SMTP_RCPT_TO_ACK: | ||
729 | status = smtp_read_ack (smtp); | ||
730 | CHECK_EAGAIN (smtp, status); | ||
731 | if (smtp->buffer[0] != '2') | ||
732 | { | ||
733 | mu_stream_close (mailer->stream); | ||
734 | CLEAR_STATE (smtp); | ||
735 | return MU_ERR_SMTP_RCPT_FAILED; | ||
736 | } | ||
737 | /* Redo the receipt sequence for every To: and Cc: recipient. */ | ||
738 | if (!smtp->bccing && smtp->rcpt_index <= smtp->rcpt_to_count) | ||
739 | goto ENV_RCPT; | ||
740 | |||
741 | /* We are done with the rcpt. */ | ||
742 | status = smtp_writeline (smtp, "DATA\r\n"); | ||
743 | CHECK_ERROR (smtp, status); | ||
744 | smtp->state = SMTP_DATA; | ||
745 | |||
746 | case SMTP_DATA: | ||
747 | status = smtp_write (smtp); | ||
748 | CHECK_EAGAIN (smtp, status); | ||
749 | smtp->state = SMTP_DATA_ACK; | ||
750 | |||
751 | case SMTP_DATA_ACK: | ||
752 | status = smtp_read_ack (smtp); | ||
753 | CHECK_EAGAIN (smtp, status); | ||
754 | if (smtp->buffer[0] != '3') | ||
755 | { | ||
756 | mu_stream_close (mailer->stream); | ||
757 | CLEAR_STATE (smtp); | ||
758 | return EACCES; | ||
759 | } | ||
760 | smtp->offset = 0; | ||
761 | smtp->state = SMTP_SEND; | ||
762 | |||
763 | if ((smtp->mailer->flags & MAILER_FLAG_DEBUG_DATA) == 0) | ||
764 | MAILER_DEBUG0 (smtp->mailer, MU_DEBUG_PROT, "> (data...)\n"); | ||
765 | |||
766 | case SMTP_SEND: | ||
767 | { | ||
768 | mu_stream_t stream; | ||
769 | size_t n = 0; | ||
770 | char data[256] = ""; | ||
771 | mu_header_t hdr; | ||
772 | mu_body_t body; | ||
773 | int found_nl; | ||
774 | |||
775 | /* We may be here after an EAGAIN so check if we have something | ||
776 | in the buffer and flush it. */ | ||
777 | status = smtp_write (smtp); | ||
778 | CHECK_EAGAIN (smtp, status); | ||
779 | |||
780 | mu_message_get_header (smtp->msg, &hdr); | ||
781 | mu_header_get_stream (hdr, &stream); | ||
782 | while ((status = mu_stream_readline (stream, data, sizeof (data), | ||
783 | smtp->offset, &n)) == 0 && n > 0) | ||
784 | { | ||
785 | int nl; | ||
786 | |||
787 | found_nl = (n == 1 && data[0] == '\n'); | ||
788 | if ((nl = (data[n - 1] == '\n'))) | ||
789 | data[n - 1] = '\0'; | ||
790 | if (data[0] == '.') | ||
791 | { | ||
792 | status = smtp_writeline (smtp, ".%s", data); | ||
793 | CHECK_ERROR (smtp, status); | ||
794 | } | ||
795 | else if (strncasecmp (data, MU_HEADER_FCC, | ||
796 | sizeof (MU_HEADER_FCC) - 1)) | ||
797 | { | ||
798 | status = smtp_writeline (smtp, "%s", data); | ||
799 | CHECK_ERROR (smtp, status); | ||
800 | status = smtp_write (smtp); | ||
801 | CHECK_EAGAIN (smtp, status); | ||
802 | } | ||
803 | else | ||
804 | nl = 0; | ||
805 | |||
806 | if (nl) | ||
807 | { | ||
808 | status = smtp_writeline (smtp, "\r\n"); | ||
809 | CHECK_ERROR (smtp, status); | ||
810 | status = smtp_write (smtp); | ||
811 | CHECK_EAGAIN (smtp, status); | ||
812 | } | ||
813 | smtp->offset += n; | ||
814 | } | ||
815 | |||
816 | if (!found_nl) | ||
817 | { | ||
818 | status = smtp_writeline (smtp, "\r\n"); | ||
819 | CHECK_ERROR (smtp, status); | ||
820 | status = smtp_write (smtp); | ||
821 | CHECK_EAGAIN (smtp, status); | ||
822 | } | ||
823 | |||
824 | mu_message_get_body (smtp->msg, &body); | ||
825 | mu_body_get_stream (body, &stream); | ||
826 | smtp->offset = 0; | ||
827 | while ((status = mu_stream_readline (stream, data, sizeof (data) - 1, | ||
828 | smtp->offset, &n)) == 0 && n > 0) | ||
829 | { | ||
830 | if (data[n - 1] == '\n') | ||
831 | data[n - 1] = '\0'; | ||
832 | if (data[0] == '.') | ||
833 | status = smtp_writeline (smtp, ".%s\r\n", data); | ||
834 | else | ||
835 | status = smtp_writeline (smtp, "%s\r\n", data); | ||
836 | CHECK_ERROR (smtp, status); | ||
837 | status = smtp_write (smtp); | ||
838 | CHECK_EAGAIN (smtp, status); | ||
839 | smtp->offset += n; | ||
840 | } | ||
841 | |||
842 | smtp->offset = 0; | ||
843 | status = smtp_writeline (smtp, ".\r\n"); | ||
844 | CHECK_ERROR (smtp, status); | ||
845 | smtp->state = SMTP_SEND_DOT; | ||
846 | } | ||
847 | |||
848 | case SMTP_SEND_DOT: | ||
849 | status = smtp_write (smtp); | ||
850 | CHECK_EAGAIN (smtp, status); | ||
851 | smtp->state = SMTP_SEND_ACK; | ||
852 | |||
853 | case SMTP_SEND_ACK: | ||
854 | status = smtp_read_ack (smtp); | ||
855 | CHECK_EAGAIN (smtp, status); | ||
856 | if (smtp->buffer[0] != '2') | ||
857 | { | ||
858 | mu_stream_close (mailer->stream); | ||
859 | CLEAR_STATE (smtp); | ||
860 | return EACCES; | ||
861 | } | ||
862 | |||
863 | /* Decide whether we need to loop again, to deliver to Bcc: | ||
864 | recipients. */ | ||
865 | if (!smtp->bccing) | ||
866 | { | ||
867 | smtp->bccing = 1; | ||
868 | smtp->rcpt_index = 1; | ||
869 | } | ||
870 | if (smtp->rcpt_index <= smtp->rcpt_bcc_count) | ||
871 | goto ENV_FROM; | ||
872 | |||
873 | mu_observable_notify (mailer->observable, MU_EVT_MAILER_MESSAGE_SENT); | ||
874 | |||
875 | default: | ||
876 | break; | ||
877 | } | ||
878 | CLEAR_STATE (smtp); | ||
879 | return 0; | ||
880 | } | ||
881 | |||
882 | int | ||
883 | smtp_address_add (mu_address_t *paddr, const char *value) | ||
884 | { | ||
885 | mu_address_t addr = NULL; | ||
886 | int status; | ||
887 | |||
888 | status = mu_address_create (&addr, value); | ||
889 | if (status) | ||
890 | return status; | ||
891 | status = mu_address_union (paddr, addr); | ||
892 | mu_address_destroy (&addr); | ||
893 | return status; | ||
894 | } | ||
895 | |||
896 | static int | ||
897 | _smtp_property_is_set (smtp_t smtp, const char *name) | ||
898 | { | ||
899 | mu_property_t property = NULL; | ||
900 | |||
901 | mu_mailer_get_property (smtp->mailer, &property); | ||
902 | return mu_property_is_set (property, name); | ||
903 | } | ||
904 | |||
905 | static int | ||
906 | _smtp_set_rcpt (smtp_t smtp, mu_message_t msg, mu_address_t to) | ||
907 | { | ||
908 | int status = 0; | ||
909 | mu_header_t header = NULL; | ||
910 | char *value; | ||
911 | |||
912 | /* Get RCPT_TO from TO, or the message. */ | ||
913 | |||
914 | if (to) | ||
915 | { | ||
916 | /* Use the specified mu_address_t. */ | ||
917 | if ((status = mu_mailer_check_to (to)) != 0) | ||
918 | { | ||
919 | MAILER_DEBUG0 (smtp->mailer, MU_DEBUG_ERROR, | ||
920 | "mu_mailer_send_message(): explicit to not valid\n"); | ||
921 | return status; | ||
922 | } | ||
923 | smtp->rcpt_to = to; | ||
924 | mu_address_get_count (smtp->rcpt_to, &smtp->rcpt_to_count); | ||
925 | |||
926 | if (status) | ||
927 | return status; | ||
928 | } | ||
929 | |||
930 | if (!to || _smtp_property_is_set (smtp, "READ_RECIPIENTS")) | ||
931 | { | ||
932 | if ((status = mu_message_get_header (msg, &header))) | ||
933 | return status; | ||
934 | |||
935 | status = mu_header_aget_value (header, MU_HEADER_TO, &value); | ||
936 | |||
937 | if (status == 0) | ||
938 | { | ||
939 | smtp_address_add (&smtp->rcpt_to, value); | ||
940 | free (value); | ||
941 | } | ||
942 | else if (status != MU_ERR_NOENT) | ||
943 | goto end; | ||
944 | |||
945 | status = mu_header_aget_value (header, MU_HEADER_CC, &value); | ||
946 | |||
947 | if (status == 0) | ||
948 | { | ||
949 | smtp_address_add (&smtp->rcpt_to, value); | ||
950 | free (value); | ||
951 | } | ||
952 | else if (status != MU_ERR_NOENT) | ||
953 | goto end; | ||
954 | |||
955 | status = mu_header_aget_value (header, MU_HEADER_BCC, &value); | ||
956 | if (status == 0) | ||
957 | { | ||
958 | smtp_address_add (&smtp->rcpt_bcc, value); | ||
959 | free (value); | ||
960 | } | ||
961 | else if (status != MU_ERR_NOENT) | ||
962 | goto end; | ||
963 | |||
964 | /* If to or bcc is present, the must be OK. */ | ||
965 | if (smtp->rcpt_to && (status = mu_mailer_check_to (smtp->rcpt_to))) | ||
966 | goto end; | ||
967 | |||
968 | if (smtp->rcpt_bcc && (status = mu_mailer_check_to (smtp->rcpt_bcc))) | ||
969 | goto end; | ||
970 | } | ||
971 | |||
972 | end: | ||
973 | |||
974 | if (status) | ||
975 | { | ||
976 | mu_address_destroy (&smtp->rcpt_to); | ||
977 | mu_address_destroy (&smtp->rcpt_bcc); | ||
978 | } | ||
979 | else | ||
980 | { | ||
981 | if (smtp->rcpt_to) | ||
982 | mu_address_get_count (smtp->rcpt_to, &smtp->rcpt_to_count); | ||
983 | |||
984 | if (smtp->rcpt_bcc) | ||
985 | mu_address_get_count (smtp->rcpt_bcc, &smtp->rcpt_bcc_count); | ||
986 | |||
987 | if (smtp->rcpt_to_count + smtp->rcpt_bcc_count == 0) | ||
988 | status = MU_ERR_MAILER_NO_RCPT_TO; | ||
989 | } | ||
990 | |||
991 | return status; | ||
992 | } | ||
993 | |||
994 | /* C99 says that a conforming implementations of snprintf () | ||
995 | should return the number of char that would have been call | ||
996 | but many GNU/Linux && BSD implementations return -1 on error. | ||
997 | Worse QNX/Neutrino actually does not put the terminal | ||
998 | null char. So let's try to cope. */ | ||
999 | static int | ||
1000 | smtp_writeline (smtp_t smtp, const char *format, ...) | ||
1001 | { | ||
1002 | int len; | ||
1003 | va_list ap; | ||
1004 | int done = 1; | ||
1005 | |||
1006 | va_start (ap, format); | ||
1007 | do | ||
1008 | { | ||
1009 | len = vsnprintf (smtp->buffer, smtp->buflen - 1, format, ap); | ||
1010 | if (len < 0 || (len >= (int) smtp->buflen) | ||
1011 | || !memchr (smtp->buffer, '\0', len + 1)) | ||
1012 | { | ||
1013 | char *buffer = NULL; | ||
1014 | size_t buflen = smtp->buflen * 2; | ||
1015 | buffer = realloc (smtp->buffer, buflen); | ||
1016 | if (smtp->buffer == NULL) | ||
1017 | return ENOMEM; | ||
1018 | smtp->buffer = buffer; | ||
1019 | smtp->buflen = buflen; | ||
1020 | done = 0; | ||
1021 | } | ||
1022 | else | ||
1023 | done = 1; | ||
1024 | } | ||
1025 | while (!done); | ||
1026 | |||
1027 | va_end (ap); | ||
1028 | |||
1029 | smtp->ptr = smtp->buffer + len; | ||
1030 | |||
1031 | while (len > 0 && isspace (smtp->buffer[len - 1])) | ||
1032 | len--; | ||
1033 | |||
1034 | if ((smtp->state != SMTP_SEND && smtp->state != SMTP_SEND_DOT) | ||
1035 | || smtp->mailer->flags & MAILER_FLAG_DEBUG_DATA) | ||
1036 | { | ||
1037 | MAILER_DEBUG2 (smtp->mailer, MU_DEBUG_PROT, "> %.*s\n", len, | ||
1038 | smtp->buffer); | ||
1039 | } | ||
1040 | |||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | static int | ||
1045 | smtp_write (smtp_t smtp) | ||
1046 | { | ||
1047 | int status = 0; | ||
1048 | size_t len; | ||
1049 | if (smtp->ptr > smtp->buffer) | ||
1050 | { | ||
1051 | len = smtp->ptr - smtp->buffer; | ||
1052 | status = mu_stream_write (smtp->mailer->stream, smtp->buffer, len, | ||
1053 | 0, &len); | ||
1054 | if (status == 0) | ||
1055 | { | ||
1056 | memmove (smtp->buffer, smtp->buffer + len, len); | ||
1057 | smtp->ptr -= len; | ||
1058 | } | ||
1059 | } | ||
1060 | else | ||
1061 | { | ||
1062 | smtp->ptr = smtp->buffer; | ||
1063 | len = 0; | ||
1064 | } | ||
1065 | return status; | ||
1066 | } | ||
1067 | |||
1068 | static int | ||
1069 | smtp_read_ack (smtp_t smtp) | ||
1070 | { | ||
1071 | int status; | ||
1072 | int multi; | ||
1073 | |||
1074 | do | ||
1075 | { | ||
1076 | multi = 0; | ||
1077 | status = smtp_readline (smtp); | ||
1078 | if ((smtp->ptr - smtp->buffer) > 4 && smtp->buffer[3] == '-') | ||
1079 | multi = 1; | ||
1080 | if (status == 0) | ||
1081 | smtp->ptr = smtp->buffer; | ||
1082 | } | ||
1083 | while (multi && status == 0); | ||
1084 | |||
1085 | if (status == 0) | ||
1086 | smtp->ptr = smtp->buffer; | ||
1087 | return status; | ||
1088 | } | ||
1089 | |||
1090 | static int | ||
1091 | smtp_parse_ehlo_ack (smtp_t smtp) | ||
1092 | { | ||
1093 | int status; | ||
1094 | int multi; | ||
1095 | |||
1096 | do | ||
1097 | { | ||
1098 | multi = 0; | ||
1099 | status = smtp_readline (smtp); | ||
1100 | if ((smtp->ptr - smtp->buffer) > 4 && smtp->buffer[3] == '-') | ||
1101 | multi = 1; | ||
1102 | if (status == 0) { | ||
1103 | smtp->ptr = smtp->buffer; | ||
1104 | |||
1105 | if (!strncasecmp (smtp->buffer, "250-STARTTLS", 12)) | ||
1106 | smtp->capa |= CAPA_STARTTLS; | ||
1107 | } | ||
1108 | } | ||
1109 | while (multi && status == 0); | ||
1110 | |||
1111 | if (status == 0) | ||
1112 | smtp->ptr = smtp->buffer; | ||
1113 | return status; | ||
1114 | } | ||
1115 | |||
1116 | /* Read a complete line form the pop server. Transform CRLF to LF, | ||
1117 | put a null in the buffer when done. */ | ||
1118 | static int | ||
1119 | smtp_readline (smtp_t smtp) | ||
1120 | { | ||
1121 | size_t n = 0; | ||
1122 | size_t total = smtp->ptr - smtp->buffer; | ||
1123 | int status; | ||
1124 | |||
1125 | /* Must get a full line before bailing out. */ | ||
1126 | do | ||
1127 | { | ||
1128 | status = mu_stream_readline (smtp->mailer->stream, smtp->buffer + total, | ||
1129 | smtp->buflen - total, smtp->s_offset, &n); | ||
1130 | if (status != 0) | ||
1131 | return status; | ||
1132 | |||
1133 | /* Server went away, consider this like an error. */ | ||
1134 | if (n == 0) | ||
1135 | return EIO; | ||
1136 | |||
1137 | total += n; | ||
1138 | smtp->s_offset += n; | ||
1139 | smtp->nl = memchr (smtp->buffer, '\n', total); | ||
1140 | if (smtp->nl == NULL) /* Do we have a full line. */ | ||
1141 | { | ||
1142 | /* Allocate a bigger buffer ? */ | ||
1143 | if (total >= smtp->buflen - 1) | ||
1144 | { | ||
1145 | smtp->buflen *= 2; | ||
1146 | smtp->buffer = realloc (smtp->buffer, smtp->buflen + 1); | ||
1147 | if (smtp->buffer == NULL) | ||
1148 | return ENOMEM; | ||
1149 | } | ||
1150 | } | ||
1151 | smtp->ptr = smtp->buffer + total; | ||
1152 | } | ||
1153 | while (smtp->nl == NULL); | ||
1154 | |||
1155 | /* \r\n --> \n\0 */ | ||
1156 | if (smtp->nl > smtp->buffer) | ||
1157 | { | ||
1158 | *(smtp->nl - 1) = '\n'; | ||
1159 | *(smtp->nl) = '\0'; | ||
1160 | smtp->ptr = smtp->nl; | ||
1161 | } | ||
1162 | |||
1163 | MAILER_DEBUG1 (smtp->mailer, MU_DEBUG_PROT, "< %s", smtp->buffer); | ||
1164 | |||
1165 | return 0; | ||
1166 | } | ||
1167 | |||
1168 | #else | ||
1169 | #include <stdio.h> | ||
1170 | #include <registrar0.h> | ||
1171 | mu_record_t mu_smtp_record = NULL; | ||
1172 | #endif |
-
Please register or sign in to post a comment