aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-11-06 23:41:38 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-11-06 23:41:38 +0000
commitb9d572a2733fa20957a9a3287bd04d66176e3b6a (patch)
tree2b9ff7454709e84b02e644db4914ad9d4b739e68
parentf10bc3964f1d92fa83ec97a625fca33ce1592f26 (diff)
downloadbusybox-w32-b9d572a2733fa20957a9a3287bd04d66176e3b6a.tar.gz
busybox-w32-b9d572a2733fa20957a9a3287bd04d66176e3b6a.tar.bz2
busybox-w32-b9d572a2733fa20957a9a3287bd04d66176e3b6a.zip
total overhaul of mail applets. again. Vladimir as usual.
-rw-r--r--Config.in1
-rw-r--r--Makefile1
-rw-r--r--include/applets.h5
-rw-r--r--include/usage.h74
-rw-r--r--networking/Config.in41
-rw-r--r--networking/Kbuild2
-rw-r--r--networking/sendmail.c563
7 files changed, 61 insertions, 626 deletions
diff --git a/Config.in b/Config.in
index d2eab538e..4fd9d1111 100644
--- a/Config.in
+++ b/Config.in
@@ -596,6 +596,7 @@ source util-linux/Config.in
596source miscutils/Config.in 596source miscutils/Config.in
597source networking/Config.in 597source networking/Config.in
598source printutils/Config.in 598source printutils/Config.in
599source mailutils/Config.in
599source procps/Config.in 600source procps/Config.in
600source runit/Config.in 601source runit/Config.in
601source selinux/Config.in 602source selinux/Config.in
diff --git a/Makefile b/Makefile
index 5d6546716..495b16cab 100644
--- a/Makefile
+++ b/Makefile
@@ -456,6 +456,7 @@ libs-y := \
456 libbb/ \ 456 libbb/ \
457 libpwdgrp/ \ 457 libpwdgrp/ \
458 loginutils/ \ 458 loginutils/ \
459 mailutils/ \
459 miscutils/ \ 460 miscutils/ \
460 modutils/ \ 461 modutils/ \
461 networking/ \ 462 networking/ \
diff --git a/include/applets.h b/include/applets.h
index d52f9f034..0e4cbd5a3 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -151,7 +151,6 @@ USE_FBSPLASH(APPLET(fbsplash, _BB_DIR_SBIN, _BB_SUID_NEVER))
151USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush)) 151USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush))
152USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 152USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
153USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER)) 153USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER))
154//USE_FETCHMAIL(APPLET(fetchmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
155USE_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER, fgrep)) 154USE_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER, fgrep))
156USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find)) 155USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find))
157USE_FINDFS(APPLET(findfs, _BB_DIR_SBIN, _BB_SUID_MAYBE)) 156USE_FINDFS(APPLET(findfs, _BB_DIR_SBIN, _BB_SUID_MAYBE))
@@ -237,8 +236,8 @@ USE_LSATTR(APPLET(lsattr, _BB_DIR_BIN, _BB_SUID_NEVER))
237USE_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_NEVER)) 236USE_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_NEVER))
238USE_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe)) 237USE_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe))
239USE_UNLZMA(APPLET_ODDNAME(lzmacat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lzmacat)) 238USE_UNLZMA(APPLET_ODDNAME(lzmacat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lzmacat))
240USE_FEATURE_SENDMAIL_MAILX(APPLET_ODDNAME(mail, sendmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sendmail))
241USE_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_NEVER)) 239USE_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_NEVER))
240USE_MAKEMIME(APPLET(makemime, _BB_DIR_BIN, _BB_SUID_NEVER))
242USE_MAN(APPLET(man, _BB_DIR_SBIN, _BB_SUID_NEVER)) 241USE_MAN(APPLET(man, _BB_DIR_SBIN, _BB_SUID_NEVER))
243USE_MATCHPATHCON(APPLET(matchpathcon, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 242USE_MATCHPATHCON(APPLET(matchpathcon, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
244USE_MD5SUM(APPLET_ODDNAME(md5sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_NEVER, md5sum)) 243USE_MD5SUM(APPLET_ODDNAME(md5sum, md5_sha1_sum, _BB_DIR_USR_BIN, _BB_SUID_NEVER, md5sum))
@@ -281,6 +280,7 @@ USE_PING6(APPLET(ping6, _BB_DIR_BIN, _BB_SUID_NEVER))
281USE_PIPE_PROGRESS(APPLET(pipe_progress, _BB_DIR_BIN, _BB_SUID_NEVER)) 280USE_PIPE_PROGRESS(APPLET(pipe_progress, _BB_DIR_BIN, _BB_SUID_NEVER))
282USE_PIVOT_ROOT(APPLET(pivot_root, _BB_DIR_SBIN, _BB_SUID_NEVER)) 281USE_PIVOT_ROOT(APPLET(pivot_root, _BB_DIR_SBIN, _BB_SUID_NEVER))
283USE_PKILL(APPLET_ODDNAME(pkill, pgrep, _BB_DIR_USR_BIN, _BB_SUID_NEVER, pkill)) 282USE_PKILL(APPLET_ODDNAME(pkill, pgrep, _BB_DIR_USR_BIN, _BB_SUID_NEVER, pkill))
283USE_POPMAILDIR(APPLET(popmaildir, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
284USE_HALT(APPLET_ODDNAME(poweroff, halt, _BB_DIR_SBIN, _BB_SUID_NEVER, poweroff)) 284USE_HALT(APPLET_ODDNAME(poweroff, halt, _BB_DIR_SBIN, _BB_SUID_NEVER, poweroff))
285USE_PRINTENV(APPLET(printenv, _BB_DIR_BIN, _BB_SUID_NEVER)) 285USE_PRINTENV(APPLET(printenv, _BB_DIR_BIN, _BB_SUID_NEVER))
286USE_PRINTF(APPLET_NOFORK(printf, printf, _BB_DIR_USR_BIN, _BB_SUID_NEVER, printf)) 286USE_PRINTF(APPLET_NOFORK(printf, printf, _BB_DIR_USR_BIN, _BB_SUID_NEVER, printf))
@@ -295,6 +295,7 @@ USE_READLINK(APPLET(readlink, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
295USE_READPROFILE(APPLET(readprofile, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 295USE_READPROFILE(APPLET(readprofile, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
296USE_REALPATH(APPLET(realpath, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 296USE_REALPATH(APPLET(realpath, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
297USE_HALT(APPLET_ODDNAME(reboot, halt, _BB_DIR_SBIN, _BB_SUID_NEVER, reboot)) 297USE_HALT(APPLET_ODDNAME(reboot, halt, _BB_DIR_SBIN, _BB_SUID_NEVER, reboot))
298USE_REFORMIME(APPLET(reformime, _BB_DIR_BIN, _BB_SUID_NEVER))
298USE_RENICE(APPLET(renice, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 299USE_RENICE(APPLET(renice, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
299USE_RESET(APPLET(reset, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 300USE_RESET(APPLET(reset, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
300USE_RESIZE(APPLET(resize, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 301USE_RESIZE(APPLET(resize, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
diff --git a/include/usage.h b/include/usage.h
index 4ee82059e..19af3486a 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -1095,18 +1095,6 @@
1095 "\n -H HEADS\n" \ 1095 "\n -H HEADS\n" \
1096 "\n -S SECTORS" \ 1096 "\n -S SECTORS" \
1097 1097
1098#define fetchmail_trivial_usage \
1099 "[-w timeout] [-H [user:pass@]server[:port]] [-S] [-t] [-z] maildir [prog]"
1100#define fetchmail_full_usage "\n\n" \
1101 "Fetch content of remote mailbox to local maildir\n" \
1102 "\nOptions:" \
1103 "\n -w timeout Network timeout" \
1104 "\n -H [user:pass@]server[:port] Server" \
1105 "\n -S Use openssl connection helper for secure servers" \
1106 "\n -t Get only headers" \
1107 "\n -z Delete messages on server" \
1108 "\n prog Run 'prog <message_file>' on message delivery" \
1109
1110#define blkid_trivial_usage \ 1098#define blkid_trivial_usage \
1111 "" 1099 ""
1112#define blkid_full_usage "\n\n" \ 1100#define blkid_full_usage "\n\n" \
@@ -2378,6 +2366,16 @@
2378 "/dev/hda[0-15]\n" 2366 "/dev/hda[0-15]\n"
2379#endif 2367#endif
2380 2368
2369#define makemime_trivial_usage \
2370 "[OPTION]... [FILE]..."
2371#define makemime_full_usage "\n\n" \
2372 "Create MIME-encoded message\n" \
2373 "\nOptions:" \
2374 "\n -C Charset" \
2375 "\n -e Tranfer encoding. Ignored. base64 is assumed" \
2376 "\n" \
2377 "\nOther options are silently ignored." \
2378
2381#define man_trivial_usage \ 2379#define man_trivial_usage \
2382 "[OPTION]... [MANPAGE]..." 2380 "[OPTION]... [MANPAGE]..."
2383#define man_full_usage "\n\n" \ 2381#define man_full_usage "\n\n" \
@@ -3095,6 +3093,33 @@
3095 "\n -v Negate the matching" \ 3093 "\n -v Negate the matching" \
3096 "\n -x Match whole name (not substring)" \ 3094 "\n -x Match whole name (not substring)" \
3097 3095
3096#define popmaildir_trivial_usage \
3097 "[OPTIONS] Maildir [connection-helper ...]"
3098#define popmaildir_full_usage "\n\n" \
3099 "Fetch content of remote mailbox to local maildir\n" \
3100 "\nOptions:" \
3101 "\n -b Binary mode. Ignored" \
3102 "\n -d Debug. Ignored" \
3103 "\n -m Show used memory. Ignored" \
3104 "\n -V Show version. Ignored" \
3105 "\n -c Use tcpclient. Ignored" \
3106 "\n -a Use APOP protocol. Implied. If server supports APOP -> use it" \
3107 "\n -s Skip authorization" \
3108 "\n -T Get messages with TOP instead with RETR" \
3109 "\n -k Keep retrieved messages on the server" \
3110 "\n -t timeout Set network timeout" \
3111 USE_FEATURE_POPMAILDIR_DELIVERY( \
3112 "\n -F \"program arg1 arg2 ...\" Filter by program. May be multiple" \
3113 "\n -M \"program arg1 arg2 ...\" Deliver by program" \
3114 ) \
3115 "\n -R size Remove old messages on the server >= size (in bytes). Ignored" \
3116 "\n -Z N1-N2 Remove messages from N1 to N2 (dangerous). Ignored" \
3117 "\n -L size Do not retrieve new messages >= size (in bytes). Ignored" \
3118 "\n -H lines Type specified number of lines of a message. Ignored"
3119#define popmaildir_example_usage \
3120 "$ popmaildir -k ~/Maildir -- nc pop.drvv.ru 110 [<password_file]\n" \
3121 "$ popmaildir ~/Maildir -- openssl s_client -quiet -connect pop.gmail.com:995 [<password_file]\n"
3122
3098#define poweroff_trivial_usage \ 3123#define poweroff_trivial_usage \
3099 "[-d delay] [-n] [-f]" 3124 "[-d delay] [-n] [-f]"
3100#define poweroff_full_usage "\n\n" \ 3125#define poweroff_full_usage "\n\n" \
@@ -3250,6 +3275,17 @@
3250 "\n -n No call to sync()" \ 3275 "\n -n No call to sync()" \
3251 "\n -f Force reboot (don't go through init)" \ 3276 "\n -f Force reboot (don't go through init)" \
3252 3277
3278#define reformime_trivial_usage \
3279 "[OPTION]... [FILE]..."
3280#define reformime_full_usage "\n\n" \
3281 "Parse MIME-encoded message\n" \
3282 "\nOptions:" \
3283 "\n -x prefix Extract content of MIME sections to files" \
3284 "\n -X prog [args] Filter content of MIME sections through prog." \
3285 "\n Must be the last option" \
3286 "\n" \
3287 "\nOther options are silently ignored." \
3288
3253#define renice_trivial_usage \ 3289#define renice_trivial_usage \
3254 "{{-n INCREMENT} | PRIORITY} [[-p | -g | -u] ID...]" 3290 "{{-n INCREMENT} | PRIORITY} [[-p | -g | -u] ID...]"
3255#define renice_full_usage "\n\n" \ 3291#define renice_full_usage "\n\n" \
@@ -3484,9 +3520,7 @@
3484#define selinuxenabled_full_usage "" 3520#define selinuxenabled_full_usage ""
3485 3521
3486#define sendmail_trivial_usage \ 3522#define sendmail_trivial_usage \
3487 "[-w timeout] [-H [user:pass@]server[:port]] [-S]\n" \ 3523 "[OPTIONS] [rcpt]..."
3488 "[-N type] [-f sender] [-F fullname] " \
3489 USE_FEATURE_SENDMAIL_MAILX("[-s subject] [-c cc-rcpt]... [-j charset] [-a attach]... [-e err-rcpt] ") "[-t] [rcpt]..."
3490#define sendmail_full_usage "\n\n" \ 3524#define sendmail_full_usage "\n\n" \
3491 "Send an email\n" \ 3525 "Send an email\n" \
3492 "\nOptions:" \ 3526 "\nOptions:" \
@@ -3498,11 +3532,15 @@
3498 "\n -F fullname Sender full name. Overrides $NAME" \ 3532 "\n -F fullname Sender full name. Overrides $NAME" \
3499 USE_FEATURE_SENDMAIL_MAILX( \ 3533 USE_FEATURE_SENDMAIL_MAILX( \
3500 "\n -s subject Subject" \ 3534 "\n -s subject Subject" \
3501 "\n -c rcpt Cc: recipient. May be multiple" \ 3535 "\n -j charset Assume charset for body and subject (" CONFIG_FEATURE_MIME_CHARSET ")" \
3502 "\n -j charset Assume charset for body and subject (" CONFIG_FEATURE_SENDMAIL_CHARSET ")" \
3503 "\n -a file File to attach. May be multiple" \ 3536 "\n -a file File to attach. May be multiple" \
3537 "\n -H \"prog args...\" Use external connection helper. E.g. openssl for secure servers" \
3538 "\n -S server[:port] Server" \
3539 ) \
3540 USE_FEATURE_SENDMAIL_MAILXX( \
3541 "\n -c rcpt Cc: recipient. May be multiple" \
3504 "\n -e rcpt Errors-To: recipient" \ 3542 "\n -e rcpt Errors-To: recipient" \
3505 ) 3543 )
3506 "\n -t Read recipients and subject from body" \ 3544 "\n -t Read recipients and subject from body" \
3507 "\n" \ 3545 "\n" \
3508 "\nOther options are silently ignored; -oi is implied" \ 3546 "\nOther options are silently ignored; -oi is implied" \
diff --git a/networking/Config.in b/networking/Config.in
index 3ae77e119..95f894230 100644
--- a/networking/Config.in
+++ b/networking/Config.in
@@ -681,47 +681,6 @@ config ROUTE
681 help 681 help
682 Route displays or manipulates the kernel's IP routing tables. 682 Route displays or manipulates the kernel's IP routing tables.
683 683
684config SENDMAIL
685 bool "sendmail"
686 default n
687 help
688 Barebones sendmail.
689
690config FEATURE_SENDMAIL_MAILX
691 bool "Allow to specify subject, attachments and their charset"
692 default y
693 depends on SENDMAIL
694 help
695 Allow to specify subject, attachments and their charset.
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:
703 Errors-To:
704
705config FEATURE_SENDMAIL_SSL
706 bool "Allow to communicate via SSL/TLS"
707 default y
708 depends on SENDMAIL
709 help
710 Allow to use secure connections provided by openssl. E.g. @gmail.com.
711
712config FEATURE_SENDMAIL_CHARSET
713 string "Default charset"
714 default "utf-8"
715 depends on SENDMAIL
716 help
717 Default charset of the message.
718
719config FETCHMAIL
720 bool "fetchmail"
721 default n
722 help
723 Barebones fetchmail.
724
725config SLATTACH 684config SLATTACH
726 bool "slattach" 685 bool "slattach"
727 default n 686 default n
diff --git a/networking/Kbuild b/networking/Kbuild
index 75cc20f58..63d0745f8 100644
--- a/networking/Kbuild
+++ b/networking/Kbuild
@@ -11,7 +11,6 @@ lib-$(CONFIG_BRCTL) += brctl.o
11lib-$(CONFIG_DNSD) += dnsd.o 11lib-$(CONFIG_DNSD) += dnsd.o
12lib-$(CONFIG_ETHER_WAKE) += ether-wake.o 12lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
13lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o 13lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o
14lib-$(CONFIG_FETCHMAIL) += sendmail.o
15lib-$(CONFIG_FTPGET) += ftpgetput.o 14lib-$(CONFIG_FTPGET) += ftpgetput.o
16lib-$(CONFIG_FTPPUT) += ftpgetput.o 15lib-$(CONFIG_FTPPUT) += ftpgetput.o
17lib-$(CONFIG_HOSTNAME) += hostname.o 16lib-$(CONFIG_HOSTNAME) += hostname.o
@@ -30,7 +29,6 @@ lib-$(CONFIG_PING) += ping.o
30lib-$(CONFIG_PING6) += ping.o 29lib-$(CONFIG_PING6) += ping.o
31lib-$(CONFIG_PSCAN) += pscan.o 30lib-$(CONFIG_PSCAN) += pscan.o
32lib-$(CONFIG_ROUTE) += route.o 31lib-$(CONFIG_ROUTE) += route.o
33lib-$(CONFIG_SENDMAIL) += sendmail.o
34lib-$(CONFIG_SLATTACH) += slattach.o 32lib-$(CONFIG_SLATTACH) += slattach.o
35lib-$(CONFIG_TC) += tc.o 33lib-$(CONFIG_TC) += tc.o
36lib-$(CONFIG_TELNET) += telnet.o 34lib-$(CONFIG_TELNET) += telnet.o
diff --git a/networking/sendmail.c b/networking/sendmail.c
deleted file mode 100644
index 9602b89ec..000000000
--- a/networking/sendmail.c
+++ /dev/null
@@ -1,563 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * bare bones sendmail
4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 *
7 * Licensed under GPLv2, see file LICENSE in this tarball for details.
8 */
9#include "libbb.h"
10
11struct globals {
12 pid_t helper_pid;
13 unsigned timeout;
14 FILE *fp0; // initial stdin
15 // arguments for SSL connection helper
16 const char *xargs[9];
17};
18#define G (*ptr_to_globals)
19#define helper_pid (G.helper_pid)
20#define timeout (G.timeout )
21#define fp0 (G.fp0 )
22#define xargs (G.xargs )
23#define INIT_G() do { \
24 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
25 xargs[0] = "openssl"; \
26 xargs[1] = "s_client"; \
27 xargs[2] = "-quiet"; \
28 xargs[3] = "-connect"; \
29 /*xargs[4] = "localhost";*/ \
30 xargs[5] = "-tls1"; \
31 xargs[6] = "-starttls"; \
32 xargs[7] = "smtp"; \
33} while (0)
34
35#define opt_connect (xargs[4])
36
37static void uuencode(char *fname, const char *text)
38{
39 enum {
40 SRC_BUF_SIZE = 45, /* This *MUST* be a multiple of 3 */
41 DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
42 };
43
44#define src_buf text
45 FILE *fp = fp;
46 ssize_t len = len;
47 char dst_buf[DST_BUF_SIZE + 1];
48
49 if (fname) {
50 fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : fp0;
51 src_buf = bb_common_bufsiz1;
52 // N.B. strlen(NULL) segfaults!
53 } else if (text) {
54 // though we do not call uuencode(NULL, NULL) explicitly
55 // still we do not want to break things suddenly
56 len = strlen(text);
57 } else
58 return;
59
60 while (1) {
61 size_t size;
62 if (fname) {
63 size = fread((char *)src_buf, 1, SRC_BUF_SIZE, fp);
64 if ((ssize_t)size < 0)
65 bb_perror_msg_and_die(bb_msg_read_error);
66 } else {
67 size = len;
68 if (len > SRC_BUF_SIZE)
69 size = SRC_BUF_SIZE;
70 }
71 if (!size)
72 break;
73 // encode the buffer we just read in
74 bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
75 if (fname) {
76 printf("\r\n");
77 } else {
78 src_buf += size;
79 len -= size;
80 }
81 fwrite(dst_buf, 1, 4 * ((size + 2) / 3), stdout);
82 }
83 if (fname)
84 fclose(fp);
85#undef src_buf
86}
87
88
89#if ENABLE_FEATURE_SENDMAIL_SSL
90static void kill_helper(void)
91{
92 // TODO!!!: is there more elegant way to terminate child on program failure?
93 if (helper_pid > 0)
94 kill(helper_pid, SIGTERM);
95}
96
97// generic signal handler
98static void signal_handler(int signo)
99{
100#define err signo
101 if (SIGALRM == signo) {
102 kill_helper();
103 bb_error_msg_and_die("timed out");
104 }
105
106 // SIGCHLD. reap zombies
107 if (wait_any_nohang(&err) > 0)
108 if (WIFEXITED(err)) {
109// if (WEXITSTATUS(err))
110 bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
111// else
112// kill(0, SIGCONT);
113 }
114#undef err
115}
116
117static void launch_helper(const char **argv)
118{
119 // setup vanilla unidirectional pipes interchange
120 int idx;
121 int pipes[4];
122
123 xpipe(pipes);
124 xpipe(pipes+2);
125 helper_pid = vfork();
126 if (helper_pid < 0)
127 bb_perror_msg_and_die("vfork");
128 idx = (!helper_pid) * 2;
129 xdup2(pipes[idx], STDIN_FILENO);
130 xdup2(pipes[3-idx], STDOUT_FILENO);
131 if (ENABLE_FEATURE_CLEAN_UP)
132 for (int i = 4; --i >= 0; )
133 if (pipes[i] > STDOUT_FILENO)
134 close(pipes[i]);
135 if (!helper_pid) {
136 // child: try to execute connection helper
137 BB_EXECVP(*argv, (char **)argv);
138 _exit(127);
139 }
140 // parent: check whether child is alive
141 bb_signals(0
142 + (1 << SIGCHLD)
143 + (1 << SIGALRM)
144 , signal_handler);
145 signal_handler(SIGCHLD);
146 // child seems OK -> parent goes on
147}
148#else
149#define kill_helper() ((void)0)
150#define launch_helper(x) bb_error_msg_and_die("no SSL support")
151#endif
152
153static const char *command(const char *fmt, const char *param)
154{
155 const char *msg = fmt;
156 alarm(timeout);
157 if (msg) {
158 msg = xasprintf(fmt, param);
159 printf("%s\r\n", msg);
160 }
161 fflush(stdout);
162 return msg;
163}
164
165static int smtp_checkp(const char *fmt, const char *param, int code)
166{
167 char *answer;
168 const char *msg = command(fmt, param);
169 // read stdin
170 // if the string has a form \d\d\d- -- read next string. E.g. EHLO response
171 // parse first bytes to a number
172 // if code = -1 then just return this number
173 // if code != -1 then checks whether the number equals the code
174 // if not equal -> die saying msg
175 while ((answer = xmalloc_fgetline(stdin)) != NULL)
176 if (strlen(answer) <= 3 || '-' != answer[3])
177 break;
178 if (answer) {
179 int n = atoi(answer);
180 alarm(0);
181 free(answer);
182 if (-1 == code || n == code)
183 return n;
184 }
185 kill_helper();
186 bb_error_msg_and_die("%s failed", msg);
187}
188
189static inline int smtp_check(const char *fmt, int code)
190{
191 return smtp_checkp(fmt, NULL, code);
192}
193
194// strip argument of bad chars
195static char *sane(char *str)
196{
197 char *s = str;
198 char *p = s;
199 while (*s) {
200 if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
201 *p++ = *s;
202 }
203 s++;
204 }
205 *p = '\0';
206 return str;
207}
208
209// NB: parse_url can modify url[] (despite const), but only if '@' is there
210static const char *parse_url(const char *url, const char **user, const char **pass)
211{
212 // parse [user[:pass]@]host
213 // return host
214 char *s = strchr(url, '@');
215 *user = *pass = NULL;
216 if (s) {
217 *s++ = '\0';
218 *user = url;
219 url = s;
220 s = strchr(*user, ':');
221 if (s) {
222 *s++ = '\0';
223 *pass = s;
224 }
225 }
226 return url;
227}
228
229static void rcptto(const char *s)
230{
231 smtp_checkp("RCPT TO:<%s>", s, 250);
232}
233
234int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
235int sendmail_main(int argc UNUSED_PARAM, char **argv)
236{
237#if ENABLE_FEATURE_SENDMAIL_MAILX
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
245#endif
246 char *opt_from, *opt_fullname;
247 const char *opt_user;
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;
255
256 enum {
257 OPT_w = 1 << 0, // network timeout
258 OPT_H = 1 << 1, // [user:password@]server[:port]
259 OPT_S = 1 << 2, // connect using openssl s_client helper
260 OPT_t = 1 << 3, // read message for recipients
261 OPT_N = 1 << 4, // request notification
262 OPT_f = 1 << 5, // sender address
263 OPT_F = 1 << 6, // sender name, overrides $NAME
264#if ENABLE_FEATURE_SENDMAIL_MAILX
265 OPT_s = 1 << 7, // subject
266 OPT_j = 1 << 8, // assumed charset
267 OPT_a = 1 << 9, // attachment(s)
268#if ENABLE_FEATURE_SENDMAIL_MAILXX
269 OPT_c = 1 << 10, // carbon copy
270 OPT_e = 1 << 11, // errors-to address
271#endif
272#endif
273 };
274
275 // init global variables
276 INIT_G();
277
278 // save initial stdin since body is piped!
279 xdup2(STDIN_FILENO, 3);
280 fp0 = fdopen(3, "r");
281
282 // parse options
283 opt_complementary = "w+:a::" USE_FEATURE_SENDMAIL_MAILXX("c::");
284 opts = getopt32(argv,
285 "w:H:St" "N:f:F:" USE_FEATURE_SENDMAIL_MAILX("s:j:a:") USE_FEATURE_SENDMAIL_MAILXX("c:e:")
286 "X:V:vq:R:O:o:nmL:Iih:GC:B:b:A:" // postfix compat only, ignored
287 // r:Q:p:M:Dd are candidates from another man page. TODO?
288 "46E", // ssmtp introduces another quirks. TODO?: -a[upm] (user, pass, method) to be supported
289 &timeout /* -w */, &opt_connect /* -H */,
290 NULL, &opt_from, &opt_fullname,
291 USE_FEATURE_SENDMAIL_MAILX(&opt_subject, &opt_charset, &opt_attachments,)
292 USE_FEATURE_SENDMAIL_MAILXX(&opt_carboncopies, &opt_errors_to,)
293 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
294 );
295 //argc -= optind;
296 argv += optind;
297
298 // connect to server
299 // host[:port] not specified ? -> use $SMTPHOST. no $SMTPHOST ? -> use localhost
300 if (!(opts & OPT_H)) {
301 opt_connect = getenv("SMTPHOST");
302 if (!opt_connect)
303 opt_connect = "127.0.0.1";
304 }
305 // fetch username and password, if any
306 // NB: parse_url modifies opt_connect[] ONLY if '@' is there.
307 // Thus "127.0.0.1" won't be modified, an is ok that it is RO.
308 opt_connect = parse_url(opt_connect, &opt_user, &opt_pass);
309
310 // username must be defined!
311 if (!opt_user) {
312 // N.B. IMHO getenv("USER") can be way easily spoofed!
313 opt_user = bb_getpwuid(NULL, -1, getuid());
314 }
315
316 // SSL ordered? ->
317 if (opts & OPT_S) {
318 // ... use openssl helper
319 launch_helper(xargs);
320 // no SSL ordered? ->
321 } else {
322 // ... make plain connect
323 int fd = create_and_connect_stream_or_die(opt_connect, 25);
324 // make ourselves a simple IO filter
325 // from now we know nothing about network :)
326 xmove_fd(fd, STDIN_FILENO);
327 xdup2(STDIN_FILENO, STDOUT_FILENO);
328 }
329
330 // got no sender address? -> use username as a resort
331 if (!(opts & OPT_f)) {
332 opt_from = xasprintf("%s@%s", opt_user, domain);
333 }
334
335 // introduce to server
336
337 // we didn't use SSL helper? ->
338 if (!(opts & OPT_S)) {
339 // ... wait for initial server OK
340 smtp_check(NULL, 220);
341 }
342
343 // we should start with modern EHLO
344 if (250 != smtp_checkp("EHLO %s", domain, -1)) {
345 smtp_checkp("HELO %s", domain, 250);
346 }
347 if (ENABLE_FEATURE_CLEAN_UP)
348 free(domain);
349
350 // set sender
351 // NOTE: if password has not been specified
352 // then no authentication is possible
353 code = (opt_pass ? -1 : 250);
354 // first try softly without authentication
355 while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) {
356 // MAIL FROM failed -> authentication needed
357 if (334 == smtp_check("AUTH LOGIN", -1)) {
358 uuencode(NULL, opt_user); // opt_user != NULL
359 smtp_check("", 334);
360 uuencode(NULL, opt_pass);
361 smtp_check("", 235);
362 }
363 // authenticated OK? -> retry to set sender
364 // but this time die on failure!
365 code = 250;
366 }
367
368 // recipients specified as arguments
369 while (*argv) {
370 char *s = sane(*argv);
371 // loose test on email address validity
372 if (strchr(s, '@')) {
373 rcptto(s);
374 llist_add_to_end(&headers, xasprintf("To: %s", s));
375 }
376 argv++;
377 }
378
379#if ENABLE_FEATURE_SENDMAIL_MAILXX
380 // carbon copies recipients specified as -c options
381 for (l = opt_carboncopies; l; l = l->link) {
382 char *s = sane(l->data);
383 // loose test on email address validity
384 if (strchr(s, '@')) {
385 rcptto(s);
386 // TODO: do we ever need to mangle the message?
387 //llist_add_to_end(&headers, xasprintf("Cc: %s", s));
388 }
389 }
390#endif
391
392 // if -t specified or no recipients specified -> read recipients from message
393 // i.e. scan stdin for To:, Cc:, Bcc: lines ...
394 // ... and then use the rest of stdin as message body
395 // N.B. subject read from body can be further overrided with one specified on command line.
396 // recipients are merged. Bcc: lines are deleted
397 // N.B. other headers are collected and will be dumped verbatim
398 if (opts & OPT_t || !headers) {
399 // fetch recipients and (optionally) subject
400 char *s;
401 while ((s = xmalloc_fgetline(fp0)) != NULL) {
402 if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Cc: ", s, 4)) {
403 rcptto(sane(s+4));
404 llist_add_to_end(&headers, s);
405 } else if (0 == strncasecmp("Bcc: ", s, 5)) {
406 rcptto(sane(s+5));
407 free(s);
408 // N.B. Bcc vanishes from headers!
409 } else if (0 == strncmp("Subject: ", s, 9)) {
410 // we read subject -> use it verbatim unless it is specified
411 // on command line
412#if ENABLE_FEATURE_SENDMAIL_MAILX
413 if (opts & OPT_s)
414 free(s);
415 else
416#endif
417 llist_add_to_end(&headers, s);
418 } else if (s[0]) {
419 // misc header
420 llist_add_to_end(&headers, s);
421 } else {
422 free(s);
423 break; // stop on the first empty line
424 }
425 }
426 }
427
428 // enter "put message" mode
429 smtp_check("DATA", 354);
430
431 // put headers we could have preread with -t
432 for (l = headers; l; l = l->link) {
433 printf("%s\r\n", l->data);
434 if (ENABLE_FEATURE_CLEAN_UP)
435 free(l->data);
436 }
437
438 // put (possibly encoded) subject
439#if ENABLE_FEATURE_SENDMAIL_MAILX
440 if (opts & OPT_j)
441 sane((char *)opt_charset);
442 if (opts & OPT_s) {
443 printf("Subject: ");
444 if (opts & OPT_j) {
445 printf("=?%s?B?", opt_charset);
446 uuencode(NULL, opt_subject);
447 printf("?=");
448 } else {
449 printf("%s", opt_subject);
450 }
451 printf("\r\n");
452 }
453#endif
454
455 // put sender name, $NAME is the default
456 if (!(opts & OPT_F))
457 opt_fullname = getenv("NAME");
458 if (opt_fullname)
459 printf("From: \"%s\" <%s>\r\n", opt_fullname, opt_from);
460
461 // put notification
462 if (opts & OPT_N)
463 printf("Disposition-Notification-To: %s\r\n", opt_from);
464
465#if ENABLE_FEATURE_SENDMAIL_MAILXX
466 // put errors recipient
467 if (opts & OPT_e)
468 printf("Errors-To: %s\r\n", opt_errors_to);
469#endif
470
471 // make a random string -- it will delimit message parts
472 srand(monotonic_us());
473 boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
474
475 // put common headers
476 // TODO: do we really need this?
477// printf("Message-ID: <%s>\r\n", boundary);
478
479#if ENABLE_FEATURE_SENDMAIL_MAILX
480 // have attachments? -> compose multipart MIME
481 if (opt_attachments) {
482 const char *fmt;
483 const char *p;
484 char *q;
485
486 printf(
487 "Mime-Version: 1.0\r\n"
488 "%smultipart/mixed; boundary=\"%s\"\r\n"
489 , "Content-Type: "
490 , boundary
491 );
492
493 // body is pseudo attachment read from stdin in first turn
494 llist_add_to(&opt_attachments, (char *)"-");
495
496 // put body + attachment(s)
497 // N.B. all these weird things just to be tiny
498 // by reusing string patterns!
499 fmt =
500 "\r\n--%s\r\n"
501 "%stext/plain; charset=%s\r\n"
502 "%s%s\r\n"
503 "%s"
504 ;
505 p = opt_charset;
506 q = (char *)"";
507 l = opt_attachments;
508 while (l) {
509 printf(
510 fmt
511 , boundary
512 , "Content-Type: "
513 , p
514 , "Content-Disposition: inline"
515 , q
516 , "Content-Transfer-Encoding: base64\r\n"
517 );
518 p = "";
519 fmt =
520 "\r\n--%s\r\n"
521 "%sapplication/octet-stream%s\r\n"
522 "%s; filename=\"%s\"\r\n"
523 "%s"
524 ;
525 uuencode(l->data, NULL);
526 l = l->link;
527 if (l)
528 q = bb_get_last_path_component_strip(l->data);
529 }
530
531 // put message terminator
532 printf("\r\n--%s--\r\n" "\r\n", boundary);
533
534 // no attachments? -> just dump message
535 } else
536#endif
537 {
538 char *s;
539 // terminate headers
540 printf("\r\n");
541 // put plain text respecting leading dots
542 while ((s = xmalloc_fgetline(fp0)) != NULL) {
543 // escape leading dots
544 // N.B. this feature is implied even if no -i (-oi) switch given
545 // N.B. we need to escape the leading dot regardless of
546 // whether it is single or not character on the line
547 if ('.' == s[0] /*&& '\0' == s[1] */)
548 printf(".");
549 // dump read line
550 printf("%s\r\n", s);
551 }
552 }
553
554 // leave "put message" mode
555 smtp_check(".", 250);
556 // ... and say goodbye
557 smtp_check("QUIT", 221);
558 // cleanup
559 if (ENABLE_FEATURE_CLEAN_UP)
560 fclose(fp0);
561
562 return EXIT_SUCCESS;
563}