diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-08 18:24:54 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-08 18:24:54 +0000 |
commit | 6d52c1ee24f77690c932dfe8456dfa31e8808f4c (patch) | |
tree | fddeffde64aeb613edc23e3580d196a28eb1ff03 | |
parent | dc1c45795b36c232fe781962c5fab1df6b8299d9 (diff) | |
download | busybox-w32-6d52c1ee24f77690c932dfe8456dfa31e8808f4c.tar.gz busybox-w32-6d52c1ee24f77690c932dfe8456dfa31e8808f4c.tar.bz2 busybox-w32-6d52c1ee24f77690c932dfe8456dfa31e8808f4c.zip |
sendmail: update from maintainer
-rw-r--r-- | include/applets.h | 3 | ||||
-rw-r--r-- | include/usage.h | 45 | ||||
-rw-r--r-- | networking/Config.in | 35 | ||||
-rw-r--r-- | networking/Kbuild | 1 | ||||
-rw-r--r-- | networking/sendmail.c | 630 |
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)) | |||
151 | USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush)) | 151 | USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush)) |
152 | USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 152 | USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
153 | USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER)) | 153 | USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER)) |
154 | USE_FETCHMAIL(APPLET_ODDNAME(fetchmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, fetchmail)) | ||
154 | USE_FEATURE_GREP_FGREP_ALIAS(APPLET_NOUSAGE(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER)) | 155 | USE_FEATURE_GREP_FGREP_ALIAS(APPLET_NOUSAGE(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER)) |
155 | USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find)) | 156 | USE_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)) | |||
299 | USE_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 300 | USE_RUNSVDIR(APPLET(runsvdir, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
300 | USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 301 | USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) |
301 | USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER)) | 302 | USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER)) |
302 | USE_SENDMAIL(APPLET(sendmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) | 303 | USE_SENDMAIL(APPLET_ODDNAME(sendmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sendmail)) |
303 | USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) | 304 | USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) |
304 | USE_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER, seq)) | 305 | USE_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER, seq)) |
305 | USE_SESTATUS(APPLET(sestatus, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) | 306 | USE_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 | ||
674 | config FEATURE_SENDMAIL_NETWORK | 674 | config 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 | |||
681 | config 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 | |||
688 | config FETCHMAIL | ||
689 | bool "fetchmail" | ||
690 | default n | ||
691 | help | ||
692 | Barebones fetchmail. | ||
693 | |||
694 | config FEATURE_FETCHMAIL_APOP | ||
695 | bool "Support APOP authentication" | ||
696 | default y | ||
697 | depends on FETCHMAIL | ||
698 | help | ||
699 | Support secure APOP authentication. | ||
700 | |||
701 | config 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 | ||
681 | config SLATTACH | 708 | config 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 | |||
11 | lib-$(CONFIG_DNSD) += dnsd.o | 11 | lib-$(CONFIG_DNSD) += dnsd.o |
12 | lib-$(CONFIG_ETHER_WAKE) += ether-wake.o | 12 | lib-$(CONFIG_ETHER_WAKE) += ether-wake.o |
13 | lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o | 13 | lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o |
14 | lib-$(CONFIG_FETCHMAIL) += sendmail.o | ||
14 | lib-$(CONFIG_FTPGET) += ftpgetput.o | 15 | lib-$(CONFIG_FTPGET) += ftpgetput.o |
15 | lib-$(CONFIG_FTPPUT) += ftpgetput.o | 16 | lib-$(CONFIG_FTPPUT) += ftpgetput.o |
16 | lib-$(CONFIG_HOSTNAME) += hostname.o | 17 | lib-$(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 | */ | ||
14 | enum { | ||
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 | ||
19 | static void uuencode(char *fname, const char *text) | 13 | static 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 | 62 | static pid_t helper_pid; |
63 | |||
64 | static 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 |
63 | static void signal_handler(int signo) | 72 | static 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 | ||
76 | static pid_t helper_pid; | 91 | static 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 | ||
82 | static 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 | |||
120 | static unsigned timeout; | ||
85 | 121 | ||
86 | // read a string and match it against the set of available answers | 122 | static 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 | |||
135 | static 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 | ||
106 | static int puts_and_check(const char *msg, int code, const char *errmsg) | 165 | static 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 | |||
170 | static 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 | |||
186 | static 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 | ||
128 | int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 206 | static void pop3_message(int fd) |
129 | int 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 | |||
224 | static 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 | |||
230 | int sendgetmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
231 | int 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, ¬ify, &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 | } |