diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-02 17:54:35 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-02 17:54:35 +0000 |
commit | a2980c6249e476926f09a185840d893bb9a03a6e (patch) | |
tree | 3d75c6a4f779f764c6b0cace107aa51d46c73df4 | |
parent | 5de754a395a36afe7ed89f300d3421ef05c2401b (diff) | |
download | busybox-w32-a2980c6249e476926f09a185840d893bb9a03a6e.tar.gz busybox-w32-a2980c6249e476926f09a185840d893bb9a03a6e.tar.bz2 busybox-w32-a2980c6249e476926f09a185840d893bb9a03a6e.zip |
sendmail: fixes by Vladimir Dronnikov <dronnikov at gmail.com>
-rw-r--r-- | include/usage.h | 16 | ||||
-rw-r--r-- | networking/sendmail.c | 180 |
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 | ||
19 | static void uuencode(const char *fname) | 19 | static 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 | ||
44 | static 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 | ||
75 | static pid_t helper_pid = -1; | 76 | static 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 | ||
105 | static int puts_and_check(const char *msg, int code, const char *errmsg) | 106 | static 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 | ||
113 | static 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 | ||
112 | int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 128 | int 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 | ||