Start 1.33.0 development cycle
[oweals/busybox.git] / mailutils / mail.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * helper routines
4  *
5  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6  *
7  * Licensed under GPLv2, see file LICENSE in this source tree.
8  */
9 #include <sys/prctl.h>
10 #include "libbb.h"
11 #include "mail.h"
12
13 // generic signal handler
14 static void signal_handler(int signo)
15 {
16 #define err signo
17         if (SIGALRM == signo) {
18                 bb_simple_error_msg_and_die("timed out");
19         }
20
21         // SIGCHLD. reap zombies
22         if (safe_waitpid(G.helper_pid, &err, WNOHANG) > 0) {
23                 if (WIFSIGNALED(err))
24                         bb_error_msg_and_die("helper killed by signal %u", WTERMSIG(err));
25                 if (WIFEXITED(err)) {
26                         G.helper_pid = 0;
27                         if (WEXITSTATUS(err))
28                                 bb_error_msg_and_die("helper exited (%u)", WEXITSTATUS(err));
29                 }
30         }
31 #undef err
32 }
33
34 void FAST_FUNC launch_helper(const char **argv)
35 {
36         // setup vanilla unidirectional pipes interchange
37         int i;
38         int pipes[4];
39
40         xpipe(pipes);
41         xpipe(pipes + 2);
42
43         // NB: handler must be installed before vfork
44         bb_signals(0
45                 + (1 << SIGCHLD)
46                 + (1 << SIGALRM)
47                 , signal_handler);
48
49         G.helper_pid = xvfork();
50
51         i = (!G.helper_pid) * 2; // for parent:0, for child:2
52         close(pipes[i + 1]);     // 1 or 3 - closing one write end
53         close(pipes[2 - i]);     // 2 or 0 - closing one read end
54         xmove_fd(pipes[i], STDIN_FILENO);      // 0 or 2 - using other read end
55         xmove_fd(pipes[3 - i], STDOUT_FILENO); // 3 or 1 - using other write end
56         // End result:
57         // parent stdout [3] -> child stdin [2]
58         // child stdout [1] -> parent stdin [0]
59
60         if (!G.helper_pid) {
61                 // child
62                 // if parent dies, get SIGTERM
63                 prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
64                 // try to execute connection helper
65                 // NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
66                 BB_EXECVP_or_die((char**)argv);
67         }
68
69         // parent goes on
70 }
71
72 char* FAST_FUNC send_mail_command(const char *fmt, const char *param)
73 {
74         char *msg;
75         if (timeout)
76                 alarm(timeout);
77         msg = (char*)fmt;
78         if (fmt) {
79                 msg = xasprintf(fmt, param);
80                 if (verbose)
81                         bb_error_msg("send:'%s'", msg);
82                 printf("%s\r\n", msg);
83         }
84         fflush_all();
85         return msg;
86 }
87
88 // NB: parse_url can modify url[] (despite const), but only if '@' is there
89 /*
90 static char* FAST_FUNC parse_url(char *url, char **user, char **pass)
91 {
92         // parse [user[:pass]@]host
93         // return host
94         char *s = strchr(url, '@');
95         *user = *pass = NULL;
96         if (s) {
97                 *s++ = '\0';
98                 *user = url;
99                 url = s;
100                 s = strchr(*user, ':');
101                 if (s) {
102                         *s++ = '\0';
103                         *pass = s;
104                 }
105         }
106         return url;
107 }
108 */
109
110 static void encode_n_base64(const char *fname, const char *text, size_t len)
111 {
112         enum {
113                 SRC_BUF_SIZE = 57,  /* This *MUST* be a multiple of 3 */
114                 DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
115         };
116 #define src_buf text
117         char src[SRC_BUF_SIZE];
118         FILE *fp = fp;
119         char dst_buf[DST_BUF_SIZE + 1];
120
121         if (fname) {
122                 fp = (NOT_LONE_DASH(fname)) ? xfopen_for_read(fname) : stdin;
123                 src_buf = src;
124         }
125
126         while (1) {
127                 size_t size;
128                 if (fname) {
129                         size = fread((char *)src_buf, 1, SRC_BUF_SIZE, fp);
130                         if ((ssize_t)size < 0)
131                                 bb_simple_perror_msg_and_die(bb_msg_read_error);
132                 } else {
133                         size = len;
134                         if (len > SRC_BUF_SIZE)
135                                 size = SRC_BUF_SIZE;
136                 }
137                 if (!size)
138                         break;
139                 // encode the buffer we just read in
140                 bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
141                 if (fname) {
142                         puts("");
143                 } else {
144                         src_buf += size;
145                         len -= size;
146                 }
147                 fwrite(dst_buf, 1, 4 * ((size + 2) / 3), stdout);
148         }
149         if (fname && NOT_LONE_DASH(fname))
150                 fclose(fp);
151 #undef src_buf
152 }
153
154 void FAST_FUNC printstr_base64(const char *text)
155 {
156         encode_n_base64(NULL, text, strlen(text));
157 }
158
159 void FAST_FUNC printbuf_base64(const char *text, unsigned len)
160 {
161         encode_n_base64(NULL, text, len);
162 }
163
164 void FAST_FUNC printfile_base64(const char *fname)
165 {
166         encode_n_base64(fname, NULL, 0);
167 }
168
169 /*
170  * get username and password from a file descriptor
171  */
172 void FAST_FUNC get_cred_or_die(int fd)
173 {
174         if (isatty(fd)) {
175                 G.user = bb_ask_noecho(fd, /* timeout: */ 0, "User: ");
176                 G.pass = bb_ask_noecho(fd, /* timeout: */ 0, "Password: ");
177         } else {
178                 G.user = xmalloc_reads(fd, /* maxsize: */ NULL);
179                 G.pass = xmalloc_reads(fd, /* maxsize: */ NULL);
180         }
181         if (!G.user || !*G.user || !G.pass)
182                 bb_simple_error_msg_and_die("no username or password");
183 }