aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/applets.h5
-rw-r--r--include/usage.h8
-rw-r--r--networking/Config.in7
-rw-r--r--networking/sendmail.c639
4 files changed, 251 insertions, 408 deletions
diff --git a/include/applets.h b/include/applets.h
index 90b941766..35649d3df 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -149,7 +149,7 @@ USE_FBSPLASH(APPLET(fbsplash, _BB_DIR_SBIN, _BB_SUID_NEVER))
149USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush)) 149USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush))
150USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 150USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
151USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER)) 151USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER))
152USE_FETCHMAIL(APPLET_ODDNAME(fetchmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, fetchmail)) 152//USE_FETCHMAIL(APPLET(fetchmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
153USE_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER, fgrep)) 153USE_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER, fgrep))
154USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find)) 154USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find))
155USE_FINDFS(APPLET(findfs, _BB_DIR_SBIN, _BB_SUID_MAYBE)) 155USE_FINDFS(APPLET(findfs, _BB_DIR_SBIN, _BB_SUID_MAYBE))
@@ -235,6 +235,7 @@ USE_LSATTR(APPLET(lsattr, _BB_DIR_BIN, _BB_SUID_NEVER))
235USE_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) 235USE_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_NEVER))
236USE_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe)) 236USE_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe))
237USE_UNLZMA(APPLET_ODDNAME(lzmacat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lzmacat)) 237USE_UNLZMA(APPLET_ODDNAME(lzmacat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lzmacat))
238USE_FEATURE_SENDMAIL_MAILX(APPLET_ODDNAME(mail, sendmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sendmail))
238USE_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_NEVER)) 239USE_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_NEVER))
239USE_MAN(APPLET(man, _BB_DIR_SBIN, _BB_SUID_NEVER)) 240USE_MAN(APPLET(man, _BB_DIR_SBIN, _BB_SUID_NEVER))
240USE_MATCHPATHCON(APPLET(matchpathcon, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 241USE_MATCHPATHCON(APPLET(matchpathcon, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
@@ -313,7 +314,7 @@ USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
313USE_SCRIPT(APPLET(script, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 314USE_SCRIPT(APPLET(script, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
314USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER)) 315USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER))
315USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 316USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
316USE_SENDMAIL(APPLET_ODDNAME(sendmail, sendgetmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER, sendmail)) 317USE_SENDMAIL(APPLET(sendmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
317USE_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER, seq)) 318USE_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER, seq))
318USE_SESTATUS(APPLET(sestatus, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 319USE_SESTATUS(APPLET(sestatus, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
319USE_SETARCH(APPLET(setarch, _BB_DIR_BIN, _BB_SUID_NEVER)) 320USE_SETARCH(APPLET(setarch, _BB_DIR_BIN, _BB_SUID_NEVER))
diff --git a/include/usage.h b/include/usage.h
index a5234e053..fa7ac3bf7 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -3468,7 +3468,7 @@
3468#define sendmail_trivial_usage \ 3468#define sendmail_trivial_usage \
3469 "[-w timeout] [-H [user:pass@]server[:port]] [-S]\n" \ 3469 "[-w timeout] [-H [user:pass@]server[:port]] [-S]\n" \
3470 "[-N type] [-f sender] [-F fullname] " \ 3470 "[-N type] [-f sender] [-F fullname] " \
3471 USE_FEATURE_SENDMAIL_MAILX("[-s subject] [-c charset] [-a attach]... ") "[-t] [rcpt]..." 3471 USE_FEATURE_SENDMAIL_MAILX("[-s subject] [-c cc-rcpt]... [-j charset] [-a attach]... [-e err-rcpt] ") "[-t] [rcpt]..."
3472#define sendmail_full_usage "\n\n" \ 3472#define sendmail_full_usage "\n\n" \
3473 "Send an email\n" \ 3473 "Send an email\n" \
3474 "\nOptions:" \ 3474 "\nOptions:" \
@@ -3480,9 +3480,11 @@
3480 "\n -F fullname Sender full name. Overrides $NAME" \ 3480 "\n -F fullname Sender full name. Overrides $NAME" \
3481 USE_FEATURE_SENDMAIL_MAILX( \ 3481 USE_FEATURE_SENDMAIL_MAILX( \
3482 "\n -s subject Subject" \ 3482 "\n -s subject Subject" \
3483 "\n -c charset Assume charset for body and subject (" CONFIG_FEATURE_SENDMAIL_CHARSET ")" \ 3483 "\n -c rcpt Cc: recipient. May be multiple" \
3484 "\n -j charset Assume charset for body and subject (" CONFIG_FEATURE_SENDMAIL_CHARSET ")" \
3484 "\n -a file File to attach. May be multiple" \ 3485 "\n -a file File to attach. May be multiple" \
3485 ) 3486 "\n -e rcpt Errors-To: recipient" \
3487 )
3486 "\n -t Read recipients and subject from body" \ 3488 "\n -t Read recipients and subject from body" \
3487 "\n" \ 3489 "\n" \
3488 "\nOther options are silently ignored; -oi is implied" \ 3490 "\nOther options are silently ignored; -oi is implied" \
diff --git a/networking/Config.in b/networking/Config.in
index 1984297a6..9aa14220f 100644
--- a/networking/Config.in
+++ b/networking/Config.in
@@ -694,6 +694,13 @@ config FEATURE_SENDMAIL_MAILX
694 help 694 help
695 Allow to specify subject, attachments and their charset. 695 Allow to specify subject, attachments and their charset.
696 696
697config FEATURE_SENDMAIL_MAILXX
698 bool "Allow to specify Cc: addresses and some additional headers"
699 default n
700 depends on FEATURE_SENDMAIL_MAILX
701 help
702 Allow to specify Cc: addresses and some additional headers: Errors-To:.
703
697config FEATURE_SENDMAIL_SSL 704config FEATURE_SENDMAIL_SSL
698 bool "Allow to communicate via SSL/TLS" 705 bool "Allow to communicate via SSL/TLS"
699 default y 706 default y
diff --git a/networking/sendmail.c b/networking/sendmail.c
index 6d00026e2..ef6e03be6 100644
--- a/networking/sendmail.c
+++ b/networking/sendmail.c
@@ -1,6 +1,6 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2/* 2/*
3 * bare bones sendmail/fetchmail 3 * bare bones sendmail
4 * 4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> 5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 * 6 *
@@ -14,15 +14,12 @@ struct globals {
14 FILE *fp0; // initial stdin 14 FILE *fp0; // initial stdin
15 // arguments for SSL connection helper 15 // arguments for SSL connection helper
16 const char *xargs[9]; 16 const char *xargs[9];
17 // arguments for postprocess helper
18 const char *fargs[3];
19}; 17};
20#define G (*ptr_to_globals) 18#define G (*ptr_to_globals)
21#define helper_pid (G.helper_pid) 19#define helper_pid (G.helper_pid)
22#define timeout (G.timeout ) 20#define timeout (G.timeout )
23#define fp0 (G.fp0 ) 21#define fp0 (G.fp0 )
24#define xargs (G.xargs ) 22#define xargs (G.xargs )
25#define fargs (G.fargs )
26#define INIT_G() do { \ 23#define INIT_G() do { \
27 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 24 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
28 xargs[0] = "openssl"; \ 25 xargs[0] = "openssl"; \
@@ -33,13 +30,9 @@ struct globals {
33 xargs[5] = "-tls1"; \ 30 xargs[5] = "-tls1"; \
34 xargs[6] = "-starttls"; \ 31 xargs[6] = "-starttls"; \
35 xargs[7] = "smtp"; \ 32 xargs[7] = "smtp"; \
36 fargs[0] = CONFIG_FEATURE_SENDMAIL_CHARSET; \
37} while (0) 33} while (0)
38 34
39#define opt_connect (xargs[4]) 35#define opt_connect (xargs[4])
40#define opt_after_connect (xargs[5])
41#define opt_charset (fargs[0])
42#define opt_subject (fargs[1])
43 36
44static void uuencode(char *fname, const char *text) 37static void uuencode(char *fname, const char *text)
45{ 38{
@@ -112,8 +105,12 @@ static void signal_handler(int signo)
112 105
113 // SIGCHLD. reap zombies 106 // SIGCHLD. reap zombies
114 if (wait_any_nohang(&err) > 0) 107 if (wait_any_nohang(&err) > 0)
115 if (WIFEXITED(err) && WEXITSTATUS(err)) 108 if (WIFEXITED(err)) {
116 bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err)); 109// if (WEXITSTATUS(err))
110 bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
111// else
112// kill(0, SIGCONT);
113 }
117#undef err 114#undef err
118} 115}
119 116
@@ -178,10 +175,8 @@ static int smtp_checkp(const char *fmt, const char *param, int code)
178 while ((answer = xmalloc_fgetline(stdin)) != NULL) 175 while ((answer = xmalloc_fgetline(stdin)) != NULL)
179 if (strlen(answer) <= 3 || '-' != answer[3]) 176 if (strlen(answer) <= 3 || '-' != answer[3])
180 break; 177 break;
181//bb_error_msg("FMT[%s]ANS[%s]", fmt, answer);
182 if (answer) { 178 if (answer) {
183 int n = atoi(answer); 179 int n = atoi(answer);
184//bb_error_msg("FMT[%s]COD[%d][%d]", fmt, n, code);
185 alarm(0); 180 alarm(0);
186 free(answer); 181 free(answer);
187 if (-1 == code || n == code) 182 if (-1 == code || n == code)
@@ -211,50 +206,6 @@ static char *sane(char *str)
211 return str; 206 return str;
212} 207}
213 208
214#if ENABLE_FETCHMAIL
215static void pop3_checkr(const char *fmt, const char *param, char **ret)
216{
217 const char *msg = command(fmt, param);
218 char *answer = xmalloc_fgetline(stdin);
219 if (answer && '+' == *answer) {
220 alarm(0);
221 if (ret)
222 *ret = answer+4; // skip "+OK "
223 else if (ENABLE_FEATURE_CLEAN_UP)
224 free(answer);
225 return;
226 }
227 kill_helper();
228 bb_error_msg_and_die("%s failed", msg);
229}
230
231static inline void pop3_check(const char *fmt, const char *param)
232{
233 pop3_checkr(fmt, param, NULL);
234}
235
236static void pop3_message(const char *filename)
237{
238 int fd;
239 char *answer;
240 // create and open file filename
241 // read stdin, copy to created file
242 fd = xopen(filename, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL);
243 while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) {
244 char *s = answer;
245 if ('.' == *answer) {
246 if ('.' == answer[1])
247 s++;
248 else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
249 break;
250 }
251 xwrite(fd, s, strlen(s));
252 free(answer);
253 }
254 close(fd);
255}
256#endif
257
258// NB: parse_url can modify url[] (despite const), but only if '@' is there 209// NB: parse_url can modify url[] (despite const), but only if '@' is there
259static const char *parse_url(const char *url, const char **user, const char **pass) 210static const char *parse_url(const char *url, const char **user, const char **pass)
260{ 211{
@@ -280,64 +231,61 @@ static void rcptto(const char *s)
280 smtp_checkp("RCPT TO:<%s>", s, 250); 231 smtp_checkp("RCPT TO:<%s>", s, 250);
281} 232}
282 233
283int sendgetmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 234int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
284int sendgetmail_main(int argc UNUSED_PARAM, char **argv) 235int sendmail_main(int argc UNUSED_PARAM, char **argv)
285{ 236{
286#if ENABLE_FEATURE_SENDMAIL_MAILX 237#if ENABLE_FEATURE_SENDMAIL_MAILX
287 llist_t *opt_attachments = NULL; 238 llist_t *opt_attachments = NULL;
239 const char *opt_subject;
240 const char *opt_charset = CONFIG_FEATURE_SENDMAIL_CHARSET;
241#if ENABLE_FEATURE_SENDMAIL_MAILXX
242 llist_t *opt_carboncopies = NULL;
243 char *opt_errors_to;
244#endif
288#endif 245#endif
289 char *opt_from, *opt_fullname; 246 char *opt_from, *opt_fullname;
290 const char *opt_user; 247 const char *opt_user;
291 const char *opt_pass; 248 const char *opt_pass;
249 int code;
250 char *boundary;
251 llist_t *l;
252 llist_t *headers = NULL;
253 char *domain = sane(safe_getdomainname());
254 unsigned opts;
292 255
293 enum { 256 enum {
294 OPT_w = 1 << 0, // network timeout 257 OPT_w = 1 << 0, // network timeout
295
296 OPT_H = 1 << 1, // [user:password@]server[:port] 258 OPT_H = 1 << 1, // [user:password@]server[:port]
297 OPT_S = 1 << 2, // connect using openssl s_client helper 259 OPT_S = 1 << 2, // connect using openssl s_client helper
298 260 OPT_t = 1 << 3, // read message for recipients
299 OPTS_t = 1 << 3, // sendmail: read message for recipients 261 OPT_N = 1 << 4, // request notification
300 OPTF_t = 1 << 3, // fetchmail: use "TOP" not "RETR" 262 OPT_f = 1 << 5, // sender address
301 263 OPT_F = 1 << 6, // sender name, overrides $NAME
302 OPTS_N = 1 << 4, // sendmail: request notification 264 OPT_s = 1 << 7, // subject
303 OPTF_z = 1 << 4, // fetchmail: delete from server 265 OPT_j = 1 << 8, // assumed charset
304 266 OPT_a = 1 << 9, // attachment(s)
305 OPTS_f = 1 << 5, // sendmail: sender address 267 OPT_c = 1 << 10, // carbon copy
306 OPTS_F = 1 << 6, // sendmail: sender name, overrides $NAME 268 OPT_e = 1 << 11, // errors-to address
307
308 OPTS_s = 1 << 7, // sendmail: subject
309 OPTS_c = 1 << 8, // sendmail: assumed charset
310 OPTS_a = 1 << 9, // sendmail: attachment(s)
311 }; 269 };
312 const char *options;
313 unsigned opts;
314 270
315 // init global variables 271 // init global variables
316 INIT_G(); 272 INIT_G();
317 273
318 // parse options, different option sets for sendmail and fetchmail 274 // save initial stdin since body is piped!
319 // N.B. opt_after_connect hereafter is NULL if we are called as fetchmail 275 xdup2(STDIN_FILENO, 3);
320 // and is NOT NULL if we are called as sendmail 276 fp0 = fdopen(3, "r");
321 if (!ENABLE_FETCHMAIL || 's' == applet_name[0]) { 277
322 // SENDMAIL 278 // parse options
323 // save initial stdin since body is piped! 279 opt_complementary = "w+:a::" USE_FEATURE_SENDMAIL_MAILXX("c::");
324 xdup2(STDIN_FILENO, 3); 280 opts = getopt32(argv,
325 fp0 = fdopen(3, "r"); 281 "w:H:St" "N:f:F:" USE_FEATURE_SENDMAIL_MAILX("s:j:a:") USE_FEATURE_SENDMAIL_MAILXX("c:e:")
326 opt_complementary = "w+:a::"; 282 "X:V:vq:R:O:o:nmL:Iih:GC:B:b:A:" // postfix compat only, ignored
327 options = "w:H:St" "N:f:F:" USE_FEATURE_SENDMAIL_MAILX("s:c:a:") 283 // r:Q:p:M:Dd are candidates from another man page. TODO?
328 "X:V:vq:R:O:o:nmL:Iih:GC:B:b:A:"; // postfix compat only, ignored 284 "46E", // ssmtp introduces another quirks. TODO?: -a[upm] (user, pass, method) to be supported
329 } else {
330 // FETCHMAIL
331 opt_after_connect = NULL;
332 opt_complementary = "-1:w+";
333 options = "w:H:St" "z";
334 }
335 opts = getopt32(argv, options,
336 &timeout /* -w */, &opt_connect /* -H */, 285 &timeout /* -w */, &opt_connect /* -H */,
337 NULL, &opt_from, &opt_fullname, 286 NULL, &opt_from, &opt_fullname,
338#if ENABLE_FEATURE_SENDMAIL_MAILX 287 USE_FEATURE_SENDMAIL_MAILX(&opt_subject, &opt_charset, &opt_attachments,)
339 &opt_subject, &opt_charset, &opt_attachments, 288 USE_FEATURE_SENDMAIL_MAILXX(&opt_carboncopies, &opt_errors_to,)
340#endif
341 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 289 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
342 ); 290 );
343 //argc -= optind; 291 //argc -= optind;
@@ -354,7 +302,6 @@ int sendgetmail_main(int argc UNUSED_PARAM, char **argv)
354 // NB: parse_url modifies opt_connect[] ONLY if '@' is there. 302 // NB: parse_url modifies opt_connect[] ONLY if '@' is there.
355 // Thus "127.0.0.1" won't be modified, an is ok that it is RO. 303 // Thus "127.0.0.1" won't be modified, an is ok that it is RO.
356 opt_connect = parse_url(opt_connect, &opt_user, &opt_pass); 304 opt_connect = parse_url(opt_connect, &opt_user, &opt_pass);
357// bb_error_msg("H[%s] U[%s] P[%s]", opt_connect, opt_user, opt_pass);
358 305
359 // username must be defined! 306 // username must be defined!
360 if (!opt_user) { 307 if (!opt_user) {
@@ -376,347 +323,233 @@ int sendgetmail_main(int argc UNUSED_PARAM, char **argv)
376 xdup2(STDIN_FILENO, STDOUT_FILENO); 323 xdup2(STDIN_FILENO, STDOUT_FILENO);
377 } 324 }
378 325
379 // are we sendmail? 326 // got no sender address? -> use username as a resort
380 if (!ENABLE_FETCHMAIL || opt_after_connect) 327 if (!(opts & OPT_f)) {
381/*************************************************** 328 opt_from = xasprintf("%s@%s", opt_user, domain);
382 * SENDMAIL 329 }
383 ***************************************************/
384 {
385 int code;
386 char *boundary;
387 llist_t *l;
388 llist_t *headers = NULL;
389 char *domain = sane(safe_getdomainname());
390
391 // got no sender address? -> use username as a resort
392 if (!(opts & OPTS_f)) {
393 opt_from = xasprintf("%s@%s", opt_user, domain);
394 }
395 330
396 // introduce to server 331 // introduce to server
397 332
398 // we didn't use SSL helper? -> 333 // we didn't use SSL helper? ->
399 if (!(opts & OPT_S)) { 334 if (!(opts & OPT_S)) {
400 // ... wait for initial server OK 335 // ... wait for initial server OK
401 smtp_check(NULL, 220); 336 smtp_check(NULL, 220);
402 } 337 }
403 338
404 // we should start with modern EHLO 339 // we should start with modern EHLO
405 if (250 != smtp_checkp("EHLO %s", domain, -1)) { 340 if (250 != smtp_checkp("EHLO %s", domain, -1)) {
406 smtp_checkp("HELO %s", domain, 250); 341 smtp_checkp("HELO %s", domain, 250);
342 }
343 if (ENABLE_FEATURE_CLEAN_UP)
344 free(domain);
345
346 // set sender
347 // NOTE: if password has not been specified
348 // then no authentication is possible
349 code = (opt_pass ? -1 : 250);
350 // first try softly without authentication
351 while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) {
352 // MAIL FROM failed -> authentication needed
353 if (334 == smtp_check("AUTH LOGIN", -1)) {
354 uuencode(NULL, opt_user); // opt_user != NULL
355 smtp_check("", 334);
356 uuencode(NULL, opt_pass);
357 smtp_check("", 235);
407 } 358 }
408 if (ENABLE_FEATURE_CLEAN_UP) 359 // authenticated OK? -> retry to set sender
409 free(domain); 360 // but this time die on failure!
410 361 code = 250;
411 // set sender 362 }
412 // NOTE: if password has not been specified 363
413 // then no authentication is possible 364 // recipients specified as arguments
414 code = (opt_pass ? -1 : 250); 365 while (*argv) {
415 // first try softly without authentication 366 char *s = sane(*argv);
416 while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) { 367 // loose test on email address validity
417 // MAIL FROM failed -> authentication needed 368 if (strchr(s, '@')) {
418 if (334 == smtp_check("AUTH LOGIN", -1)) { 369 rcptto(s);
419 uuencode(NULL, opt_user); // opt_user != NULL 370 llist_add_to_end(&headers, xasprintf("To: %s", s));
420 smtp_check("", 334);
421 uuencode(NULL, opt_pass);
422 smtp_check("", 235);
423 }
424 // authenticated OK? -> retry to set sender
425 // but this time die on failure!
426 code = 250;
427 } 371 }
372 argv++;
373 }
428 374
429 // recipients specified as arguments 375#if ENABLE_FEATURE_SENDMAIL_MAILXX
430 while (*argv) { 376 // carbon copies recipients specified as -c options
431 // loose test on email address validity 377 for (l = opt_carboncopies; l; l = l->link) {
432 if (strchr(sane(*argv), '@')) { 378 char *s = sane(l->data);
433 rcptto(sane(*argv)); 379 // loose test on email address validity
434 llist_add_to_end(&headers, xasprintf("To: %s", *argv)); 380 if (strchr(s, '@')) {
435 } 381 rcptto(s);
436 argv++; 382 // TODO: do we ever need to mangle the message?
383 //llist_add_to_end(&headers, xasprintf("Cc: %s", s));
437 } 384 }
385 }
386#endif
438 387
439 // if -t specified or no recipients specified -> read recipients from message 388 // if -t specified or no recipients specified -> read recipients from message
440 // i.e. scan stdin for To:, Cc:, Bcc: lines ... 389 // i.e. scan stdin for To:, Cc:, Bcc: lines ...
441 // ... and then use the rest of stdin as message body 390 // ... and then use the rest of stdin as message body
442 // N.B. subject read from body can be further overrided with one specified on command line. 391 // N.B. subject read from body can be further overrided with one specified on command line.
443 // recipients are merged. Bcc: lines are deleted 392 // recipients are merged. Bcc: lines are deleted
444 // N.B. other headers are collected and will be dumped verbatim 393 // N.B. other headers are collected and will be dumped verbatim
445 if (opts & OPTS_t || !headers) { 394 if (opts & OPT_t || !headers) {
446 // fetch recipients and (optionally) subject 395 // fetch recipients and (optionally) subject
447 char *s; 396 char *s;
448 while ((s = xmalloc_fgetline(fp0)) != NULL) { 397 while ((s = xmalloc_fgetline(fp0)) != NULL) {
449 if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Cc: ", s, 4)) { 398 if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Cc: ", s, 4)) {
450 rcptto(sane(s+4)); 399 rcptto(sane(s+4));
451 llist_add_to_end(&headers, s); 400 llist_add_to_end(&headers, s);
452 } else if (0 == strncasecmp("Bcc: ", s, 5)) { 401 } else if (0 == strncasecmp("Bcc: ", s, 5)) {
453 rcptto(sane(s+5)); 402 rcptto(sane(s+5));
454 free(s); 403 free(s);
455 // N.B. Bcc vanishes from headers! 404 // N.B. Bcc vanishes from headers!
456 } else if (0 == strncmp("Subject: ", s, 9)) { 405 } else if (0 == strncmp("Subject: ", s, 9)) {
457 // we read subject -> use it verbatim unless it is specified 406 // we read subject -> use it verbatim unless it is specified
458 // on command line 407 // on command line
459 if (!(opts & OPTS_s)) 408 if (!(opts & OPT_s))
460 llist_add_to_end(&headers, s);
461 else
462 free(s);
463 } else if (s[0]) {
464 // misc header
465 llist_add_to_end(&headers, s); 409 llist_add_to_end(&headers, s);
466 } else { 410 else
467 free(s); 411 free(s);
468 break; // stop on the first empty line 412 } else if (s[0]) {
469 } 413 // misc header
414 llist_add_to_end(&headers, s);
415 } else {
416 free(s);
417 break; // stop on the first empty line
470 } 418 }
471 } 419 }
420 }
472 421
473 // enter "put message" mode 422 // enter "put message" mode
474 smtp_check("DATA", 354); 423 smtp_check("DATA", 354);
475 424
476 // put headers we could have preread with -t 425 // put headers we could have preread with -t
477 for (l = headers; l; l = l->link) { 426 for (l = headers; l; l = l->link) {
478 printf("%s\r\n", l->data); 427 printf("%s\r\n", l->data);
479 if (ENABLE_FEATURE_CLEAN_UP) 428 if (ENABLE_FEATURE_CLEAN_UP)
480 free(l->data); 429 free(l->data);
481 } 430 }
482 431
483 // put (possibly encoded) subject 432 // put (possibly encoded) subject
484 if (opts & OPTS_c) 433 if (opts & OPT_j)
485 sane((char *)opt_charset); 434 sane((char *)opt_charset);
486 if (opts & OPTS_s) { 435 if (opts & OPT_s) {
487 printf("Subject: "); 436 printf("Subject: ");
488 if (opts & OPTS_c) { 437 if (opts & OPT_j) {
489 printf("=?%s?B?", opt_charset); 438 printf("=?%s?B?", opt_charset);
490 uuencode(NULL, opt_subject); 439 uuencode(NULL, opt_subject);
491 printf("?="); 440 printf("?=");
492 } else { 441 } else {
493 printf("%s", opt_subject); 442 printf("%s", opt_subject);
494 }
495 printf("\r\n");
496 } 443 }
444 printf("\r\n");
445 }
497 446
498 // put sender name, $NAME is the default 447 // put sender name, $NAME is the default
499 if (!(opts & OPTS_F)) 448 if (!(opts & OPT_F))
500 opt_fullname = getenv("NAME"); 449 opt_fullname = getenv("NAME");
501 if (opt_fullname) 450 if (opt_fullname)
502 printf("From: \"%s\" <%s>\r\n", opt_fullname, opt_from); 451 printf("From: \"%s\" <%s>\r\n", opt_fullname, opt_from);
503 452
504 // put notification 453 // put notification
505 if (opts & OPTS_N) 454 if (opts & OPT_N)
506 printf("Disposition-Notification-To: %s\r\n", opt_from); 455 printf("Disposition-Notification-To: %s\r\n", opt_from);
507 456
508 // make a random string -- it will delimit message parts 457#if ENABLE_FEATURE_SENDMAIL_MAILXX
509 srand(monotonic_us()); 458 // put errors recipient
510 boundary = xasprintf("%d-%d-%d", rand(), rand(), rand()); 459 if (opts & OPT_e)
460 printf("Errors-To: %s\r\n", opt_errors_to);
461#endif
511 462
512 // put common headers 463 // make a random string -- it will delimit message parts
513 // TODO: do we really need this? 464 srand(monotonic_us());
514// printf("Message-ID: <%s>\r\n", boundary); 465 boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
515 466
516#if ENABLE_FEATURE_SENDMAIL_MAILX 467 // put common headers
517 // have attachments? -> compose multipart MIME 468 // TODO: do we really need this?
518 if (opt_attachments) { 469// printf("Message-ID: <%s>\r\n", boundary);
519 const char *fmt;
520 const char *p;
521 char *q;
522 470
471#if ENABLE_FEATURE_SENDMAIL_MAILX
472 // have attachments? -> compose multipart MIME
473 if (opt_attachments) {
474 const char *fmt;
475 const char *p;
476 char *q;
477
478 printf(
479 "Mime-Version: 1.0\r\n"
480 "%smultipart/mixed; boundary=\"%s\"\r\n"
481 , "Content-Type: "
482 , boundary
483 );
484
485 // body is pseudo attachment read from stdin in first turn
486 llist_add_to(&opt_attachments, (char *)"-");
487
488 // put body + attachment(s)
489 // N.B. all these weird things just to be tiny
490 // by reusing string patterns!
491 fmt =
492 "\r\n--%s\r\n"
493 "%stext/plain; charset=%s\r\n"
494 "%s%s\r\n"
495 "%s"
496 ;
497 p = opt_charset;
498 q = (char *)"";
499 l = opt_attachments;
500 while (l) {
523 printf( 501 printf(
524 "Mime-Version: 1.0\r\n" 502 fmt
525 "%smultipart/mixed; boundary=\"%s\"\r\n"
526 , "Content-Type: "
527 , boundary 503 , boundary
504 , "Content-Type: "
505 , p
506 , "Content-Disposition: inline"
507 , q
508 , "Content-Transfer-Encoding: base64\r\n"
528 ); 509 );
529 510 p = "";
530 // body is pseudo attachment read from stdin in first turn
531 llist_add_to(&opt_attachments, (char *)"-");
532
533 // put body + attachment(s)
534 // N.B. all these weird things just to be tiny
535 // by reusing string patterns!
536 fmt = 511 fmt =
537 "\r\n--%s\r\n" 512 "\r\n--%s\r\n"
538 "%stext/plain; charset=%s\r\n" 513 "%sapplication/octet-stream%s\r\n"
539 "%s%s\r\n" 514 "%s; filename=\"%s\"\r\n"
540 "%s" 515 "%s"
541 ; 516 ;
542 p = opt_charset; 517 uuencode(l->data, NULL);
543 q = (char *)""; 518 l = l->link;
544 l = opt_attachments; 519 if (l)
545 while (l) { 520 q = bb_get_last_path_component_strip(l->data);
546 printf( 521 }
547 fmt
548 , boundary
549 , "Content-Type: "
550 , p
551 , "Content-Disposition: inline"
552 , q
553 , "Content-Transfer-Encoding: base64\r\n"
554 );
555 p = "";
556 fmt =
557 "\r\n--%s\r\n"
558 "%sapplication/octet-stream%s\r\n"
559 "%s; filename=\"%s\"\r\n"
560 "%s"
561 ;
562 uuencode(l->data, NULL);
563 l = l->link;
564 if (l)
565 q = bb_get_last_path_component_strip(l->data);
566 }
567 522
568 // put message terminator 523 // put message terminator
569 printf("\r\n--%s--\r\n" "\r\n", boundary); 524 printf("\r\n--%s--\r\n" "\r\n", boundary);
570 525
571 // no attachments? -> just dump message 526 // no attachments? -> just dump message
572 } else 527 } else
573#endif 528#endif
574 { 529 {
575 char *s; 530 char *s;
576 // terminate headers 531 // terminate headers
577 printf("\r\n"); 532 printf("\r\n");
578 // put plain text respecting leading dots 533 // put plain text respecting leading dots
579 while ((s = xmalloc_fgetline(fp0)) != NULL) { 534 while ((s = xmalloc_fgetline(fp0)) != NULL) {
580 // escape leading dots 535 // escape leading dots
581 // N.B. this feature is implied even if no -i switch given 536 // N.B. this feature is implied even if no -i (-oi) switch given
582 // N.B. we need to escape the leading dot regardless of 537 // N.B. we need to escape the leading dot regardless of
583 // whether it is single or not character on the line 538 // whether it is single or not character on the line
584 if (/*(opts & OPTS_i) && */ '.' == s[0] /*&& '\0' == s[1] */) 539 if ('.' == s[0] /*&& '\0' == s[1] */)
585 printf("."); 540 printf(".");
586 // dump read line 541 // dump read line
587 printf("%s\r\n", s); 542 printf("%s\r\n", s);
588 }
589 } 543 }
590
591 // leave "put message" mode
592 smtp_check(".", 250);
593 // ... and say goodbye
594 smtp_check("QUIT", 221);
595 // cleanup
596 if (ENABLE_FEATURE_CLEAN_UP)
597 fclose(fp0);
598 } 544 }
599#if ENABLE_FETCHMAIL
600/***************************************************
601 * FETCHMAIL
602 ***************************************************/
603 else {
604 char *buf;
605 unsigned nmsg;
606 char *hostname;
607 pid_t pid;
608
609 // cache fetch command:
610 // TOP will return only the headers
611 // RETR will dump the whole message
612 const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
613
614 // goto maildir
615 xchdir(*argv++);
616
617 // cache postprocess program
618 *fargs = *argv;
619
620 // authenticate
621
622 // password is mandatory
623 if (!opt_pass) {
624 bb_error_msg_and_die("no password");
625 }
626 545
627 // get server greeting 546 // leave "put message" mode
628 pop3_checkr(NULL, NULL, &buf); 547 smtp_check(".", 250);
629 548 // ... and say goodbye
630 // server supports APOP? 549 smtp_check("QUIT", 221);
631 if ('<' == *buf) { 550 // cleanup
632 md5_ctx_t md5; 551 if (ENABLE_FEATURE_CLEAN_UP)
633 // yes! compose <stamp><password> 552 fclose(fp0);
634 char *s = strchr(buf, '>');
635 if (s)
636 strcpy(s+1, opt_pass);
637 s = buf;
638 // get md5 sum of <stamp><password>
639 md5_begin(&md5);
640 md5_hash(s, strlen(s), &md5);
641 md5_end(s, &md5);
642 // NOTE: md5 struct contains enough space
643 // so we reuse md5 space instead of xzalloc(16*2+1)
644#define md5_hex ((uint8_t *)&md5)
645// uint8_t *md5_hex = (uint8_t *)&md5;
646 *bin2hex((char *)md5_hex, s, 16) = '\0';
647 // APOP
648 s = xasprintf("%s %s", opt_user, md5_hex);
649#undef md5_hex
650 pop3_check("APOP %s", s);
651 if (ENABLE_FEATURE_CLEAN_UP) {
652 free(s);
653 free(buf-4); // buf is "+OK " away from malloc'ed string
654 }
655 // server ignores APOP -> use simple text authentication
656 } else {
657 // USER
658 pop3_check("USER %s", opt_user);
659 // PASS
660 pop3_check("PASS %s", opt_pass);
661 }
662
663 // get mailbox statistics
664 pop3_checkr("STAT", NULL, &buf);
665
666 // prepare message filename suffix
667 hostname = safe_gethostname();
668 pid = getpid();
669
670 // get messages counter
671 // NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
672 // we only need nmsg and atoi is just exactly what we need
673 // if atoi fails to convert buf into number it returns 0
674 // in this case the following loop simply will not be executed
675 nmsg = atoi(buf);
676 if (ENABLE_FEATURE_CLEAN_UP)
677 free(buf-4); // buf is "+OK " away from malloc'ed string
678
679 // loop through messages
680 for (; nmsg; nmsg--) {
681
682 // generate unique filename
683 char *filename = xasprintf("tmp/%llu.%u.%s",
684 monotonic_us(), (unsigned)pid, hostname);
685 char *target;
686 int rc;
687
688 // retrieve message in ./tmp/
689 pop3_check(retr, (const char *)(ptrdiff_t)nmsg);
690 pop3_message(filename);
691 // delete message from server
692 if (opts & OPTF_z)
693 pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg);
694
695 // run postprocessing program
696 if (*fargs) {
697 fargs[1] = filename;
698 rc = wait4pid(spawn((char **)fargs));
699 if (99 == rc)
700 break;
701 if (1 == rc)
702 goto skip;
703 }
704
705 // atomically move message to ./new/
706 target = xstrdup(filename);
707 strncpy(target, "new", 3);
708 // ... or just stop receiving on error
709 if (rename_or_warn(filename, target))
710 break;
711 free(target);
712 skip:
713 free(filename);
714 }
715
716 // Bye
717 pop3_check("QUIT", NULL);
718 }
719#endif // ENABLE_FETCHMAIL
720 553
721 return EXIT_SUCCESS; 554 return EXIT_SUCCESS;
722} 555}