aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-02-08 18:24:54 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-02-08 18:24:54 +0000
commit6d52c1ee24f77690c932dfe8456dfa31e8808f4c (patch)
treefddeffde64aeb613edc23e3580d196a28eb1ff03
parentdc1c45795b36c232fe781962c5fab1df6b8299d9 (diff)
downloadbusybox-w32-6d52c1ee24f77690c932dfe8456dfa31e8808f4c.tar.gz
busybox-w32-6d52c1ee24f77690c932dfe8456dfa31e8808f4c.tar.bz2
busybox-w32-6d52c1ee24f77690c932dfe8456dfa31e8808f4c.zip
sendmail: update from maintainer
-rw-r--r--include/applets.h3
-rw-r--r--include/usage.h45
-rw-r--r--networking/Config.in35
-rw-r--r--networking/Kbuild1
-rw-r--r--networking/sendmail.c630
5 files changed, 472 insertions, 242 deletions
diff --git a/include/applets.h b/include/applets.h
index a78ce1c77..40fa39069 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -151,6 +151,7 @@ USE_FBSET(APPLET(fbset, _BB_DIR_USR_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))
154USE_FETCHMAIL(APPLET_ODDNAME(fetchmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, fetchmail))
154USE_FEATURE_GREP_FGREP_ALIAS(APPLET_NOUSAGE(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER)) 155USE_FEATURE_GREP_FGREP_ALIAS(APPLET_NOUSAGE(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER))
155USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find)) 156USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find))
156//USE_FINDFS(APPLET_NOUSAGE(findfs, tune2fs, _BB_DIR_SBIN, _BB_SUID_NEVER)) 157//USE_FINDFS(APPLET_NOUSAGE(findfs, tune2fs, _BB_DIR_SBIN, _BB_SUID_NEVER))
@@ -299,7 +300,7 @@ USE_RUNSV(APPLET(runsv, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
299USE_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 300USE_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
300USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 301USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
301USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER)) 302USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER))
302USE_SENDMAIL(APPLET(sendmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 303USE_SENDMAIL(APPLET_ODDNAME(sendmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sendmail))
303USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 304USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
304USE_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER, seq)) 305USE_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER, seq))
305USE_SESTATUS(APPLET(sestatus, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 306USE_SESTATUS(APPLET(sestatus, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
diff --git a/include/usage.h b/include/usage.h
index dfa751597..77a86074b 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -1017,6 +1017,19 @@ USE_FEATURE_BRCTL_FANCY("\n" \
1017 " -S SECTORS Set the number of sectors\n" \ 1017 " -S SECTORS Set the number of sectors\n" \
1018 " -v Give fdisk version" 1018 " -v Give fdisk version"
1019 1019
1020#define fetchmail_trivial_usage \
1021 "[-C dir] [-w timeout] [-U user] -P password [-X] [-t] [-z] server[:port]"
1022#define fetchmail_full_usage \
1023 "Fetch content of remote mailbox to local Maildir." \
1024 "\n\nOptions:\n" \
1025 " -C dir Set Maildir location\n" \
1026 " -w timeout Set timeout on network operations\n" \
1027 " -U username Authenticate with specified username\n" \
1028 " -P password Authenticate with specified password\n" \
1029 " -X Use openssl connection helper for secured servers\n" \
1030 " -t Get only headers\n" \
1031 " -z Delete messages on server"
1032
1020#define find_trivial_usage \ 1033#define find_trivial_usage \
1021 "[PATH...] [EXPRESSION]" 1034 "[PATH...] [EXPRESSION]"
1022#define find_full_usage \ 1035#define find_full_usage \
@@ -3138,29 +3151,21 @@ USE_FEATURE_RUN_PARTS_FANCY("\n -l Prints names of all matching files even when
3138#define selinuxenabled_full_usage 3151#define selinuxenabled_full_usage
3139 3152
3140#define sendmail_trivial_usage \ 3153#define sendmail_trivial_usage \
3141 "{-t to}+ {-f from} [-n[notify]] [-s subject] [-b file]*\n" \ 3154 "[-C dir] [-w timeout] [-U user] [-P password] [-X]\n" \
3142 "[-a attachment]* [-c charset]" \ 3155 "-t to [-t to] -f from [-n] [-s subject] [-c charset] server[:port] [body] [attachment ...]"
3143 USE_FEATURE_SENDMAIL_NETWORK("\n" \
3144 " [-d] [-w timeout] [-h server] [-p port] [-U user] [-P password]" \
3145 )
3146#define sendmail_full_usage \ 3156#define sendmail_full_usage \
3147 "Send an email <from> <to> with <subject> and optional attachments." \ 3157 "Send an email with optional attachments." \
3148 "\n\nArguments:\n" \ 3158 "\n\nOptions:\n" \
3159 " -C dir Change current directory to dir\n" \
3160 " -w timeout Set timeout on network operations\n" \
3161 " -U username Authenticate with specified username\n" \
3162 " -P password Authenticate with specified password\n" \
3163 " -X Use openssl connection helper for secured servers\n" \
3149 " -t to Recipient email. May be multiple\n" \ 3164 " -t to Recipient email. May be multiple\n" \
3150 " -f from Sender address\n" \ 3165 " -f from Sender address\n" \
3151 " -n[notify] Optional notification address. If just -n given then notifies the sender\n" \ 3166 " -n Request delivery notification to sender\n" \
3152 " -s subject Optional subject\n" \ 3167 " -s subject Subject\n" \
3153 " -b filename Optional body content file. May be multiple\n" \ 3168 " -c charset Assumed charset for body and subject [utf-8]"
3154 " -a filename Optional file attachment. May be multiple\n" \
3155 " -c charset Assumed charset for body and subject [koi8-r]" \
3156 USE_FEATURE_SENDMAIL_NETWORK("\n" \
3157 " -d Just dump composed message\n" \
3158 " -w timeout Set timeout on network operations\n" \
3159 " -h server Optional mail server name or IP [127.0.0.1]\n" \
3160 " -p port Optional mail server port [25]\n" \
3161 " -U username Authenticate using AUTH LOGIN with specified username\n" \
3162 " -P password Authenticate using AUTH LOGIN with specified password" \
3163 )
3164 3169
3165#define seq_trivial_usage \ 3170#define seq_trivial_usage \
3166 "[first [increment]] last" 3171 "[first [increment]] last"
diff --git a/networking/Config.in b/networking/Config.in
index e8820e10d..ed87a178d 100644
--- a/networking/Config.in
+++ b/networking/Config.in
@@ -671,12 +671,39 @@ config SENDMAIL
671 help 671 help
672 Barebones sendmail. 672 Barebones sendmail.
673 673
674config FEATURE_SENDMAIL_NETWORK 674config FEATURE_SENDMAIL_EHLO
675 bool "Support network connectivity" 675 bool "Support EHLO command"
676 default y 676 default n
677 depends on SENDMAIL
678 help
679 Support ESMTP EHLO command.
680
681config FEATURE_SENDMAIL_BLOATY
682 bool "Be more verbose"
683 default n
677 depends on SENDMAIL 684 depends on SENDMAIL
678 help 685 help
679 Add ability to send, not only compose messages. 686 Should be turned off.
687
688config FETCHMAIL
689 bool "fetchmail"
690 default n
691 help
692 Barebones fetchmail.
693
694config FEATURE_FETCHMAIL_APOP
695 bool "Support APOP authentication"
696 default y
697 depends on FETCHMAIL
698 help
699 Support secure APOP authentication.
700
701config FEATURE_FETCHMAIL_FILTER
702 bool "Pipe thru external filter"
703 default n
704 depends on FETCHMAIL
705 help
706 Support piping incoming messages thru external filter.
680 707
681config SLATTACH 708config SLATTACH
682 bool "slattach" 709 bool "slattach"
diff --git a/networking/Kbuild b/networking/Kbuild
index 2819e8507..8f309cdb2 100644
--- a/networking/Kbuild
+++ b/networking/Kbuild
@@ -11,6 +11,7 @@ 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
14lib-$(CONFIG_FTPGET) += ftpgetput.o 15lib-$(CONFIG_FTPGET) += ftpgetput.o
15lib-$(CONFIG_FTPPUT) += ftpgetput.o 16lib-$(CONFIG_FTPPUT) += ftpgetput.o
16lib-$(CONFIG_HOSTNAME) += hostname.o 17lib-$(CONFIG_HOSTNAME) += hostname.o
diff --git a/networking/sendmail.c b/networking/sendmail.c
index fa1abc7ff..5dababc4b 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 3 * bare bones sendmail/fetchmail
4 * 4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com> 5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 * 6 *
@@ -8,23 +8,24 @@
8 */ 8 */
9#include "libbb.h" 9#include "libbb.h"
10 10
11/* 11#define INITIAL_STDIN_FILENO 3
12 Extracted from BB uuencode.c
13 */
14enum {
15 SRC_BUF_SIZE = 45, /* This *MUST* be a multiple of 3 */
16 DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
17};
18 12
19static void uuencode(char *fname, const char *text) 13static void uuencode(char *fname, const char *text)
20{ 14{
15 enum {
16 SRC_BUF_SIZE = 45, /* This *MUST* be a multiple of 3 */
17 DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
18 };
19
21#define src_buf text 20#define src_buf text
22 int fd; 21 int fd;
23#define len fd 22#define len fd
24 char dst_buf[DST_BUF_SIZE + 1]; 23 char dst_buf[DST_BUF_SIZE + 1];
25 24
26 if (fname) { 25 if (fname) {
27 fd = xopen(fname, O_RDONLY); 26 fd = INITIAL_STDIN_FILENO;
27 if (NOT_LONE_DASH(fname))
28 fd = xopen(fname, O_RDONLY);
28 src_buf = bb_common_bufsiz1; 29 src_buf = bb_common_bufsiz1;
29 } else { 30 } else {
30 len = strlen(text); 31 len = strlen(text);
@@ -44,69 +45,147 @@ static void uuencode(char *fname, const char *text)
44 } 45 }
45 if (!size) 46 if (!size)
46 break; 47 break;
47 // Encode the buffer we just read in 48 // encode the buffer we just read in
48 bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64); 49 bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
49 if (fname) { 50 if (fname) {
50 xwrite(STDOUT_FILENO, "\n", 1); 51 xwrite(STDOUT_FILENO, "\r\n", 2);
51 } else { 52 } else {
52 src_buf += size; 53 src_buf += size;
53 len -= size; 54 len -= size;
54 } 55 }
55 xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3)); 56 xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3));
56 } 57 }
57 if (ENABLE_FEATURE_CLEAN_UP && fname) 58 if (fname)
58 close(fd); 59 close(fd);
59} 60}
60 61
61#if ENABLE_FEATURE_SENDMAIL_NETWORK 62static pid_t helper_pid;
63
64static void kill_helper(void)
65{
66 // TODO!!!: is there more elegant way to terminate child on program failure?
67 if (helper_pid > 0)
68 kill(helper_pid, SIGTERM);
69}
70
62// generic signal handler 71// generic signal handler
63static void signal_handler(int signo) 72static void signal_handler(int signo)
64{ 73{
65 int err; 74 int err;
66 75
67 if (SIGALRM == signo) 76 if (SIGALRM == signo) {
77 kill_helper();
68 bb_error_msg_and_die("timed out"); 78 bb_error_msg_and_die("timed out");
79 }
69 80
70 // SIGCHLD. reap zombies 81 // SIGCHLD. reap zombies
71 if (wait_any_nohang(&err) > 0) 82 if (wait_any_nohang(&err) > 0)
72 if (WIFEXITED(err) && WEXITSTATUS(err)) 83 if (WIFEXITED(err) && WEXITSTATUS(err))
84#if ENABLE_FEATURE_SENDMAIL_BLOATY
73 bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err)); 85 bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
86#else
87 bb_error_msg_and_die("child failed");
88#endif
74} 89}
75 90
76static pid_t helper_pid; 91static void launch_helper(const char **argv)
77
78// read stdin, parses first bytes to a number, i.e. server response
79// if code = -1 then just return this number
80// if code != -1 then checks whether the number equals the code
81// if not equal -> die saying errmsg
82static int check(int code, const char *errmsg)
83{ 92{
84 char *answer; 93 // setup vanilla unidirectional pipes interchange
94 int idx;
95 int pipes[4];
96 xpipe(pipes);
97 xpipe(pipes+2);
98 helper_pid = vfork();
99 if (helper_pid < 0)
100 bb_perror_msg_and_die("vfork");
101 idx = (!helper_pid)*2;
102 xdup2(pipes[idx], STDIN_FILENO);
103 xdup2(pipes[3-idx], STDOUT_FILENO);
104 if (ENABLE_FEATURE_CLEAN_UP)
105 for (int i = 4; --i >= 0; )
106 if (pipes[i] > STDOUT_FILENO)
107 close(pipes[i]);
108 if (!helper_pid) {
109 // child - try to execute connection helper
110 BB_EXECVP(argv[0], (char **)argv);
111 _exit(127);
112 }
113 // parent - check whether child is alive
114 sig_catch(SIGCHLD, signal_handler);
115 sig_catch(SIGALRM, signal_handler);
116 signal_handler(SIGCHLD);
117 // child seems OK -> parent goes on
118}
119
120static unsigned timeout;
85 121
86 // read a string and match it against the set of available answers 122static char *command(const char *fmt, const char *param)
123{
124 char *msg = (char *)fmt;
125 alarm(timeout);
126 if (msg) {
127// if (param)
128 msg = xasprintf(fmt, param);
129 printf("%s\r\n", msg);
130 }
87 fflush(stdout); 131 fflush(stdout);
132 return msg;
133}
134
135static int smtp_checkp(const char *fmt, const char *param, int code)
136{
137 char *answer;
138 char *msg = command(fmt, param);
139 // read stdin
140 // if the string has a form \d\d\d- -- read next string. E.g. EHLO response
141 // parse first bytes to a number
142 // if code = -1 then just return this number
143 // if code != -1 then checks whether the number equals the code
144 // if not equal -> die saying msg
145#if ENABLE_FEATURE_SENDMAIL_EHLO
146 while ((answer = xmalloc_getline(stdin)) && '-' == answer[3]) ;
147#else
88 answer = xmalloc_getline(stdin); 148 answer = xmalloc_getline(stdin);
149#endif
89 if (answer) { 150 if (answer) {
90 int n = atoi(answer); 151 int n = atoi(answer);
91 if (-1 == code || n == code) { 152 alarm(0);
153 if (ENABLE_FEATURE_CLEAN_UP) {
154 free(msg);
92 free(answer); 155 free(answer);
156 }
157 if (-1 == code || n == code) {
93 return n; 158 return n;
94 } 159 }
95 } 160 }
96 // TODO: is there more elegant way to terminate child on program failure? 161 kill_helper();
97 if (helper_pid > 0) 162 bb_error_msg_and_die("%s failed", msg);
98 kill(helper_pid, SIGTERM);
99 if (!answer)
100 answer = (char*)"EOF";
101 else
102 *strchrnul(answer, '\r') = '\0';
103 bb_error_msg_and_die("error at %s: got '%s' instead", errmsg, answer);
104} 163}
105 164
106static int puts_and_check(const char *msg, int code, const char *errmsg) 165static int smtp_check(const char *fmt, int code)
107{ 166{
108 printf("%s\r\n", msg); 167 return smtp_checkp(fmt, NULL, code);
109 return check(code, errmsg); 168}
169
170static void pop3_checkr(const char *fmt, const char *param, char **ret)
171{
172 char *msg = command(fmt, param);
173 char *answer = xmalloc_getline(stdin);
174 if (answer && '+' == answer[0]) {
175 alarm(0);
176 if (ret)
177 *ret = answer;
178 else
179 free(answer);
180 return;
181 }
182 kill_helper();
183 bb_error_msg_and_die("%s failed", msg);
184}
185
186static void pop3_check(const char *fmt, const char *param)
187{
188 pop3_checkr(fmt, param, NULL);
110} 189}
111 190
112// strip argument of bad chars 191// strip argument of bad chars
@@ -123,215 +202,332 @@ static char *sane(char *str)
123 *p = '\0'; 202 *p = '\0';
124 return str; 203 return str;
125} 204}
126#endif
127 205
128int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 206static void pop3_message(int fd)
129int sendmail_main(int argc, char **argv) 207{
208 char *answer;
209 // read stdin, copy to file fd
210 while ((answer = xmalloc_fgets_str(stdin, "\r\n"))) {
211 char *s = answer;
212 if ('.' == answer[0]) {
213 if ('.' == answer[1])
214 s++;
215 else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
216 break;
217 }
218 xwrite(fd, s, strlen(s));
219 free(answer);
220 }
221 close(fd);
222}
223
224static const char *args[] = {
225 "openssl", "s_client", "-quiet", "-connect", NULL, "-tls1", "-starttls", "smtp", NULL
226};
227#define opt_connect args[4]
228#define opt_after_connect args[5]
229
230int sendgetmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
231int sendgetmail_main(int argc, char **argv)
130{ 232{
131 llist_t *recipients = NULL; 233 llist_t *recipients = NULL;
132 llist_t *bodies = NULL;
133 llist_t *attachments = NULL;
134 char *from; 234 char *from;
135 char *notify = NULL;
136 const char *subject; 235 const char *subject;
137 char *charset = (char*)"utf-8"; 236 char *charset = (char *)"utf-8";
138#if ENABLE_FEATURE_SENDMAIL_NETWORK 237
139 const char *wsecs = "10";
140 const char *server = "127.0.0.1";
141 const char *port = NULL;
142 const char *opt_user; 238 const char *opt_user;
143 const char *opt_pass; 239 const char *opt_pass;
144 unsigned timeout; 240 const char *opt_timeout;
145#endif 241 const char *opt_chdir;
146 char *boundary; 242
147 unsigned opts;
148 enum { 243 enum {
149 OPT_f = 1 << 0, // sender 244 OPT_C = 1 << 0, // chdir
150 OPT_n = 1 << 2, // notification 245 OPT_w = 1 << 1, // network timeout
151 OPT_s = 1 << 3, // subject given 246 OPT_U = 1 << 2, // user
152 OPT_c = 1 << 6, // charset 247 OPT_P = 1 << 3, // password
153 OPT_d = 1 << 7, // dry run - no networking 248 OPT_X = 1 << 4, // use openssl connection helper
154 OPT_w = 1 << 8, // network timeout 249
155 OPT_h = 1 << 9, // server 250 OPTS_t = 1 << 5, // sendmail "to"
156 OPT_p = 1 << 10, // port 251 OPTF_t = 1 << 5, // fetchmail "TOP"
157 OPT_U = 1 << 11, // user specified 252
158 OPT_P = 1 << 12, // password specified 253 OPTS_f = 1 << 6, // sendmail "from"
254 OPTF_z = 1 << 6, // fetchmail "delete"
255
256 OPTS_n = 1 << 7, // notification
257 OPTS_s = 1 << 8, // subject given
258 OPTS_c = 1 << 9, // charset for subject and body
159 }; 259 };
160 260
161 // -f must be specified 261 const char *options;
162 // -t, -b, -a may be multiple 262 unsigned opts;
163 opt_complementary = "f:t::b::a::"; 263
164 opts = getopt32(argv, 264 // SENDMAIL
165 "f:t:n::s:b:a:c:" USE_FEATURE_SENDMAIL_NETWORK("dw:h:p:U:P:"), 265 if ('s' == applet_name[0]) {
166 &from, &recipients, &notify, &subject, &bodies, &attachments, &charset 266 // save initial stdin
167 USE_FEATURE_SENDMAIL_NETWORK(, &wsecs, &server, &port, &opt_user, &opt_pass) 267 xdup2(STDIN_FILENO, INITIAL_STDIN_FILENO);
268 // -f must be specified
269 // -t may be multiple
270 opt_complementary = "-1:f:t::";
271 options = "C:w:U:P:X" "t:f:ns:c:";
272 // FETCHMAIL
273 } else {
274 opt_after_connect = NULL;
275 opt_complementary = "=1:P";
276 options = "C:w:U:P:X" "tz";
277 }
278 opts = getopt32(argv, options,
279 &opt_chdir, &opt_timeout, &opt_user, &opt_pass,
280 &recipients, &from, &subject, &charset
168 ); 281 );
282
169 //argc -= optind; 283 //argc -= optind;
170 argv += optind; 284 argv += optind;
171 285
172 // sanitize user input 286 // first argument is remote server[:port]
173 sane(from); 287 opt_connect = *argv++;
174 if (opts & OPT_c) 288
175 sane(charset); 289 if (opts & OPT_w)
176 290 timeout = xatou(opt_timeout);
177 // establish connection 291
178#if ENABLE_FEATURE_SENDMAIL_NETWORK 292 // chdir
179 timeout = xatou(wsecs); 293 if (opts & OPT_C)
180 if (!(opts & OPT_d)) { 294 xchdir(opt_chdir);
181 // ask password if we need to and while we're still have terminal 295
182 // TODO: get password directly from /dev/tty? or from a secret file? 296 // connect to server
183 if ((opts & (OPT_U+OPT_P)) == OPT_U) { 297 if (opts & OPT_X) {
184 if (!isatty(STDIN_FILENO) || !(opt_pass = bb_askpass(0, "Password: "))) { 298 launch_helper(args);
185 bb_error_msg_and_die("no password"); 299 } else {
186 } 300 // no connection helper provided -> make plain connect
301 int fd = create_and_connect_stream_or_die(opt_connect, 0);
302 xmove_fd(fd, STDIN_FILENO);
303 xdup2(STDIN_FILENO, STDOUT_FILENO);
304 }
305
306 // randomize
307 srand(time(NULL));
308
309 // SENDMAIL
310 if (recipients) {
311 int code;
312 char *boundary;
313
314 // wait for initial OK on plain connect
315 if (!(opts & OPT_X))
316 smtp_check(NULL, 220);
317
318 sane(from);
319 // introduce to server
320 // should we respect modern (but useless here) EHLO?
321 // or should they respect we wanna be tiny?!
322 if (!ENABLE_FEATURE_SENDMAIL_EHLO || 250 != smtp_checkp("EHLO %s", from, -1)) {
323 smtp_checkp("HELO %s", from, 250);
187 } 324 }
188 // set chat timeout 325
189 alarm(timeout); 326 // set sender
190 // connect to server 327 // NOTE: if password has not been specified ->
191 if (argv[0]) { 328 // no authentication is possible
192 // if connection helper given 329 code = (opts & OPT_P) ? -1 : 250;
193 // setup vanilla unidirectional pipes interchange 330 // first try softly without authentication
194 int idx; 331 while (250 != smtp_checkp("MAIL FROM:<%s>", from, code)) {
195 int pipes[4]; 332 // MAIL FROM failed -> authentication needed
196 xpipe(pipes); 333 // do we have username?
197 xpipe(pipes+2); 334 if (!(opts & OPT_U)) {
198 helper_pid = vfork(); 335 // no! fetch it from "from" option
199 if (helper_pid < 0) 336 //opts |= OPT_U;
200 bb_perror_msg_and_die("vfork"); 337 opt_user = xstrdup(from);
201 idx = (!helper_pid)*2; 338 *strchrnul(opt_user, '@') = '\0';
202 xdup2(pipes[idx], STDIN_FILENO);
203 xdup2(pipes[3-idx], STDOUT_FILENO);
204 if (ENABLE_FEATURE_CLEAN_UP)
205 for (int i = 4; --i >= 0;)
206 if (pipes[i] > STDOUT_FILENO)
207 close(pipes[i]);
208 // replace child with connection helper
209 if (!helper_pid) {
210 // child - try to execute connection helper
211 BB_EXECVP(argv[0], argv);
212 _exit(127);
213 } 339 }
214 // parent - check whether child is alive 340 // now it seems we have username
215 sig_catch(SIGCHLD, signal_handler); 341 // try to authenticate
216 sig_catch(SIGALRM, signal_handler); 342 if (334 == smtp_check("AUTH LOGIN", -1)) {
217 signal_handler(SIGCHLD); 343 uuencode(NULL, opt_user);
218 // child seems OK -> parent goes on SMTP chat 344 smtp_check("", 334);
219 } else { 345 uuencode(NULL, opt_pass);
220 // no connection helper provided -> make plain connect 346 smtp_check("", 235);
221 int fd = create_and_connect_stream_or_die( 347 }
222 server, 348 // authenticated -> retry set sender
223 bb_lookup_port(port, "tcp", 25) 349 // but now die on failure
224 ); 350 code = 250;
225 xmove_fd(fd, STDIN_FILENO);
226 xdup2(STDIN_FILENO, STDOUT_FILENO);
227 // wait for OK
228 check(220, "INIT");
229 } 351 }
230 // mail user specified? try modern AUTHentication 352 // set recipients
231 if ((opts & OPT_U) 353 for (llist_t *to = recipients; to; to = to->link) {
232 && (334 == puts_and_check("auth login", -1, "auth login")) 354 smtp_checkp("RCPT TO:<%s>", sane(to->data), 250);
233 ) {
234 uuencode(NULL, opt_user);
235 puts_and_check("", 334, "AUTH");
236 uuencode(NULL, opt_pass);
237 puts_and_check("", 235, "AUTH");
238 // no mail user specified or modern AUTHentication is not supported?
239 } else {
240 // fallback to simple HELO authentication
241 // fetch domain name (defaults to local)
242 const char *domain = strchr(from, '@');
243 if (domain)
244 domain++;
245 else
246 domain = "local";
247 printf("helo %s\r\n", domain);
248 check(250, "HELO");
249 } 355 }
250 356
251 // set addresses 357 // now put message
252 printf("mail from:<%s>\r\n", from); 358 smtp_check("DATA", 354);
253 check(250, "MAIL FROM"); 359 // put address headers
360 printf("From: %s\r\n", from);
254 for (llist_t *to = recipients; to; to = to->link) { 361 for (llist_t *to = recipients; to; to = to->link) {
255 printf("rcpt to:<%s>\r\n", sane(to->data)); 362 printf("To: %s\r\n", to->data);
256 check(250, "RCPT TO");
257 } 363 }
258 puts_and_check("data", 354, "DATA"); 364 // put encoded subject
259 // no timeout while sending message 365 if (opts & OPTS_c)
260 alarm(0); 366 sane(charset);
261 } 367 if (opts & OPTS_s) {
262#endif 368 printf("Subject: =?%s?B?", charset);
263 369 uuencode(NULL, subject);
264 // now put message 370 printf("?=\r\n");
265 // put address headers 371 }
266 printf("From: %s\r\n", from); 372 // put notification
267 for (llist_t *to = recipients; to; to = to->link) { 373 if (opts & OPTS_n)
268 printf("To: %s\r\n", sane(to->data)); 374 printf("Disposition-Notification-To: %s\r\n", from);
269 } 375 // put common headers and body start
270 // put encoded subject 376 boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
271 if (opts & OPT_s) {
272 printf("Subject: =?%s?B?", charset);
273 uuencode(NULL, subject);
274 puts("?=\r");
275 }
276 // put notification
277 if (opts & OPT_n) {
278 // -n without parameter?
279 if (!notify)
280 notify = from; // notify sender by default
281 printf("Disposition-Notification-To: %s\r\n", sane(notify));
282 }
283 // put common headers and body start
284 //srand(?);
285 boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
286 printf(
287 "X-Mailer: busybox " BB_VER " sendmail\r\n"
288 "Message-ID: <%s>\r\n"
289 "Mime-Version: 1.0\r\n"
290 "%smultipart/mixed; boundary=\"%s\"\r\n"
291 "\r\n"
292 "--%s\r\n"
293 "%stext/plain; charset=%s\r\n"
294 "%s\r\n%s"
295 , boundary
296 , "Content-Type: "
297 , boundary, boundary
298 , "Content-Type: "
299 , charset
300 , "Content-Disposition: inline"
301 , "Content-Transfer-Encoding: base64\r\n"
302 );
303 // put body(ies)
304 for (llist_t *f = bodies; f; f = f->link) {
305 uuencode(f->data, NULL);
306 }
307 // put attachment(s)
308 for (llist_t *f = attachments; f; f = f->link) {
309 printf( 377 printf(
310 "\r\n--%s\r\n" 378 USE_FEATURE_SENDMAIL_BLOATY("X-Mailer: busybox " BB_VER " sendmail\r\n")
311 "%sapplication/octet-stream\r\n" 379 "Message-ID: <%s>\r\n"
312 "%s; filename=\"%s\"\r\n" 380 "Mime-Version: 1.0\r\n"
313 "%s" 381 "%smultipart/mixed; boundary=\"%s\"\r\n"
314 , boundary 382 , boundary
315 , "Content-Type: " 383 , "Content-Type: "
316 , "Content-Disposition: inline" 384 , boundary
317 , bb_get_last_path_component_strip(f->data)
318 , "Content-Transfer-Encoding: base64\r\n"
319 ); 385 );
320 uuencode(f->data, NULL); 386 // put body + attachment(s)
321 } 387 {
322 // put terminator 388 const char *fmt =
323 printf("\r\n--%s--\r\n\r\n", boundary); 389 "\r\n--%s\r\n"
324 if (ENABLE_FEATURE_CLEAN_UP) 390 "%stext/plain; charset=%s\r\n"
325 free(boundary); 391 "%s%s\r\n"
326 392 "%s"
327#if ENABLE_FEATURE_SENDMAIL_NETWORK 393 ;
328 // end message and say goodbye 394 const char *p = charset;
329 if (!(opts & OPT_d)) { 395 char *q = (char *)"";
330 alarm(timeout); 396 while (argv[0]) {
331 puts_and_check(".", 250, "BODY"); 397 printf(
332 puts_and_check("quit", 221, "QUIT"); 398 fmt
399 , boundary
400 , "Content-Type: "
401 , p
402 , "Content-Disposition: inline"
403 , q
404 , "Content-Transfer-Encoding: base64\r\n"
405 );
406 p = "";
407 fmt =
408 "\r\n--%s\r\n"
409 "%sapplication/octet-stream%s\r\n"
410 "%s; filename=\"%s\"\r\n"
411 "%s"
412 ;
413 uuencode(*argv, NULL);
414 if (*(++argv))
415 q = bb_get_last_path_component_strip(argv[0]);
416 }
333 } 417 }
418 // put terminator
419 printf("\r\n--%s--\r\n" "\r\n", boundary);
420 if (ENABLE_FEATURE_CLEAN_UP)
421 free(boundary);
422
423 // end message and say goodbye
424 smtp_check(".", 250);
425 smtp_check("QUIT", 221);
426
427 // FETCHMAIL
428 } else {
429 // authenticate
430 char *buf;
431 unsigned nmsg;
432 if (!(opts & OPT_U)) {
433 //opts |= OPT_U;
434 opt_user = getenv("USER");
435 }
436#if ENABLE_FEATURE_FETCHMAIL_APOP
437 pop3_checkr(NULL, NULL, &buf);
438 // server supports APOP?
439 if ('<' == buf[4]) {
440 md5_ctx_t md5;
441 uint8_t hex[16*2 + 1];
442 // yes. compose <stamp><password>
443 char *s = strchr(buf, '>');
444 if (s)
445 strcpy(s+1, opt_pass);
446 s = buf+4;
447 // get md5 sum of <stamp><password>
448 md5_begin(&md5);
449 md5_hash(s, strlen(s), &md5);
450 md5_end(s, &md5);
451 bin2hex(hex, s, 16);
452 // APOP
453 s = xasprintf("%s %s", opt_user, hex);
454 pop3_check("APOP %s", s);
455 if (ENABLE_FEATURE_CLEAN_UP) {
456 free(s);
457 free(buf);
458 }
459 } else {
460#else
461 {
462 pop3_check(NULL, NULL);
334#endif 463#endif
464 // USER
465 pop3_check("USER %s", opt_user);
466 // PASS
467 pop3_check("PASS %s", opt_pass);
468 }
469
470 // get statistics
471 pop3_checkr("STAT", NULL, &buf);
472
473 // get number of messages
474 nmsg = atoi(buf+4);
475 if (ENABLE_FEATURE_CLEAN_UP)
476 free(buf);
477
478 // lock maildir
479 ////USE_FEATURE_CLEAN_UP(close)(xopen(".lock", O_CREAT | O_WRONLY | O_TRUNC | O_EXCL));
480
481 // make tempnam(dir, salt) respect dir argument
482 unsetenv("TMPDIR");
483
484 // TODO: piping thru external filter argv... if *argv
485
486 // cache fetch command
487 {
488 const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
489 // loop thru messages
490 for (; nmsg; nmsg--) {
491 int fd;
492 char tmp_name[sizeof("tmp/XXXXXX")];
493 char new_name[sizeof("new/XXXXXX")];
494
495 // retrieve message in ./tmp
496 strcpy(tmp_name, "tmp/XXXXXX");
497 fd = mkstemp(tmp_name);
498 if (fd < 0)
499 bb_perror_msg_and_die("cannot create unique file");
500 pop3_check(retr, (const char *)nmsg);
501 pop3_message(fd); // NB: closes fd
502
503 // move file to ./new atomically
504 strncpy(new_name, "new", 3);
505 strcpy(new_name + 3, tmp_name + 3);
506 if (rename(tmp_name, new_name) < 0) {
507 // rats! such file exists! try to make unique name
508 strcpy(new_name + 3, "tmp/XXXXXX" + 3);
509 fd = mkstemp(new_name);
510 if (fd < 0)
511 bb_perror_msg_and_die("cannot create unique file");
512 close(fd);
513 if (rename(tmp_name, new_name) < 0) {
514 // something is very wrong
515 bb_perror_msg_and_die("cannot move %s to %s", tmp_name, new_name);
516 }
517 }
518
519 // delete message from server
520 if (opts & OPTF_z)
521 pop3_check("DELE %u", (const char*)nmsg);
522 }
523 }
524
525 // Bye
526 pop3_check("QUIT", NULL);
527
528 // unlock maildir
529 ////unlink(".lock");
530 }
335 531
336 return 0; 532 return 0;
337} 533}