"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 #include "imap.h"
2 #include "util.h"
3
4 #include <string.h>
5
6 #include <openssl/err.h>
7 #include <openssl/ssl.h>
8
9 #define IMAP_DEFAULT_PORT 143
10 #define IMAP_DEFAULT_SSL_PORT 993
11
12 typedef void (*UntaggedCallback)(ImapContext *, char *, char *);
13
14 void handle_ignore(ImapContext *c, char *resp, char *args) {
15 return;
16 }
17
18 void handle_status(ImapContext *c, char *resp, char *args) {
19 char *p;
20
21 if(strcasecmp(resp, "STATUS"))
22 return;
23
24 if(!args)
25 goto error;
26
27 /* Kill the final close paren */
28 p = args + strlen(args) - 1;
29 *p = '\0';
30
31 /* And then find the first one */
32 p = strchr(args, '(');
33 if(!p)
34 goto error;
35
36 p = strchr(p, ' ');
37 if(!p)
38 goto error;
39
40 p++;
41 c->recent = atoi(p);
42 return;
43
44 error:
45 c->recent = 0;
46 }
47
48 static void imap_error(ImapContext *c, const char *fmt, ...) {
49 va_list ap;
50
51 c->error = 1;
52
53 va_start(ap, fmt);
54 vsnprintf(c->errstr, IMAP_BUFSIZE, fmt, ap);
55 va_end(ap);
56 }
57
58 static void imap_serror(ImapContext *c, const char *fmt, ...) {
59 va_list ap;
60 char ssl_error[1024];
61 char buf[IMAP_BUFSIZE];
62
63 c->error =1;
64
65 va_start(ap, fmt);
66 vsnprintf(buf, IMAP_BUFSIZE, fmt, ap);
67 va_end(ap);
68
69 ERR_error_string(ERR_get_error(), ssl_error);
70 snprintf(c->errstr, IMAP_BUFSIZE, "%s: %s", buf, ssl_error);
71 }
72
73 static void imap_close(ImapContext *c) {
74 if(c->out) {
75 BIO_puts(c->out, ". LOGOUT\r\n");
76 BIO_free(c->out);
77 }
78 if(c->in)
79 BIO_free(c->in);
80 c->out = c->in = NULL;
81 }
82
83 static ImapContext *imap_new() {
84 ImapContext *c;
85
86 c = malloc(sizeof(ImapContext));
87 if(!c)
88 return NULL;
89
90 c->in = c->out = NULL;
91 c->error = 0;
92 c->recent = 0;
93 memset(c->buf, 0, IMAP_BUFSIZE);
94 memset(c->errstr, 0, IMAP_BUFSIZE);
95
96 return c;
97 }
98
99 static int imap_getline(ImapContext *c) {
100 if(BIO_gets(c->in, c->buf, IMAP_BUFSIZE) < 0) {
101 imap_serror(c, "read");
102 imap_close(c);
103 return -1;
104 }
105 return 0;
106 }
107
108 static int imap_command(ImapContext *c, UntaggedCallback ucb,
109 const char *fmt, ...) {
110 va_list ap;
111 BIO *b;
112
113 va_start(ap, fmt);
114
115 if(c->out)
116 b = c->out;
117 else
118 b = c->in;
119
120 BIO_puts(b, ". ");
121 BIO_vprintf(b, fmt, ap);
122 BIO_puts(b, "\r\n");
123 BIO_flush(b);
124
125 va_end(ap);
126
127 while(1) {
128 char *resp[3];
129 int nresp;
130
131 if(imap_getline(c) < 0)
132 return -1;
133
134 util_chomp(c->buf);
135 nresp = util_split(" ", c->buf, resp, 3);
136 if(nresp < 2) {
137 imap_close(c);
138 imap_error(c, "Confusing imap response: %s %s %s", nresp ? resp[0] : "",
139 nresp > 1 ? resp[1] : "", nresp > 2 ? resp[2] : "");
140 return -1;
141 }
142 if(!strcmp(resp[0], ".")) {
143 if(!strcasecmp(resp[1], "OK"))
144 return 1;
145 else if(!strcasecmp(resp[1], "BAD"))
146 return 0;
147 else if(!strcasecmp(resp[1], "NO"))
148 return 0;
149 else {
150 imap_close(c);
151 imap_error(c, "Confusing imap response: %s %s %s", nresp ? resp[0] : "",
152 nresp > 1 ? resp[1] : "", nresp > 2 ? resp[2] : "");
153 return -1;
154 }
155 }
156 else if(!strcmp(resp[0], "*"))
157 ucb(c, resp[1], resp[2]);
158 else {
159 imap_close(c);
160 imap_error(c, "Confusing imap response: %s %s %s", nresp ? resp[0] : "",
161 nresp > 1 ? resp[1] : "", nresp > 2 ? resp[2] : "");
162 return -1;
163 }
164 }
165 }
166
167 static int imap_open_io(ImapContext *c, int use_ssl, int rfd, int wfd) {
168 SSL_library_init();
169 ERR_load_crypto_strings();
170 ERR_load_SSL_strings();
171
172 if(use_ssl) {
173 SSL_CTX *ssl_ctx;
174 BIO *bio;
175
176 ssl_ctx = SSL_CTX_new(SSLv23_client_method());
177 if(!ssl_ctx) {
178 imap_serror(c, "Unable to initialize SSL context:");
179 return -1;
180 }
181
182 c->in = BIO_new_socket(rfd, BIO_CLOSE);
183 if(!c->in) {
184 imap_serror(c, "BIO_new_socket");
185 return -1;
186 }
187
188 bio = BIO_new_ssl(ssl_ctx, 1);
189 if(!bio) {
190 imap_serror(c, "BIO_new_ssl");
191 return -1;
192 }
193 c->in = BIO_push(bio, c->in);
194
195 bio = BIO_new(BIO_f_buffer());
196 if(!bio) {
197 imap_serror(c, "BIO_new_buffer");
198 return -1;
199 }
200 c->in = BIO_push(bio, c->in);
201 }
202 else {
203 c->in = BIO_new_socket(rfd, BIO_CLOSE);
204 c->in = BIO_push(BIO_new(BIO_f_buffer()), c->in);
205 if(wfd >= 0) {
206 c->out = BIO_new_socket(wfd, BIO_CLOSE);
207 c->out = BIO_push(BIO_new(BIO_f_buffer()), c->out);
208 }
209 }
210
211 return 0;
212 }
213
214 ImapContext *imap_preauth(char * const cmd[]) {
215 ImapContext *c;
216 pid_t pid;
217 int read, write;
218 char *resp[3];
219 int nresp;
220
221 c = imap_new();
222 if(!c)
223 return NULL;
224
225 pid = util_pipeto(cmd, &read, &write);
226 if(!pid) {
227 imap_error(c, "%s", strerror(errno));
228 return c;
229 }
230
231 if(imap_open_io(c, 0, read, write) < 0)
232 return c;
233
234 if(imap_getline(c) < 0)
235 return c;
236 util_chomp(c->buf);
237 nresp = util_split(" ", c->buf, resp, 3);
238 if(nresp < 2 || strcmp(resp[0], "*") || strcasecmp(resp[1], "PREAUTH")) {
239 imap_close(c);
240 imap_error(c, "Unexpected preauth greeting: %s %s %s",
241 nresp ? resp[0] : "", nresp > 1 ? resp[1] : "",
242 nresp > 2 ? resp[2] : "");
243 return c;
244 }
245
246 return c;
247 }
248
249 static int parse_host(const char *s, char **ret_host, int *use_ssl,
250 unsigned short *port) {
251 char *port_string;
252 char *ssl_string;
253 char *host;
254
255 *ret_host = host = strdup(s);
256 if(!host)
257 return -1;
258
259 port_string = strchr(host, ':');
260 if(port_string)
261 *port_string++ = '\0';
262
263 ssl_string = strchr(host, '/');
264 if(ssl_string && !strcasecmp(ssl_string, "/ssl")) {
265 *ssl_string = '\0';
266 *use_ssl = 1;
267 }
268 else
269 *use_ssl = 0;
270
271 /* !! we should use one port and support STARTTLS */
272 if(!port_string)
273 *port = *use_ssl ? IMAP_DEFAULT_SSL_PORT : IMAP_DEFAULT_PORT;
274 else
275 *port = atoi(port_string);
276
277 return 0;
278 }
279
280 ImapContext *imap_login(const char *host, const char *login, const char *pass) {
281 ImapContext *c;
282
283 int use_ssl;
284 unsigned short port;
285 char *hostname;
286
287 int fd;
288
289 char *resp[3];
290 int nresp;
291
292 c = imap_new();
293 if(!c)
294 return NULL;
295
296 if(parse_host(host, &hostname, &use_ssl, &port) < 0) {
297 imap_destroy(c);
298 return NULL;
299 }
300
301 fd = util_iconnect(hostname, port);
302 if(fd < 0) {
303 if(fd < -1) {
304 #ifdef HAVE_HSTRERROR
305 imap_error(c, "Unable to resolve %s: %s", host, hstrerror(h_errno));
306 #else
307 imap_error(c, "Unable to resolve %s", host);
308 #endif /* HAVE_STRERROR */
309 return c;
310 }
311 else {
312 imap_error(c, "Unable to connect to %s/%hu: %s", host, port,
313 strerror(errno));
314 return c;
315 }
316 }
317
318 if(imap_open_io(c, use_ssl, fd, -1) < 0)
319 return c;
320
321 if(imap_getline(c) < 0)
322 return c;
323 util_chomp(c->buf);
324 nresp = util_split(" ", c->buf, resp, 3);
325 if(nresp < 2 || strcmp(resp[0], "*") || strcasecmp(resp[1], "OK")) {
326 imap_close(c);
327 imap_error(c, "Unexpected IMAP greeting: %s %s %s",
328 nresp ? resp[0] : "", nresp > 1 ? resp[1] : "",
329 nresp > 2 ? resp[2] : "");
330 return c;
331 }
332
333 switch(imap_command(c, handle_ignore, "LOGIN %s \"%s\"", login, pass)) {
334 case -1:
335 return c;
336 case 0:
337 imap_close(c);
338 imap_error(c, "LOGIN command failed");
339 return c;
340 default:
341 return c;
342 }
343 }
344
345 void imap_destroy(ImapContext *c) {
346 imap_close(c);
347 free(c);
348 }
349
350 int imap_get_recent(ImapContext *c, const char *folder) {
351 c->error = 0;
352 switch(imap_command(c, handle_status, "STATUS %s (UNSEEN)", folder)) {
353 case -1:
354 return -1;
355 case 0:
356 return 0;
357 default:
358 return c->recent;
359 }
360 }
361