1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
/* vi: set sw=4 ts=4: */
/*
* popmaildir: a simple yet powerful POP3 client
* Delivers contents of remote mailboxes to local Maildir
*
* Inspired by original utility by Nikola Vladov
*
* Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
*
* Licensed under GPLv2, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "mail.h"
static void pop3_checkr(const char *fmt, const char *param, char **ret)
{
const char *msg = command(fmt, param);
char *answer = xmalloc_fgetline(stdin);
if (answer && '+' == answer[0]) {
if (timeout)
alarm(0);
if (ret) {
// skip "+OK "
memmove(answer, answer + 4, strlen(answer) - 4);
*ret = answer;
} else
free(answer);
return;
}
bb_error_msg_and_die("%s failed: %s", msg, answer);
}
static void pop3_check(const char *fmt, const char *param)
{
pop3_checkr(fmt, param, NULL);
}
int popmaildir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int popmaildir_main(int argc UNUSED_PARAM, char **argv)
{
char *buf;
unsigned nmsg;
char *hostname;
pid_t pid;
const char *retr;
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
const char *delivery;
#endif
unsigned opt_nlines = 0;
enum {
OPT_b = 1 << 0, // -b binary mode. Ignored
OPT_d = 1 << 1, // -d,-dd,-ddd debug. Ignored
OPT_m = 1 << 2, // -m show used memory. Ignored
OPT_V = 1 << 3, // -V version. Ignored
OPT_c = 1 << 4, // -c use tcpclient. Ignored
OPT_a = 1 << 5, // -a use APOP protocol
OPT_s = 1 << 6, // -s skip authorization
OPT_T = 1 << 7, // -T get messages with TOP instead with RETR
OPT_k = 1 << 8, // -k keep retrieved messages on the server
OPT_t = 1 << 9, // -t90 set timeout to 90 sec
OPT_R = 1 << 10, // -R20000 remove old messages on the server >= 20000 bytes (requires -k). Ignored
OPT_Z = 1 << 11, // -Z11-23 remove messages from 11 to 23 (dangerous). Ignored
OPT_L = 1 << 12, // -L50000 not retrieve new messages >= 50000 bytes. Ignored
OPT_H = 1 << 13, // -H30 type first 30 lines of a message; (-L12000 -H30). Ignored
OPT_M = 1 << 14, // -M\"program arg1 arg2 ...\"; deliver by program. Treated like -F
OPT_F = 1 << 15, // -F\"program arg1 arg2 ...\"; filter by program. Treated like -M
};
// init global variables
INIT_G();
// parse options
opt_complementary = "-1:dd:t+:R+:L+:H+";
opts = getopt32(argv,
"bdmVcasTkt:" "R:Z:L:H:" USE_FEATURE_POPMAILDIR_DELIVERY("M:F:"),
&timeout, NULL, NULL, NULL, &opt_nlines
USE_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same
);
//argc -= optind;
argv += optind;
// get auth info
if (!(opts & OPT_s))
get_cred_or_die(STDIN_FILENO);
// goto maildir
xchdir(*argv++);
// launch connect helper, if any
if (*argv)
launch_helper((const char **)argv);
// get server greeting
pop3_checkr(NULL, NULL, &buf);
// authenticate (if no -s given)
if (!(opts & OPT_s)) {
// server supports APOP and we want it?
if ('<' == buf[0] && (opts & OPT_a)) {
union { // save a bit of stack
md5_ctx_t ctx;
char hex[16 * 2 + 1];
} md5;
uint32_t res[16 / 4];
char *s = strchr(buf, '>');
if (s)
s[1] = '\0';
// get md5 sum of "<stamp>password" string
md5_begin(&md5.ctx);
md5_hash(buf, strlen(buf), &md5.ctx);
md5_hash(G.pass, strlen(G.pass), &md5.ctx);
md5_end(res, &md5.ctx);
*bin2hex(md5.hex, (char*)res, 16) = '\0';
// APOP
s = xasprintf("%s %s", G.user, md5.hex);
pop3_check("APOP %s", s);
free(s);
free(buf);
// server ignores APOP -> use simple text authentication
} else {
// USER
pop3_check("USER %s", G.user);
// PASS
pop3_check("PASS %s", G.pass);
}
}
// get mailbox statistics
pop3_checkr("STAT", NULL, &buf);
// prepare message filename suffix
hostname = safe_gethostname();
pid = getpid();
// get messages counter
// NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
// we only need nmsg and atoi is just exactly what we need
// if atoi fails to convert buf into number it returns 0
// in this case the following loop simply will not be executed
nmsg = atoi(buf);
free(buf);
// loop through messages
retr = (opts & OPT_T) ? xasprintf("TOP %%u %u", opt_nlines) : "RETR %u";
for (; nmsg; nmsg--) {
char *filename;
char *target;
char *answer;
FILE *fp;
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
int rc;
#endif
// generate unique filename
filename = xasprintf("tmp/%llu.%u.%s",
monotonic_us(), (unsigned)pid, hostname);
// retrieve message in ./tmp/ unless filter is specified
pop3_check(retr, (const char *)(ptrdiff_t)nmsg);
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
// delivery helper ordered? -> setup pipe
if (opts & (OPT_F|OPT_M)) {
// helper will have $FILENAME set to filename
xsetenv("FILENAME", filename);
fp = popen(delivery, "w");
unsetenv("FILENAME");
if (!fp) {
bb_perror_msg("delivery helper");
break;
}
} else
#endif
// create and open file filename
fp = xfopen_for_write(filename);
// copy stdin to fp (either filename or delivery helper)
while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) {
char *s = answer;
if ('.' == answer[0]) {
if ('.' == answer[1])
s++;
else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
break;
}
//*strchrnul(s, '\r') = '\n';
fputs(s, fp);
free(answer);
}
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
// analyse delivery status
if (opts & (OPT_F|OPT_M)) {
rc = pclose(fp);
if (99 == rc) // 99 means bail out
break;
// if (rc) // !0 means skip to the next message
goto skip;
// // 0 means continue
} else {
// close filename
fclose(fp);
}
#endif
// delete message from server
if (!(opts & OPT_k))
pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg);
// atomically move message to ./new/
target = xstrdup(filename);
strncpy(target, "new", 3);
// ... or just stop receiving on failure
if (rename_or_warn(filename, target))
break;
free(target);
#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
skip:
#endif
free(filename);
}
// Bye
pop3_check("QUIT", NULL);
if (ENABLE_FEATURE_CLEAN_UP) {
free(G.user);
free(G.pass);
}
return EXIT_SUCCESS;
}
|