aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/usage.h16
-rw-r--r--networking/sendmail.c180
2 files changed, 111 insertions, 85 deletions
diff --git a/include/usage.h b/include/usage.h
index 0ef17b520..989ed9134 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -3135,26 +3135,28 @@ USE_FEATURE_RUN_PARTS_FANCY("\n -l Prints names of all matching files even when
3135#define selinuxenabled_full_usage 3135#define selinuxenabled_full_usage
3136 3136
3137#define sendmail_trivial_usage \ 3137#define sendmail_trivial_usage \
3138 "[-d] {-t to}+ [-f from] [-n[notify]] [-s subject] [-b file]*\n" \ 3138 "{-t to}+ {-f from} [-n[notify]] [-s subject] [-b file]*\n" \
3139 "[-a attachment]* [-c charset] [-w timeout] [-h server] [-p port] [-U user] [-P password]" 3139 "[-a attachment]* [-c charset]" \
3140 USE_FEATURE_SENDMAIL_NETWORK("\n" \
3141 " [-d] [-w timeout] [-h server] [-p port] [-U user] [-P password]" \
3142 )
3140#define sendmail_full_usage \ 3143#define sendmail_full_usage \
3141 "Send an email from to with subject and optional attachments.\n" \ 3144 "Send an email <from> <to> with <subject> and optional attachments." \
3142 "Body is read from stdin or from optional files" \
3143 "\n\nArguments:\n" \ 3145 "\n\nArguments:\n" \
3144 " -d Just dump composed message\n" \
3145 " -t to Recipient email. May be multiple\n" \ 3146 " -t to Recipient email. May be multiple\n" \
3146 " -f from Sender email\n" \ 3147 " -f from Sender address\n" \
3147 " -n[notify] Optional notification address. If just -n given then notifies the sender\n" \ 3148 " -n[notify] Optional notification address. If just -n given then notifies the sender\n" \
3148 " -s subject Optional subject\n" \ 3149 " -s subject Optional subject\n" \
3149 " -b filename Optional body content file. May be multiple\n" \ 3150 " -b filename Optional body content file. May be multiple\n" \
3150 " -a filename Optional file attachment. May be multiple\n" \ 3151 " -a filename Optional file attachment. May be multiple\n" \
3151 " -c charset Assumed charset for body and subject [koi8-r]" \ 3152 " -c charset Assumed charset for body and subject [koi8-r]" \
3152 USE_FEATURE_SENDMAIL_NETWORK("\n" \ 3153 USE_FEATURE_SENDMAIL_NETWORK("\n" \
3154 " -d Just dump composed message\n" \
3153 " -w timeout Set timeout on network operations\n" \ 3155 " -w timeout Set timeout on network operations\n" \
3154 " -h server Optional mail server name or IP [127.0.0.1]\n" \ 3156 " -h server Optional mail server name or IP [127.0.0.1]\n" \
3155 " -p port Optional mail server port [25]\n" \ 3157 " -p port Optional mail server port [25]\n" \
3156 " -U username Authenticate using AUTH LOGIN with specified username\n" \ 3158 " -U username Authenticate using AUTH LOGIN with specified username\n" \
3157 " -P password Authenticate using AUTH LOGIN with specified password"\ 3159 " -P password Authenticate using AUTH LOGIN with specified password" \
3158 ) 3160 )
3159 3161
3160#define seq_trivial_usage \ 3162#define seq_trivial_usage \
diff --git a/networking/sendmail.c b/networking/sendmail.c
index 2ea10b124..fa1abc7ff 100644
--- a/networking/sendmail.c
+++ b/networking/sendmail.c
@@ -16,45 +16,46 @@ enum {
16 DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), 16 DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
17}; 17};
18 18
19static void uuencode(const char *fname) 19static void uuencode(char *fname, const char *text)
20{ 20{
21#define src_buf text
21 int fd; 22 int fd;
22 char src_buf[SRC_BUF_SIZE]; 23#define len fd
23 char dst_buf[1 + DST_BUF_SIZE + 1]; 24 char dst_buf[DST_BUF_SIZE + 1];
24 25
25 fd = xopen(fname, O_RDONLY); 26 if (fname) {
26 fflush(stdout); 27 fd = xopen(fname, O_RDONLY);
27 dst_buf[0] = '\n'; 28 src_buf = bb_common_bufsiz1;
29 } else {
30 len = strlen(text);
31 }
32
33 fflush(stdout); // sync stdio and unistd output
28 while (1) { 34 while (1) {
29 size_t size = full_read(fd, src_buf, SRC_BUF_SIZE); 35 size_t size;
36 if (fname) {
37 size = full_read(fd, (char *)src_buf, SRC_BUF_SIZE);
38 if ((ssize_t)size < 0)
39 bb_perror_msg_and_die(bb_msg_read_error);
40 } else {
41 size = len;
42 if (len > SRC_BUF_SIZE)
43 size = SRC_BUF_SIZE;
44 }
30 if (!size) 45 if (!size)
31 break; 46 break;
32 if ((ssize_t)size < 0) 47 // Encode the buffer we just read in
33 bb_perror_msg_and_die(bb_msg_read_error); 48 bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
34 /* Encode the buffer we just read in */ 49 if (fname) {
35 bb_uuencode(dst_buf + 1, src_buf, size, bb_uuenc_tbl_base64); 50 xwrite(STDOUT_FILENO, "\n", 1);
36 xwrite(STDOUT_FILENO, dst_buf, 1 + 4 * ((size + 2) / 3)); 51 } else {
37 } 52 src_buf += size;
38 close(fd); 53 len -= size;
39} 54 }
40 55 xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3));
41// "inline" version
42// encodes content of given buffer instead of fd
43// used to encode subject and authentication terms
44static void uuencode_inline(const char *src_buf)
45{
46 size_t len;
47 char dst_buf[DST_BUF_SIZE + 1];
48
49 len = strlen(src_buf);
50 fflush(stdout);
51 while (len > 0) {
52 size_t chunk = (len <= SRC_BUF_SIZE) ? len : SRC_BUF_SIZE;
53 bb_uuencode(dst_buf, src_buf, chunk, bb_uuenc_tbl_base64);
54 xwrite(STDOUT_FILENO, dst_buf, 4 * ((chunk + 2) / 3));
55 src_buf += chunk;
56 len -= chunk;
57 } 56 }
57 if (ENABLE_FEATURE_CLEAN_UP && fname)
58 close(fd);
58} 59}
59 60
60#if ENABLE_FEATURE_SENDMAIL_NETWORK 61#if ENABLE_FEATURE_SENDMAIL_NETWORK
@@ -72,7 +73,7 @@ static void signal_handler(int signo)
72 bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err)); 73 bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
73} 74}
74 75
75static pid_t helper_pid = -1; 76static pid_t helper_pid;
76 77
77// read stdin, parses first bytes to a number, i.e. server response 78// read stdin, parses first bytes to a number, i.e. server response
78// if code = -1 then just return this number 79// if code = -1 then just return this number
@@ -92,7 +93,7 @@ static int check(int code, const char *errmsg)
92 return n; 93 return n;
93 } 94 }
94 } 95 }
95 // TODO!!!: is there more elegant way to terminate child on program failure? 96 // TODO: is there more elegant way to terminate child on program failure?
96 if (helper_pid > 0) 97 if (helper_pid > 0)
97 kill(helper_pid, SIGTERM); 98 kill(helper_pid, SIGTERM);
98 if (!answer) 99 if (!answer)
@@ -104,9 +105,24 @@ static int check(int code, const char *errmsg)
104 105
105static int puts_and_check(const char *msg, int code, const char *errmsg) 106static int puts_and_check(const char *msg, int code, const char *errmsg)
106{ 107{
107 puts(msg); 108 printf("%s\r\n", msg);
108 return check(code, errmsg); 109 return check(code, errmsg);
109} 110}
111
112// strip argument of bad chars
113static char *sane(char *str)
114{
115 char *s = str;
116 char *p = s;
117 while (*s) {
118 if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
119 *p++ = *s;
120 }
121 s++;
122 }
123 *p = '\0';
124 return str;
125}
110#endif 126#endif
111 127
112int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 128int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -115,10 +131,10 @@ int sendmail_main(int argc, char **argv)
115 llist_t *recipients = NULL; 131 llist_t *recipients = NULL;
116 llist_t *bodies = NULL; 132 llist_t *bodies = NULL;
117 llist_t *attachments = NULL; 133 llist_t *attachments = NULL;
118 const char *from; 134 char *from;
119 const char *notify; 135 char *notify = NULL;
120 const char *subject; 136 const char *subject;
121 const char *charset = "utf-8"; 137 char *charset = (char*)"utf-8";
122#if ENABLE_FEATURE_SENDMAIL_NETWORK 138#if ENABLE_FEATURE_SENDMAIL_NETWORK
123 const char *wsecs = "10"; 139 const char *wsecs = "10";
124 const char *server = "127.0.0.1"; 140 const char *server = "127.0.0.1";
@@ -133,6 +149,7 @@ int sendmail_main(int argc, char **argv)
133 OPT_f = 1 << 0, // sender 149 OPT_f = 1 << 0, // sender
134 OPT_n = 1 << 2, // notification 150 OPT_n = 1 << 2, // notification
135 OPT_s = 1 << 3, // subject given 151 OPT_s = 1 << 3, // subject given
152 OPT_c = 1 << 6, // charset
136 OPT_d = 1 << 7, // dry run - no networking 153 OPT_d = 1 << 7, // dry run - no networking
137 OPT_w = 1 << 8, // network timeout 154 OPT_w = 1 << 8, // network timeout
138 OPT_h = 1 << 9, // server 155 OPT_h = 1 << 9, // server
@@ -152,9 +169,10 @@ int sendmail_main(int argc, char **argv)
152 //argc -= optind; 169 //argc -= optind;
153 argv += optind; 170 argv += optind;
154 171
155//printf("OPTS[%4x]\n", opts); 172 // sanitize user input
156 173 sane(from);
157 // TODO!!!: strip recipients and sender from <> 174 if (opts & OPT_c)
175 sane(charset);
158 176
159 // establish connection 177 // establish connection
160#if ENABLE_FEATURE_SENDMAIL_NETWORK 178#if ENABLE_FEATURE_SENDMAIL_NETWORK
@@ -167,24 +185,24 @@ int sendmail_main(int argc, char **argv)
167 bb_error_msg_and_die("no password"); 185 bb_error_msg_and_die("no password");
168 } 186 }
169 } 187 }
170//printf("OPTS[%4x][%s][%s]\n", opts, opt_user, opt_pass);
171//exit(0);
172 // set chat timeout 188 // set chat timeout
173 alarm(timeout); 189 alarm(timeout);
174 // connect to server 190 // connect to server
175 if (argv[0]) { 191 if (argv[0]) {
176 // if connection helper given 192 // if connection helper given
177 // setup vanilla unidirectional pipes interchange 193 // setup vanilla unidirectional pipes interchange
194 int idx;
178 int pipes[4]; 195 int pipes[4];
179 xpipe(pipes); 196 xpipe(pipes);
180 xpipe(pipes+2); 197 xpipe(pipes+2);
181 helper_pid = vfork(); 198 helper_pid = vfork();
182 if (helper_pid < 0) 199 if (helper_pid < 0)
183 bb_perror_msg_and_die("vfork"); 200 bb_perror_msg_and_die("vfork");
184 xdup2(pipes[(helper_pid)?0:2], STDIN_FILENO); 201 idx = (!helper_pid)*2;
185 xdup2(pipes[(helper_pid)?3:1], STDOUT_FILENO); 202 xdup2(pipes[idx], STDIN_FILENO);
203 xdup2(pipes[3-idx], STDOUT_FILENO);
186 if (ENABLE_FEATURE_CLEAN_UP) 204 if (ENABLE_FEATURE_CLEAN_UP)
187 for (int i = 4; --i >= 0; ) 205 for (int i = 4; --i >= 0;)
188 if (pipes[i] > STDOUT_FILENO) 206 if (pipes[i] > STDOUT_FILENO)
189 close(pipes[i]); 207 close(pipes[i]);
190 // replace child with connection helper 208 // replace child with connection helper
@@ -210,10 +228,12 @@ int sendmail_main(int argc, char **argv)
210 check(220, "INIT"); 228 check(220, "INIT");
211 } 229 }
212 // mail user specified? try modern AUTHentication 230 // mail user specified? try modern AUTHentication
213 if (opt_user && (334 == puts_and_check("auth login", -1, "auth login"))) { 231 if ((opts & OPT_U)
214 uuencode_inline(opt_user); 232 && (334 == puts_and_check("auth login", -1, "auth login"))
233 ) {
234 uuencode(NULL, opt_user);
215 puts_and_check("", 334, "AUTH"); 235 puts_and_check("", 334, "AUTH");
216 uuencode_inline(opt_pass); 236 uuencode(NULL, opt_pass);
217 puts_and_check("", 235, "AUTH"); 237 puts_and_check("", 235, "AUTH");
218 // no mail user specified or modern AUTHentication is not supported? 238 // no mail user specified or modern AUTHentication is not supported?
219 } else { 239 } else {
@@ -224,15 +244,15 @@ int sendmail_main(int argc, char **argv)
224 domain++; 244 domain++;
225 else 245 else
226 domain = "local"; 246 domain = "local";
227 printf("helo %s\n", domain); 247 printf("helo %s\r\n", domain);
228 check(250, "HELO"); 248 check(250, "HELO");
229 } 249 }
230 250
231 // set addresses 251 // set addresses
232 printf("mail from:<%s>\n", from); 252 printf("mail from:<%s>\r\n", from);
233 check(250, "MAIL FROM"); 253 check(250, "MAIL FROM");
234 for (llist_t *to = recipients; to; to = to->link) { 254 for (llist_t *to = recipients; to; to = to->link) {
235 printf("rcpt to:<%s>\n", to->data); 255 printf("rcpt to:<%s>\r\n", sane(to->data));
236 check(250, "RCPT TO"); 256 check(250, "RCPT TO");
237 } 257 }
238 puts_and_check("data", 354, "DATA"); 258 puts_and_check("data", 354, "DATA");
@@ -243,60 +263,64 @@ int sendmail_main(int argc, char **argv)
243 263
244 // now put message 264 // now put message
245 // put address headers 265 // put address headers
246 printf("From: %s\n", from); 266 printf("From: %s\r\n", from);
247 for (llist_t *to = recipients; to; to = to->link) { 267 for (llist_t *to = recipients; to; to = to->link) {
248 printf("To: %s\n", to->data); 268 printf("To: %s\r\n", sane(to->data));
249 } 269 }
250 // put encoded subject 270 // put encoded subject
251 if (opts & OPT_s) { 271 if (opts & OPT_s) {
252 printf("Subject: =?%s?B?", charset); 272 printf("Subject: =?%s?B?", charset);
253 uuencode_inline(subject); 273 uuencode(NULL, subject);
254 puts("?="); 274 puts("?=\r");
255 } 275 }
256 // put notification 276 // put notification
257 if (opts & OPT_n) { 277 if (opts & OPT_n) {
258 const char *s = notify; 278 // -n without parameter?
259 if (!s[0]) 279 if (!notify)
260 s = from; // notify sender by default 280 notify = from; // notify sender by default
261 printf("Disposition-Notification-To: %s\n", s); 281 printf("Disposition-Notification-To: %s\r\n", sane(notify));
262 } 282 }
263 // put common headers and body start 283 // put common headers and body start
264 //srand(?); 284 //srand(?);
265 boundary = xasprintf("%d-%d-%d", rand(), rand(), rand()); 285 boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
266 printf( 286 printf(
267 "X-Mailer: busybox " BB_VER " sendmail\n" 287 "X-Mailer: busybox " BB_VER " sendmail\r\n"
268 "X-Priority: 3\n" 288 "Message-ID: <%s>\r\n"
269 "Message-ID: <%s>\n" 289 "Mime-Version: 1.0\r\n"
270 "Mime-Version: 1.0\n" 290 "%smultipart/mixed; boundary=\"%s\"\r\n"
271 "Content-Type: multipart/mixed; boundary=\"%s\"\n" 291 "\r\n"
272 "\n" 292 "--%s\r\n"
273 "--%s\n" 293 "%stext/plain; charset=%s\r\n"
274 "Content-Type: text/plain; charset=%s\n" 294 "%s\r\n%s"
275 "%s\n%s" 295 , boundary
276 , boundary, boundary, boundary, charset 296 , "Content-Type: "
297 , boundary, boundary
298 , "Content-Type: "
299 , charset
277 , "Content-Disposition: inline" 300 , "Content-Disposition: inline"
278 , "Content-Transfer-Encoding: base64\n" 301 , "Content-Transfer-Encoding: base64\r\n"
279 ); 302 );
280 // put body(ies) 303 // put body(ies)
281 for (llist_t *f = bodies; f; f = f->link) { 304 for (llist_t *f = bodies; f; f = f->link) {
282 uuencode(f->data); 305 uuencode(f->data, NULL);
283 } 306 }
284 // put attachment(s) 307 // put attachment(s)
285 for (llist_t *f = attachments; f; f = f->link) { 308 for (llist_t *f = attachments; f; f = f->link) {
286 printf( 309 printf(
287 "\n--%s\n" 310 "\r\n--%s\r\n"
288 "Content-Type: application/octet-stream\n" 311 "%sapplication/octet-stream\r\n"
289 "%s; filename=\"%s\"\n" 312 "%s; filename=\"%s\"\r\n"
290 "%s" 313 "%s"
291 , boundary 314 , boundary
315 , "Content-Type: "
292 , "Content-Disposition: inline" 316 , "Content-Disposition: inline"
293 , bb_get_last_path_component_strip(f->data) 317 , bb_get_last_path_component_strip(f->data)
294 , "Content-Transfer-Encoding: base64\n" 318 , "Content-Transfer-Encoding: base64\r\n"
295 ); 319 );
296 uuencode(f->data); 320 uuencode(f->data, NULL);
297 } 321 }
298 // put terminator 322 // put terminator
299 printf("\n--%s--\n\n", boundary); 323 printf("\r\n--%s--\r\n\r\n", boundary);
300 if (ENABLE_FEATURE_CLEAN_UP) 324 if (ENABLE_FEATURE_CLEAN_UP)
301 free(boundary); 325 free(boundary);
302 326