diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-31 23:41:53 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-31 23:41:53 +0000 |
commit | 88b8f0a3475fc1f2e1b3cf58b63902551dae0169 (patch) | |
tree | 816f021aa601eba0c1f76c2a3296cf0255dfa9e0 /mailutils/sendmail.c | |
parent | d3081062453cb394052dd2d8ecda94aaaff3c6df (diff) | |
download | busybox-w32-88b8f0a3475fc1f2e1b3cf58b63902551dae0169.tar.gz busybox-w32-88b8f0a3475fc1f2e1b3cf58b63902551dae0169.tar.bz2 busybox-w32-88b8f0a3475fc1f2e1b3cf58b63902551dae0169.zip |
sendmail: update by Vladimir
build system: tweak for rare case where include/autoconf.h
does not get updated
function old new delta
packed_usage 26238 26242 +4
sendmail_main 1353 897 -456
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 4/-456) Total: -452 bytes
Diffstat (limited to 'mailutils/sendmail.c')
-rw-r--r-- | mailutils/sendmail.c | 347 |
1 files changed, 109 insertions, 238 deletions
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c index b027f94d2..7e57a94cf 100644 --- a/mailutils/sendmail.c +++ b/mailutils/sendmail.c | |||
@@ -61,35 +61,23 @@ static void rcptto(const char *s) | |||
61 | int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 61 | int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
62 | int sendmail_main(int argc UNUSED_PARAM, char **argv) | 62 | int sendmail_main(int argc UNUSED_PARAM, char **argv) |
63 | { | 63 | { |
64 | #if ENABLE_FEATURE_SENDMAIL_MAILX | ||
65 | llist_t *opt_attachments = NULL; | ||
66 | const char *opt_subject; | ||
67 | #if ENABLE_FEATURE_SENDMAIL_MAILXX | ||
68 | llist_t *opt_carboncopies = NULL; | ||
69 | char *opt_errors_to; | ||
70 | #endif | ||
71 | #endif | ||
72 | char *opt_connect = opt_connect; | 64 | char *opt_connect = opt_connect; |
73 | char *opt_from, *opt_fullname; | 65 | char *opt_from; |
74 | char *boundary; | 66 | char *s; |
75 | llist_t *l; | 67 | llist_t *list = NULL; |
76 | llist_t *headers = NULL; | ||
77 | char *domain = sane_address(safe_getdomainname()); | 68 | char *domain = sane_address(safe_getdomainname()); |
78 | int code; | 69 | int code; |
79 | 70 | ||
80 | enum { | 71 | enum { |
81 | OPT_w = 1 << 0, // network timeout | 72 | //--- standard options |
82 | OPT_t = 1 << 1, // read message for recipients | 73 | OPT_t = 1 << 0, // read message for recipients, append them to those on cmdline |
83 | OPT_N = 1 << 2, // request notification | 74 | OPT_f = 1 << 1, // sender address |
84 | OPT_f = 1 << 3, // sender address | 75 | OPT_o = 1 << 2, // various options. -oi IMPLIED! others are IGNORED! |
85 | OPT_F = 1 << 4, // sender name, overrides $NAME | 76 | //--- BB specific options |
86 | OPT_s = 1 << 5, // subject | 77 | OPT_w = 1 << 3, // network timeout |
87 | OPT_j = 1 << 6, // assumed charset | 78 | OPT_H = 1 << 4, // use external connection helper |
88 | OPT_a = 1 << 7, // attachment(s) | 79 | OPT_S = 1 << 5, // specify connection string |
89 | OPT_H = 1 << 8, // use external connection helper | 80 | OPT_a = 1 << 6, // authentication tokens |
90 | OPT_S = 1 << 9, // specify connection string | ||
91 | OPT_c = 1 << 10, // carbon copy | ||
92 | OPT_e = 1 << 11, // errors-to address | ||
93 | }; | 81 | }; |
94 | 82 | ||
95 | // init global variables | 83 | // init global variables |
@@ -100,35 +88,42 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
100 | G.fp0 = fdopen(3, "r"); | 88 | G.fp0 = fdopen(3, "r"); |
101 | 89 | ||
102 | // parse options | 90 | // parse options |
103 | opt_complementary = "w+" USE_FEATURE_SENDMAIL_MAILX(":a::H--S:S--H") USE_FEATURE_SENDMAIL_MAILXX(":c::"); | 91 | // -f is required. -H and -S are mutually exclusive |
104 | opts = getopt32(argv, | 92 | opt_complementary = "f:w+:H--S:S--H:a::"; |
105 | "w:t" "N:f:F:" USE_FEATURE_SENDMAIL_MAILX("s:j:a:H:S:") USE_FEATURE_SENDMAIL_MAILXX("c:e:") | 93 | // N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect |
106 | "X:V:vq:R:O:o:nmL:Iih:GC:B:b:A:" // postfix compat only, ignored | 94 | // -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility, |
107 | // r:Q:p:M:Dd are candidates from another man page. TODO? | 95 | // it is still under development. |
108 | "46E", // ssmtp introduces another quirks. TODO?: -a[upm] (user, pass, method) to be supported | 96 | opts = getopt32(argv, "tf:o:w:H:S:a::", &opt_from, NULL, &timeout, &opt_connect, &opt_connect, &list); |
109 | &timeout /* -w */, NULL, &opt_from, &opt_fullname, | ||
110 | USE_FEATURE_SENDMAIL_MAILX(&opt_subject, &G.opt_charset, &opt_attachments, &opt_connect, &opt_connect,) | ||
111 | USE_FEATURE_SENDMAIL_MAILXX(&opt_carboncopies, &opt_errors_to,) | ||
112 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL | ||
113 | ); | ||
114 | //argc -= optind; | 97 | //argc -= optind; |
115 | argv += optind; | 98 | argv += optind; |
116 | 99 | ||
100 | // process -a[upm]<token> options | ||
101 | if ((opts & OPT_a) && !list) | ||
102 | bb_show_usage(); | ||
103 | while (list) { | ||
104 | char *a = (char *) llist_pop(&list); | ||
105 | if ('u' == a[0]) | ||
106 | G.user = xstrdup(a+1); | ||
107 | if ('p' == a[0]) | ||
108 | G.pass = xstrdup(a+1); | ||
109 | // N.B. we support only AUTH LOGIN so far | ||
110 | //if ('m' == a[0]) | ||
111 | // G.method = xstrdup(a+1); | ||
112 | } | ||
113 | // N.B. list == NULL here | ||
114 | //bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv); | ||
115 | |||
117 | // connect to server | 116 | // connect to server |
118 | 117 | ||
119 | #if ENABLE_FEATURE_SENDMAIL_MAILX | ||
120 | // N.B. -H and -S are mutually exclusive so they do not spoil opt_connect | ||
121 | // connection helper ordered? -> | 118 | // connection helper ordered? -> |
122 | if (opts & OPT_H) { | 119 | if (opts & OPT_H) { |
123 | const char *args[] = { "sh", "-c", opt_connect, NULL }; | 120 | const char *args[] = { "sh", "-c", opt_connect, NULL }; |
124 | // plug it in | 121 | // plug it in |
125 | launch_helper(args); | 122 | launch_helper(args); |
126 | // vanilla connection | 123 | // vanilla connection |
127 | } else | 124 | } else { |
128 | #endif | ||
129 | { | ||
130 | int fd; | 125 | int fd; |
131 | // host[:port] not explicitly specified ? -> use $SMTPHOST | 126 | // host[:port] not explicitly specified? -> use $SMTPHOST |
132 | // no $SMTPHOST ? -> use localhost | 127 | // no $SMTPHOST ? -> use localhost |
133 | if (!(opts & OPT_S)) { | 128 | if (!(opts & OPT_S)) { |
134 | opt_connect = getenv("SMTPHOST"); | 129 | opt_connect = getenv("SMTPHOST"); |
@@ -145,7 +140,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
145 | 140 | ||
146 | // wait for initial server OK | 141 | // wait for initial server OK |
147 | // N.B. if we used openssl the initial 220 answer is already swallowed during openssl TLS init procedure | 142 | // N.B. if we used openssl the initial 220 answer is already swallowed during openssl TLS init procedure |
148 | // so we need to push the server to see whether we are ok | 143 | // so we need to kick the server to see whether we are ok |
149 | code = smtp_check("NOOP", -1); | 144 | code = smtp_check("NOOP", -1); |
150 | // 220 on plain connection, 250 on openssl-helped TLS session | 145 | // 220 on plain connection, 250 on openssl-helped TLS session |
151 | if (220 == code) | 146 | if (220 == code) |
@@ -157,6 +152,20 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
157 | if (250 != smtp_checkp("EHLO %s", domain, -1)) { | 152 | if (250 != smtp_checkp("EHLO %s", domain, -1)) { |
158 | smtp_checkp("HELO %s", domain, 250); | 153 | smtp_checkp("HELO %s", domain, 250); |
159 | } | 154 | } |
155 | if (ENABLE_FEATURE_CLEAN_UP) | ||
156 | free(domain); | ||
157 | |||
158 | // perform authentication | ||
159 | if (opts & OPT_a) { | ||
160 | smtp_check("AUTH LOGIN", 334); | ||
161 | // we must read credentials unless they are given via -a[up] options | ||
162 | if (!G.user || !G.pass) | ||
163 | get_cred_or_die(4); | ||
164 | encode_base64(NULL, G.user, NULL); | ||
165 | smtp_check("", 334); | ||
166 | encode_base64(NULL, G.pass, NULL); | ||
167 | smtp_check("", 235); | ||
168 | } | ||
160 | 169 | ||
161 | // set sender | 170 | // set sender |
162 | // N.B. we have here a very loosely defined algotythm | 171 | // N.B. we have here a very loosely defined algotythm |
@@ -170,201 +179,25 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
170 | // file descriptor (e.g. 4), or again from a secured file. | 179 | // file descriptor (e.g. 4), or again from a secured file. |
171 | 180 | ||
172 | // got no sender address? -> use system username as a resort | 181 | // got no sender address? -> use system username as a resort |
173 | if (!(opts & OPT_f)) { | 182 | // N.B. we marked -f as required option! |
174 | // N.B. IMHO getenv("USER") can be way easily spoofed! | 183 | //if (!G.user) { |
175 | G.user = xuid2uname(getuid()); | 184 | // // N.B. IMHO getenv("USER") can be way easily spoofed! |
176 | opt_from = xasprintf("%s@%s", G.user, domain); | 185 | // G.user = xuid2uname(getuid()); |
177 | } | 186 | // opt_from = xasprintf("%s@%s", G.user, domain); |
178 | if (ENABLE_FEATURE_CLEAN_UP) | 187 | //} |
179 | free(domain); | 188 | //if (ENABLE_FEATURE_CLEAN_UP) |
180 | 189 | // free(domain); | |
181 | code = -1; // first try softly without authentication | 190 | smtp_checkp("MAIL FROM:<%s>", opt_from, 250); |
182 | while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) { | 191 | |
183 | // MAIL FROM failed -> authentication needed | 192 | // process message |
184 | if (334 == smtp_check("AUTH LOGIN", -1)) { | 193 | |
185 | // we must read credentials | 194 | // read recipients from message and add them to those given on cmdline. |
186 | get_cred_or_die(4); | 195 | // this means we scan stdin for To:, Cc:, Bcc: lines until an empty line |
187 | encode_base64(NULL, G.user, NULL); | 196 | // and then use the rest of stdin as message body |
188 | smtp_check("", 334); | 197 | code = 0; // set "analyze headers" mode |
189 | encode_base64(NULL, G.pass, NULL); | 198 | while ((s = xmalloc_fgetline(G.fp0)) != NULL) { |
190 | smtp_check("", 235); | 199 | // put message lines doubling leading dots |
191 | } | 200 | if (code) { |
192 | // authenticated OK? -> retry to set sender | ||
193 | // but this time die on failure! | ||
194 | code = 250; | ||
195 | } | ||
196 | |||
197 | // recipients specified as arguments | ||
198 | while (*argv) { | ||
199 | char *s = sane_address(*argv); | ||
200 | // loose test on email address validity | ||
201 | // if (strchr(s, '@')) { | ||
202 | rcptto(s); | ||
203 | llist_add_to_end(&headers, xasprintf("To: %s", s)); | ||
204 | // } | ||
205 | argv++; | ||
206 | } | ||
207 | |||
208 | #if ENABLE_FEATURE_SENDMAIL_MAILXX | ||
209 | // carbon copies recipients specified as -c options | ||
210 | for (l = opt_carboncopies; l; l = l->link) { | ||
211 | char *s = sane_address(l->data); | ||
212 | // loose test on email address validity | ||
213 | // if (strchr(s, '@')) { | ||
214 | rcptto(s); | ||
215 | // TODO: do we ever need to mangle the message? | ||
216 | //llist_add_to_end(&headers, xasprintf("Cc: %s", s)); | ||
217 | // } | ||
218 | } | ||
219 | #endif | ||
220 | |||
221 | // if -t specified or no recipients specified -> read recipients from message | ||
222 | // i.e. scan stdin for To:, Cc:, Bcc: lines ... | ||
223 | // ... and then use the rest of stdin as message body | ||
224 | // N.B. subject read from body can be further overrided with one specified on command line. | ||
225 | // recipients are merged. Bcc: lines are deleted | ||
226 | // N.B. other headers are collected and will be dumped verbatim | ||
227 | if (opts & OPT_t || !headers) { | ||
228 | // fetch recipients and (optionally) subject | ||
229 | char *s; | ||
230 | while ((s = xmalloc_fgetline(G.fp0)) != NULL) { | ||
231 | if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Cc: ", s, 4)) { | ||
232 | rcptto(sane_address(s+4)); | ||
233 | llist_add_to_end(&headers, s); | ||
234 | } else if (0 == strncasecmp("Bcc: ", s, 5)) { | ||
235 | rcptto(sane_address(s+5)); | ||
236 | free(s); | ||
237 | // N.B. Bcc vanishes from headers! | ||
238 | } else if (0 == strncmp("Subject: ", s, 9)) { | ||
239 | // we read subject -> use it verbatim unless it is specified | ||
240 | // on command line | ||
241 | if (!(opts & OPT_s)) | ||
242 | llist_add_to_end(&headers, s); | ||
243 | else | ||
244 | free(s); | ||
245 | } else if (s[0]) { | ||
246 | // misc header | ||
247 | llist_add_to_end(&headers, s); | ||
248 | } else { | ||
249 | free(s); | ||
250 | break; // stop on the first empty line | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | |||
255 | // enter "put message" mode | ||
256 | smtp_check("DATA", 354); | ||
257 | |||
258 | // put headers we could have preread with -t | ||
259 | for (l = headers; l; l = l->link) { | ||
260 | printf("%s\r\n", l->data); | ||
261 | if (ENABLE_FEATURE_CLEAN_UP) | ||
262 | free(l->data); | ||
263 | } | ||
264 | |||
265 | // put (possibly encoded) subject | ||
266 | #if ENABLE_FEATURE_SENDMAIL_MAILX | ||
267 | if (opts & OPT_s) { | ||
268 | printf("Subject: "); | ||
269 | if (opts & OPT_j) { | ||
270 | printf("=?%s?B?", G.opt_charset); | ||
271 | encode_base64(NULL, opt_subject, NULL); | ||
272 | printf("?="); | ||
273 | } else { | ||
274 | printf("%s", opt_subject); | ||
275 | } | ||
276 | printf("\r\n"); | ||
277 | } | ||
278 | #endif | ||
279 | |||
280 | // put sender name, $NAME is the default | ||
281 | if (!(opts & OPT_F)) | ||
282 | opt_fullname = getenv("NAME"); | ||
283 | if (opt_fullname) | ||
284 | printf("From: \"%s\" <%s>\r\n", opt_fullname, opt_from); | ||
285 | |||
286 | // put notification | ||
287 | if (opts & OPT_N) | ||
288 | printf("Disposition-Notification-To: %s\r\n", opt_from); | ||
289 | |||
290 | #if ENABLE_FEATURE_SENDMAIL_MAILXX | ||
291 | // put errors recipient | ||
292 | if (opts & OPT_e) | ||
293 | printf("Errors-To: %s\r\n", opt_errors_to); | ||
294 | #endif | ||
295 | |||
296 | // make a random string -- it will delimit message parts | ||
297 | srand(monotonic_us()); | ||
298 | boundary = xasprintf("%d=_%d-%d", rand(), rand(), rand()); | ||
299 | |||
300 | // put common headers | ||
301 | // TODO: do we really need this? | ||
302 | // printf("Message-ID: <%s>\r\n", boundary); | ||
303 | |||
304 | #if ENABLE_FEATURE_SENDMAIL_MAILX | ||
305 | // have attachments? -> compose multipart MIME | ||
306 | if (opt_attachments) { | ||
307 | const char *fmt; | ||
308 | const char *p; | ||
309 | char *q; | ||
310 | |||
311 | printf( | ||
312 | "Mime-Version: 1.0\r\n" | ||
313 | "%smultipart/mixed; boundary=\"%s\"\r\n" | ||
314 | , "Content-Type: " | ||
315 | , boundary | ||
316 | ); | ||
317 | |||
318 | // body is pseudo attachment read from stdin in first turn | ||
319 | llist_add_to(&opt_attachments, (char *)"-"); | ||
320 | |||
321 | // put body + attachment(s) | ||
322 | // N.B. all these weird things just to be tiny | ||
323 | // by reusing string patterns! | ||
324 | fmt = | ||
325 | "\r\n--%s\r\n" | ||
326 | "%stext/plain; charset=%s\r\n" | ||
327 | "%s%s\r\n" | ||
328 | "%s" | ||
329 | ; | ||
330 | p = G.opt_charset; | ||
331 | q = (char *)""; | ||
332 | l = opt_attachments; | ||
333 | while (l) { | ||
334 | printf( | ||
335 | fmt | ||
336 | , boundary | ||
337 | , "Content-Type: " | ||
338 | , p | ||
339 | , "Content-Disposition: inline" | ||
340 | , q | ||
341 | , "Content-Transfer-Encoding: base64\r\n" | ||
342 | ); | ||
343 | p = ""; | ||
344 | fmt = | ||
345 | "\r\n--%s\r\n" | ||
346 | "%sapplication/octet-stream%s\r\n" | ||
347 | "%s; filename=\"%s\"\r\n" | ||
348 | "%s" | ||
349 | ; | ||
350 | encode_base64(l->data, (const char *)G.fp0, "\r"); | ||
351 | l = l->link; | ||
352 | if (l) | ||
353 | q = bb_get_last_path_component_strip(l->data); | ||
354 | } | ||
355 | |||
356 | // put message terminator | ||
357 | printf("\r\n--%s--\r\n" "\r\n", boundary); | ||
358 | |||
359 | // no attachments? -> just dump message | ||
360 | } else | ||
361 | #endif | ||
362 | { | ||
363 | char *s; | ||
364 | // terminate headers | ||
365 | printf("\r\n"); | ||
366 | // put plain text respecting leading dots | ||
367 | while ((s = xmalloc_fgetline(G.fp0)) != NULL) { | ||
368 | // escape leading dots | 201 | // escape leading dots |
369 | // N.B. this feature is implied even if no -i (-oi) switch given | 202 | // N.B. this feature is implied even if no -i (-oi) switch given |
370 | // N.B. we need to escape the leading dot regardless of | 203 | // N.B. we need to escape the leading dot regardless of |
@@ -373,10 +206,48 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv) | |||
373 | printf("."); | 206 | printf("."); |
374 | // dump read line | 207 | // dump read line |
375 | printf("%s\r\n", s); | 208 | printf("%s\r\n", s); |
209 | free(s); | ||
210 | continue; | ||
211 | } | ||
212 | |||
213 | // analyze headers | ||
214 | // To: or Cc: headers add recipients | ||
215 | if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Bcc: " + 1, s, 4)) { | ||
216 | rcptto(sane_address(s+4)); | ||
217 | // goto addh; | ||
218 | llist_add_to_end(&list, s); | ||
219 | // Bcc: header adds blind copy (hidden) recipient | ||
220 | } else if (0 == strncasecmp("Bcc: ", s, 5)) { | ||
221 | rcptto(sane_address(s+5)); | ||
222 | free(s); | ||
223 | // N.B. Bcc: vanishes from headers! | ||
224 | // other headers go verbatim | ||
225 | } else if (s[0]) { | ||
226 | // addh: | ||
227 | llist_add_to_end(&list, s); | ||
228 | // the empty line stops analyzing headers | ||
229 | } else { | ||
230 | free(s); | ||
231 | // put recipients specified on cmdline | ||
232 | while (*argv) { | ||
233 | s = sane_address(*argv); | ||
234 | rcptto(s); | ||
235 | llist_add_to_end(&list, xasprintf("To: %s", s)); | ||
236 | argv++; | ||
237 | } | ||
238 | // enter "put message" mode | ||
239 | smtp_check("DATA", 354); | ||
240 | // dump the headers | ||
241 | while (list) { | ||
242 | printf("%s\r\n", (char *) llist_pop(&list)); | ||
243 | } | ||
244 | printf("%s\r\n" + 2); // quirk for format string to be reused | ||
245 | // stop analyzing headers | ||
246 | code++; | ||
376 | } | 247 | } |
377 | } | 248 | } |
378 | 249 | ||
379 | // leave "put message" mode | 250 | // finalize the message |
380 | smtp_check(".", 250); | 251 | smtp_check(".", 250); |
381 | // ... and say goodbye | 252 | // ... and say goodbye |
382 | smtp_check("QUIT", 221); | 253 | smtp_check("QUIT", 221); |