blob: adc6c76264ad3baac05cc0e59ca4b3ac5af3ce4a [file] [log] [blame]
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +00001/* vi: set sw=4 ts=4: */
2/*
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +00003 * bare bones sendmail/fetchmail
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +00004 *
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
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +000011#define INITIAL_STDIN_FILENO 3
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +000012
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000013static void uuencode(char *fname, const char *text)
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +000014{
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +000015 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
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000020#define src_buf text
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +000021 int fd;
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000022#define len fd
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +000023 char dst_buf[DST_BUF_SIZE + 1];
24
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000025 if (fname) {
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +000026 fd = INITIAL_STDIN_FILENO;
27 if (NOT_LONE_DASH(fname))
28 fd = xopen(fname, O_RDONLY);
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000029 src_buf = bb_common_bufsiz1;
Denis Vlasenko17db1a92008-02-26 21:13:17 +000030 // N.B. strlen(NULL) segfaults!
31 } else if (text) {
32 // though we do not call uuencode(NULL, NULL) explicitly
33 // still we do not want to break things suddenly
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000034 len = strlen(text);
Denis Vlasenko17db1a92008-02-26 21:13:17 +000035 } else
36 return;
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000037
38 fflush(stdout); // sync stdio and unistd output
39 while (1) {
40 size_t size;
41 if (fname) {
42 size = full_read(fd, (char *)src_buf, SRC_BUF_SIZE);
43 if ((ssize_t)size < 0)
44 bb_perror_msg_and_die(bb_msg_read_error);
45 } else {
46 size = len;
47 if (len > SRC_BUF_SIZE)
48 size = SRC_BUF_SIZE;
49 }
50 if (!size)
51 break;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +000052 // encode the buffer we just read in
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000053 bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
54 if (fname) {
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +000055 xwrite(STDOUT_FILENO, "\r\n", 2);
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000056 } else {
57 src_buf += size;
58 len -= size;
59 }
60 xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3));
61 }
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +000062 if (fname)
Denis Vlasenkoa2980c62008-02-02 17:54:35 +000063 close(fd);
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +000064}
65
Denis Vlasenko17db1a92008-02-26 21:13:17 +000066static const char *const init_xargs[9] = {
67 "openssl", "s_client", "-quiet", "-connect",
68 NULL, "-tls1", "-starttls", "smtp"
69};
70
71struct globals {
72 pid_t helper_pid;
73 unsigned timeout;
74 // arguments for SSL connection helper
75 const char *xargs[9];
76#if ENABLE_FEATURE_FETCHMAIL_FILTER
77 // arguments for postprocess helper
78 const char *fargs[3];
79#endif
80};
81#define G (*ptr_to_globals)
82#define helper_pid (G.helper_pid)
83#define timeout (G.timeout )
84#define xargs (G.xargs )
85#define fargs (G.fargs )
86#define INIT_G() do { \
87 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
88 memcpy(xargs, init_xargs, sizeof(init_xargs)); \
89 fargs[0] = "utf-8"; \
90} while (0)
91
92#define opt_connect (xargs[4])
93#define opt_after_connect (xargs[5])
94#if ENABLE_FEATURE_FETCHMAIL_FILTER
95#define opt_charset (fargs[0])
96#define opt_subject (fargs[1])
97#endif
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +000098
99static void kill_helper(void)
100{
101 // TODO!!!: is there more elegant way to terminate child on program failure?
102 if (helper_pid > 0)
103 kill(helper_pid, SIGTERM);
104}
105
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000106// generic signal handler
107static void signal_handler(int signo)
108{
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000109#define err signo
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000110
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000111 if (SIGALRM == signo) {
112 kill_helper();
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000113 bb_error_msg_and_die("timed out");
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000114 }
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000115
116 // SIGCHLD. reap zombies
117 if (wait_any_nohang(&err) > 0)
118 if (WIFEXITED(err) && WEXITSTATUS(err))
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000119#if ENABLE_FEATURE_SENDMAIL_BLOATY
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000120 bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000121#else
122 bb_error_msg_and_die("child failed");
123#endif
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000124}
125
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000126static void launch_helper(const char **argv)
127{
128 // setup vanilla unidirectional pipes interchange
129 int idx;
130 int pipes[4];
131 xpipe(pipes);
132 xpipe(pipes+2);
133 helper_pid = vfork();
134 if (helper_pid < 0)
135 bb_perror_msg_and_die("vfork");
136 idx = (!helper_pid)*2;
137 xdup2(pipes[idx], STDIN_FILENO);
138 xdup2(pipes[3-idx], STDOUT_FILENO);
139 if (ENABLE_FEATURE_CLEAN_UP)
140 for (int i = 4; --i >= 0; )
141 if (pipes[i] > STDOUT_FILENO)
142 close(pipes[i]);
143 if (!helper_pid) {
144 // child - try to execute connection helper
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000145// close(STDERR_FILENO);
146 BB_EXECVP(*argv, (char **)argv);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000147 _exit(127);
148 }
149 // parent - check whether child is alive
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000150 bb_signals(0
151 + (1 << SIGCHLD)
152 + (1 << SIGALRM)
153 , signal_handler);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000154 signal_handler(SIGCHLD);
155 // child seems OK -> parent goes on
156}
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000157
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000158static char *command(const char *fmt, const char *param)
159{
160 char *msg = (char *)fmt;
161 alarm(timeout);
162 if (msg) {
163// if (param)
164 msg = xasprintf(fmt, param);
165 printf("%s\r\n", msg);
166 }
167 fflush(stdout);
168 return msg;
169}
170
171static int smtp_checkp(const char *fmt, const char *param, int code)
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000172{
173 char *answer;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000174 char *msg = command(fmt, param);
175 // read stdin
176 // if the string has a form \d\d\d- -- read next string. E.g. EHLO response
177 // parse first bytes to a number
178 // if code = -1 then just return this number
179 // if code != -1 then checks whether the number equals the code
180 // if not equal -> die saying msg
181#if ENABLE_FEATURE_SENDMAIL_EHLO
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000182 while ((answer = xmalloc_getline(stdin)) != NULL)
183 if (strlen(answer) <= 3 || '-' != answer[3])
184 break;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000185#else
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000186 answer = xmalloc_getline(stdin);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000187#endif
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000188 if (answer) {
189 int n = atoi(answer);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000190 alarm(0);
191 if (ENABLE_FEATURE_CLEAN_UP) {
192 free(msg);
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000193 free(answer);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000194 }
195 if (-1 == code || n == code) {
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000196 return n;
197 }
198 }
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000199 kill_helper();
200 bb_error_msg_and_die("%s failed", msg);
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000201}
202
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000203static int smtp_check(const char *fmt, int code)
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000204{
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000205 return smtp_checkp(fmt, NULL, code);
206}
207
Denis Vlasenkoa2980c62008-02-02 17:54:35 +0000208// strip argument of bad chars
209static char *sane(char *str)
210{
211 char *s = str;
212 char *p = s;
213 while (*s) {
214 if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
215 *p++ = *s;
216 }
217 s++;
218 }
219 *p = '\0';
220 return str;
221}
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000222
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000223#if ENABLE_FETCHMAIL
224static void pop3_checkr(const char *fmt, const char *param, char **ret)
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000225{
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000226 char *msg = command(fmt, param);
227 char *answer = xmalloc_getline(stdin);
228 if (answer && '+' == *answer) {
229 alarm(0);
230 if (ret)
231 *ret = answer+4; // skip "+OK "
232 else if (ENABLE_FEATURE_CLEAN_UP)
233 free(answer);
234 return;
235 }
236 kill_helper();
237 bb_error_msg_and_die("%s failed", msg);
238}
239
240static void pop3_check(const char *fmt, const char *param)
241{
242 pop3_checkr(fmt, param, NULL);
243}
244
245static void pop3_message(const char *filename)
246{
247 int fd;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000248 char *answer;
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000249 // create and open file filename
250 // read stdin, copy to created file
251 fd = xopen(filename, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000252 while ((answer = xmalloc_fgets_str(stdin, "\r\n"))) {
253 char *s = answer;
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000254 if ('.' == *answer) {
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000255 if ('.' == answer[1])
256 s++;
257 else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
258 break;
259 }
260 xwrite(fd, s, strlen(s));
261 free(answer);
262 }
263 close(fd);
264}
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000265#endif
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000266
267int sendgetmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
268int sendgetmail_main(int argc, char **argv)
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000269{
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000270 llist_t *opt_recipients = NULL;
271#if !ENABLE_FEATURE_FETCHMAIL_FILTER
272 char *opt_subject;
273 char *opt_charset = (char *)"utf-8";
274#endif
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000275
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000276 const char *opt_user;
277 const char *opt_pass;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000278
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000279 enum {
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000280 OPT_w = 1 << 0, // network timeout
281 OPT_U = 1 << 1, // user
282 OPT_P = 1 << 2, // password
283 OPT_X = 1 << 3, // connect using openssl s_client helper
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000284
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000285 OPTS_n = 1 << 4, // sendmail: request notification
286 OPTF_t = 1 << 4, // fetchmail: use "TOP" not "RETR"
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000287
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000288 OPTS_s = 1 << 5, // sendmail: subject
289 OPTF_z = 1 << 5, // fetchmail: delete from server
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000290
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000291 OPTS_c = 1 << 6, // sendmail: assumed charset
292 OPTS_t = 1 << 7, // sendmail: recipient(s)
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000293 };
294
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000295 const char *options;
296 unsigned opts;
297
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000298 INIT_G();
299
300 if (!ENABLE_FETCHMAIL || 's' == applet_name[0]) {
301 // SENDMAIL
302 // save initial stdin (body or attachements can be piped!)
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000303 xdup2(STDIN_FILENO, INITIAL_STDIN_FILENO);
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000304 opt_complementary = "-2:w+:t:t::"; // count(-t) > 0
305 options = "w:U:P:X" "ns:c:t:";
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000306 } else {
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000307 // FETCHMAIL
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000308 opt_after_connect = NULL;
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000309 opt_complementary = "-2:w+:P";
310 options = "w:U:P:X" "tz";
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000311 }
312 opts = getopt32(argv, options,
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000313 &timeout, &opt_user, &opt_pass,
314 &opt_subject, &opt_charset, &opt_recipients
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000315 );
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000316
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000317 //argc -= optind;
318 argv += optind;
319
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000320 // first argument is remote server[:port]
321 opt_connect = *argv++;
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000322
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000323 // connect to server
324 if (opts & OPT_X) {
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000325 launch_helper(xargs);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000326 } else {
327 // no connection helper provided -> make plain connect
328 int fd = create_and_connect_stream_or_die(opt_connect, 0);
329 xmove_fd(fd, STDIN_FILENO);
330 xdup2(STDIN_FILENO, STDOUT_FILENO);
331 }
332
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000333#if ENABLE_FETCHMAIL
334 if (opt_after_connect)
335#endif
336 {
337 // SENDMAIL
338 char *opt_from;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000339 int code;
340 char *boundary;
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000341 const char *fmt;
342 const char *p;
343 char *q;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000344
345 // wait for initial OK on plain connect
346 if (!(opts & OPT_X))
347 smtp_check(NULL, 220);
348
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000349 // get specified sender
350 opt_from = sane(*argv++);
351
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000352 // introduce to server
353 // should we respect modern (but useless here) EHLO?
354 // or should they respect we wanna be tiny?!
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000355 if (!ENABLE_FEATURE_SENDMAIL_EHLO || 250 != smtp_checkp("EHLO %s", opt_from, -1)) {
356 smtp_checkp("HELO %s", opt_from, 250);
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000357 }
358
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000359 // set sender
360 // NOTE: if password has not been specified ->
361 // no authentication is possible
362 code = (opts & OPT_P) ? -1 : 250;
363 // first try softly without authentication
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000364 while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) {
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000365 // MAIL FROM failed -> authentication needed
366 // do we have username?
367 if (!(opts & OPT_U)) {
368 // no! fetch it from "from" option
369 //opts |= OPT_U;
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000370 opt_user = xstrdup(opt_from);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000371 *strchrnul(opt_user, '@') = '\0';
372 }
373 // now it seems we have username
374 // try to authenticate
375 if (334 == smtp_check("AUTH LOGIN", -1)) {
376 uuencode(NULL, opt_user);
377 smtp_check("", 334);
378 uuencode(NULL, opt_pass);
379 smtp_check("", 235);
380 }
381 // authenticated -> retry set sender
382 // but now die on failure
383 code = 250;
384 }
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000385
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000386 // set recipients
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000387 for (llist_t *to = opt_recipients; to; to = to->link) {
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000388 smtp_checkp("RCPT TO:<%s>", sane(to->data), 250);
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000389 }
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000390
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000391 // now put message
392 smtp_check("DATA", 354);
393 // put address headers
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000394 printf("From: %s\r\n", opt_from);
395 for (llist_t *to = opt_recipients; to; to = to->link) {
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000396 printf("To: %s\r\n", to->data);
397 }
398 // put encoded subject
399 if (opts & OPTS_c)
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000400 sane((char *)opt_charset);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000401 if (opts & OPTS_s) {
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000402 printf("Subject: =?%s?B?", opt_charset);
403 uuencode(NULL, opt_subject);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000404 printf("?=\r\n");
405 }
406 // put notification
407 if (opts & OPTS_n)
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000408 printf("Disposition-Notification-To: %s\r\n", opt_from);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000409 // put common headers and body start
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000410 // randomize
411#if ENABLE_FEATURE_SENDMAIL_BLOATY
412 srand(time(NULL));
413#endif
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000414 boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000415 printf(
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000416 USE_FEATURE_SENDMAIL_BLOATY("X-Mailer: busybox " BB_VER " sendmail\r\n")
417 "Message-ID: <%s>\r\n"
418 "Mime-Version: 1.0\r\n"
419 "%smultipart/mixed; boundary=\"%s\"\r\n"
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000420 , boundary
Denis Vlasenkoa2980c62008-02-02 17:54:35 +0000421 , "Content-Type: "
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000422 , boundary
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000423 );
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000424 // put body + attachment(s)
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000425
426 fmt =
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000427 "\r\n--%s\r\n"
428 "%stext/plain; charset=%s\r\n"
429 "%s%s\r\n"
430 "%s"
431 ;
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000432 p = opt_charset;
433 q = (char *)"";
434 while (*argv) {
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000435 printf(
436 fmt
437 , boundary
438 , "Content-Type: "
439 , p
440 , "Content-Disposition: inline"
441 , q
442 , "Content-Transfer-Encoding: base64\r\n"
443 );
444 p = "";
445 fmt =
446 "\r\n--%s\r\n"
447 "%sapplication/octet-stream%s\r\n"
448 "%s; filename=\"%s\"\r\n"
449 "%s"
450 ;
451 uuencode(*argv, NULL);
452 if (*(++argv))
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000453 q = bb_get_last_path_component_strip(*argv);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000454 }
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000455
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000456 // put terminator
457 printf("\r\n--%s--\r\n" "\r\n", boundary);
458 if (ENABLE_FEATURE_CLEAN_UP)
459 free(boundary);
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000460
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000461 // end message and say goodbye
462 smtp_check(".", 250);
463 smtp_check("QUIT", 221);
464
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000465#if ENABLE_FETCHMAIL
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000466 } else {
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000467 // FETCHMAIL
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000468 char *buf;
469 unsigned nmsg;
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000470 char *hostname;
471 pid_t pid;
472 // cache fetch command
473 const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
474
475 // goto maildir
476 xchdir(*argv++);
477
478#if ENABLE_FEATURE_FETCHMAIL_FILTER
479 // cache postprocess program
480 *fargs = *argv;
481#endif
482
483 // authenticate
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000484 if (!(opts & OPT_U)) {
485 //opts |= OPT_U;
486 opt_user = getenv("USER");
487 }
488#if ENABLE_FEATURE_FETCHMAIL_APOP
489 pop3_checkr(NULL, NULL, &buf);
490 // server supports APOP?
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000491 if ('<' == *buf) {
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000492 md5_ctx_t md5;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000493 // yes. compose <stamp><password>
494 char *s = strchr(buf, '>');
495 if (s)
496 strcpy(s+1, opt_pass);
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000497 s = buf;
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000498 // get md5 sum of <stamp><password>
499 md5_begin(&md5);
500 md5_hash(s, strlen(s), &md5);
501 md5_end(s, &md5);
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000502 // NOTE: md5 struct contains enough space
503 // so we reuse md5 space instead of xzalloc(16*2+1)
504#define md5_hex ((uint8_t *)&md5)
505// uint8_t *md5_hex = (uint8_t *)&md5;
506 *bin2hex(md5_hex, s, 16) = '\0';
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000507 // APOP
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000508 s = xasprintf("%s %s", opt_user, md5_hex);
509#undef md5_hex
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000510 pop3_check("APOP %s", s);
511 if (ENABLE_FEATURE_CLEAN_UP) {
512 free(s);
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000513 free(buf-4); // buf is "+OK " away from malloc'ed string
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000514 }
515 } else {
516#else
517 {
518 pop3_check(NULL, NULL);
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000519#endif
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000520 // USER
521 pop3_check("USER %s", opt_user);
522 // PASS
523 pop3_check("PASS %s", opt_pass);
524 }
525
526 // get statistics
527 pop3_checkr("STAT", NULL, &buf);
528
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000529 // prepare message filename suffix
530 hostname = xzalloc(MAXHOSTNAMELEN+1);
531 gethostname(hostname, MAXHOSTNAMELEN);
532 pid = getpid();
533
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000534 // get number of messages
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000535 // NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
536 // we only need nmsg and atoi is just exactly what we need
537 // if atoi fails to convert buf into number it returns 0
538 // in this case the following loop simply will not be executed
539 nmsg = atoi(buf);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000540 if (ENABLE_FEATURE_CLEAN_UP)
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000541 free(buf-4); // buf is "+OK " away from malloc'ed string
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000542
Bernhard Reutner-Fischera985d302008-02-11 11:44:38 +0000543 // loop through messages
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000544 for (; nmsg; nmsg--) {
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000545 char *filename = xasprintf("tmp/%llu.%u.%s", monotonic_us(), pid, hostname);
546 char *target;
547#if ENABLE_FEATURE_FETCHMAIL_FILTER
548 int rc;
549#endif
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000550 // retrieve message in ./tmp
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000551 pop3_check(retr, (const char *)nmsg);
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000552 pop3_message(filename);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000553 // delete message from server
554 if (opts & OPTF_z)
555 pop3_check("DELE %u", (const char*)nmsg);
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000556
557#if ENABLE_FEATURE_FETCHMAIL_FILTER
558 // run postprocessing program
559 if (*fargs) {
560 fargs[1] = filename;
561 rc = wait4pid(spawn((char **)fargs));
562 if (99 == rc)
563 break;
564 if (1 == rc)
565 goto skip;
566 }
567#endif
568 // atomically move message to ./new
569 target = xstrdup(filename);
570 strncpy(target, "new", 3);
571 // ... or just stop receiving on error
572 if (rename_or_warn(filename, target))
573 break;
574 free(target);
575#if ENABLE_FEATURE_FETCHMAIL_FILTER
576 skip:
577#endif
578 free(filename);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000579 }
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000580
581 if (ENABLE_FEATURE_CLEAN_UP)
582 free(hostname);
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000583
584 // Bye
585 pop3_check("QUIT", NULL);
Denis Vlasenko17db1a92008-02-26 21:13:17 +0000586#endif // ENABLE_FETCHMAIL
Denis Vlasenko6d52c1e2008-02-08 18:24:54 +0000587 }
Denis Vlasenkoddd42cb2008-01-29 00:59:15 +0000588
589 return 0;
590}