Mercurial > masqmail
comparison src/smtp_out.c @ 0:08114f7dcc23 0.2.21
this is masqmail-0.2.21 from oliver kurth
author | meillo@marmaro.de |
---|---|
date | Fri, 26 Sep 2008 17:05:23 +0200 |
parents | |
children | 26e34ae9a3e3 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:08114f7dcc23 |
---|---|
1 /* smtp_out.c, Copyright (C) 1999-2001 Oliver Kurth, | |
2 * | |
3 * This program is free software; you can redistribute it and/or modify | |
4 * it under the terms of the GNU General Public License as published by | |
5 * the Free Software Foundation; either version 2 of the License, or | |
6 * (at your option) any later version. | |
7 * | |
8 * This program is distributed in the hope that it will be useful, | |
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 * GNU General Public License for more details. | |
12 * | |
13 * You should have received a copy of the GNU General Public License | |
14 * along with this program; if not, write to the Free Software | |
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
16 */ | |
17 | |
18 /* | |
19 send bugs to: kurth@innominate.de | |
20 */ | |
21 | |
22 /* | |
23 I always forget these rfc numbers: | |
24 RFC 821 (SMTP) | |
25 RFC 1869 (ESMTP) | |
26 RFC 1870 (ESMTP SIZE) | |
27 RFC 2197 (ESMTP PIPELINE) | |
28 RFC 2554 (ESMTP AUTH) | |
29 */ | |
30 | |
31 #include "masqmail.h" | |
32 #include "smtp_out.h" | |
33 #include "readsock.h" | |
34 | |
35 #ifdef ENABLE_AUTH | |
36 | |
37 #ifdef USE_LIB_CRYPTO | |
38 #include <openssl/hmac.h> | |
39 #include <openssl/md5.h> | |
40 #include <openssl/evp.h> | |
41 #else | |
42 #include "md5/global.h" | |
43 #include "md5/md5.h" | |
44 #include "md5/hmac_md5.h" | |
45 #endif | |
46 | |
47 #include "base64/base64.h" | |
48 #endif | |
49 | |
50 void destroy_smtpbase(smtp_base *psb) | |
51 { | |
52 fclose(psb->in); | |
53 fclose(psb->out); | |
54 | |
55 close(psb->sock); | |
56 | |
57 if(psb->helo_name) g_free(psb->helo_name); | |
58 if(psb->buffer) g_free(psb->buffer); | |
59 if(psb->auth_names) g_strfreev(psb->auth_names); | |
60 | |
61 if(psb->auth_name) g_free(psb->auth_name); | |
62 if(psb->auth_login) g_free(psb->auth_login); | |
63 if(psb->auth_secret) g_free(psb->auth_secret); | |
64 } | |
65 | |
66 gchar *set_heloname(smtp_base *psb, gchar *default_name, gboolean do_correct) | |
67 { | |
68 struct sockaddr_in sname; | |
69 int len = sizeof(struct sockaddr_in); | |
70 struct hostent *host_entry; | |
71 | |
72 if(do_correct){ | |
73 getsockname(psb->sock, (struct sockaddr *)(&sname), &len); | |
74 DEBUG(5) debugf("socket: name.sin_addr = %s\n", inet_ntoa(sname.sin_addr)); | |
75 host_entry = | |
76 gethostbyaddr((const char *)&(sname.sin_addr), | |
77 sizeof(sname.sin_addr), AF_INET); | |
78 if(host_entry){ | |
79 psb->helo_name = g_strdup(host_entry->h_name); | |
80 }else{ | |
81 /* we failed to look up our own name. Instead of giving our local hostname, | |
82 we may give our IP number to show the server that we are at least | |
83 willing to be honest. For the really picky ones.*/ | |
84 DEBUG(5) debugf("failed to look up own host name.\n"); | |
85 psb->helo_name = g_strdup_printf("[%s]", inet_ntoa(sname.sin_addr)); | |
86 } | |
87 DEBUG(5) debugf("helo_name = %s\n", psb->helo_name); | |
88 } | |
89 if(psb->helo_name == NULL){ | |
90 psb->helo_name = g_strdup(default_name); | |
91 } | |
92 return psb->helo_name; | |
93 } | |
94 | |
95 #ifdef ENABLE_AUTH | |
96 | |
97 gboolean set_auth(smtp_base *psb, gchar *name, gchar *login, gchar *secret) | |
98 { | |
99 if((strcasecmp(name, "CRAM-MD5") == 0) || | |
100 (strcasecmp(name, "LOGIN") == 0)) { | |
101 psb->auth_name = g_strdup(name); | |
102 psb->auth_login = g_strdup(login); | |
103 psb->auth_secret = g_strdup(secret); | |
104 | |
105 return TRUE; | |
106 } | |
107 return FALSE; | |
108 } | |
109 | |
110 #endif | |
111 | |
112 static | |
113 smtp_base *create_smtpbase(gint sock) | |
114 { | |
115 gint dup_sock; | |
116 | |
117 smtp_base *psb = (smtp_base *)g_malloc(sizeof(smtp_base)); | |
118 | |
119 psb->sock = sock; | |
120 | |
121 psb->use_esmtp = FALSE; | |
122 psb->use_size = FALSE; | |
123 psb->use_pipelining = FALSE; | |
124 psb->use_auth = FALSE; | |
125 | |
126 psb->max_size = 0; | |
127 psb->auth_names = NULL; | |
128 | |
129 psb->buffer = (gchar *)g_malloc(SMTP_BUF_LEN); | |
130 | |
131 dup_sock = dup(sock); | |
132 psb->out = fdopen(sock, "w"); | |
133 psb->in = fdopen(dup_sock, "r"); | |
134 | |
135 psb->error = smtp_ok; | |
136 | |
137 psb->helo_name = NULL; | |
138 | |
139 psb->auth_name = psb->auth_login = psb->auth_secret = NULL; | |
140 | |
141 return psb; | |
142 } | |
143 | |
144 static | |
145 gboolean read_response(smtp_base *psb, int timeout) | |
146 { | |
147 gint buf_pos = 0; | |
148 gchar code[5]; | |
149 gint i, len; | |
150 | |
151 do{ | |
152 len = read_sockline(psb->in, &(psb->buffer[buf_pos]), | |
153 SMTP_BUF_LEN - buf_pos, timeout, READSOCKL_CHUG); | |
154 if(len == -3){ | |
155 psb->error = smtp_timeout; | |
156 return FALSE; | |
157 } | |
158 else if(len == -2){ | |
159 psb->error = smtp_syntax; | |
160 return FALSE; | |
161 } | |
162 else if(len == -1){ | |
163 psb->error = smtp_eof; | |
164 return FALSE; | |
165 } | |
166 for(i = 0; i < 4; i++) | |
167 code[i] = psb->buffer[buf_pos+i]; | |
168 code[i] = 0; | |
169 psb->last_code = atoi(code); | |
170 | |
171 buf_pos += len; | |
172 | |
173 }while(code[3] == '-'); | |
174 | |
175 return TRUE; | |
176 } | |
177 | |
178 static | |
179 gboolean check_response(smtp_base *psb, gboolean after_data) | |
180 { | |
181 char c = psb->buffer[0]; | |
182 | |
183 if(((c == '2') && !after_data) || ((c == '3') && after_data)){ | |
184 psb->error = smtp_ok; | |
185 DEBUG(6) debugf("response OK:'%s' after_date = %d\n", psb->buffer, (int)after_data); | |
186 return TRUE; | |
187 }else{ | |
188 if(c == '4') | |
189 psb->error = smtp_trylater; | |
190 else if(c == '5') | |
191 psb->error = smtp_fail; | |
192 else | |
193 psb->error = smtp_syntax; | |
194 DEBUG(6) debugf("response failure:'%s' after_date = %d\n", psb->buffer, (int)after_data); | |
195 return FALSE; | |
196 } | |
197 } | |
198 | |
199 static | |
200 gboolean check_init_response(smtp_base *psb) | |
201 { | |
202 if(check_response(psb, FALSE)){ | |
203 psb->use_esmtp = (strstr(psb->buffer, "ESMTP") != NULL); | |
204 | |
205 DEBUG(4) debugf(psb->use_esmtp ? "uses esmtp\n" : "no esmtp\n"); | |
206 | |
207 return TRUE; | |
208 } | |
209 return FALSE; | |
210 } | |
211 | |
212 static | |
213 gchar *get_response_arg(gchar *response) | |
214 { | |
215 gchar buf[SMTP_BUF_LEN]; | |
216 gchar *p = response, *q = buf; | |
217 | |
218 while(*p && (*p != '\n') && isspace(*p)) p++; | |
219 if(*p && (*p != '\n')){ | |
220 while(*p && (*p != '\n') && (*p != '\r') && (q < buf+SMTP_BUF_LEN-1)) *(q++) = *(p++); | |
221 *q = 0; | |
222 return g_strdup(buf); | |
223 } | |
224 return NULL; | |
225 } | |
226 | |
227 static | |
228 gboolean check_helo_response(smtp_base *psb) | |
229 { | |
230 gchar *ptr = psb->buffer; | |
231 | |
232 if(!check_response(psb, FALSE)) | |
233 return FALSE; | |
234 | |
235 while(*ptr){ | |
236 if(strncasecmp(&(ptr[4]), "SIZE", 4) == 0){ | |
237 gchar *arg; | |
238 psb->use_size = TRUE; | |
239 arg = get_response_arg(&(ptr[8])); | |
240 if(arg){ | |
241 psb->max_size = atoi(arg); | |
242 g_free(arg); | |
243 } | |
244 } | |
245 | |
246 if(strncasecmp(&(ptr[4]), "PIPELINING", 10) == 0) | |
247 psb->use_pipelining = TRUE; | |
248 | |
249 if(strncasecmp(&(ptr[4]), "AUTH", 4) == 0){ | |
250 if((ptr[8] == ' ') || (ptr[8] == '=') || (ptr[8] == '\t')){ /* not sure about '\t' */ | |
251 gchar *arg; | |
252 psb->use_auth = TRUE; | |
253 arg = get_response_arg(&(ptr[9])); /* after several years I finally learnt to count */ | |
254 if(arg){ | |
255 psb->auth_names = g_strsplit(arg, " " , 0); | |
256 g_free(arg); | |
257 | |
258 DEBUG(4){ | |
259 gint i = 0; | |
260 while(psb->auth_names[i]){ | |
261 debugf("offered AUTH %s\n", psb->auth_names[i]); | |
262 i++; | |
263 } | |
264 } | |
265 } | |
266 } | |
267 } | |
268 | |
269 while(*ptr != '\n') ptr++; | |
270 ptr++; | |
271 } | |
272 | |
273 DEBUG(4){ | |
274 debugf(psb->use_size ? "uses SIZE\n" : "no size\n"); | |
275 debugf(psb->use_pipelining ? "uses PIPELINING\n" : "no pipelining\n"); | |
276 debugf(psb->use_auth ? "uses AUTH\n" : "no auth\n"); | |
277 } | |
278 | |
279 return TRUE; | |
280 } | |
281 | |
282 static | |
283 gboolean smtp_helo(smtp_base *psb, gchar *helo) | |
284 { | |
285 while(TRUE){ | |
286 if(psb->use_esmtp){ | |
287 fprintf(psb->out, "EHLO %s\r\n", helo); fflush(psb->out); | |
288 | |
289 DEBUG(4) debugf("EHLO %s\r\n", helo); | |
290 | |
291 }else{ | |
292 fprintf(psb->out, "HELO %s\r\n", helo); fflush(psb->out); | |
293 | |
294 DEBUG(4) debugf("HELO %s\r\n", helo); | |
295 | |
296 } | |
297 | |
298 if(!read_response(psb, SMTP_CMD_TIMEOUT)) | |
299 return FALSE; | |
300 | |
301 if(check_helo_response(psb)) | |
302 return TRUE; | |
303 else{ | |
304 if(psb->error == smtp_fail){ | |
305 if(psb->use_esmtp){ | |
306 /* our guess that server understands EHLO was wrong, | |
307 try again with HELO | |
308 */ | |
309 psb->use_esmtp = FALSE; | |
310 }else{ | |
311 /* what sort of server ist THAT ?! | |
312 give up... | |
313 */ | |
314 return FALSE; | |
315 } | |
316 }else | |
317 return FALSE; | |
318 } | |
319 } | |
320 } | |
321 | |
322 static | |
323 void smtp_cmd_mailfrom(smtp_base *psb, address *return_path, guint size) | |
324 { | |
325 if(psb->use_size){ | |
326 fprintf(psb->out, "MAIL FROM:%s SIZE=%d\r\n", | |
327 addr_string(return_path), size); | |
328 fflush(psb->out); | |
329 | |
330 DEBUG(4) debugf("MAIL FROM:%s SIZE=%d\r\n", | |
331 addr_string(return_path), size); | |
332 | |
333 }else{ | |
334 fprintf(psb->out, "MAIL FROM:%s\r\n", addr_string(return_path)); | |
335 fflush(psb->out); | |
336 | |
337 DEBUG(4) debugf("MAIL FROM:%s\r\n", addr_string(return_path)); | |
338 } | |
339 } | |
340 | |
341 static | |
342 void smtp_cmd_rcptto(smtp_base *psb, address *rcpt) | |
343 { | |
344 fprintf(psb->out, "RCPT TO:%s\r\n", addr_string(rcpt)); | |
345 fflush(psb->out); | |
346 DEBUG(4) debugf("RCPT TO:%s\n", addr_string(rcpt)); | |
347 } | |
348 | |
349 static | |
350 void send_data_line(smtp_base *psb, gchar *data) | |
351 { | |
352 /* According to RFC 821 each line should be terminated with CRLF. | |
353 Since a dot on a line itself marks the end of data, each line | |
354 beginning with a dot is prepended with another dot. | |
355 */ | |
356 gchar *ptr; | |
357 gboolean new_line = TRUE; /* previous versions assumed that each item was | |
358 exactly one line. This is no longer the case */ | |
359 | |
360 ptr = data; | |
361 while(*ptr){ | |
362 int c = (int)(*ptr); | |
363 if(c == '.') | |
364 if(new_line) | |
365 putc('.', psb->out); | |
366 if(c == '\n'){ | |
367 putc('\r', psb->out); | |
368 putc('\n', psb->out); | |
369 new_line = TRUE; | |
370 }else{ | |
371 putc(c, psb->out); | |
372 new_line = FALSE; | |
373 } | |
374 ptr++; | |
375 } | |
376 } | |
377 | |
378 static | |
379 void send_header(smtp_base *psb, GList *hdr_list) | |
380 { | |
381 GList *node; | |
382 gint num_hdrs = 0; | |
383 | |
384 /* header */ | |
385 if(hdr_list){ | |
386 foreach(hdr_list, node){ | |
387 if(node->data){ | |
388 header *hdr = (header *)(node->data); | |
389 if(hdr->header){ | |
390 send_data_line(psb, hdr->header); | |
391 num_hdrs++; | |
392 } | |
393 } | |
394 } | |
395 } | |
396 | |
397 /* empty line separating headers from data: */ | |
398 putc('\r', psb->out); | |
399 putc('\n', psb->out); | |
400 | |
401 DEBUG(4) debugf("sent %d headers\n", num_hdrs); | |
402 } | |
403 | |
404 static | |
405 void send_data(smtp_base *psb, message *msg) | |
406 { | |
407 GList *node; | |
408 gint num_lines = 0; | |
409 | |
410 /* data */ | |
411 if(msg->data_list){ | |
412 for(node = g_list_first(msg->data_list); node; node = g_list_next(node)){ | |
413 if(node->data){ | |
414 send_data_line(psb, node->data); | |
415 num_lines++; | |
416 } | |
417 } | |
418 } | |
419 | |
420 DEBUG(4) debugf("sent %d lines of data\n", num_lines); | |
421 | |
422 fprintf(psb->out, ".\r\n"); | |
423 fflush(psb->out); | |
424 } | |
425 | |
426 void smtp_out_mark_rcpts(smtp_base *psb, GList *rcpt_list) | |
427 { | |
428 GList *rcpt_node; | |
429 for(rcpt_node = g_list_first(rcpt_list); | |
430 rcpt_node; | |
431 rcpt_node = g_list_next(rcpt_node)){ | |
432 address *rcpt = (address *)(rcpt_node->data); | |
433 | |
434 addr_unmark_delivered(rcpt); | |
435 | |
436 if((psb->error == smtp_trylater) || (psb->error == smtp_timeout) || | |
437 (psb->error == smtp_eof)){ | |
438 addr_mark_defered(rcpt); | |
439 }else{ | |
440 addr_mark_failed(rcpt); | |
441 } | |
442 } | |
443 } | |
444 | |
445 void smtp_out_log_failure(smtp_base *psb, message *msg) | |
446 { | |
447 gchar *err_str; | |
448 | |
449 if(psb->error == smtp_timeout) | |
450 err_str = g_strdup("connection timed out."); | |
451 else if(psb->error == smtp_eof) | |
452 err_str = g_strdup("connection terminated prematurely."); | |
453 else if(psb->error == smtp_syntax) | |
454 err_str = g_strdup_printf("got unexpected response: %s", psb->buffer); | |
455 else if(psb->error == smtp_cancel) | |
456 err_str = g_strdup("delivery was canceled.\n"); | |
457 else | |
458 /* error message should still be in the buffer */ | |
459 err_str = g_strdup_printf("failed: %s\n", psb->buffer); | |
460 | |
461 if(msg == NULL) | |
462 logwrite(LOG_NOTICE, "host=%s %s\n", | |
463 psb->remote_host, err_str); | |
464 else | |
465 logwrite(LOG_NOTICE, "%s == host=%s %s\n", | |
466 msg->uid, psb->remote_host, err_str); | |
467 | |
468 g_free(err_str); | |
469 } | |
470 | |
471 smtp_base *smtp_out_open(gchar *host, gint port, GList *resolve_list) | |
472 { | |
473 smtp_base *psb; | |
474 gint sock; | |
475 mxip_addr *addr; | |
476 | |
477 DEBUG(5) debugf("smtp_out_open entered, host = %s\n", host); | |
478 | |
479 if((addr = connect_resolvelist(&sock, host, port, resolve_list))){ | |
480 /* create structure to hold status data: */ | |
481 psb = create_smtpbase(sock); | |
482 psb->remote_host = addr->name; | |
483 | |
484 DEBUG(5){ | |
485 struct sockaddr_in name; | |
486 int len = sizeof(struct sockaddr); | |
487 getsockname(sock, (struct sockaddr *)(&name), &len); | |
488 debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr)); | |
489 } | |
490 return psb; | |
491 }else{ | |
492 DEBUG(5) debugf("connect_resolvelist failed: %s %s\n", strerror(errno), hstrerror(h_errno)); | |
493 } | |
494 | |
495 return NULL; | |
496 } | |
497 | |
498 smtp_base *smtp_out_open_child(gchar *cmd) | |
499 { | |
500 smtp_base *psb; | |
501 gint sock; | |
502 | |
503 DEBUG(5) debugf("smtp_out_open_child entered, cmd = %s\n", cmd); | |
504 | |
505 sock = child(cmd); | |
506 | |
507 if(sock > 0){ | |
508 psb = create_smtpbase(sock); | |
509 psb->remote_host = NULL; | |
510 | |
511 return psb; | |
512 } | |
513 | |
514 return NULL; | |
515 } | |
516 | |
517 gboolean smtp_out_rset(smtp_base *psb) | |
518 { | |
519 gboolean ok; | |
520 | |
521 fprintf(psb->out, "RSET\r\n"); fflush(psb->out); | |
522 DEBUG(4) debugf("RSET\n"); | |
523 | |
524 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))) | |
525 if(check_response(psb, FALSE)) | |
526 return TRUE; | |
527 | |
528 smtp_out_log_failure(psb, NULL); | |
529 | |
530 return FALSE; | |
531 } | |
532 | |
533 #ifdef ENABLE_AUTH | |
534 | |
535 static | |
536 gboolean smtp_out_auth_cram_md5(smtp_base *psb) | |
537 { | |
538 gboolean ok = FALSE; | |
539 | |
540 fprintf(psb->out, "AUTH CRAM-MD5\r\n"); fflush(psb->out); | |
541 DEBUG(4) debugf("AUTH CRAM-MD5\n"); | |
542 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){ | |
543 if((ok = check_response(psb, TRUE))){ | |
544 gchar *chall64 = get_response_arg(&(psb->buffer[4])); | |
545 gint chall_size; | |
546 gchar *chall = base64_decode(chall64, &chall_size); | |
547 guchar digest[16], *reply64, *reply; | |
548 gchar digest_string[33]; | |
549 gint i; | |
550 #ifdef USE_LIB_CRYPTO | |
551 unsigned int digest_len; | |
552 #endif | |
553 | |
554 DEBUG(5) debugf("encoded challenge = %s\n", chall64); | |
555 DEBUG(5) debugf("decoded challenge = %s, size = %d\n", chall, chall_size); | |
556 | |
557 DEBUG(5) debugf("secret = %s\n", psb->auth_secret); | |
558 | |
559 #ifdef USE_LIB_CRYPTO | |
560 HMAC(EVP_md5(), psb->auth_secret, strlen(psb->auth_secret), chall, chall_size, digest, &digest_len); | |
561 #else | |
562 hmac_md5(chall, chall_size, psb->auth_secret, strlen(psb->auth_secret), digest); | |
563 #endif | |
564 | |
565 for(i = 0; i < 16; i++) | |
566 sprintf(&(digest_string[i+i]), "%02x", (unsigned int)(digest[i])); | |
567 digest_string[32] = 0; | |
568 | |
569 DEBUG(5) debugf("digest = %s\n", digest_string); | |
570 | |
571 reply = g_strdup_printf("%s %s", psb->auth_login, digest_string); | |
572 DEBUG(5) debugf("unencoded reply = %s\n", reply); | |
573 | |
574 reply64 = base64_encode(reply, strlen(reply)); | |
575 DEBUG(5) debugf("encoded reply = %s\n", reply64); | |
576 | |
577 fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out); | |
578 DEBUG(4) debugf("%s\n", reply64); | |
579 | |
580 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))) | |
581 ok = check_response(psb, FALSE); | |
582 | |
583 g_free(reply64); | |
584 g_free(reply); | |
585 g_free(chall); | |
586 g_free(chall64); | |
587 } | |
588 } | |
589 return ok; | |
590 } | |
591 | |
592 static | |
593 gboolean smtp_out_auth_login(smtp_base *psb) | |
594 { | |
595 gboolean ok = FALSE; | |
596 fprintf(psb->out, "AUTH LOGIN\r\n"); fflush(psb->out); | |
597 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){ | |
598 if((ok = check_response(psb, TRUE))){ | |
599 gchar *resp64; | |
600 guchar *resp; | |
601 gint resp_size; | |
602 gchar *reply64; | |
603 | |
604 resp64 = get_response_arg(&(psb->buffer[4])); | |
605 DEBUG(5) debugf("encoded response = %s\n", resp64); | |
606 resp = base64_decode(resp64, &resp_size); | |
607 g_free(resp64); | |
608 DEBUG(5) debugf("decoded response = %s, size = %d\n", | |
609 resp, resp_size); | |
610 g_free(resp); | |
611 reply64 = base64_encode(psb->auth_login, | |
612 strlen(psb->auth_login)); | |
613 fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out); | |
614 g_free(reply64); | |
615 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))) { | |
616 if ((ok = check_response(psb, TRUE))) { | |
617 resp64 = get_response_arg(&(psb->buffer[4])); | |
618 DEBUG(5) debugf("encoded response = %s\n", resp64); | |
619 resp = base64_decode(resp64, &resp_size); | |
620 g_free(resp64); | |
621 DEBUG(5) debugf("decoded response = %s, size = %d\n", | |
622 resp, resp_size); | |
623 g_free(resp); | |
624 reply64 = base64_encode(psb->auth_secret, | |
625 strlen(psb->auth_secret)); | |
626 fprintf(psb->out, "%s\r\n", reply64); fflush(psb->out); | |
627 g_free(reply64); | |
628 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))) | |
629 ok = check_response(psb, FALSE); | |
630 } | |
631 } | |
632 } | |
633 } | |
634 return ok; | |
635 } | |
636 | |
637 gboolean smtp_out_auth(smtp_base *psb) | |
638 { | |
639 gboolean ok = FALSE; | |
640 gint i = 0; | |
641 while(psb->auth_names[i]){ | |
642 if(strcasecmp(psb->auth_names[i], psb->auth_name) == 0) | |
643 break; | |
644 i++; | |
645 } | |
646 if(psb->auth_names[i]){ | |
647 if(strcasecmp(psb->auth_name, "cram-md5") == 0){ | |
648 smtp_out_auth_cram_md5(psb); | |
649 }else if(strcasecmp(psb->auth_name, "login") == 0){ | |
650 smtp_out_auth_login(psb); | |
651 }else{ | |
652 logwrite(LOG_ERR, "auth method %s not supported\n", psb->auth_name); | |
653 } | |
654 }else{ | |
655 logwrite(LOG_ERR, "no auth method %s found.\n", psb->auth_name); | |
656 } | |
657 return ok; | |
658 } | |
659 | |
660 #endif | |
661 | |
662 gboolean smtp_out_init(smtp_base *psb) | |
663 { | |
664 gboolean ok; | |
665 | |
666 if((ok = read_response(psb, SMTP_INITIAL_TIMEOUT))){ | |
667 if((ok = check_init_response(psb))){ | |
668 | |
669 if((ok = smtp_helo(psb, psb->helo_name))){ | |
670 #ifdef ENABLE_AUTH | |
671 if(psb->auth_name && psb->use_auth){ | |
672 /* we completely disregard the response of server here. If | |
673 authentication fails, the server will complain later | |
674 anyway. I know, this is not polite... */ | |
675 smtp_out_auth(psb); | |
676 } | |
677 #endif | |
678 } | |
679 } | |
680 } | |
681 if(!ok) | |
682 smtp_out_log_failure(psb, NULL); | |
683 return ok; | |
684 } | |
685 | |
686 gint smtp_out_msg(smtp_base *psb, | |
687 message *msg, address *return_path, GList *rcpt_list, | |
688 GList *hdr_list) | |
689 { | |
690 gint i, size; | |
691 gboolean ok = TRUE; | |
692 int rcpt_cnt; | |
693 int rcpt_accept = 0; | |
694 | |
695 DEBUG(5) debugf("smtp_out_msg entered\n"); | |
696 | |
697 /* defaults: */ | |
698 if(return_path == NULL) | |
699 return_path = msg->return_path; | |
700 if(hdr_list == NULL) | |
701 hdr_list = msg->hdr_list; | |
702 if(rcpt_list == NULL) | |
703 rcpt_list = msg->rcpt_list; | |
704 rcpt_cnt = g_list_length(rcpt_list); | |
705 | |
706 size = msg_calc_size(msg, TRUE); | |
707 | |
708 /* respect maximum size given by server: */ | |
709 if((psb->max_size > 0) && (size > psb->max_size)){ | |
710 logwrite(LOG_WARNING, | |
711 "%s == host=%s message size (%d) > fixed maximum message size of server (%d)", | |
712 msg->uid, psb->remote_host, size, psb->max_size); | |
713 psb->error = smtp_cancel; | |
714 ok = FALSE; | |
715 } | |
716 | |
717 if(ok){ | |
718 smtp_cmd_mailfrom(psb, return_path, | |
719 psb->use_size ? | |
720 size + SMTP_SIZE_ADD : 0); | |
721 | |
722 if(!psb->use_pipelining){ | |
723 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))) | |
724 ok = check_response(psb, FALSE); | |
725 } | |
726 } | |
727 if(ok){ | |
728 GList *rcpt_node; | |
729 rcpt_accept = 0; | |
730 | |
731 for(rcpt_node = g_list_first(rcpt_list); | |
732 rcpt_node != NULL; | |
733 rcpt_node = g_list_next(rcpt_node)){ | |
734 address *rcpt = (address *)(rcpt_node->data); | |
735 smtp_cmd_rcptto(psb, rcpt); | |
736 if(!psb->use_pipelining){ | |
737 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))) | |
738 if(check_response(psb, FALSE)){ | |
739 rcpt_accept++; | |
740 addr_mark_delivered(rcpt); | |
741 } | |
742 else{ | |
743 /* if server returned an error for one recp. we | |
744 may still try the others. But if it is a timeout, eof | |
745 or unexpected response, it is more serious and we should | |
746 give up. */ | |
747 if((psb->error != smtp_trylater) && | |
748 (psb->error != smtp_fail)){ | |
749 ok = FALSE; | |
750 break; | |
751 }else{ | |
752 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s", | |
753 msg->uid, addr_string(rcpt), | |
754 psb->remote_host, psb->buffer); | |
755 if(psb->error == smtp_trylater){ | |
756 addr_mark_defered(rcpt); | |
757 }else{ | |
758 addr_mark_failed(rcpt); | |
759 } | |
760 } | |
761 } | |
762 else | |
763 break; | |
764 } | |
765 } | |
766 | |
767 /* There is no point in going on if no recp.s were accpted. | |
768 But we can check that at this point only if not pipelining: */ | |
769 ok = (ok && (psb->use_pipelining || (rcpt_accept > 0))); | |
770 if(ok){ | |
771 | |
772 fprintf(psb->out, "DATA\r\n"); fflush(psb->out); | |
773 | |
774 DEBUG(4) debugf("DATA\r\n"); | |
775 | |
776 if(psb->use_pipelining){ | |
777 /* the first pl'ed command was MAIL FROM | |
778 the last was DATA, whose response can be handled by the 'normal' code | |
779 all in between were RCPT TO: | |
780 */ | |
781 /* response to MAIL FROM: */ | |
782 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){ | |
783 if((ok = check_response(psb, FALSE))){ | |
784 | |
785 /* response(s) to RCPT TO: | |
786 this is very similar to the sequence above for no pipeline | |
787 */ | |
788 for(i = 0; i < rcpt_cnt; i++){ | |
789 if((ok = read_response(psb, SMTP_CMD_TIMEOUT))){ | |
790 address *rcpt = g_list_nth_data(rcpt_list, i); | |
791 if(check_response(psb, FALSE)){ | |
792 rcpt_accept++; | |
793 addr_mark_delivered(rcpt); | |
794 } | |
795 else{ | |
796 /* if server returned an error 4xx or 5xx for one recp. we | |
797 may still try the others. But if it is a timeout, eof | |
798 or unexpected response, it is more serious and we | |
799 should give up. */ | |
800 if((psb->error != smtp_trylater) && | |
801 (psb->error != smtp_fail)){ | |
802 ok = FALSE; | |
803 break; | |
804 }else{ | |
805 logwrite(LOG_NOTICE, "%s == %s host=%s failed: %s", | |
806 msg->uid, addr_string(rcpt), | |
807 psb->remote_host, psb->buffer); | |
808 if(psb->error == smtp_trylater){ | |
809 addr_mark_defered(rcpt); | |
810 }else{ | |
811 addr_mark_failed(rcpt); | |
812 } | |
813 } | |
814 } | |
815 }else{ | |
816 DEBUG(5) debugf("check_response failed after RCPT TO\n"); | |
817 break; | |
818 } | |
819 } | |
820 if(rcpt_accept == 0) | |
821 ok = FALSE; | |
822 }else{ | |
823 DEBUG(5) debugf("check_response failed after MAIL FROM\n"); | |
824 } | |
825 }else{ | |
826 DEBUG(5) debugf("read_response failed after MAIL FROM\n"); | |
827 } | |
828 } /* if(psb->use_pipelining) */ | |
829 | |
830 /* response to the DATA cmd */ | |
831 if(ok){ | |
832 if(read_response(psb, SMTP_DATA_TIMEOUT)){ | |
833 if(check_response(psb, TRUE)){ | |
834 send_header(psb, hdr_list); | |
835 send_data(psb, msg); | |
836 | |
837 if(read_response(psb, SMTP_FINAL_TIMEOUT)) | |
838 ok = check_response(psb, FALSE); | |
839 } | |
840 } | |
841 } | |
842 } | |
843 } | |
844 | |
845 DEBUG(5){ | |
846 debugf("psb->error = %d\n", psb->error); | |
847 debugf("ok = %d\n", ok); | |
848 debugf("rcpt_accept = %d\n", rcpt_accept); | |
849 } | |
850 | |
851 if(psb->error == smtp_ok){ | |
852 GList *rcpt_node; | |
853 for(rcpt_node = g_list_first(rcpt_list); | |
854 rcpt_node; | |
855 rcpt_node = g_list_next(rcpt_node)){ | |
856 address *rcpt = (address *)(rcpt_node->data); | |
857 if(addr_is_delivered(rcpt)) | |
858 logwrite(LOG_NOTICE, "%s => %s host=%s with %s\n", | |
859 msg->uid, addr_string(rcpt), psb->remote_host, | |
860 psb->use_esmtp ? "esmtp" : "smtp"); | |
861 } | |
862 }else{ | |
863 /* if something went wrong, | |
864 we have to unmark the rcpts prematurely marked as delivered | |
865 and mark the status */ | |
866 smtp_out_mark_rcpts(psb, rcpt_list); | |
867 | |
868 /* log the failure: */ | |
869 smtp_out_log_failure(psb, msg); | |
870 } | |
871 return rcpt_accept; | |
872 } | |
873 | |
874 gboolean smtp_out_quit(smtp_base *psb) | |
875 { | |
876 fprintf(psb->out, "QUIT\r\n"); fflush(psb->out); | |
877 | |
878 DEBUG(4) debugf("QUIT\n"); | |
879 | |
880 signal(SIGALRM, SIG_DFL); | |
881 | |
882 return TRUE; | |
883 } | |
884 | |
885 gint smtp_deliver(gchar *host, gint port, GList *resolve_list, | |
886 message *msg, | |
887 address *return_path, | |
888 GList *rcpt_list) | |
889 { | |
890 smtp_base *psb; | |
891 smtp_error err; | |
892 | |
893 DEBUG(5) debugf("smtp_deliver entered\n"); | |
894 | |
895 if(return_path == NULL) | |
896 return_path = msg->return_path; | |
897 | |
898 if((psb = smtp_out_open(host, port, resolve_list))){ | |
899 set_heloname(psb, return_path->domain, TRUE); | |
900 /* initiate connection, send message and quit: */ | |
901 if(smtp_out_init(psb)){ | |
902 smtp_out_msg(psb, msg, return_path, rcpt_list, NULL); | |
903 if(psb->error == smtp_ok || | |
904 (psb->error == smtp_fail) || | |
905 (psb->error == smtp_trylater) || | |
906 (psb->error == smtp_syntax) || | |
907 (psb->error == smtp_cancel)) | |
908 | |
909 smtp_out_quit(psb); | |
910 } | |
911 | |
912 err = psb->error; | |
913 destroy_smtpbase(psb); | |
914 | |
915 return err; | |
916 } | |
917 return -1; | |
918 } |