Moved to the corresponding subdirectory
Showing
14 changed files
with
0 additions
and
12250 deletions
mailbox/folder_imap.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef ENABLE_IMAP | ||
23 | |||
24 | #include <stdlib.h> | ||
25 | #include <ctype.h> | ||
26 | #include <stdarg.h> | ||
27 | #include <errno.h> | ||
28 | #include <string.h> | ||
29 | #include <assert.h> | ||
30 | #include <fnmatch.h> | ||
31 | |||
32 | #ifdef HAVE_ALLOCA_H | ||
33 | # include <alloca.h> | ||
34 | #endif | ||
35 | |||
36 | #ifdef HAVE_STRINGS_H | ||
37 | # include <strings.h> | ||
38 | #endif | ||
39 | |||
40 | #include <imap0.h> | ||
41 | #include <url0.h> | ||
42 | |||
43 | #include <mailutils/auth.h> | ||
44 | #include <mailutils/attribute.h> | ||
45 | #include <mailutils/debug.h> | ||
46 | #include <mailutils/error.h> | ||
47 | #include <mailutils/header.h> | ||
48 | #include <mailutils/observer.h> | ||
49 | #include <mailutils/stream.h> | ||
50 | |||
51 | /* For dbg purposes set to one to see different level of traffic. */ | ||
52 | /* Print to stderr the command sent to the IMAP server. */ | ||
53 | #define DEBUG_SHOW_COMMAND 0 | ||
54 | /* Print to stderr the responses received from the IMAP server. */ | ||
55 | #define DEBUG_SHOW_RESPONSE 0 | ||
56 | /* Print to stderr the literal/quoted string received from the IMAP server. */ | ||
57 | #define DEBUG_SHOW_DATA 0 | ||
58 | |||
59 | /* Variable use for the registrar. */ | ||
60 | static struct _record _imap_record = | ||
61 | { | ||
62 | MU_IMAP_SCHEME, | ||
63 | _url_imap_init, /* url entry. */ | ||
64 | _mailbox_imap_init, /* Mailbox entry. */ | ||
65 | NULL, /* Mailer entry. */ | ||
66 | _folder_imap_init, /* Folder entry. */ | ||
67 | NULL, /* No need for a back pointer. */ | ||
68 | NULL, /* _is_scheme method. */ | ||
69 | NULL, /* _get_url method. */ | ||
70 | NULL, /* _get_mailbox method. */ | ||
71 | NULL, /* _get_mailer method. */ | ||
72 | NULL /* _get_folder method. */ | ||
73 | }; | ||
74 | |||
75 | /* We export this variable: url parsing and the initialisation of the mailbox, | ||
76 | via the register entry/record. */ | ||
77 | record_t imap_record = &_imap_record; | ||
78 | |||
79 | #ifndef HAVE_STRTOK_R | ||
80 | char *strtok_r __P ((char *, const char *, char **)); | ||
81 | #endif | ||
82 | |||
83 | /* Concrete folder_t IMAP implementation. */ | ||
84 | static int folder_imap_open __P ((folder_t, int)); | ||
85 | static int folder_imap_close __P ((folder_t)); | ||
86 | static void folder_imap_destroy __P ((folder_t)); | ||
87 | static int folder_imap_delete __P ((folder_t, const char *)); | ||
88 | static int folder_imap_list __P ((folder_t, const char *, const char *, | ||
89 | struct folder_list *)); | ||
90 | static int folder_imap_lsub __P ((folder_t, const char *, const char *, | ||
91 | struct folder_list *)); | ||
92 | static int folder_imap_rename __P ((folder_t, const char *, | ||
93 | const char *)); | ||
94 | static int folder_imap_subscribe __P ((folder_t, const char *)); | ||
95 | static int folder_imap_unsubscribe __P ((folder_t, const char *)); | ||
96 | static int folder_imap_get_authority __P ((folder_t, authority_t *)); | ||
97 | |||
98 | static int authenticate_imap_login __P ((authority_t)); | ||
99 | static int authenticate_imap_sasl_anon __P ((authority_t)); | ||
100 | |||
101 | /* FETCH */ | ||
102 | static int imap_fetch __P ((f_imap_t)); | ||
103 | static int imap_rfc822 __P ((f_imap_t, char **)); | ||
104 | static int imap_rfc822_size __P ((f_imap_t, char **)); | ||
105 | static int imap_rfc822_header __P ((f_imap_t, char **)); | ||
106 | static int imap_rfc822_text __P ((f_imap_t, char **)); | ||
107 | static int imap_fetch_flags __P ((f_imap_t, char **)); | ||
108 | static int imap_permanentflags __P ((f_imap_t, char **)); | ||
109 | static int imap_flags __P ((char **, int *)); | ||
110 | static int imap_bodystructure __P ((f_imap_t, char **)); | ||
111 | static int imap_body __P ((f_imap_t, char **)); | ||
112 | static int imap_internaldate __P ((f_imap_t, char **)); | ||
113 | |||
114 | static int imap_uid __P ((f_imap_t, char **)); | ||
115 | static int imap_status __P ((f_imap_t)); | ||
116 | static int imap_expunge __P ((f_imap_t, unsigned int)); | ||
117 | static int imap_search __P ((f_imap_t)); | ||
118 | |||
119 | /* String. */ | ||
120 | static int imap_literal_string __P ((f_imap_t, char **)); | ||
121 | static int imap_string __P ((f_imap_t, char **)); | ||
122 | static int imap_quoted_string __P ((f_imap_t, char **)); | ||
123 | static int imap_mailbox_name_match __P ((const char* pattern, const char* mailbox)); | ||
124 | |||
125 | static int imap_token __P ((char *, size_t, char **)); | ||
126 | |||
127 | /* Initialize the concrete IMAP mailbox: overload the folder functions */ | ||
128 | int | ||
129 | _folder_imap_init (folder_t folder) | ||
130 | { | ||
131 | int status; | ||
132 | f_imap_t f_imap; | ||
133 | |||
134 | /* Set the authority early: | ||
135 | (1) so we can check for errors. | ||
136 | (2) allow the client to get the authority for setting the ticket | ||
137 | before the open. */ | ||
138 | status = folder_imap_get_authority (folder, NULL); | ||
139 | if (status != 0) | ||
140 | return status; | ||
141 | |||
142 | f_imap = folder->data = calloc (1, sizeof (*f_imap)); | ||
143 | if (f_imap == NULL) | ||
144 | return ENOMEM; | ||
145 | |||
146 | f_imap->folder = folder; | ||
147 | f_imap->state = IMAP_NO_STATE; | ||
148 | |||
149 | folder->_destroy = folder_imap_destroy; | ||
150 | |||
151 | folder->_open = folder_imap_open; | ||
152 | folder->_close = folder_imap_close; | ||
153 | |||
154 | folder->_list = folder_imap_list; | ||
155 | folder->_lsub = folder_imap_lsub; | ||
156 | folder->_subscribe = folder_imap_subscribe; | ||
157 | folder->_unsubscribe = folder_imap_unsubscribe; | ||
158 | folder->_delete = folder_imap_delete; | ||
159 | folder->_rename = folder_imap_rename; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | /* Destroy the folder resources. */ | ||
165 | static void | ||
166 | folder_imap_destroy (folder_t folder) | ||
167 | { | ||
168 | if (folder->data) | ||
169 | { | ||
170 | f_imap_t f_imap = folder->data; | ||
171 | if (f_imap->buffer) | ||
172 | free (f_imap->buffer); | ||
173 | if (f_imap->capa) | ||
174 | free (f_imap->capa); | ||
175 | free (f_imap); | ||
176 | folder->data = NULL; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | static int | ||
181 | folder_imap_get_authority (folder_t folder, authority_t *pauth) | ||
182 | { | ||
183 | int status = 0; | ||
184 | if (folder->authority == NULL) | ||
185 | { | ||
186 | /* assert (folder->url); */ | ||
187 | if (folder->url == NULL) | ||
188 | return EINVAL; | ||
189 | |||
190 | if (folder->url->auth == NULL | ||
191 | || strcasecmp (folder->url->auth, "*") == 0) | ||
192 | { | ||
193 | status = authority_create (&folder->authority, NULL, folder); | ||
194 | authority_set_authenticate (folder->authority, | ||
195 | authenticate_imap_login, folder); | ||
196 | } | ||
197 | else if (strcasecmp (folder->url->auth, "anon") == 0) | ||
198 | { | ||
199 | status = authority_create (&folder->authority, NULL, folder); | ||
200 | authority_set_authenticate (folder->authority, | ||
201 | authenticate_imap_sasl_anon, folder); | ||
202 | } | ||
203 | else | ||
204 | { | ||
205 | /* Not a supported authentication mechanism. */ | ||
206 | status = ENOSYS; | ||
207 | } | ||
208 | } | ||
209 | if (pauth) | ||
210 | *pauth = folder->authority; | ||
211 | return status; | ||
212 | } | ||
213 | |||
214 | /* Simple User/pass authentication for imap. */ | ||
215 | static int | ||
216 | authenticate_imap_login (authority_t auth) | ||
217 | { | ||
218 | folder_t folder = authority_get_owner (auth); | ||
219 | f_imap_t f_imap = folder->data; | ||
220 | ticket_t ticket; | ||
221 | int status = 0; | ||
222 | |||
223 | switch (f_imap->state) | ||
224 | { | ||
225 | case IMAP_AUTH: | ||
226 | { | ||
227 | /* Grab the User and Passwd information. */ | ||
228 | size_t n = 0; | ||
229 | authority_get_ticket (auth, &ticket); | ||
230 | if (f_imap->user) | ||
231 | free (f_imap->user); | ||
232 | if (f_imap->passwd) | ||
233 | free (f_imap->passwd); | ||
234 | /* Was it in the URL? */ | ||
235 | status = url_get_user (folder->url, NULL, 0, &n); | ||
236 | if (status != 0 || n == 0) | ||
237 | ticket_pop (ticket, folder->url, "Imap User: ", &f_imap->user); | ||
238 | else | ||
239 | { | ||
240 | f_imap->user = calloc (1, n + 1); | ||
241 | url_get_user (folder->url, f_imap->user, n + 1, NULL); | ||
242 | } | ||
243 | /* Was it in the URL? */ | ||
244 | status = url_get_passwd (folder->url, NULL, 0, &n); | ||
245 | if (status != 0 || n == 0) | ||
246 | ticket_pop (ticket, folder->url, "Imap Passwd: ", &f_imap->passwd); | ||
247 | else | ||
248 | { | ||
249 | f_imap->passwd = calloc (1, n + 1); | ||
250 | url_get_passwd (folder->url, f_imap->passwd, n + 1, NULL); | ||
251 | } | ||
252 | |||
253 | if (f_imap->user == NULL || f_imap->passwd == NULL) | ||
254 | { | ||
255 | CHECK_ERROR_CLOSE (folder, f_imap, EINVAL); | ||
256 | } | ||
257 | status = imap_writeline (f_imap, "g%u LOGIN %s %s\r\n", | ||
258 | f_imap->seq, f_imap->user, f_imap->passwd); | ||
259 | CHECK_ERROR_CLOSE(folder, f_imap, status); | ||
260 | FOLDER_DEBUG2 (folder, MU_DEBUG_PROT, "g%u LOGIN %s *\n", | ||
261 | f_imap->seq, f_imap->user); | ||
262 | f_imap->seq++; | ||
263 | free (f_imap->user); | ||
264 | f_imap->user = NULL; | ||
265 | /* We have to nuke the passwd. */ | ||
266 | memset (f_imap->passwd, '\0', strlen (f_imap->passwd)); | ||
267 | free (f_imap->passwd); | ||
268 | f_imap->passwd = NULL; | ||
269 | f_imap->state = IMAP_LOGIN; | ||
270 | } | ||
271 | |||
272 | case IMAP_LOGIN: | ||
273 | /* Send it across. */ | ||
274 | status = imap_send (f_imap); | ||
275 | CHECK_EAGAIN (f_imap, status); | ||
276 | /* Clear the buffer it contains the passwd. */ | ||
277 | memset (f_imap->buffer, '\0', f_imap->buflen); | ||
278 | f_imap->state = IMAP_LOGIN_ACK; | ||
279 | |||
280 | case IMAP_LOGIN_ACK: | ||
281 | /* Get the login ack. */ | ||
282 | status = imap_parse (f_imap); | ||
283 | CHECK_EAGAIN (f_imap, status); | ||
284 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
285 | f_imap->state = IMAP_AUTH_DONE; | ||
286 | |||
287 | default: | ||
288 | break; /* We're outta here. */ | ||
289 | } | ||
290 | CLEAR_STATE (f_imap); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | The anonymous SASL mechanism is defined in rfc2245.txt as a single | ||
296 | message from client to server: | ||
297 | |||
298 | message = [email / token] | ||
299 | |||
300 | So the message is optional. | ||
301 | |||
302 | The command is: | ||
303 | |||
304 | C: <tag> authenticate anonymous | ||
305 | |||
306 | The server responds with a request for continuation data (the "message" | ||
307 | in the SASL syntax). We respond with no data, which is legal. | ||
308 | |||
309 | S: + | ||
310 | C: | ||
311 | |||
312 | The server should then respond with OK on success, or else a failure | ||
313 | code (NO or BAD). | ||
314 | |||
315 | If OK, then we are authenticated! | ||
316 | |||
317 | So, states are: | ||
318 | |||
319 | AUTH_ANON_REQ | ||
320 | |||
321 | > g%u AUTHENTICATE ANONYMOUS | ||
322 | |||
323 | AUTH_ANON_WAIT_CONT | ||
324 | |||
325 | < + | ||
326 | |||
327 | AUTH_ANON_MSG | ||
328 | |||
329 | > | ||
330 | |||
331 | AUTH_ANON_WAIT_RESP | ||
332 | |||
333 | < NO/BAD/OK | ||
334 | |||
335 | */ | ||
336 | |||
337 | static int | ||
338 | authenticate_imap_sasl_anon (authority_t auth) | ||
339 | { | ||
340 | folder_t folder = authority_get_owner (auth); | ||
341 | f_imap_t f_imap = folder->data; | ||
342 | int status = 0; | ||
343 | |||
344 | assert (f_imap->state == IMAP_AUTH); | ||
345 | |||
346 | switch (f_imap->auth_state) | ||
347 | { | ||
348 | case IMAP_AUTH_ANON_REQ_WRITE: | ||
349 | { | ||
350 | FOLDER_DEBUG1 (folder, MU_DEBUG_PROT, "g%u AUTHENTICATE ANONYMOUS\n", | ||
351 | f_imap->seq); | ||
352 | status = imap_writeline (f_imap, "g%u AUTHENTICATE ANONYMOUS\r\n", | ||
353 | f_imap->seq); | ||
354 | f_imap->seq++; | ||
355 | CHECK_ERROR_CLOSE (folder, f_imap, status); | ||
356 | f_imap->state = IMAP_AUTH_ANON_REQ_SEND; | ||
357 | } | ||
358 | |||
359 | case IMAP_AUTH_ANON_REQ_SEND: | ||
360 | status = imap_send (f_imap); | ||
361 | CHECK_EAGAIN (f_imap, status); | ||
362 | f_imap->state = IMAP_AUTH_ANON_WAIT_CONT; | ||
363 | |||
364 | case IMAP_AUTH_ANON_WAIT_CONT: | ||
365 | status = imap_parse (f_imap); | ||
366 | CHECK_EAGAIN (f_imap, status); | ||
367 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
368 | if (strncmp ("+", f_imap->buffer, 2) == 0) | ||
369 | { | ||
370 | f_imap->state = IMAP_AUTH_ANON_MSG; | ||
371 | } | ||
372 | else | ||
373 | { | ||
374 | /* Something is wrong! */ | ||
375 | } | ||
376 | f_imap->state = IMAP_AUTH_ANON_MSG; | ||
377 | |||
378 | case IMAP_AUTH_ANON_MSG: | ||
379 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, "\n"); | ||
380 | status = imap_writeline (f_imap, "\r\n"); | ||
381 | CHECK_ERROR_CLOSE (folder, f_imap, status); | ||
382 | f_imap->state = IMAP_AUTH_ANON_MSG_SEND; | ||
383 | |||
384 | case IMAP_AUTH_ANON_MSG_SEND: | ||
385 | status = imap_send (f_imap); | ||
386 | CHECK_EAGAIN (f_imap, status); | ||
387 | |||
388 | f_imap->state = IMAP_AUTH_ANON_WAIT_RESP; | ||
389 | |||
390 | case IMAP_AUTH_ANON_WAIT_RESP: | ||
391 | status = imap_parse (f_imap); | ||
392 | CHECK_EAGAIN (f_imap, status); | ||
393 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
394 | |||
395 | default: | ||
396 | break; /* We're outta here. */ | ||
397 | } | ||
398 | CLEAR_STATE (f_imap); | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | /* Create/Open the stream for IMAP. */ | ||
403 | static int | ||
404 | folder_imap_open (folder_t folder, int flags) | ||
405 | { | ||
406 | f_imap_t f_imap = folder->data; | ||
407 | char *host; | ||
408 | long port = 143; /* default imap port. */ | ||
409 | int status = 0; | ||
410 | size_t len = 0; | ||
411 | |||
412 | /* If we are already open for business, noop. */ | ||
413 | monitor_wrlock (folder->monitor); | ||
414 | if (f_imap->isopen) | ||
415 | { | ||
416 | monitor_unlock (folder->monitor); | ||
417 | return 0; | ||
418 | } | ||
419 | monitor_unlock (folder->monitor); | ||
420 | |||
421 | /* Fetch the server name and the port in the url_t. */ | ||
422 | status = url_get_host (folder->url, NULL, 0, &len); | ||
423 | if (status != 0) | ||
424 | return status; | ||
425 | host = alloca (len + 1); | ||
426 | url_get_host (folder->url, host, len + 1, NULL); | ||
427 | url_get_port (folder->url, &port); | ||
428 | |||
429 | folder->flags = flags; | ||
430 | |||
431 | switch (f_imap->state) | ||
432 | { | ||
433 | case IMAP_NO_STATE: | ||
434 | /* allocate working io buffer. */ | ||
435 | if (f_imap->buffer == NULL) | ||
436 | { | ||
437 | /* There is no particular limit on the length of a command/response | ||
438 | in IMAP. We start with 255, which is quite reasonnable and grow | ||
439 | as we go along. */ | ||
440 | f_imap->buflen = 255; | ||
441 | f_imap->buffer = calloc (f_imap->buflen + 1, 1); | ||
442 | if (f_imap->buffer == NULL) | ||
443 | { | ||
444 | CHECK_ERROR (f_imap, ENOMEM); | ||
445 | } | ||
446 | status = memory_stream_create (&f_imap->string.stream, NULL, MU_STREAM_RDWR); | ||
447 | CHECK_ERROR (f_imap, status); | ||
448 | stream_open (f_imap->string.stream); | ||
449 | } | ||
450 | else | ||
451 | { | ||
452 | /* Clear from any residue. */ | ||
453 | memset (f_imap->buffer, '\0', f_imap->buflen); | ||
454 | stream_truncate (f_imap->string.stream, 0); | ||
455 | f_imap->string.offset = 0; | ||
456 | f_imap->string.nleft = 0; | ||
457 | } | ||
458 | f_imap->ptr = f_imap->buffer; | ||
459 | |||
460 | /* Create the networking stack. */ | ||
461 | if (folder->stream == NULL) | ||
462 | { | ||
463 | status = tcp_stream_create (&folder->stream, host, port, folder->flags); | ||
464 | CHECK_ERROR (f_imap, status); | ||
465 | /* Ask for the stream internal buffering mechanism scheme. */ | ||
466 | stream_setbufsiz (folder->stream, BUFSIZ); | ||
467 | } | ||
468 | else | ||
469 | stream_close (folder->stream); | ||
470 | FOLDER_DEBUG2 (folder, MU_DEBUG_PROT, "imap_open (%s:%d)\n", host, port); | ||
471 | f_imap->state = IMAP_OPEN_CONNECTION; | ||
472 | |||
473 | case IMAP_OPEN_CONNECTION: | ||
474 | /* Establish the connection. */ | ||
475 | status = stream_open (folder->stream); | ||
476 | CHECK_EAGAIN (f_imap, status); | ||
477 | /* Can't recover bailout. */ | ||
478 | CHECK_ERROR_CLOSE (folder, f_imap, status); | ||
479 | f_imap->state = IMAP_GREETINGS; | ||
480 | |||
481 | case IMAP_GREETINGS: | ||
482 | { | ||
483 | /* Swallow the greetings. */ | ||
484 | status = imap_readline (f_imap); | ||
485 | CHECK_EAGAIN (f_imap, status); | ||
486 | f_imap->ptr = f_imap->buffer; | ||
487 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
488 | /* Are they open for business ? The server send an untag response | ||
489 | for greeting. Thenically it can be OK/PREAUTH/BYE. The BYE is | ||
490 | the one that we do not want, server being unfriendly. */ | ||
491 | if (strncasecmp (f_imap->buffer, "* PREAUTH", 9) == 0) | ||
492 | { | ||
493 | f_imap->state = IMAP_AUTH_DONE; | ||
494 | } | ||
495 | else | ||
496 | { | ||
497 | if (strncasecmp (f_imap->buffer, "* OK", 4) != 0) | ||
498 | CHECK_ERROR_CLOSE (folder, f_imap, EACCES); | ||
499 | f_imap->state = IMAP_AUTH; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | case IMAP_AUTH: | ||
504 | case IMAP_LOGIN: | ||
505 | case IMAP_LOGIN_ACK: | ||
506 | assert (folder->authority); | ||
507 | { | ||
508 | status = authority_authenticate (folder->authority); | ||
509 | CHECK_EAGAIN (f_imap, status); | ||
510 | } | ||
511 | |||
512 | case IMAP_AUTH_DONE: | ||
513 | default: | ||
514 | break; | ||
515 | } | ||
516 | f_imap->state = IMAP_NO_STATE; | ||
517 | monitor_wrlock (folder->monitor); | ||
518 | f_imap->isopen++; | ||
519 | monitor_unlock (folder->monitor); | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | |||
524 | /* Shutdown the connection. */ | ||
525 | static int | ||
526 | folder_imap_close (folder_t folder) | ||
527 | { | ||
528 | f_imap_t f_imap = folder->data; | ||
529 | int status = 0; | ||
530 | |||
531 | monitor_wrlock (folder->monitor); | ||
532 | f_imap->isopen--; | ||
533 | if (f_imap->isopen) | ||
534 | { | ||
535 | monitor_unlock (folder->monitor); | ||
536 | return 0; | ||
537 | } | ||
538 | monitor_unlock (folder->monitor); | ||
539 | |||
540 | switch (f_imap->state) | ||
541 | { | ||
542 | case IMAP_NO_STATE: | ||
543 | status = imap_writeline (f_imap, "g%u LOGOUT\r\n", f_imap->seq++); | ||
544 | CHECK_ERROR (f_imap, status); | ||
545 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
546 | f_imap->state = IMAP_LOGOUT; | ||
547 | |||
548 | case IMAP_LOGOUT: | ||
549 | status = imap_send (f_imap); | ||
550 | CHECK_EAGAIN (f_imap, status); | ||
551 | f_imap->state = IMAP_LOGOUT_ACK; | ||
552 | |||
553 | case IMAP_LOGOUT_ACK: | ||
554 | /* Check for "* Bye" from the imap server. */ | ||
555 | status = imap_parse (f_imap); | ||
556 | CHECK_EAGAIN (f_imap, status); | ||
557 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
558 | /* This is done when we received the BYE in the parser code. */ | ||
559 | /* stream_close (folder->stream); */ | ||
560 | /* f_imap->isopen = 0 ; */ | ||
561 | |||
562 | default: | ||
563 | break; | ||
564 | } | ||
565 | f_imap->state = IMAP_NO_STATE; | ||
566 | f_imap->selected = NULL; | ||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | /* Remove a mailbox. */ | ||
571 | static int | ||
572 | folder_imap_delete (folder_t folder, const char *name) | ||
573 | { | ||
574 | f_imap_t f_imap = folder->data; | ||
575 | int status = 0; | ||
576 | |||
577 | if (name == NULL) | ||
578 | return EINVAL; | ||
579 | |||
580 | status = folder_open (folder, folder->flags); | ||
581 | if (status != 0) | ||
582 | return status; | ||
583 | |||
584 | switch (f_imap->state) | ||
585 | { | ||
586 | case IMAP_NO_STATE: | ||
587 | status = imap_writeline (f_imap, "g%u DELETE %s\r\n", f_imap->seq++, | ||
588 | name); | ||
589 | CHECK_ERROR (f_imap, status); | ||
590 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
591 | f_imap->state = IMAP_DELETE; | ||
592 | |||
593 | case IMAP_DELETE: | ||
594 | status = imap_send (f_imap); | ||
595 | CHECK_EAGAIN (f_imap, status); | ||
596 | f_imap->state = IMAP_DELETE_ACK; | ||
597 | |||
598 | case IMAP_DELETE_ACK: | ||
599 | status = imap_parse (f_imap); | ||
600 | CHECK_EAGAIN (f_imap, status); | ||
601 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
602 | |||
603 | default: | ||
604 | break; | ||
605 | } | ||
606 | f_imap->state = IMAP_NO_STATE; | ||
607 | return status; | ||
608 | } | ||
609 | |||
610 | /* Since mailutils API does not offer recursive listing. There is no need | ||
611 | to follow IMAP "bizarre" recursive rules. The use of '%' is sufficient. So | ||
612 | the approach is everywhere there is a regex in the path we change that | ||
613 | branch for '%' and do the matching ourself with fnmatch(). */ | ||
614 | static int | ||
615 | folder_imap_list (folder_t folder, const char *ref, const char *name, | ||
616 | struct folder_list *pflist) | ||
617 | { | ||
618 | f_imap_t f_imap = folder->data; | ||
619 | int status = 0; | ||
620 | char *path = NULL; | ||
621 | |||
622 | /* NOOP. */ | ||
623 | if (pflist == NULL) | ||
624 | return EINVAL; | ||
625 | |||
626 | status = folder_open (folder, folder->flags); | ||
627 | if (status != 0) | ||
628 | return status; | ||
629 | |||
630 | if (ref == NULL) | ||
631 | ref = ""; | ||
632 | if (name == NULL) | ||
633 | name = ""; | ||
634 | |||
635 | path = strdup (""); | ||
636 | if (path == NULL) | ||
637 | return ENOMEM; | ||
638 | |||
639 | /* We break the string to pieces and change the occurences of "*?[" for | ||
640 | the imap magic "%" for expansion. Then reassemble the string: | ||
641 | "/home/?/Mail/a*lain*" --> "/usr/%/Mail/%". */ | ||
642 | { | ||
643 | int done = 0; | ||
644 | size_t i; | ||
645 | char **node = NULL; | ||
646 | size_t nodelen = 0; | ||
647 | const char *p = name; | ||
648 | /* Disassemble. */ | ||
649 | while (!done && *p) | ||
650 | { | ||
651 | char **n; | ||
652 | n = realloc (node, (nodelen + 1) * sizeof (*node)); | ||
653 | if (n == NULL) | ||
654 | break; | ||
655 | node = n; | ||
656 | if (*p == '/') | ||
657 | { | ||
658 | node[nodelen] = strdup ("/"); | ||
659 | p++; | ||
660 | } | ||
661 | else | ||
662 | { | ||
663 | const char *s = strchr (p, '/'); | ||
664 | if (s) | ||
665 | { | ||
666 | node[nodelen] = calloc (s - p + 1, 1); | ||
667 | if (node[nodelen]) | ||
668 | memcpy (node[nodelen], p, s - p); | ||
669 | p = s; | ||
670 | } | ||
671 | else | ||
672 | { | ||
673 | node[nodelen] = strdup (p); | ||
674 | done = 1; | ||
675 | } | ||
676 | if (node[nodelen] && strpbrk (node[nodelen], "*?[")) | ||
677 | { | ||
678 | free (node[nodelen]); | ||
679 | node[nodelen] = strdup ("%"); | ||
680 | } | ||
681 | } | ||
682 | nodelen++; | ||
683 | if (done) | ||
684 | break; | ||
685 | } | ||
686 | /* Reassemble. */ | ||
687 | for (i = 0; i < nodelen; i++) | ||
688 | { | ||
689 | if (node[i]) | ||
690 | { | ||
691 | char *pth; | ||
692 | pth = realloc (path, strlen (path) + strlen (node[i]) + 1); | ||
693 | if (pth) | ||
694 | { | ||
695 | path = pth; | ||
696 | strcat (path, node[i]); | ||
697 | } | ||
698 | free (node[i]); | ||
699 | } | ||
700 | } | ||
701 | if (node) | ||
702 | free (node); | ||
703 | } | ||
704 | |||
705 | switch (f_imap->state) | ||
706 | { | ||
707 | case IMAP_NO_STATE: | ||
708 | status = imap_writeline (f_imap, "g%u LIST \"%s\" \"%s\"\r\n", | ||
709 | f_imap->seq++, ref, path); | ||
710 | free (path); | ||
711 | CHECK_ERROR (f_imap, status); | ||
712 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
713 | f_imap->state = IMAP_LIST; | ||
714 | |||
715 | case IMAP_LIST: | ||
716 | status = imap_send (f_imap); | ||
717 | CHECK_EAGAIN (f_imap, status); | ||
718 | f_imap->state = IMAP_LIST_ACK; | ||
719 | |||
720 | case IMAP_LIST_ACK: | ||
721 | status = imap_parse (f_imap); | ||
722 | CHECK_EAGAIN (f_imap, status); | ||
723 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
724 | |||
725 | default: | ||
726 | break; | ||
727 | } | ||
728 | |||
729 | /* Build the folder list. */ | ||
730 | if (f_imap->flist.num > 0) | ||
731 | { | ||
732 | struct list_response **plist = NULL; | ||
733 | size_t num = f_imap->flist.num; | ||
734 | size_t j = 0; | ||
735 | plist = calloc (num, sizeof (*plist)); | ||
736 | if (plist) | ||
737 | { | ||
738 | size_t i; | ||
739 | for (i = 0; i < num; i++) | ||
740 | { | ||
741 | struct list_response *lr = f_imap->flist.element[i]; | ||
742 | if (imap_mailbox_name_match (name, lr->name) == 0) | ||
743 | { | ||
744 | /* | ||
745 | FOLDER_DEBUG2(folder, MU_DEBUG_TRACE, | ||
746 | "fnmatch against %s: %s - match!\n", name, lr->name); | ||
747 | */ | ||
748 | plist[i] = calloc (1, sizeof (**plist)); | ||
749 | if (plist[i] == NULL | ||
750 | || (plist[i]->name = strdup (lr->name)) == NULL) | ||
751 | { | ||
752 | break; | ||
753 | } | ||
754 | plist[i]->type = lr->type; | ||
755 | plist[i]->separator = lr->separator; | ||
756 | j++; | ||
757 | } | ||
758 | /* | ||
759 | else | ||
760 | FOLDER_DEBUG2(folder, MU_DEBUG_TRACE, | ||
761 | "fnmatch against %s: %s - no match!\n", name, lr->name); | ||
762 | */ | ||
763 | } | ||
764 | } | ||
765 | pflist->element = plist; | ||
766 | pflist->num = j; | ||
767 | } | ||
768 | folder_list_destroy (&(f_imap->flist)); | ||
769 | f_imap->state = IMAP_NO_STATE; | ||
770 | return status; | ||
771 | } | ||
772 | |||
773 | static int | ||
774 | folder_imap_lsub (folder_t folder, const char *ref, const char *name, | ||
775 | struct folder_list *pflist) | ||
776 | { | ||
777 | f_imap_t f_imap = folder->data; | ||
778 | int status = 0; | ||
779 | |||
780 | /* NOOP. */ | ||
781 | if (pflist == NULL) | ||
782 | return EINVAL; | ||
783 | |||
784 | status = folder_open (folder, folder->flags); | ||
785 | if (status != 0) | ||
786 | return status; | ||
787 | |||
788 | if (ref == NULL) ref = ""; | ||
789 | if (name == NULL) name = ""; | ||
790 | |||
791 | switch (f_imap->state) | ||
792 | { | ||
793 | case IMAP_NO_STATE: | ||
794 | status = imap_writeline (f_imap, "g%u LSUB \"%s\" \"%s\"\r\n", | ||
795 | f_imap->seq++, ref, name); | ||
796 | CHECK_ERROR (f_imap, status); | ||
797 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
798 | f_imap->state = IMAP_LSUB; | ||
799 | |||
800 | case IMAP_LSUB: | ||
801 | status = imap_send (f_imap); | ||
802 | CHECK_EAGAIN (f_imap, status); | ||
803 | f_imap->state = IMAP_LSUB_ACK; | ||
804 | |||
805 | case IMAP_LSUB_ACK: | ||
806 | status = imap_parse (f_imap); | ||
807 | CHECK_EAGAIN (f_imap, status); | ||
808 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
809 | |||
810 | default: | ||
811 | break; | ||
812 | } | ||
813 | |||
814 | /* Build the folder list. */ | ||
815 | if (f_imap->flist.num > 0) | ||
816 | { | ||
817 | struct list_response **plist = NULL; | ||
818 | size_t num = f_imap->flist.num; | ||
819 | size_t j = 0; | ||
820 | plist = calloc (num, sizeof (*plist)); | ||
821 | if (plist) | ||
822 | { | ||
823 | size_t i; | ||
824 | for (i = 0; i < num; i++) | ||
825 | { | ||
826 | struct list_response *lr = f_imap->flist.element[i]; | ||
827 | /* printf ("%s --> %s\n", lr->name, name); */ | ||
828 | plist[i] = calloc (1, sizeof (**plist)); | ||
829 | if (plist[i] == NULL | ||
830 | || (plist[i]->name = strdup (lr->name)) == NULL) | ||
831 | { | ||
832 | break; | ||
833 | } | ||
834 | plist[i]->type = lr->type; | ||
835 | plist[i]->separator = lr->separator; | ||
836 | j++; | ||
837 | } | ||
838 | } | ||
839 | pflist->element = plist; | ||
840 | pflist->num = j; | ||
841 | folder_list_destroy (&(f_imap->flist)); | ||
842 | } | ||
843 | f_imap->state = IMAP_NO_STATE; | ||
844 | f_imap->state = IMAP_NO_STATE; | ||
845 | return 0; | ||
846 | } | ||
847 | |||
848 | static int | ||
849 | folder_imap_rename (folder_t folder, const char *oldpath, const char *newpath) | ||
850 | { | ||
851 | f_imap_t f_imap = folder->data; | ||
852 | int status = 0; | ||
853 | if (oldpath == NULL || newpath == NULL) | ||
854 | return EINVAL; | ||
855 | |||
856 | status = folder_open (folder, folder->flags); | ||
857 | if (status != 0) | ||
858 | return status; | ||
859 | |||
860 | switch (f_imap->state) | ||
861 | { | ||
862 | case IMAP_NO_STATE: | ||
863 | status = imap_writeline (f_imap, "g%u RENAME %s %s\r\n", | ||
864 | f_imap->seq++, oldpath, newpath); | ||
865 | CHECK_ERROR (f_imap, status); | ||
866 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
867 | f_imap->state = IMAP_RENAME; | ||
868 | |||
869 | case IMAP_RENAME: | ||
870 | status = imap_send (f_imap); | ||
871 | CHECK_EAGAIN (f_imap, status); | ||
872 | f_imap->state = IMAP_RENAME_ACK; | ||
873 | |||
874 | case IMAP_RENAME_ACK: | ||
875 | status = imap_parse (f_imap); | ||
876 | CHECK_EAGAIN (f_imap, status); | ||
877 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
878 | |||
879 | default: | ||
880 | break; | ||
881 | } | ||
882 | f_imap->state = IMAP_NO_STATE; | ||
883 | return status; | ||
884 | } | ||
885 | |||
886 | static int | ||
887 | folder_imap_subscribe (folder_t folder, const char *name) | ||
888 | { | ||
889 | f_imap_t f_imap = folder->data; | ||
890 | int status = 0; | ||
891 | |||
892 | status = folder_open (folder, folder->flags); | ||
893 | if (status != 0) | ||
894 | return status; | ||
895 | |||
896 | if (name == NULL) | ||
897 | return EINVAL; | ||
898 | switch (f_imap->state) | ||
899 | { | ||
900 | case IMAP_NO_STATE: | ||
901 | status = imap_writeline (f_imap, "g%u SUBSCRIBE %s\r\n", | ||
902 | f_imap->seq++, name); | ||
903 | CHECK_ERROR (f_imap, status); | ||
904 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
905 | f_imap->state = IMAP_SUBSCRIBE; | ||
906 | |||
907 | case IMAP_SUBSCRIBE: | ||
908 | status = imap_send (f_imap); | ||
909 | CHECK_EAGAIN (f_imap, status); | ||
910 | f_imap->state = IMAP_SUBSCRIBE_ACK; | ||
911 | |||
912 | case IMAP_SUBSCRIBE_ACK: | ||
913 | status = imap_parse (f_imap); | ||
914 | CHECK_EAGAIN (f_imap, status); | ||
915 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
916 | |||
917 | default: | ||
918 | break; | ||
919 | } | ||
920 | f_imap->state = IMAP_NO_STATE; | ||
921 | return status; | ||
922 | } | ||
923 | |||
924 | static int | ||
925 | folder_imap_unsubscribe (folder_t folder, const char *name) | ||
926 | { | ||
927 | f_imap_t f_imap = folder->data; | ||
928 | int status = 0; | ||
929 | |||
930 | status = folder_open (folder, folder->flags); | ||
931 | if (status != 0) | ||
932 | return status; | ||
933 | |||
934 | if (name == NULL) | ||
935 | return EINVAL; | ||
936 | switch (f_imap->state) | ||
937 | { | ||
938 | case IMAP_NO_STATE: | ||
939 | status = imap_writeline (f_imap, "g%u UNSUBSCRIBE %s\r\n", | ||
940 | f_imap->seq++, name); | ||
941 | CHECK_ERROR (f_imap, status); | ||
942 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
943 | f_imap->state = IMAP_UNSUBSCRIBE; | ||
944 | |||
945 | case IMAP_UNSUBSCRIBE: | ||
946 | status = imap_send (f_imap); | ||
947 | CHECK_EAGAIN (f_imap, status); | ||
948 | f_imap->state = IMAP_UNSUBSCRIBE_ACK; | ||
949 | |||
950 | case IMAP_UNSUBSCRIBE_ACK: | ||
951 | status = imap_parse (f_imap); | ||
952 | CHECK_EAGAIN (f_imap, status); | ||
953 | FOLDER_DEBUG0 (folder, MU_DEBUG_PROT, f_imap->buffer); | ||
954 | |||
955 | default: | ||
956 | break; | ||
957 | } | ||
958 | f_imap->state = IMAP_NO_STATE; | ||
959 | return status; | ||
960 | } | ||
961 | |||
962 | /* A literal is a sequence of zero or more octets (including CR and LF), | ||
963 | prefix-quoted with an octet count in the form of an open brace ("{"), | ||
964 | the number of octets, close brace ("}"), and CRLF. The sequence is read | ||
965 | and put in the string buffer. */ | ||
966 | static int | ||
967 | imap_literal_string (f_imap_t f_imap, char **ptr) | ||
968 | { | ||
969 | size_t len, len0, total; | ||
970 | int status = 0; | ||
971 | int nl; | ||
972 | /* The (len + 1) in the for is to count the strip '\r' by imap_readline. */ | ||
973 | for (len0 = len = total = 0; total < f_imap->string.nleft; total += (len + 1)) | ||
974 | { | ||
975 | status = imap_readline (f_imap); | ||
976 | if (DEBUG_SHOW_DATA) | ||
977 | fprintf (stderr, "%s", f_imap->buffer); | ||
978 | if (status != 0) | ||
979 | { | ||
980 | /* Return what we got so far. */ | ||
981 | break; | ||
982 | } | ||
983 | f_imap->ptr = f_imap->buffer; | ||
984 | |||
985 | /* How much ? */ | ||
986 | len0 = len = f_imap->nl - f_imap->buffer; | ||
987 | /* Check if the last read did not finish on a line, if yes do not copy in | ||
988 | string buffer the terminating sequence ")\r\n". We are doing this | ||
989 | by checking if the amount(total) we got so far + the len of the line | ||
990 | +1 (taking to account the strip '\r') goes behond the request. */ | ||
991 | if ((total + len + 1) > f_imap->string.nleft) | ||
992 | { | ||
993 | len0 = len = f_imap->string.nleft - total; | ||
994 | /* ALERT: if we ask for a substring, for example we have : | ||
995 | "123456\n", and ask for body[]<0.7> the server will send | ||
996 | body[] {7} --> "123456\r". There was not enough space | ||
997 | to fit the nl .. annoying. Take care of this here. */ | ||
998 | if (f_imap->buffer[len - 1] == '\r') | ||
999 | len0--; | ||
1000 | } | ||
1001 | |||
1002 | stream_write (f_imap->string.stream, f_imap->buffer, | ||
1003 | len0, f_imap->string.offset, NULL); | ||
1004 | f_imap->string.offset += len0; | ||
1005 | |||
1006 | /* Depending on the type of request we incremente the xxxx_lines | ||
1007 | and xxxx_sizes. */ | ||
1008 | nl = (memchr (f_imap->buffer, '\n', len0)) ? 1 : 0; | ||
1009 | if (f_imap->string.msg_imap) | ||
1010 | { | ||
1011 | switch (f_imap->string.type) | ||
1012 | { | ||
1013 | case IMAP_HEADER: | ||
1014 | f_imap->string.msg_imap->header_lines += nl; | ||
1015 | f_imap->string.msg_imap->header_size += len0; | ||
1016 | break; | ||
1017 | |||
1018 | case IMAP_BODY: | ||
1019 | f_imap->string.msg_imap->body_lines += nl; | ||
1020 | f_imap->string.msg_imap->body_size += len0; | ||
1021 | break; | ||
1022 | |||
1023 | case IMAP_MESSAGE: | ||
1024 | f_imap->string.msg_imap->message_lines += nl; | ||
1025 | /* The message size is known by sending RFC822.SIZE. */ | ||
1026 | |||
1027 | default: | ||
1028 | break; | ||
1029 | } | ||
1030 | } | ||
1031 | } | ||
1032 | f_imap->string.nleft -= total; | ||
1033 | /* We may have trailing junk like the closing ")\r\n" from a literal string | ||
1034 | glob it by moving the command buffer, or doing a full readline. */ | ||
1035 | if (len == (size_t)(f_imap->nl - f_imap->buffer)) | ||
1036 | { | ||
1037 | len = 0; | ||
1038 | status = imap_readline (f_imap); | ||
1039 | } | ||
1040 | *ptr = f_imap->buffer + len; | ||
1041 | return status; | ||
1042 | } | ||
1043 | |||
1044 | /* A quoted string is a sequence of zero or more 7-bit characters, | ||
1045 | excluding CR and LF, with double quote (<">) characters at each end. | ||
1046 | Same thing as the literal, diferent format the result is put in the | ||
1047 | string buffer for the mailbox/callee. */ | ||
1048 | static int | ||
1049 | imap_quoted_string (f_imap_t f_imap, char **ptr) | ||
1050 | { | ||
1051 | char *bquote; | ||
1052 | int escaped = 0; | ||
1053 | int len; | ||
1054 | |||
1055 | (*ptr)++; | ||
1056 | bquote = *ptr; | ||
1057 | while (**ptr && (**ptr != '"' || escaped)) | ||
1058 | { | ||
1059 | escaped = (**ptr == '\\') ? 1 : 0; | ||
1060 | (*ptr)++; | ||
1061 | } | ||
1062 | |||
1063 | len = *ptr - bquote; | ||
1064 | stream_write (f_imap->string.stream, bquote, len, | ||
1065 | f_imap->string.offset, NULL); | ||
1066 | f_imap->string.offset += len; | ||
1067 | if (**ptr == '"') | ||
1068 | (*ptr)++; | ||
1069 | if (DEBUG_SHOW_DATA) | ||
1070 | fprintf (stderr, "%.*s", len, bquote); | ||
1071 | return 0; | ||
1072 | } | ||
1073 | |||
1074 | /* Find which type of string the response is: literal or quoted and let the | ||
1075 | function fill the string buffer. */ | ||
1076 | static int | ||
1077 | imap_string (f_imap_t f_imap, char **ptr) | ||
1078 | { | ||
1079 | int status = 0; | ||
1080 | |||
1081 | /* Skip whites. */ | ||
1082 | while (**ptr == ' ') | ||
1083 | (*ptr)++; | ||
1084 | switch (**ptr) | ||
1085 | { | ||
1086 | case '{': | ||
1087 | f_imap->string.nleft = strtol ((*ptr) + 1, ptr, 10); | ||
1088 | if (**ptr == '}') | ||
1089 | { | ||
1090 | (*ptr)++; | ||
1091 | /* Reset the buffer to the beginning. */ | ||
1092 | f_imap->ptr = f_imap->buffer; | ||
1093 | status = imap_literal_string (f_imap, ptr); | ||
1094 | } | ||
1095 | break; | ||
1096 | case '"': | ||
1097 | status = imap_quoted_string (f_imap, ptr); | ||
1098 | break; | ||
1099 | /* NIL */ | ||
1100 | case 'N': | ||
1101 | case 'n': | ||
1102 | (*ptr)++; /* N|n */ | ||
1103 | (*ptr)++; /* I|i */ | ||
1104 | (*ptr)++; /* L|l */ | ||
1105 | break; | ||
1106 | default: | ||
1107 | /* Problem. */ | ||
1108 | status = 1; | ||
1109 | break; | ||
1110 | } | ||
1111 | return status; | ||
1112 | } | ||
1113 | |||
1114 | /* FIXME: does not work for nonblocking. */ | ||
1115 | static int | ||
1116 | imap_list (f_imap_t f_imap) | ||
1117 | { | ||
1118 | char *tok; | ||
1119 | char *sp = NULL; | ||
1120 | size_t len = f_imap->nl - f_imap->buffer - 1; | ||
1121 | char *buffer; | ||
1122 | struct list_response **plr; | ||
1123 | struct list_response *lr; | ||
1124 | int status = 0; | ||
1125 | |||
1126 | buffer = alloca (len); | ||
1127 | memcpy (buffer, f_imap->buffer, len); | ||
1128 | buffer[len] = '\0'; | ||
1129 | plr = realloc (f_imap->flist.element, | ||
1130 | (f_imap->flist.num + 1) * sizeof (*plr)); | ||
1131 | if (plr == NULL) | ||
1132 | return ENOMEM; | ||
1133 | f_imap->flist.element = plr; | ||
1134 | lr = plr[f_imap->flist.num] = calloc (1, sizeof (*lr)); | ||
1135 | if (lr == NULL) | ||
1136 | return ENOMEM; | ||
1137 | (f_imap->flist.num)++; | ||
1138 | |||
1139 | /* Glob untag. */ | ||
1140 | tok = strtok_r (buffer, " ", &sp); | ||
1141 | /* Glob LIST. */ | ||
1142 | tok = strtok_r (NULL, " ", &sp); | ||
1143 | /* Get the attibutes. */ | ||
1144 | tok = strtok_r (NULL, ")", &sp); | ||
1145 | if (tok) | ||
1146 | { | ||
1147 | char *s = NULL; | ||
1148 | char *p = tok; | ||
1149 | while ((tok = strtok_r (p, " ()", &s)) != NULL) | ||
1150 | { | ||
1151 | if (strcasecmp (tok, "\\Noselect") == 0) | ||
1152 | { | ||
1153 | lr->type |= MU_FOLDER_ATTRIBUTE_DIRECTORY; | ||
1154 | } | ||
1155 | else if (strcasecmp (tok, "\\Marked") == 0) | ||
1156 | { | ||
1157 | } | ||
1158 | else if (strcasecmp (tok, "\\Unmarked") == 0) | ||
1159 | { | ||
1160 | } | ||
1161 | else if (strcasecmp (tok, "\\Noinferiors") == 0) | ||
1162 | { | ||
1163 | lr->type |= MU_FOLDER_ATTRIBUTE_FILE; | ||
1164 | } | ||
1165 | else | ||
1166 | { | ||
1167 | lr->type |= MU_FOLDER_ATTRIBUTE_DIRECTORY; | ||
1168 | } | ||
1169 | p = NULL; | ||
1170 | } | ||
1171 | } | ||
1172 | /* Hiearchy delimeter. */ | ||
1173 | tok = strtok_r (NULL, " ", &sp); | ||
1174 | if (tok && strlen (tok) > 2 && strcasecmp (tok, "NIL")) | ||
1175 | lr->separator = tok[1]; | ||
1176 | /* The path. */ | ||
1177 | tok = strtok_r (NULL, " ", &sp); | ||
1178 | if (tok) | ||
1179 | { | ||
1180 | char *s = strchr (tok, '{'); | ||
1181 | if (s) | ||
1182 | { | ||
1183 | size_t n = strtoul (s + 1, NULL, 10); | ||
1184 | lr->name = calloc (n + 1, 1); | ||
1185 | if (!lr->name) | ||
1186 | status = ENOMEM; | ||
1187 | else | ||
1188 | { | ||
1189 | f_imap->ptr = f_imap->buffer; | ||
1190 | imap_readline (f_imap); | ||
1191 | memcpy (lr->name, f_imap->buffer, n); | ||
1192 | } | ||
1193 | } | ||
1194 | else if ((status = imap_string (f_imap, &tok)) == 0) | ||
1195 | { | ||
1196 | off_t sz = 0; | ||
1197 | |||
1198 | stream_size (f_imap->string.stream, &sz); | ||
1199 | lr->name = calloc (sz + 1, 1); | ||
1200 | if (!lr->name) | ||
1201 | status = ENOMEM; | ||
1202 | else | ||
1203 | stream_read (f_imap->string.stream, lr->name, sz, 0, NULL); | ||
1204 | stream_truncate (f_imap->string.stream, 0); | ||
1205 | f_imap->string.offset = 0; | ||
1206 | f_imap->string.nleft = 0; | ||
1207 | } | ||
1208 | else | ||
1209 | { | ||
1210 | lr->name = strdup (tok); | ||
1211 | if (!lr->name) | ||
1212 | status = ENOMEM; | ||
1213 | } | ||
1214 | } | ||
1215 | return status; | ||
1216 | } | ||
1217 | /* Helping function to figure out the section name of the message: for example | ||
1218 | a 2 part message with the first part being sub in two will be: | ||
1219 | {1}, {1,1} {1,2} The first subpart of the message and its sub parts | ||
1220 | {2} The second subpar of the message. */ | ||
1221 | char * | ||
1222 | section_name (msg_imap_t msg_imap) | ||
1223 | { | ||
1224 | size_t sectionlen = 0; | ||
1225 | char *section = strdup (""); | ||
1226 | |||
1227 | /* Build the section name, but it is in reverse. */ | ||
1228 | for (; msg_imap; msg_imap = msg_imap->parent) | ||
1229 | { | ||
1230 | if (msg_imap->part != 0) | ||
1231 | { | ||
1232 | char *tmp; | ||
1233 | char part[64]; | ||
1234 | size_t partlen; | ||
1235 | snprintf (part, sizeof part, "%lu", (unsigned long) msg_imap->part); | ||
1236 | partlen = strlen (part); | ||
1237 | tmp = realloc (section, sectionlen + partlen + 2); | ||
1238 | if (tmp == NULL) | ||
1239 | break; | ||
1240 | section = tmp; | ||
1241 | memset (section + sectionlen, '\0', partlen + 2); | ||
1242 | if (sectionlen != 0) | ||
1243 | strcat (section, "."); | ||
1244 | strcat (section, part); | ||
1245 | sectionlen = strlen (section); | ||
1246 | } | ||
1247 | } | ||
1248 | |||
1249 | /* Reverse the string. */ | ||
1250 | if (section) | ||
1251 | { | ||
1252 | char *begin, *last; | ||
1253 | char c; | ||
1254 | for (begin = section, last = section + sectionlen - 1; begin < last; | ||
1255 | begin++, last--) | ||
1256 | { | ||
1257 | c = *begin; | ||
1258 | *begin = *last; | ||
1259 | *last = c; | ||
1260 | } | ||
1261 | } | ||
1262 | return section; | ||
1263 | } | ||
1264 | |||
1265 | /* We do not pay particular attention to the content of the bodystructure | ||
1266 | but rather to the paremetized list layout to discover how many messages | ||
1267 | the originial message is composed of. The information is later retrieve | ||
1268 | when needed via the body[header.fields] command. Note that this function | ||
1269 | is recursive. */ | ||
1270 | static int | ||
1271 | imap_bodystructure0 (msg_imap_t msg_imap, char **ptr) | ||
1272 | { | ||
1273 | int paren = 0; | ||
1274 | int no_arg = 0; | ||
1275 | int status = 0; | ||
1276 | int have_size = 0; | ||
1277 | |||
1278 | /* Skip space. */ | ||
1279 | while (**ptr == ' ') | ||
1280 | (*ptr)++; | ||
1281 | /* Pass lparen. */ | ||
1282 | if (**ptr == '(') | ||
1283 | { | ||
1284 | ++(*ptr); | ||
1285 | paren++; | ||
1286 | no_arg++; | ||
1287 | } | ||
1288 | /* NOTE : this loop has side effects in strtol() and imap_string(), the | ||
1289 | order of the if's are important. */ | ||
1290 | while (**ptr) | ||
1291 | { | ||
1292 | /* Skip the string argument. */ | ||
1293 | if (**ptr != '(' && **ptr != ')') | ||
1294 | { | ||
1295 | char *start = *ptr; | ||
1296 | /* FIXME: set the command callback if EAGAIN to resume. */ | ||
1297 | status = imap_string (msg_imap->m_imap->f_imap, ptr); | ||
1298 | if (status != 0) | ||
1299 | return status; | ||
1300 | if (start != *ptr) | ||
1301 | no_arg = 0; | ||
1302 | } | ||
1303 | |||
1304 | if (isdigit ((unsigned)**ptr)) | ||
1305 | { | ||
1306 | char *start = *ptr; | ||
1307 | size_t size = strtoul (*ptr, ptr, 10); | ||
1308 | if (start != *ptr) | ||
1309 | { | ||
1310 | if (!have_size && msg_imap && msg_imap->parent) | ||
1311 | msg_imap->message_size = size; | ||
1312 | have_size = 1; | ||
1313 | no_arg = 0; | ||
1314 | } | ||
1315 | } | ||
1316 | |||
1317 | if (**ptr == '(') | ||
1318 | { | ||
1319 | if (no_arg) | ||
1320 | { | ||
1321 | msg_imap_t new_part; | ||
1322 | msg_imap_t *tmp; | ||
1323 | tmp = realloc (msg_imap->parts, | ||
1324 | ((msg_imap->num_parts + 1) * sizeof (*tmp))); | ||
1325 | if (tmp) | ||
1326 | { | ||
1327 | new_part = calloc (1, sizeof (*new_part)); | ||
1328 | if (new_part) | ||
1329 | { | ||
1330 | msg_imap->parts = tmp; | ||
1331 | msg_imap->parts[msg_imap->num_parts] = new_part; | ||
1332 | new_part->part = ++(msg_imap->num_parts); | ||
1333 | new_part->parent = msg_imap; | ||
1334 | new_part->num = msg_imap->num; | ||
1335 | new_part->m_imap = msg_imap->m_imap; | ||
1336 | new_part->flags = msg_imap->flags; | ||
1337 | status = imap_bodystructure0 (new_part, ptr); | ||
1338 | /* Jump up, the rparen been swallen already. */ | ||
1339 | continue; | ||
1340 | } | ||
1341 | else | ||
1342 | { | ||
1343 | status = ENOMEM; | ||
1344 | free (tmp); | ||
1345 | break; | ||
1346 | } | ||
1347 | } | ||
1348 | else | ||
1349 | { | ||
1350 | status = ENOMEM; | ||
1351 | break; | ||
1352 | } | ||
1353 | } | ||
1354 | paren++; | ||
1355 | } | ||
1356 | |||
1357 | if (**ptr == ')') | ||
1358 | { | ||
1359 | no_arg = 1; | ||
1360 | paren--; | ||
1361 | /* Did we reach the same number of close paren ? */ | ||
1362 | if (paren <= 0) | ||
1363 | { | ||
1364 | /* Swallow the rparen. */ | ||
1365 | (*ptr)++; | ||
1366 | break; | ||
1367 | } | ||
1368 | } | ||
1369 | |||
1370 | if (**ptr == '\0') | ||
1371 | break; | ||
1372 | |||
1373 | (*ptr)++; | ||
1374 | } | ||
1375 | return status; | ||
1376 | } | ||
1377 | |||
1378 | static int | ||
1379 | imap_bodystructure (f_imap_t f_imap, char **ptr) | ||
1380 | { | ||
1381 | return imap_bodystructure0 (f_imap->string.msg_imap, ptr); | ||
1382 | } | ||
1383 | |||
1384 | /* The Format for a FLAG response is : | ||
1385 | mailbox_data ::= "FLAGS" SPACE flag_list | ||
1386 | flag_list ::= "(" #flag ")" | ||
1387 | flag ::= "\Answered" / "\Flagged" / "\Deleted" / | ||
1388 | "\Seen" / "\Draft" / flag_keyword / flag_extension | ||
1389 | flag_extension ::= "\" atom | ||
1390 | ;; Future expansion. Client implementations | ||
1391 | ;; MUST accept flag_extension flags. Server | ||
1392 | ;; implementations MUST NOT generate | ||
1393 | ;; flag_extension flags except as defined by | ||
1394 | ;; future standard or standards-track | ||
1395 | ;; revisions of this specification. | ||
1396 | flag_keyword ::= atom | ||
1397 | |||
1398 | S: * 14 FETCH (FLAGS (\Seen \Deleted)) | ||
1399 | S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) | ||
1400 | |||
1401 | We assume that the '*' or the FLAGS keyword are strip. | ||
1402 | |||
1403 | FIXME: User flags are not take to account. */ | ||
1404 | static int | ||
1405 | imap_fetch_flags (f_imap_t f_imap, char **ptr) | ||
1406 | { | ||
1407 | msg_imap_t msg_imap = f_imap->string.msg_imap; | ||
1408 | if (msg_imap) | ||
1409 | imap_flags (ptr, &msg_imap->flags); | ||
1410 | return 0; | ||
1411 | } | ||
1412 | |||
1413 | static int | ||
1414 | imap_permanentflags (f_imap_t f_imap, char **ptr) | ||
1415 | { | ||
1416 | imap_flags (ptr, &f_imap->flags); | ||
1417 | return 0; | ||
1418 | } | ||
1419 | |||
1420 | static int | ||
1421 | imap_flags (char **ptr, int *pflags) | ||
1422 | { | ||
1423 | char *start; | ||
1424 | char *end; | ||
1425 | int flags = 0; | ||
1426 | |||
1427 | /* Skip space. */ | ||
1428 | while (**ptr == ' ') | ||
1429 | (*ptr)++; | ||
1430 | |||
1431 | /* Skip LPAREN. */ | ||
1432 | if (**ptr == '(') | ||
1433 | (*ptr)++; | ||
1434 | |||
1435 | /* Go through the list and break on ')' */ | ||
1436 | do | ||
1437 | { | ||
1438 | /* Skip space before next word. */ | ||
1439 | while (**ptr == ' ') | ||
1440 | (*ptr)++; | ||
1441 | |||
1442 | /* Save the beginning of the word. */ | ||
1443 | start = *ptr; | ||
1444 | /* Get the next word boundary. */ | ||
1445 | while (**ptr && **ptr != ' ' && **ptr != ')') | ||
1446 | ++(*ptr); | ||
1447 | |||
1448 | /* Save the end for the strcasecmp. */ | ||
1449 | end = *ptr; | ||
1450 | |||
1451 | /* Bail out. */ | ||
1452 | if (*start == '\0') | ||
1453 | break; | ||
1454 | |||
1455 | /* Guess the flag. */ | ||
1456 | if (end == start) | ||
1457 | flags |= MU_ATTRIBUTE_SEEN; | ||
1458 | else | ||
1459 | { | ||
1460 | if (strncasecmp (start, "\\Seen", end - start) == 0) | ||
1461 | { | ||
1462 | flags |= MU_ATTRIBUTE_READ; | ||
1463 | } | ||
1464 | else if (strncasecmp (start, "\\Answered", end - start) == 0) | ||
1465 | { | ||
1466 | flags |= MU_ATTRIBUTE_ANSWERED; | ||
1467 | } | ||
1468 | else if (strncasecmp (start, "\\Flagged", end - start) == 0) | ||
1469 | { | ||
1470 | flags |= MU_ATTRIBUTE_FLAGGED; | ||
1471 | } | ||
1472 | else if (strncasecmp (start, "\\Deleted", end - start) == 0) | ||
1473 | { | ||
1474 | flags |= MU_ATTRIBUTE_DELETED; | ||
1475 | } | ||
1476 | else if (strncasecmp (start, "\\Draft", end - start) == 0) | ||
1477 | { | ||
1478 | flags |= MU_ATTRIBUTE_DRAFT; | ||
1479 | } | ||
1480 | else if (strncasecmp (start, "\\Recent", end - start)) | ||
1481 | flags |= MU_ATTRIBUTE_SEEN; | ||
1482 | } | ||
1483 | } | ||
1484 | while (**ptr && **ptr != ')'); /* do {} */ | ||
1485 | |||
1486 | /* Skip the last rparen. */ | ||
1487 | if (**ptr == ')') | ||
1488 | (*ptr)++; | ||
1489 | |||
1490 | if (pflags) | ||
1491 | *pflags = flags; | ||
1492 | return 0; | ||
1493 | } | ||
1494 | |||
1495 | static int | ||
1496 | imap_body (f_imap_t f_imap, char **ptr) | ||
1497 | { | ||
1498 | int status; | ||
1499 | |||
1500 | /* Skip leading spaces. */ | ||
1501 | while (**ptr && **ptr == ' ') | ||
1502 | (*ptr)++; | ||
1503 | |||
1504 | if (**ptr == '[') | ||
1505 | { | ||
1506 | char *sep = strchr (*ptr, ']'); | ||
1507 | (*ptr)++; /* Move pass the '[' */ | ||
1508 | if (sep) | ||
1509 | { | ||
1510 | size_t len = sep - *ptr; | ||
1511 | char *section = alloca (len + 1); | ||
1512 | char *p = section; | ||
1513 | strncpy (section, *ptr, len); | ||
1514 | section[len] = '\0'; | ||
1515 | /* strupper. */ | ||
1516 | for (; *p; p++) if (isupper((unsigned)*p)) *p = toupper ((unsigned)*p); | ||
1517 | /* Set the string type to update the correct line count. */ | ||
1518 | /*if (!strstr (section, "FIELD"))*/ | ||
1519 | { | ||
1520 | if (strstr (section, "MIME") || (strstr (section, "HEADER"))) | ||
1521 | { | ||
1522 | f_imap->string.type = IMAP_HEADER; | ||
1523 | } | ||
1524 | else if (strstr (section, "TEXT") || len > 0) | ||
1525 | { | ||
1526 | f_imap->string.type = IMAP_BODY; | ||
1527 | } | ||
1528 | else if (len == 0) /* body[] */ | ||
1529 | { | ||
1530 | f_imap->string.type = IMAP_MESSAGE; | ||
1531 | } | ||
1532 | } | ||
1533 | sep++; /* Move pass the ']' */ | ||
1534 | *ptr = sep; | ||
1535 | } | ||
1536 | } | ||
1537 | while (**ptr && **ptr == ' ') | ||
1538 | (*ptr)++; | ||
1539 | if (**ptr == '<') | ||
1540 | { | ||
1541 | char *sep = strchr (*ptr, '>'); | ||
1542 | if (sep) | ||
1543 | { | ||
1544 | sep++; | ||
1545 | *ptr = sep; | ||
1546 | } | ||
1547 | } | ||
1548 | status = imap_string (f_imap, ptr); | ||
1549 | |||
1550 | /* If the state scan. Catch it here. */ | ||
1551 | if (f_imap->state == IMAP_SCAN_ACK) | ||
1552 | { | ||
1553 | char *buffer; | ||
1554 | off_t total = 0; | ||
1555 | if (f_imap->string.msg_imap && f_imap->string.msg_imap->fheader) | ||
1556 | header_destroy (&f_imap->string.msg_imap->fheader, NULL); | ||
1557 | stream_size (f_imap->string.stream, &total); | ||
1558 | buffer = malloc (total + 1); | ||
1559 | stream_read (f_imap->string.stream, buffer, total, 0, NULL); | ||
1560 | status = header_create (&f_imap->string.msg_imap->fheader, | ||
1561 | buffer, total, NULL); | ||
1562 | free (buffer); | ||
1563 | stream_truncate (f_imap->string.stream, 0); | ||
1564 | f_imap->string.offset = 0; | ||
1565 | f_imap->string.nleft = 0; | ||
1566 | } | ||
1567 | return status; | ||
1568 | } | ||
1569 | |||
1570 | static int | ||
1571 | imap_internaldate (f_imap_t f_imap, char **ptr) | ||
1572 | { | ||
1573 | return imap_string (f_imap, ptr); | ||
1574 | } | ||
1575 | |||
1576 | static int | ||
1577 | imap_uid (f_imap_t f_imap, char **ptr) | ||
1578 | { | ||
1579 | char token[128]; | ||
1580 | imap_token (token, sizeof token, ptr); | ||
1581 | if (f_imap->string.msg_imap) | ||
1582 | f_imap->string.msg_imap->uid = strtoul (token, NULL, 10); | ||
1583 | return 0; | ||
1584 | } | ||
1585 | |||
1586 | static int | ||
1587 | imap_rfc822 (f_imap_t f_imap, char **ptr) | ||
1588 | { | ||
1589 | int status; | ||
1590 | f_imap->string.type = IMAP_MESSAGE; | ||
1591 | status = imap_body (f_imap, ptr); | ||
1592 | f_imap->string.type = 0; | ||
1593 | return status; | ||
1594 | } | ||
1595 | |||
1596 | static int | ||
1597 | imap_rfc822_size (f_imap_t f_imap, char **ptr) | ||
1598 | { | ||
1599 | char token[128]; | ||
1600 | imap_token (token, sizeof token, ptr); | ||
1601 | if (f_imap->string.msg_imap) | ||
1602 | f_imap->string.msg_imap->message_size = strtoul (token, NULL, 10); | ||
1603 | return 0; | ||
1604 | } | ||
1605 | |||
1606 | static int | ||
1607 | imap_rfc822_header (f_imap_t f_imap, char **ptr) | ||
1608 | { | ||
1609 | int status; | ||
1610 | f_imap->string.type = IMAP_HEADER; | ||
1611 | status = imap_string (f_imap, ptr); | ||
1612 | f_imap->string.type = 0; | ||
1613 | return status; | ||
1614 | } | ||
1615 | |||
1616 | static int | ||
1617 | imap_rfc822_text (f_imap_t f_imap, char **ptr) | ||
1618 | { | ||
1619 | int status; | ||
1620 | f_imap->string.type = IMAP_HEADER; | ||
1621 | status = imap_string (f_imap, ptr); | ||
1622 | f_imap->string.type = 0; | ||
1623 | return status; | ||
1624 | } | ||
1625 | |||
1626 | /* Parse imap unfortunately FETCH is use as response for many commands. | ||
1627 | We just use a small set an ignore the other ones : | ||
1628 | not use : ALL | ||
1629 | use : BODY | ||
1630 | not use : BODY[<section>]<<partial>> | ||
1631 | use : BODY.PEEK[<section>]<<partial>> | ||
1632 | not use : BODYSTRUCTURE | ||
1633 | not use : ENVELOPE | ||
1634 | not use : FAST | ||
1635 | use : FLAGS | ||
1636 | not use : FULL | ||
1637 | use : INTERNALDATE | ||
1638 | not use : RFC822 | ||
1639 | not use : RFC822.HEADER | ||
1640 | use : RFC822.SIZE | ||
1641 | not use : RFC822.TEXT | ||
1642 | not use : UID | ||
1643 | */ | ||
1644 | static int | ||
1645 | imap_fetch (f_imap_t f_imap) | ||
1646 | { | ||
1647 | char token[128]; | ||
1648 | size_t msgno = 0; | ||
1649 | m_imap_t m_imap = f_imap->selected; | ||
1650 | int status = 0; | ||
1651 | char *sp = NULL; | ||
1652 | |||
1653 | /* We should have a mailbox selected. */ | ||
1654 | assert (m_imap != NULL); | ||
1655 | |||
1656 | /* Parse the FETCH respones to extract the pieces. */ | ||
1657 | sp = f_imap->buffer; | ||
1658 | |||
1659 | /* Skip untag '*'. */ | ||
1660 | imap_token (token, sizeof token, &sp); | ||
1661 | /* Get msgno. */ | ||
1662 | imap_token (token, sizeof token, &sp); | ||
1663 | msgno = strtol (token, NULL, 10); | ||
1664 | /* Skip FETCH . */ | ||
1665 | imap_token (token, sizeof token, &sp); | ||
1666 | |||
1667 | /* It is actually possible, but higly unlikely that we do not have the | ||
1668 | message yet, for example a "FETCH (FLAGS (\Recent))" notification | ||
1669 | for a newly messsage. */ | ||
1670 | if (f_imap->string.msg_imap == NULL | ||
1671 | || f_imap->string.msg_imap->num != msgno) | ||
1672 | { | ||
1673 | /* Find the imap mesg struct. */ | ||
1674 | size_t i; | ||
1675 | message_t msg = NULL; | ||
1676 | mailbox_get_message (m_imap->mailbox, msgno, &msg); | ||
1677 | for (i = 0; i < m_imap->imessages_count; i++) | ||
1678 | { | ||
1679 | if (m_imap->imessages[i] && m_imap->imessages[i]->num == msgno) | ||
1680 | { | ||
1681 | f_imap->string.msg_imap = m_imap->imessages[i]; | ||
1682 | break; | ||
1683 | } | ||
1684 | } | ||
1685 | /* message_destroy (&msg); */ | ||
1686 | } | ||
1687 | |||
1688 | while (*sp && *sp != ')') | ||
1689 | { | ||
1690 | /* Get the token. */ | ||
1691 | imap_token (token, sizeof token, &sp); | ||
1692 | |||
1693 | if (strncmp (token, "FLAGS", 5) == 0) | ||
1694 | { | ||
1695 | status = imap_fetch_flags (f_imap, &sp); | ||
1696 | } | ||
1697 | else if (strcasecmp (token, "BODY") == 0) | ||
1698 | { | ||
1699 | if (*sp == '[') | ||
1700 | status = imap_body (f_imap, &sp); | ||
1701 | else | ||
1702 | status = imap_bodystructure (f_imap, &sp); | ||
1703 | } | ||
1704 | else if (strcasecmp (token, "BODYSTRUCTURE") == 0) | ||
1705 | { | ||
1706 | status = imap_bodystructure (f_imap, &sp); | ||
1707 | } | ||
1708 | else if (strncmp (token, "INTERNALDATE", 12) == 0) | ||
1709 | { | ||
1710 | status = imap_internaldate (f_imap, &sp); | ||
1711 | } | ||
1712 | else if (strncmp (token, "RFC822", 10) == 0) | ||
1713 | { | ||
1714 | if (*sp == '.') | ||
1715 | { | ||
1716 | sp++; | ||
1717 | imap_token (token, sizeof token, &sp); | ||
1718 | if (strcasecmp (token, "SIZE") == 0) | ||
1719 | { | ||
1720 | status = imap_rfc822_size (f_imap, &sp); | ||
1721 | } | ||
1722 | else if (strcasecmp (token, "TEXT") == 0) | ||
1723 | { | ||
1724 | status = imap_rfc822_text (f_imap, &sp); | ||
1725 | } | ||
1726 | else if (strcasecmp (token, "HEADER") == 0) | ||
1727 | { | ||
1728 | status = imap_rfc822_header (f_imap, &sp); | ||
1729 | } | ||
1730 | /* else mu_error ("not supported RFC822 option\n"); */ | ||
1731 | } | ||
1732 | else | ||
1733 | { | ||
1734 | status = imap_rfc822 (f_imap, &sp); | ||
1735 | } | ||
1736 | } | ||
1737 | else if (strncmp (token, "UID", 3) == 0) | ||
1738 | { | ||
1739 | status = imap_uid (f_imap, &sp); | ||
1740 | } | ||
1741 | /* else mu_error ("not supported FETCH command\n"); */ | ||
1742 | } | ||
1743 | return status; | ||
1744 | } | ||
1745 | |||
1746 | static int | ||
1747 | imap_search (f_imap_t f_imap) | ||
1748 | { | ||
1749 | (void)f_imap; | ||
1750 | /* Not implemented. No provision for this in the API, yet. */ | ||
1751 | return 0; | ||
1752 | } | ||
1753 | |||
1754 | static int | ||
1755 | imap_status (f_imap_t f_imap) | ||
1756 | { | ||
1757 | (void)f_imap; | ||
1758 | /* Not implemented. No provision for this in the API, yet. */ | ||
1759 | return 0; | ||
1760 | } | ||
1761 | |||
1762 | static int | ||
1763 | imap_expunge (f_imap_t f_imap, unsigned msgno) | ||
1764 | { | ||
1765 | (void)f_imap; (void)msgno; | ||
1766 | /* We should not have this, since do not send the expunge, but rather | ||
1767 | use SELECT/CLOSE. */ | ||
1768 | return 0; | ||
1769 | } | ||
1770 | |||
1771 | |||
1772 | /* This function will advance ptr to the next character that IMAP | ||
1773 | recognise as special: " .()[]<>" and put the result in buf which | ||
1774 | is of size len. */ | ||
1775 | static int | ||
1776 | imap_token (char *buf, size_t len, char **ptr) | ||
1777 | { | ||
1778 | char *start = *ptr; | ||
1779 | size_t i; | ||
1780 | /* Skip leading space. */ | ||
1781 | while (**ptr && **ptr == ' ') | ||
1782 | (*ptr)++; | ||
1783 | /* Break the string by token, when we recognise Atoms we stop. */ | ||
1784 | for (i = 1; **ptr && i < len; (*ptr)++, buf++, i++) | ||
1785 | { | ||
1786 | if (**ptr == ' ' || **ptr == '.' | ||
1787 | || **ptr == '(' || **ptr == ')' | ||
1788 | || **ptr == '[' || **ptr == ']' | ||
1789 | || **ptr == '<' || **ptr == '>') | ||
1790 | { | ||
1791 | /* Advance. */ | ||
1792 | if (start == (*ptr)) | ||
1793 | (*ptr)++; | ||
1794 | break; | ||
1795 | } | ||
1796 | *buf = **ptr; | ||
1797 | } | ||
1798 | *buf = '\0'; | ||
1799 | /* Skip trailing space. */ | ||
1800 | while (**ptr && **ptr == ' ') | ||
1801 | (*ptr)++; | ||
1802 | return *ptr - start;; | ||
1803 | } | ||
1804 | |||
1805 | /* Checks to see if a mailbox name matches a pattern, treating | ||
1806 | INBOX case insensitively, as required (INBOX is a special | ||
1807 | name no matter what the case is). | ||
1808 | */ | ||
1809 | static int | ||
1810 | imap_mailbox_name_match(const char* pattern, const char* mailbox) | ||
1811 | { | ||
1812 | if(strcasecmp(pattern, "inbox") == 0) | ||
1813 | { | ||
1814 | return strcasecmp(pattern, mailbox); | ||
1815 | } | ||
1816 | return fnmatch(pattern, mailbox, 0); | ||
1817 | } | ||
1818 | |||
1819 | /* C99 says that a conforming implementations of snprintf () should return the | ||
1820 | number of char that would have been call but many GNU/Linux && BSD | ||
1821 | implementations return -1 on error. Worse QnX/Neutrino actually does not | ||
1822 | put the terminal null char. So let's try to cope. */ | ||
1823 | int | ||
1824 | imap_writeline (f_imap_t f_imap, const char *format, ...) | ||
1825 | { | ||
1826 | int len; | ||
1827 | va_list ap; | ||
1828 | int done = 1; | ||
1829 | |||
1830 | va_start(ap, format); | ||
1831 | do | ||
1832 | { | ||
1833 | len = vsnprintf (f_imap->buffer, f_imap->buflen - 1, format, ap); | ||
1834 | if (len < 0 || len >= (int)f_imap->buflen | ||
1835 | || !memchr (f_imap->buffer, '\0', len + 1)) | ||
1836 | { | ||
1837 | f_imap->buflen *= 2; | ||
1838 | f_imap->buffer = realloc (f_imap->buffer, f_imap->buflen); | ||
1839 | if (f_imap->buffer == NULL) | ||
1840 | return ENOMEM; | ||
1841 | done = 0; | ||
1842 | } | ||
1843 | else | ||
1844 | done = 1; | ||
1845 | } | ||
1846 | while (!done); | ||
1847 | va_end(ap); | ||
1848 | f_imap->ptr = f_imap->buffer + len; | ||
1849 | |||
1850 | if (DEBUG_SHOW_COMMAND) | ||
1851 | fprintf (stderr, "%s", f_imap->buffer); | ||
1852 | return 0; | ||
1853 | } | ||
1854 | |||
1855 | /* Cover to send requests. */ | ||
1856 | int | ||
1857 | imap_send (f_imap_t f_imap) | ||
1858 | { | ||
1859 | int status = 0; | ||
1860 | if (f_imap->ptr > f_imap->buffer) | ||
1861 | { | ||
1862 | size_t len; | ||
1863 | size_t n = 0; | ||
1864 | len = f_imap->ptr - f_imap->buffer; | ||
1865 | status = stream_write (f_imap->folder->stream, f_imap->buffer, len, | ||
1866 | 0, &n); | ||
1867 | if (status == 0) | ||
1868 | { | ||
1869 | memmove (f_imap->buffer, f_imap->buffer + n, len - n); | ||
1870 | f_imap->ptr -= n; | ||
1871 | } | ||
1872 | } | ||
1873 | else | ||
1874 | f_imap->ptr = f_imap->buffer; | ||
1875 | return status; | ||
1876 | } | ||
1877 | |||
1878 | /* Read a complete line form the imap server. Transform CRLF to LF, put a null | ||
1879 | in the buffer when done. Note f_imap->offset is not really of any use | ||
1880 | but rather to keep the stream internal buffer scheme happy, so we have to | ||
1881 | be in sync with the stream. */ | ||
1882 | int | ||
1883 | imap_readline (f_imap_t f_imap) | ||
1884 | { | ||
1885 | size_t n = 0; | ||
1886 | size_t total = f_imap->ptr - f_imap->buffer; | ||
1887 | int status; | ||
1888 | |||
1889 | /* Must get a full line before bailing out. */ | ||
1890 | do | ||
1891 | { | ||
1892 | status = stream_readline (f_imap->folder->stream, f_imap->buffer + total, | ||
1893 | f_imap->buflen - total, f_imap->offset, &n); | ||
1894 | if (status != 0) | ||
1895 | return status; | ||
1896 | |||
1897 | /* The server went away: It maybe a timeout and some imap server | ||
1898 | does not send the BYE. Consider like an error. */ | ||
1899 | if (n == 0) | ||
1900 | return EIO; | ||
1901 | |||
1902 | total += n; | ||
1903 | f_imap->offset += n; | ||
1904 | f_imap->nl = memchr (f_imap->buffer, '\n', total); | ||
1905 | if (f_imap->nl == NULL) /* Do we have a full line. */ | ||
1906 | { | ||
1907 | /* Allocate a bigger buffer ? */ | ||
1908 | if (total >= f_imap->buflen -1) | ||
1909 | { | ||
1910 | f_imap->buflen *= 2; | ||
1911 | f_imap->buffer = realloc (f_imap->buffer, f_imap->buflen + 1); | ||
1912 | if (f_imap->buffer == NULL) | ||
1913 | return ENOMEM; | ||
1914 | } | ||
1915 | } | ||
1916 | f_imap->ptr = f_imap->buffer + total; | ||
1917 | } | ||
1918 | while (f_imap->nl == NULL); | ||
1919 | |||
1920 | /* Conversion \r\n --> \n\0 */ | ||
1921 | if (f_imap->nl > f_imap->buffer) | ||
1922 | { | ||
1923 | *(f_imap->nl - 1) = '\n'; | ||
1924 | *(f_imap->nl) = '\0'; | ||
1925 | f_imap->ptr = f_imap->nl; | ||
1926 | } | ||
1927 | return 0; | ||
1928 | } | ||
1929 | |||
1930 | /* | ||
1931 | The parsing was inspired by this article form the BeNews channel: "BE | ||
1932 | ENGINEERING INSIGHTS: IMAP for the Masses." By Adam Haberlach adam@be.com | ||
1933 | |||
1934 | The server responses are in three forms: status responses, server data, | ||
1935 | and command continuation request, ... | ||
1936 | An untagged response is indicated by the token "*" instead of a tag. | ||
1937 | Untagged status responses indicate server greeting, or server status | ||
1938 | that does not indicate the completion of a command (for example, an | ||
1939 | impending system shutdown alert). | ||
1940 | .... | ||
1941 | The client MUST be prepared to accept any response at all times. | ||
1942 | |||
1943 | Status responses are OK, NO, BAD, PREAUTH and BYE. OK, NO, and BAD | ||
1944 | may be tagged or untagged. PREAUTH and BYE are always untagged. | ||
1945 | |||
1946 | Server Responses - Status Responses | ||
1947 | BAD *|tag | ||
1948 | BYE * | ||
1949 | NO *|tag | ||
1950 | OK *|tag | ||
1951 | PREAUTH * | ||
1952 | |||
1953 | The code for status responses are | ||
1954 | ALERT | ||
1955 | BADCHARSET(IMAPV) | ||
1956 | CAPABILITY(IMAPV) | ||
1957 | NEWNAME | ||
1958 | PARSE | ||
1959 | PERMANENTFLAGS | ||
1960 | READ-ONLY | ||
1961 | READ-WRITE | ||
1962 | TRYCREATE | ||
1963 | UIDNEXT | ||
1964 | UIDVALIDITY | ||
1965 | UNSEEN | ||
1966 | |||
1967 | Server Responses - Server and Mailbox Status. | ||
1968 | These responses are always untagged. | ||
1969 | CAPABILITY * | ||
1970 | EXISTS * | ||
1971 | EXPUNGE * | ||
1972 | FLAGS * | ||
1973 | FETCH * | ||
1974 | LIST * | ||
1975 | LSUB * | ||
1976 | RECENT * | ||
1977 | SEARCH * | ||
1978 | STATUS * | ||
1979 | |||
1980 | Server Responses - Command Continuation Request | ||
1981 | + | ||
1982 | |||
1983 | */ | ||
1984 | int | ||
1985 | imap_parse (f_imap_t f_imap) | ||
1986 | { | ||
1987 | int done = 0; | ||
1988 | int status = 0; | ||
1989 | char empty[2]; | ||
1990 | char *buffer = NULL; | ||
1991 | folder_t folder = f_imap->folder; | ||
1992 | |||
1993 | /* We use that moronic hack to not check null for the tockenize strings. */ | ||
1994 | empty[0] = '\0'; | ||
1995 | empty[1] = '\0'; | ||
1996 | while (! done) | ||
1997 | { | ||
1998 | char *tag, *response, *remainder; | ||
1999 | |||
2000 | status = imap_readline (f_imap); | ||
2001 | if (status != 0) | ||
2002 | { | ||
2003 | break; | ||
2004 | } | ||
2005 | /* Comment out to see all reading traffic. */ | ||
2006 | if (DEBUG_SHOW_RESPONSE) | ||
2007 | mu_error ("\t\t%s", f_imap->buffer); | ||
2008 | |||
2009 | /* We do not want to step over f_imap->buffer since it can be use | ||
2010 | further down the chain. */ | ||
2011 | if (buffer) | ||
2012 | { | ||
2013 | free (buffer); | ||
2014 | buffer = NULL; | ||
2015 | } | ||
2016 | buffer = calloc ((f_imap->ptr - f_imap->buffer) + 1, 1); | ||
2017 | memcpy (buffer, f_imap->buffer, (f_imap->ptr - f_imap->buffer)); | ||
2018 | |||
2019 | /* Tokenize the string. */ | ||
2020 | { | ||
2021 | char *sp = NULL; | ||
2022 | tag = strtok_r (buffer, " ", &sp); | ||
2023 | response = strtok_r (NULL, " ", &sp); | ||
2024 | if (!response) | ||
2025 | response = empty; | ||
2026 | remainder = strtok_r (NULL, "\n", &sp); | ||
2027 | if (!remainder) | ||
2028 | remainder = empty; | ||
2029 | } | ||
2030 | |||
2031 | /* Is the response untagged ? */ | ||
2032 | if (tag && tag[0] == '*') | ||
2033 | { | ||
2034 | FOLDER_DEBUG2(folder, MU_DEBUG_PROT, "* %s %s\n", | ||
2035 | response, remainder); | ||
2036 | /* Is it a Status Response. */ | ||
2037 | if (strcasecmp (response, "OK") == 0) | ||
2038 | { | ||
2039 | /* Check for status response [code]. */ | ||
2040 | if (*remainder == '[') | ||
2041 | { | ||
2042 | char *cruft, *subtag; | ||
2043 | char *sp = NULL; | ||
2044 | remainder++; | ||
2045 | cruft = strtok_r (remainder, "]", &sp); | ||
2046 | if (!cruft) cruft = empty; | ||
2047 | subtag = strtok_r (cruft, " ", &sp); | ||
2048 | if (!subtag) subtag = empty; | ||
2049 | |||
2050 | if (strcasecmp (subtag, "ALERT") == 0) | ||
2051 | { | ||
2052 | /* The human-readable text contains a special alert that | ||
2053 | MUST be presented to the user in a fashion that calls | ||
2054 | the user's attention to the message. */ | ||
2055 | mu_error ("ALERT: %s\n", (sp) ? sp : ""); | ||
2056 | } | ||
2057 | else if (strcasecmp (subtag, "BADCHARSET") == 0) | ||
2058 | { | ||
2059 | /* Optionally followed by a parenthesized list of | ||
2060 | charsets. A SEARCH failed because the given charset | ||
2061 | is not supported by this implementation. If the | ||
2062 | optional list of charsets is given, this lists the | ||
2063 | charsets that are supported by this implementation. */ | ||
2064 | mu_error ("BADCHARSET: %s\n", (sp) ? sp : ""); | ||
2065 | } | ||
2066 | else if (strcasecmp (subtag, "CAPABILITY") == 0) | ||
2067 | { | ||
2068 | /* Followed by a list of capabilities. This can appear | ||
2069 | in the initial OK or PREAUTH response to transmit an | ||
2070 | initial capabilities list. This makes it unnecessary | ||
2071 | for a client to send a separate CAPABILITY command if | ||
2072 | it recognizes this response. */ | ||
2073 | if (f_imap->capa) | ||
2074 | free (f_imap->capa); | ||
2075 | f_imap->capa = strdup (cruft); | ||
2076 | } | ||
2077 | else if (strcasecmp (subtag, "NEWNAME") == 0) | ||
2078 | { | ||
2079 | /* Followed by a mailbox name and a new mailbox name. A | ||
2080 | SELECT or EXAMINE failed because the target mailbox | ||
2081 | name (which once existed) was renamed to the new | ||
2082 | mailbox name. This is a hint to the client that the | ||
2083 | operation can succeed if the SELECT or EXAMINE is | ||
2084 | reissued with the new mailbox name. */ | ||
2085 | mu_error ("NEWNAME: %s\n", (sp) ? sp : ""); | ||
2086 | } | ||
2087 | else if (strcasecmp (subtag, "PARSE") == 0) | ||
2088 | { | ||
2089 | /* The human-readable text represents an error in | ||
2090 | parsing the [RFC-822] header or [MIME-IMB] headers | ||
2091 | of a message in the mailbox. */ | ||
2092 | mu_error ("PARSE: %s\n", (sp) ? sp : ""); | ||
2093 | } | ||
2094 | else if (strcasecmp (subtag, "PERMANENTFLAGS") == 0) | ||
2095 | { | ||
2096 | /* Followed by a parenthesized list of flags, indicates | ||
2097 | which of the known flags that the client can change | ||
2098 | permanently. Any flags that are in the FLAGS | ||
2099 | untagged response, but not the PERMANENTFLAGS list, | ||
2100 | can not be set permanently. If the client attempts | ||
2101 | to STORE a flag that is not in the PERMANENTFLAGS | ||
2102 | list, the server will either ignore the change or | ||
2103 | store the state change for the remainder of the | ||
2104 | current session only. The PERMANENTFLAGS list can | ||
2105 | also include the special flag \*, which indicates | ||
2106 | that it is possible to create new keywords by | ||
2107 | attempting to store those flags in the mailbox. */ | ||
2108 | } | ||
2109 | else if (strcasecmp (subtag, "READ-ONLY") == 0) | ||
2110 | { | ||
2111 | /* The mailbox is selected read-only, or its access | ||
2112 | while selected has changed from read-write to | ||
2113 | read-only. */ | ||
2114 | } | ||
2115 | else if (strcasecmp (subtag, "READ-WRITE") == 0) | ||
2116 | { | ||
2117 | /* The mailbox is selected read-write, or its access | ||
2118 | while selected has changed from read-only to | ||
2119 | read-write. */ | ||
2120 | } | ||
2121 | else if (strcasecmp (subtag, "TRYCREATE") == 0) | ||
2122 | { | ||
2123 | /* An APPEND or COPY attempt is failing because the | ||
2124 | target mailbox does not exist (as opposed to some | ||
2125 | other reason). This is a hint to the client that | ||
2126 | the operation can succeed if the mailbox is first | ||
2127 | created by the CREATE command. */ | ||
2128 | mu_error ("TRYCREATE: %s\n", (sp) ? sp : ""); | ||
2129 | } | ||
2130 | else if (strcasecmp (subtag, "UIDNEXT") == 0) | ||
2131 | { | ||
2132 | /* Followed by a decimal number, indicates the next | ||
2133 | unique identifier value. Refer to section 2.3.1.1 | ||
2134 | for more information. */ | ||
2135 | char *value = strtok_r (NULL, " ", &sp); | ||
2136 | f_imap->selected->uidnext = strtol (value, NULL, 10); | ||
2137 | } | ||
2138 | else if (strcasecmp (subtag, "UIDVALIDITY") == 0) | ||
2139 | { | ||
2140 | /* Followed by a decimal number, indicates the unique | ||
2141 | identifier validity value. Refer to section 2.3.1.1 | ||
2142 | for more information. */ | ||
2143 | char *value = strtok_r (NULL, " ", &sp); | ||
2144 | f_imap->selected->uidvalidity = strtol (value, NULL, 10); | ||
2145 | } | ||
2146 | else if (strcasecmp (subtag, "UNSEEN") == 0) | ||
2147 | { | ||
2148 | /* Followed by a decimal number, indicates the number of | ||
2149 | the first message without the \Seen flag set. */ | ||
2150 | char *value = strtok_r (NULL, " ", &sp); | ||
2151 | f_imap->selected->unseen = strtol (value, NULL, 10); | ||
2152 | } | ||
2153 | else | ||
2154 | { | ||
2155 | /* Additional response codes defined by particular | ||
2156 | client or server implementations SHOULD be prefixed | ||
2157 | with an "X" until they are added to a revision of | ||
2158 | this protocol. Client implementations SHOULD ignore | ||
2159 | response codes that they do not recognize. */ | ||
2160 | } | ||
2161 | } /* End of code. */ | ||
2162 | else | ||
2163 | { | ||
2164 | /* Not sure why we would get an untagged ok...but we do... */ | ||
2165 | /* Still should we be verbose about is ? */ | ||
2166 | mu_error ("Untagged OK: %s\n", remainder); | ||
2167 | } | ||
2168 | } | ||
2169 | else if (strcasecmp (response, "NO") == 0) | ||
2170 | { | ||
2171 | /* This does not mean failure but rather a strong warning. */ | ||
2172 | mu_error ("Untagged NO: %s\n", remainder); | ||
2173 | } | ||
2174 | else if (strcasecmp (response, "BAD") == 0) | ||
2175 | { | ||
2176 | /* We're dead, protocol/syntax error. */ | ||
2177 | mu_error ("Untagged BAD: %s\n", remainder); | ||
2178 | } | ||
2179 | else if (strcasecmp (response, "PREAUTH") == 0) | ||
2180 | { | ||
2181 | /* Should we be dealing with this? */ | ||
2182 | } | ||
2183 | else if (strcasecmp (response, "BYE") == 0) | ||
2184 | { | ||
2185 | /* We should close the stream. This is not recoverable. */ | ||
2186 | done = 1; | ||
2187 | monitor_wrlock (f_imap->folder->monitor); | ||
2188 | f_imap->isopen = 0; | ||
2189 | f_imap->selected = NULL; | ||
2190 | monitor_unlock (f_imap->folder->monitor); | ||
2191 | stream_close (f_imap->folder->stream); | ||
2192 | } | ||
2193 | else if (strcasecmp (response, "CAPABILITY") == 0) | ||
2194 | { | ||
2195 | if (f_imap->capa) | ||
2196 | free (f_imap->capa); | ||
2197 | f_imap->capa = strdup (remainder); | ||
2198 | } | ||
2199 | else if (strcasecmp (remainder, "EXISTS") == 0) | ||
2200 | { | ||
2201 | f_imap->selected->messages_count = strtol (response, NULL, 10); | ||
2202 | } | ||
2203 | else if (strcasecmp (remainder, "EXPUNGE") == 0) | ||
2204 | { | ||
2205 | unsigned int msgno = strtol (response, NULL, 10); | ||
2206 | status = imap_expunge (f_imap, msgno); | ||
2207 | } | ||
2208 | else if (strncasecmp (remainder, "FETCH", 5) == 0) | ||
2209 | { | ||
2210 | status = imap_fetch (f_imap); | ||
2211 | if (status != 0) | ||
2212 | break; | ||
2213 | } | ||
2214 | else if (strcasecmp (response, "FLAGS") == 0) | ||
2215 | { | ||
2216 | /* Flags define on the mailbox not a message flags. */ | ||
2217 | status = imap_permanentflags (f_imap, &remainder); | ||
2218 | } | ||
2219 | else if (strcasecmp (response, "LIST") == 0) | ||
2220 | { | ||
2221 | status = imap_list (f_imap); | ||
2222 | } | ||
2223 | else if (strcasecmp (response, "LSUB") == 0) | ||
2224 | { | ||
2225 | status = imap_list (f_imap); | ||
2226 | } | ||
2227 | else if (strcasecmp (remainder, "RECENT") == 0) | ||
2228 | { | ||
2229 | f_imap->selected->recent = strtol (response, NULL, 10); | ||
2230 | } | ||
2231 | else if (strcasecmp (response, "SEARCH") == 0) | ||
2232 | { | ||
2233 | status = imap_search (f_imap); | ||
2234 | } | ||
2235 | else if (strcasecmp (response, "STATUS") == 0) | ||
2236 | { | ||
2237 | status = imap_status (f_imap); | ||
2238 | } | ||
2239 | else | ||
2240 | { | ||
2241 | /* Once again, check for something strange. */ | ||
2242 | mu_error ("unknown untagged response: \"%s\" %s\n", | ||
2243 | response, remainder); | ||
2244 | } | ||
2245 | } | ||
2246 | /* Continuation token ???. */ | ||
2247 | else if (tag && tag[0] == '+') | ||
2248 | { | ||
2249 | done = 1; | ||
2250 | } | ||
2251 | else | ||
2252 | { | ||
2253 | /* Every transaction ends with a tagged response. */ | ||
2254 | done = 1; | ||
2255 | if (strcasecmp (response, "OK") == 0) | ||
2256 | { | ||
2257 | /* Cool we are doing ok. */ | ||
2258 | } | ||
2259 | else /* NO and BAD */ | ||
2260 | { | ||
2261 | if (strncasecmp (remainder, "LOGIN", 5) == 0) | ||
2262 | { | ||
2263 | observable_t observable = NULL; | ||
2264 | folder_get_observable (f_imap->folder, &observable); | ||
2265 | observable_notify (observable, MU_EVT_AUTHORITY_FAILED); | ||
2266 | } | ||
2267 | mu_error ("NO/Bad Tagged: %s %s\n", response, remainder); | ||
2268 | status = EINVAL; | ||
2269 | } | ||
2270 | } | ||
2271 | f_imap->ptr = f_imap->buffer; | ||
2272 | } | ||
2273 | |||
2274 | if (buffer) | ||
2275 | free (buffer); | ||
2276 | return status; | ||
2277 | } | ||
2278 | |||
2279 | #else | ||
2280 | #include <stdio.h> | ||
2281 | #include <registrar0.h> | ||
2282 | record_t imap_record = NULL; | ||
2283 | #endif |
mailbox/folder_mbox.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #include <errno.h> | ||
23 | #include <sys/types.h> | ||
24 | #include <dirent.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <unistd.h> | ||
28 | #include <string.h> | ||
29 | #include <glob.h> | ||
30 | #include <fnmatch.h> | ||
31 | #include <stdio.h> | ||
32 | #include <stdlib.h> | ||
33 | |||
34 | #include <folder0.h> | ||
35 | #include <registrar0.h> | ||
36 | |||
37 | #include <mailutils/auth.h> | ||
38 | #include <mailutils/url.h> | ||
39 | #include <mailutils/stream.h> | ||
40 | |||
41 | /* We export url parsing and the initialisation of | ||
42 | the mailbox, via the register entry/record. */ | ||
43 | |||
44 | static struct _record _mbox_record = | ||
45 | { | ||
46 | MU_MBOX_SCHEME, | ||
47 | _url_mbox_init, /* Mailbox init. */ | ||
48 | _mailbox_mbox_init, /* Mailbox init. */ | ||
49 | NULL, /* Mailer init. */ | ||
50 | _folder_mbox_init, /* Folder init. */ | ||
51 | NULL, /* No need for an back pointer. */ | ||
52 | NULL, /* _is_scheme method. */ | ||
53 | NULL, /* _get_url method. */ | ||
54 | NULL, /* _get_mailbox method. */ | ||
55 | NULL, /* _get_mailer method. */ | ||
56 | NULL /* _get_folder method. */ | ||
57 | }; | ||
58 | record_t mbox_record = &_mbox_record; | ||
59 | |||
60 | static struct _record _file_record = | ||
61 | { | ||
62 | MU_FILE_SCHEME, | ||
63 | _url_file_init, /* Mailbox init. */ | ||
64 | _mailbox_file_init, /* Mailbox init. */ | ||
65 | NULL, /* Mailer init. */ | ||
66 | _folder_mbox_init, /* Folder init. */ | ||
67 | NULL, /* No need for an owner. */ | ||
68 | NULL, /* _is_scheme method. */ | ||
69 | NULL, /* _get_url method. */ | ||
70 | NULL, /* _get_mailbox method. */ | ||
71 | NULL, /* _get_mailer method. */ | ||
72 | NULL /* _get_folder method. */ | ||
73 | }; | ||
74 | record_t file_record = &_file_record; | ||
75 | |||
76 | static struct _record _path_record = | ||
77 | { | ||
78 | MU_PATH_SCHEME, | ||
79 | _url_path_init, /* Mailbox init. */ | ||
80 | _mailbox_file_init, /* Mailbox init. */ | ||
81 | NULL, /* Mailer init. */ | ||
82 | _folder_mbox_init, /* Folder init. */ | ||
83 | NULL, /* No need for an owner. */ | ||
84 | NULL, /* is_scheme method. */ | ||
85 | NULL, /* get_url method. */ | ||
86 | NULL, /* get_mailbox method. */ | ||
87 | NULL, /* get_mailer method. */ | ||
88 | NULL /* get_folder method. */ | ||
89 | }; | ||
90 | record_t path_record = &_path_record; | ||
91 | |||
92 | /* lsub/subscribe/unsubscribe are not needed. */ | ||
93 | static void folder_mbox_destroy __P ((folder_t)); | ||
94 | static int folder_mbox_open __P ((folder_t, int)); | ||
95 | static int folder_mbox_close __P ((folder_t)); | ||
96 | static int folder_mbox_delete __P ((folder_t, const char *)); | ||
97 | static int folder_mbox_rename __P ((folder_t , const char *, | ||
98 | const char *)); | ||
99 | static int folder_mbox_list __P ((folder_t, const char *, const char *, | ||
100 | struct folder_list *)); | ||
101 | static int folder_mbox_subscribe __P ((folder_t, const char *)); | ||
102 | static int folder_mbox_unsubscribe __P ((folder_t, const char *)); | ||
103 | static int folder_mbox_lsub __P ((folder_t, const char *, const char *, | ||
104 | struct folder_list *)); | ||
105 | |||
106 | |||
107 | static char *get_pathname __P ((const char *, const char *)); | ||
108 | |||
109 | static int folder_mbox_get_authority __P ((folder_t folder, | ||
110 | authority_t * pauth)); | ||
111 | |||
112 | struct _fmbox | ||
113 | { | ||
114 | char *dirname; | ||
115 | char **subscribe; | ||
116 | size_t sublen; | ||
117 | }; | ||
118 | typedef struct _fmbox *fmbox_t; | ||
119 | |||
120 | |||
121 | int | ||
122 | _folder_mbox_init (folder_t folder) | ||
123 | { | ||
124 | fmbox_t dfolder; | ||
125 | size_t name_len = 0; | ||
126 | int status = 0; | ||
127 | |||
128 | /* We create an authority so the API is uniform across the mailbox | ||
129 | types. */ | ||
130 | status = folder_mbox_get_authority (folder, NULL); | ||
131 | if (status != 0) | ||
132 | return status; | ||
133 | |||
134 | dfolder = folder->data = calloc (1, sizeof (*dfolder)); | ||
135 | if (dfolder == NULL) | ||
136 | return ENOMEM; | ||
137 | |||
138 | url_get_path (folder->url, NULL, 0, &name_len); | ||
139 | dfolder->dirname = calloc (name_len + 1, sizeof (char)); | ||
140 | if (dfolder->dirname == NULL) | ||
141 | { | ||
142 | free (dfolder); | ||
143 | folder->data = NULL; | ||
144 | return ENOMEM; | ||
145 | } | ||
146 | url_get_path (folder->url, dfolder->dirname, name_len + 1, NULL); | ||
147 | |||
148 | folder->_destroy = folder_mbox_destroy; | ||
149 | |||
150 | folder->_open = folder_mbox_open; | ||
151 | folder->_close = folder_mbox_close; | ||
152 | |||
153 | folder->_list = folder_mbox_list; | ||
154 | folder->_lsub = folder_mbox_lsub; | ||
155 | folder->_subscribe = folder_mbox_subscribe; | ||
156 | folder->_unsubscribe = folder_mbox_unsubscribe; | ||
157 | folder->_delete = folder_mbox_delete; | ||
158 | folder->_rename = folder_mbox_rename; | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | void | ||
163 | folder_mbox_destroy (folder_t folder) | ||
164 | { | ||
165 | if (folder->data) | ||
166 | { | ||
167 | fmbox_t fmbox = folder->data; | ||
168 | if (fmbox->dirname) | ||
169 | free (fmbox->dirname); | ||
170 | if (fmbox->subscribe) | ||
171 | free (fmbox->subscribe); | ||
172 | free (folder->data); | ||
173 | folder->data = NULL; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /* Noop. */ | ||
178 | static int | ||
179 | folder_mbox_open (folder_t folder, int flags) | ||
180 | { | ||
181 | fmbox_t fmbox = folder->data; | ||
182 | if (flags & MU_STREAM_CREAT) | ||
183 | { | ||
184 | return (mkdir (fmbox->dirname, S_IRWXU) == 0) ? 0 : errno; | ||
185 | } | ||
186 | (void)(flags); | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /* Noop. */ | ||
191 | static int | ||
192 | folder_mbox_close (folder_t folder) | ||
193 | { | ||
194 | (void)(folder); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int | ||
199 | folder_mbox_delete (folder_t folder, const char *filename) | ||
200 | { | ||
201 | fmbox_t fmbox = folder->data; | ||
202 | if (filename) | ||
203 | { | ||
204 | int status = 0; | ||
205 | char *pathname = get_pathname (fmbox->dirname, filename); | ||
206 | if (pathname) | ||
207 | { | ||
208 | if (remove (pathname) != 0) | ||
209 | status = errno; | ||
210 | free (pathname); | ||
211 | } | ||
212 | else | ||
213 | status = ENOMEM; | ||
214 | return status; | ||
215 | } | ||
216 | return EINVAL; | ||
217 | } | ||
218 | |||
219 | static int | ||
220 | folder_mbox_rename (folder_t folder, const char *oldpath, const char *newpath) | ||
221 | { | ||
222 | fmbox_t fmbox = folder->data; | ||
223 | if (oldpath && newpath) | ||
224 | { | ||
225 | int status = 0; | ||
226 | char *pathold = get_pathname (fmbox->dirname, oldpath); | ||
227 | if (pathold) | ||
228 | { | ||
229 | char *pathnew = get_pathname (fmbox->dirname, newpath); | ||
230 | if (pathnew) | ||
231 | { | ||
232 | if (rename (pathold, pathnew) != 0) | ||
233 | status = errno; | ||
234 | free (pathnew); | ||
235 | } | ||
236 | else | ||
237 | status = ENOMEM; | ||
238 | free (pathold); | ||
239 | } | ||
240 | else | ||
241 | status = ENOMEM; | ||
242 | return status; | ||
243 | } | ||
244 | return EINVAL; | ||
245 | } | ||
246 | |||
247 | /* The listing is not recursif and we use glob() some expansion for us. | ||
248 | Unfortunately glob() does not expand the '~'. We also return | ||
249 | The full pathname so it can be use to create other folders. */ | ||
250 | static int | ||
251 | folder_mbox_list (folder_t folder, const char *dirname, const char *pattern, | ||
252 | struct folder_list *pflist) | ||
253 | { | ||
254 | fmbox_t fmbox = folder->data; | ||
255 | char *pathname = NULL; | ||
256 | int status; | ||
257 | size_t num = 0; | ||
258 | glob_t gl; | ||
259 | |||
260 | if (dirname == NULL || dirname[0] == '\0') | ||
261 | dirname = (const char *)fmbox->dirname; | ||
262 | |||
263 | pathname = get_pathname (dirname, pattern); | ||
264 | if (pathname) | ||
265 | { | ||
266 | memset(&gl, 0, sizeof(gl)); | ||
267 | status = glob (pathname, 0, NULL, &gl); | ||
268 | free (pathname); | ||
269 | num = gl.gl_pathc; | ||
270 | } | ||
271 | else | ||
272 | status = ENOMEM; | ||
273 | |||
274 | /* Build the folder list from glob. */ | ||
275 | if (status == 0) | ||
276 | { | ||
277 | if (pflist) | ||
278 | { | ||
279 | struct list_response **plist; | ||
280 | plist = calloc (num, sizeof (*plist)); | ||
281 | if (plist) | ||
282 | { | ||
283 | size_t i; | ||
284 | struct stat stbuf; | ||
285 | for (i = 0; i < num; i++) | ||
286 | { | ||
287 | plist[i] = calloc (1, sizeof (**plist)); | ||
288 | if (plist[i] == NULL | ||
289 | || (plist[i]->name = strdup (gl.gl_pathv[i])) == NULL) | ||
290 | { | ||
291 | num = i; | ||
292 | break; | ||
293 | } | ||
294 | if (stat (gl.gl_pathv[i], &stbuf) == 0) | ||
295 | { | ||
296 | if (S_ISDIR(stbuf.st_mode)) | ||
297 | plist[i]->type = MU_FOLDER_ATTRIBUTE_DIRECTORY; | ||
298 | if (S_ISREG(stbuf.st_mode)) | ||
299 | plist[i]->type = MU_FOLDER_ATTRIBUTE_FILE; | ||
300 | } | ||
301 | plist[i]->separator = '/'; | ||
302 | } | ||
303 | } | ||
304 | pflist->element = plist; | ||
305 | pflist->num = num; | ||
306 | } | ||
307 | globfree (&gl); | ||
308 | } | ||
309 | else | ||
310 | { | ||
311 | status = (status == GLOB_NOSPACE) ? ENOMEM : EINVAL; | ||
312 | } | ||
313 | return status; | ||
314 | } | ||
315 | |||
316 | static int | ||
317 | folder_mbox_lsub (folder_t folder, const char *ref, const char *name, | ||
318 | struct folder_list *pflist) | ||
319 | { | ||
320 | fmbox_t fmbox = folder->data; | ||
321 | size_t j = 0; | ||
322 | |||
323 | if (pflist == NULL) | ||
324 | return EINVAL; | ||
325 | |||
326 | (void)ref; | ||
327 | if (name == NULL || *name == '\0') | ||
328 | name = "*"; | ||
329 | |||
330 | if (fmbox->sublen > 0) | ||
331 | { | ||
332 | struct list_response **plist; | ||
333 | size_t i; | ||
334 | plist = calloc (fmbox->sublen, sizeof (*plist)); | ||
335 | for (i = 0; i < fmbox->sublen; i++) | ||
336 | { | ||
337 | if (fmbox->subscribe[i] | ||
338 | && fnmatch (name, fmbox->subscribe[i], 0) == 0) | ||
339 | { | ||
340 | plist[i] = calloc (1, sizeof (**plist)); | ||
341 | if (plist[i] == NULL | ||
342 | || (plist[i]->name = strdup (fmbox->subscribe[i])) == NULL) | ||
343 | break; | ||
344 | plist[i]->type = MU_FOLDER_ATTRIBUTE_FILE; | ||
345 | plist[i]->separator = '/'; | ||
346 | j++; | ||
347 | } | ||
348 | } | ||
349 | pflist->element = plist; | ||
350 | } | ||
351 | pflist->num = j; | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int | ||
356 | folder_mbox_subscribe (folder_t folder, const char *name) | ||
357 | { | ||
358 | fmbox_t fmbox = folder->data; | ||
359 | char **tmp; | ||
360 | size_t i; | ||
361 | for (i = 0; i < fmbox->sublen; i++) | ||
362 | { | ||
363 | if (fmbox->subscribe[i] && strcmp (fmbox->subscribe[i], name) == 0) | ||
364 | return 0; | ||
365 | } | ||
366 | tmp = realloc (fmbox->subscribe, (fmbox->sublen + 1) * sizeof (*tmp)); | ||
367 | if (tmp == NULL) | ||
368 | return ENOMEM; | ||
369 | fmbox->subscribe = tmp; | ||
370 | fmbox->subscribe[fmbox->sublen] = strdup (name); | ||
371 | if (fmbox->subscribe[fmbox->sublen] == NULL) | ||
372 | return ENOMEM; | ||
373 | fmbox->sublen++; | ||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static int | ||
378 | folder_mbox_unsubscribe (folder_t folder, const char *name) | ||
379 | { | ||
380 | fmbox_t fmbox = folder->data; | ||
381 | size_t i; | ||
382 | for (i = 0; i < fmbox->sublen; i++) | ||
383 | { | ||
384 | if (fmbox->subscribe[i] && strcmp (fmbox->subscribe[i], name) == 0) | ||
385 | { | ||
386 | free (fmbox->subscribe[i]); | ||
387 | fmbox->subscribe[i] = NULL; | ||
388 | return 0; | ||
389 | } | ||
390 | } | ||
391 | return ENOENT; | ||
392 | } | ||
393 | |||
394 | static char * | ||
395 | get_pathname (const char *dirname, const char *basename) | ||
396 | { | ||
397 | char *pathname = NULL; | ||
398 | /* null basename gives dirname. */ | ||
399 | if (basename == NULL) | ||
400 | pathname = (dirname) ? strdup (dirname) : strdup ("."); | ||
401 | /* Absolute. */ | ||
402 | else if (basename[0] == '/') | ||
403 | pathname = strdup (basename); | ||
404 | /* Relative. */ | ||
405 | else | ||
406 | { | ||
407 | size_t len = strlen (basename); | ||
408 | pathname = calloc (strlen (dirname) + len + 2, sizeof (char)); | ||
409 | if (pathname) | ||
410 | sprintf (pathname, "%s/%s", dirname, basename); | ||
411 | } | ||
412 | return pathname; | ||
413 | } | ||
414 | |||
415 | static int | ||
416 | folder_mbox_get_authority (folder_t folder, authority_t *pauth) | ||
417 | { | ||
418 | int status = 0; | ||
419 | if (folder->authority == NULL) | ||
420 | { | ||
421 | status = authority_create_null (&folder->authority, folder); | ||
422 | } | ||
423 | if (!status && pauth) | ||
424 | *pauth = folder->authority; | ||
425 | return status; | ||
426 | } | ||
427 |
mailbox/folder_mh.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef ENABLE_MH | ||
23 | |||
24 | #include <errno.h> | ||
25 | |||
26 | #include <folder0.h> | ||
27 | #include <registrar0.h> | ||
28 | |||
29 | static struct _record _mh_record = | ||
30 | { | ||
31 | MU_MH_SCHEME, | ||
32 | _url_mh_init, /* Url init. */ | ||
33 | _mailbox_mh_init, /* Mailbox init. */ | ||
34 | NULL, /* Mailer init. */ | ||
35 | _folder_mh_init, /* Folder init. */ | ||
36 | NULL, /* back pointer. */ | ||
37 | NULL, /* _is_scheme method. */ | ||
38 | NULL, /* _get_url method. */ | ||
39 | NULL, /* _get_mailbox method. */ | ||
40 | NULL, /* _get_mailer method. */ | ||
41 | NULL /* _get_folder method. */ | ||
42 | }; | ||
43 | record_t mh_record = &_mh_record; | ||
44 | |||
45 | int | ||
46 | _folder_mh_init (folder_t folder) | ||
47 | { | ||
48 | (void)folder; | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | #else | ||
53 | #include <stdio.h> | ||
54 | #include <registrar0.h> | ||
55 | record_t mh_record = NULL; | ||
56 | #endif |
mailbox/folder_pop.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef ENABLE_POP | ||
23 | |||
24 | #include <errno.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | |||
28 | #ifdef HAVE_STRINGS_H | ||
29 | # include <strings.h> | ||
30 | #endif | ||
31 | |||
32 | #include <mailutils/auth.h> | ||
33 | #include <mailutils/mailbox.h> | ||
34 | |||
35 | #include <folder0.h> | ||
36 | #include <registrar0.h> | ||
37 | #include <url0.h> | ||
38 | |||
39 | /* We export url parsing and the initialisation of | ||
40 | the mailbox, via the register entry/record. */ | ||
41 | |||
42 | static struct _record _pop_record = | ||
43 | { | ||
44 | MU_POP_SCHEME, | ||
45 | _url_pop_init, /* Url init. */ | ||
46 | _mailbox_pop_init, /* Mailbox init. */ | ||
47 | NULL, /* Mailer init. */ | ||
48 | _folder_pop_init, /* Folder init. */ | ||
49 | NULL, /* No need for an back pointer. */ | ||
50 | NULL, /* _is_scheme method. */ | ||
51 | NULL, /* _get_url method. */ | ||
52 | NULL, /* _get_mailbox method. */ | ||
53 | NULL, /* _get_mailer method. */ | ||
54 | NULL /* _get_folder method. */ | ||
55 | }; | ||
56 | record_t pop_record = &_pop_record; | ||
57 | |||
58 | static int folder_pop_open __P ((folder_t, int)); | ||
59 | static int folder_pop_close __P ((folder_t)); | ||
60 | static int folder_pop_get_authority __P ((folder_t, authority_t *)); | ||
61 | extern int _pop_user __P ((authority_t)); | ||
62 | extern int _pop_apop __P ((authority_t)); | ||
63 | |||
64 | /* XXX: The way, the POP folder is handle is not clean at all. | ||
65 | the I/O functions should have been here on folder, not in mbx_pop.c */ | ||
66 | int | ||
67 | _folder_pop_init (folder_t folder) | ||
68 | { | ||
69 | int status; | ||
70 | |||
71 | /* Set the authority early: | ||
72 | (1) so we can check for errors. | ||
73 | (2) allow the client to get the authority for setting the ticket | ||
74 | before the open. */ | ||
75 | status = folder_pop_get_authority (folder, NULL); | ||
76 | if (status != 0) | ||
77 | return status; | ||
78 | |||
79 | folder->_open = folder_pop_open; | ||
80 | folder->_close = folder_pop_close; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int | ||
85 | folder_pop_open (folder_t folder, int flags) | ||
86 | { | ||
87 | mailbox_t mbox = folder->data; | ||
88 | return mailbox_open (mbox, flags); | ||
89 | } | ||
90 | |||
91 | static int | ||
92 | folder_pop_close (folder_t folder) | ||
93 | { | ||
94 | mailbox_t mbox = folder->data; | ||
95 | return mailbox_close (mbox); | ||
96 | } | ||
97 | |||
98 | static int | ||
99 | folder_pop_get_authority (folder_t folder, authority_t *pauth) | ||
100 | { | ||
101 | int status = 0; | ||
102 | if (folder->authority == NULL) | ||
103 | { | ||
104 | /* assert (folder->url); */ | ||
105 | if (folder->url == NULL) | ||
106 | return EINVAL; | ||
107 | |||
108 | if (folder->url->auth == NULL | ||
109 | || strcasecmp (folder->url->auth, "*") == 0) | ||
110 | { | ||
111 | status = authority_create (&folder->authority, NULL, folder); | ||
112 | authority_set_authenticate (folder->authority, _pop_user, folder); | ||
113 | } | ||
114 | /* | ||
115 | "+apop" could be supported. | ||
116 | Anything else starting with "+" is an extension mechanism. | ||
117 | Without a "+" it's a SASL mechanism. | ||
118 | */ | ||
119 | else if (strcasecmp (folder->url->auth, "+APOP") == 0) | ||
120 | { | ||
121 | status = authority_create (&folder->authority, NULL, folder); | ||
122 | authority_set_authenticate (folder->authority, _pop_apop, folder); | ||
123 | } | ||
124 | else | ||
125 | { | ||
126 | status = ENOSYS; | ||
127 | } | ||
128 | } | ||
129 | if (pauth) | ||
130 | *pauth = folder->authority; | ||
131 | return status; | ||
132 | } | ||
133 | |||
134 | #else | ||
135 | #include <stdio.h> | ||
136 | #include <registrar0.h> | ||
137 | record_t pop_record = NULL; | ||
138 | #endif |
mailbox/mbx_imap.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef ENABLE_IMAP | ||
23 | |||
24 | #include <errno.h> | ||
25 | #include <string.h> | ||
26 | #ifdef HAVE_STRINGS_H | ||
27 | #include <strings.h> | ||
28 | #endif | ||
29 | #include <stdlib.h> | ||
30 | #include <assert.h> | ||
31 | #include <time.h> | ||
32 | |||
33 | #include <mailutils/address.h> | ||
34 | #include <mailutils/attribute.h> | ||
35 | #include <mailutils/body.h> | ||
36 | #include <mailutils/debug.h> | ||
37 | #include <mailutils/envelope.h> | ||
38 | #include <mailutils/error.h> | ||
39 | #include <mailutils/header.h> | ||
40 | #include <mailutils/message.h> | ||
41 | #include <mailutils/mutil.h> | ||
42 | #include <mailutils/observer.h> | ||
43 | #include <mailutils/property.h> | ||
44 | #include <mailutils/stream.h> | ||
45 | |||
46 | #include <imap0.h> | ||
47 | #include <mailbox0.h> | ||
48 | #include <registrar0.h> | ||
49 | #include <url0.h> | ||
50 | |||
51 | #undef min | ||
52 | #define min(a,b) ((a) < (b) ? (a) : (b)) | ||
53 | |||
54 | #define MU_IMAP_CACHE_HEADERS "Bcc Cc Content-Language Content-Transfer-Encoding Content-Type Date From In-Reply-To Message-ID Reference Reply-To Sender Subject To X-UIDL" | ||
55 | |||
56 | /* mailbox_t API. */ | ||
57 | static void mailbox_imap_destroy __P ((mailbox_t)); | ||
58 | static int mailbox_imap_open __P ((mailbox_t, int)); | ||
59 | static int mailbox_imap_close __P ((mailbox_t)); | ||
60 | static int imap_uidvalidity __P ((mailbox_t, unsigned long *)); | ||
61 | static int imap_uidnext __P ((mailbox_t, size_t *)); | ||
62 | static int imap_expunge __P ((mailbox_t)); | ||
63 | static int imap_get_message __P ((mailbox_t, size_t, message_t *)); | ||
64 | static int imap_messages_count __P ((mailbox_t, size_t *)); | ||
65 | static int imap_messages_recent __P ((mailbox_t, size_t *)); | ||
66 | static int imap_message_unseen __P ((mailbox_t, size_t *)); | ||
67 | static int imap_scan __P ((mailbox_t, size_t, size_t *)); | ||
68 | static int imap_scan0 __P ((mailbox_t, size_t, size_t *, int)); | ||
69 | static int imap_is_updated __P ((mailbox_t)); | ||
70 | static int imap_append_message __P ((mailbox_t, message_t)); | ||
71 | static int imap_append_message0 __P ((mailbox_t, message_t)); | ||
72 | static int imap_copy_message __P ((mailbox_t, message_t)); | ||
73 | |||
74 | /* message_t API. */ | ||
75 | static int imap_submessage_size __P ((msg_imap_t, size_t *)); | ||
76 | static int imap_message_size __P ((message_t, size_t *)); | ||
77 | static int imap_message_lines __P ((message_t, size_t *)); | ||
78 | static int imap_message_fd __P ((stream_t, int *)); | ||
79 | static int imap_message_read __P ((stream_t , char *, size_t, off_t, size_t *)); | ||
80 | static int imap_message_uid __P ((message_t, size_t *)); | ||
81 | |||
82 | /* mime_t API. */ | ||
83 | static int imap_is_multipart __P ((message_t, int *)); | ||
84 | static int imap_get_num_parts __P ((message_t, size_t *)); | ||
85 | static int imap_get_part __P ((message_t, size_t, message_t *)); | ||
86 | |||
87 | /* envelope_t API */ | ||
88 | static int imap_envelope_sender __P ((envelope_t, char *, size_t, size_t *)); | ||
89 | static int imap_envelope_date __P ((envelope_t, char *, size_t, size_t *)); | ||
90 | |||
91 | /* attribute_t API */ | ||
92 | static int imap_attr_get_flags __P ((attribute_t, int *)); | ||
93 | static int imap_attr_set_flags __P ((attribute_t, int)); | ||
94 | static int imap_attr_unset_flags __P ((attribute_t, int)); | ||
95 | |||
96 | /* header_t API. */ | ||
97 | static int imap_header_read __P ((header_t, char*, size_t, off_t, size_t *)); | ||
98 | static int imap_header_get_value __P ((header_t, const char*, char *, size_t, size_t *)); | ||
99 | static int imap_header_get_fvalue __P ((header_t, const char*, char *, size_t, size_t *)); | ||
100 | |||
101 | /* body_t API. */ | ||
102 | static int imap_body_read __P ((stream_t, char *, size_t, off_t, | ||
103 | size_t *)); | ||
104 | static int imap_body_size __P ((body_t, size_t *)); | ||
105 | static int imap_body_lines __P ((body_t, size_t *)); | ||
106 | static int imap_body_fd __P ((stream_t, int *)); | ||
107 | |||
108 | /* Helpers. */ | ||
109 | static int imap_get_fd __P ((msg_imap_t, int *)); | ||
110 | static int imap_get_message0 __P ((msg_imap_t, message_t *)); | ||
111 | static int fetch_operation __P ((f_imap_t, msg_imap_t, char *, size_t, size_t *)); | ||
112 | static void free_subparts __P ((msg_imap_t)); | ||
113 | static int flags_to_string __P ((char **, int)); | ||
114 | static int delete_to_string __P ((m_imap_t, char **)); | ||
115 | static int is_same_folder __P ((mailbox_t, message_t)); | ||
116 | |||
117 | /* Initialize the concrete object mailbox_t by overloading the function of the | ||
118 | structure. */ | ||
119 | int | ||
120 | _mailbox_imap_init (mailbox_t mailbox) | ||
121 | { | ||
122 | m_imap_t m_imap; | ||
123 | size_t name_len = 0; | ||
124 | folder_t folder = NULL; | ||
125 | |||
126 | assert(mailbox); | ||
127 | |||
128 | folder = mailbox->folder; | ||
129 | |||
130 | m_imap = mailbox->data = calloc (1, sizeof (*m_imap)); | ||
131 | if (m_imap == NULL) | ||
132 | return ENOMEM; | ||
133 | |||
134 | /* Retrieve the name of the mailbox from the URL. */ | ||
135 | url_get_path (mailbox->url, NULL, 0, &name_len); | ||
136 | if (name_len == 0) | ||
137 | { | ||
138 | /* name "INBOX" is the default. */ | ||
139 | m_imap->name = calloc (6, sizeof (char)); | ||
140 | strcpy (m_imap->name, "INBOX"); | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | m_imap->name = calloc (name_len + 1, sizeof (char)); | ||
145 | url_get_path (mailbox->url, m_imap->name, name_len + 1, NULL); | ||
146 | } | ||
147 | |||
148 | /* Overload the functions. */ | ||
149 | mailbox->_destroy = mailbox_imap_destroy; | ||
150 | |||
151 | mailbox->_open = mailbox_imap_open; | ||
152 | mailbox->_close = mailbox_imap_close; | ||
153 | |||
154 | /* Messages. */ | ||
155 | mailbox->_get_message = imap_get_message; | ||
156 | mailbox->_append_message = imap_append_message; | ||
157 | mailbox->_messages_count = imap_messages_count; | ||
158 | mailbox->_messages_recent = imap_messages_recent; | ||
159 | mailbox->_message_unseen = imap_message_unseen; | ||
160 | mailbox->_expunge = imap_expunge; | ||
161 | mailbox->_uidvalidity = imap_uidvalidity; | ||
162 | mailbox->_uidnext = imap_uidnext; | ||
163 | |||
164 | mailbox->_scan = imap_scan; | ||
165 | mailbox->_is_updated = imap_is_updated; | ||
166 | |||
167 | /* Get the back pointer of the concrete folder. */ | ||
168 | if (mailbox->folder) | ||
169 | m_imap->f_imap = mailbox->folder->data; | ||
170 | /* maibox back pointer. */ | ||
171 | m_imap->mailbox = mailbox; | ||
172 | |||
173 | /* Set our properties. */ | ||
174 | { | ||
175 | property_t property = NULL; | ||
176 | mailbox_get_property (mailbox, &property); | ||
177 | property_set_value (property, "TYPE", "IMAP4", 1); | ||
178 | } | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /* Recursive call to free all the subparts of a message. */ | ||
184 | static void | ||
185 | free_subparts (msg_imap_t msg_imap) | ||
186 | { | ||
187 | size_t i; | ||
188 | for (i = 0; i < msg_imap->num_parts; i++) | ||
189 | { | ||
190 | if (msg_imap->parts[i]) | ||
191 | free_subparts (msg_imap->parts[i]); | ||
192 | } | ||
193 | |||
194 | if (msg_imap->message) | ||
195 | message_destroy (&(msg_imap->message), msg_imap); | ||
196 | if (msg_imap->parts) | ||
197 | free (msg_imap->parts); | ||
198 | if (msg_imap->fheader) | ||
199 | header_destroy (&msg_imap->fheader, NULL); | ||
200 | if (msg_imap->internal_date) | ||
201 | free (msg_imap->internal_date); | ||
202 | free(msg_imap); | ||
203 | } | ||
204 | |||
205 | /* Give back all the resources. But it does not mean to shutdown the channel | ||
206 | this is done on the folder. */ | ||
207 | static void | ||
208 | mailbox_imap_destroy (mailbox_t mailbox) | ||
209 | { | ||
210 | if (mailbox->data) | ||
211 | { | ||
212 | m_imap_t m_imap = mailbox->data; | ||
213 | f_imap_t f_imap = m_imap->f_imap; | ||
214 | size_t i; | ||
215 | |||
216 | /* Deselect. */ | ||
217 | if (m_imap != f_imap->selected) | ||
218 | f_imap->selected = NULL; | ||
219 | |||
220 | monitor_wrlock (mailbox->monitor); | ||
221 | /* Destroy the imap messages and ressources associated to them. */ | ||
222 | for (i = 0; i < m_imap->imessages_count; i++) | ||
223 | { | ||
224 | if (m_imap->imessages[i]) | ||
225 | free_subparts (m_imap->imessages[i]); | ||
226 | } | ||
227 | if (m_imap->imessages) | ||
228 | free (m_imap->imessages); | ||
229 | if (m_imap->name) | ||
230 | free (m_imap->name); | ||
231 | free (m_imap); | ||
232 | mailbox->data = NULL; | ||
233 | monitor_unlock (mailbox->monitor); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | /* If the connection was not up it is open by the folder since the stream | ||
238 | socket is actually created by the folder. It is not necessary | ||
239 | to set select the mailbox right away, there are maybe on going operations. | ||
240 | But on any operation by a particular mailbox, it will be selected first. */ | ||
241 | static int | ||
242 | mailbox_imap_open (mailbox_t mailbox, int flags) | ||
243 | { | ||
244 | int status = 0; | ||
245 | m_imap_t m_imap = mailbox->data; | ||
246 | f_imap_t f_imap = m_imap->f_imap; | ||
247 | folder_t folder = f_imap->folder; | ||
248 | struct folder_list folders = { 0, 0 }; | ||
249 | |||
250 | /* m_imap must have been created during mailbox initialization. */ | ||
251 | assert (mailbox->data); | ||
252 | assert (m_imap->name); | ||
253 | |||
254 | mailbox->flags = flags; | ||
255 | |||
256 | if ((status = folder_open (mailbox->folder, flags))) | ||
257 | return status; | ||
258 | |||
259 | /* We might not have to SELECT the mailbox, but we need to know it | ||
260 | exists, and CREATE it if it doesn't, and CREATE is specified in | ||
261 | the flags. | ||
262 | */ | ||
263 | |||
264 | switch (m_imap->state) | ||
265 | { | ||
266 | case IMAP_NO_STATE: | ||
267 | m_imap->state = IMAP_LIST; | ||
268 | |||
269 | case IMAP_LIST: | ||
270 | status = folder_list (folder, NULL, m_imap->name, &folders); | ||
271 | if (status != 0) | ||
272 | { | ||
273 | if (status != EAGAIN && status != EINPROGRESS && status != EINTR) | ||
274 | m_imap->state = IMAP_NO_STATE; | ||
275 | |||
276 | return status; | ||
277 | } | ||
278 | m_imap->state = IMAP_NO_STATE; | ||
279 | if (folders.num) | ||
280 | return 0; | ||
281 | |||
282 | if ((flags & MU_STREAM_CREAT) == 0) | ||
283 | return ENOENT; | ||
284 | |||
285 | m_imap->state = IMAP_CREATE; | ||
286 | |||
287 | case IMAP_CREATE: | ||
288 | switch (f_imap->state) | ||
289 | { | ||
290 | case IMAP_NO_STATE: | ||
291 | { | ||
292 | char *path; | ||
293 | size_t len; | ||
294 | url_get_path (folder->url, NULL, 0, &len); | ||
295 | if (len == 0) | ||
296 | return 0; | ||
297 | path = calloc (len + 1, sizeof (*path)); | ||
298 | if (path == NULL) | ||
299 | return ENOMEM; | ||
300 | url_get_path (folder->url, path, len + 1, NULL); | ||
301 | status = imap_writeline (f_imap, "g%u CREATE %s\r\n", | ||
302 | f_imap->seq, path); | ||
303 | MAILBOX_DEBUG2 (folder, MU_DEBUG_PROT, "g%u CREATE %s\n", | ||
304 | f_imap->seq, path); | ||
305 | f_imap->seq++; | ||
306 | free (path); | ||
307 | if (status != 0) | ||
308 | { | ||
309 | m_imap->state = f_imap->state = IMAP_NO_STATE; | ||
310 | return status; | ||
311 | } | ||
312 | f_imap->state = IMAP_CREATE; | ||
313 | } | ||
314 | |||
315 | case IMAP_CREATE: | ||
316 | status = imap_send (f_imap); | ||
317 | if (status != 0) | ||
318 | { | ||
319 | if (status != EAGAIN && status != EINPROGRESS | ||
320 | && status != EINTR) | ||
321 | m_imap->state = f_imap->state = IMAP_NO_STATE; | ||
322 | |||
323 | return status; | ||
324 | } | ||
325 | f_imap->state = IMAP_CREATE_ACK; | ||
326 | |||
327 | case IMAP_CREATE_ACK: | ||
328 | status = imap_parse (f_imap); | ||
329 | if (status != 0) | ||
330 | { | ||
331 | if (status == EINVAL) | ||
332 | status = EACCES; | ||
333 | |||
334 | if (status != EAGAIN && status != EINPROGRESS | ||
335 | && status != EINTR) | ||
336 | m_imap->state = f_imap->state = IMAP_NO_STATE; | ||
337 | |||
338 | return status; | ||
339 | } | ||
340 | f_imap->state = IMAP_NO_STATE; | ||
341 | |||
342 | default: | ||
343 | status = EINVAL; | ||
344 | break; | ||
345 | } | ||
346 | m_imap->state = IMAP_NO_STATE; | ||
347 | break; | ||
348 | |||
349 | default: | ||
350 | status = EINVAL; | ||
351 | break; | ||
352 | } | ||
353 | |||
354 | return status; | ||
355 | } | ||
356 | |||
357 | /* We can not close the folder in term of shuting down the connection but if | ||
358 | we were the selected mailbox we send the close and deselect ourself. | ||
359 | The CLOSE is also use to expunge instead of sending expunge. */ | ||
360 | static int | ||
361 | mailbox_imap_close (mailbox_t mailbox) | ||
362 | { | ||
363 | m_imap_t m_imap = mailbox->data; | ||
364 | f_imap_t f_imap = m_imap->f_imap; | ||
365 | int status = 0; | ||
366 | |||
367 | /* If we are not the selected mailbox, just close the stream. */ | ||
368 | if (m_imap != f_imap->selected) | ||
369 | return folder_close (mailbox->folder); | ||
370 | |||
371 | /* Select first. */ | ||
372 | status = imap_messages_count (mailbox, NULL); | ||
373 | if (status != 0) | ||
374 | return status; | ||
375 | |||
376 | switch (f_imap->state) | ||
377 | { | ||
378 | case IMAP_NO_STATE: | ||
379 | status = imap_writeline (f_imap, "g%d CLOSE\r\n", f_imap->seq++); | ||
380 | CHECK_ERROR (f_imap, status); | ||
381 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
382 | f_imap->state = IMAP_CLOSE; | ||
383 | |||
384 | case IMAP_CLOSE: | ||
385 | status = imap_send (f_imap); | ||
386 | CHECK_EAGAIN (f_imap, status); | ||
387 | f_imap->state = IMAP_CLOSE_ACK; | ||
388 | |||
389 | case IMAP_CLOSE_ACK: | ||
390 | { | ||
391 | size_t i; | ||
392 | status = imap_parse (f_imap); | ||
393 | CHECK_EAGAIN (f_imap, status); | ||
394 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
395 | |||
396 | monitor_wrlock (mailbox->monitor); | ||
397 | /* Destroy the imap messages and ressources associated to them. */ | ||
398 | for (i = 0; i < m_imap->imessages_count; i++) | ||
399 | { | ||
400 | if (m_imap->imessages[i]) | ||
401 | free_subparts (m_imap->imessages[i]); | ||
402 | } | ||
403 | if (m_imap->imessages) | ||
404 | free (m_imap->imessages); | ||
405 | m_imap->imessages = NULL; | ||
406 | m_imap->imessages_count = 0; | ||
407 | m_imap->messages_count = 0; | ||
408 | m_imap->recent = 0; | ||
409 | m_imap->unseen = 0; | ||
410 | /* Clear the callback string structure. */ | ||
411 | stream_truncate (f_imap->string.stream, 0); | ||
412 | f_imap->string.offset = 0; | ||
413 | f_imap->string.nleft = 0; | ||
414 | f_imap->string.type = IMAP_NO_STATE; | ||
415 | f_imap->string.msg_imap = NULL; | ||
416 | monitor_unlock (mailbox->monitor); | ||
417 | } | ||
418 | break; | ||
419 | |||
420 | default: | ||
421 | /* mu_error ("imap_close unknown state: reconnect\n");*/ | ||
422 | break; | ||
423 | } | ||
424 | |||
425 | /* Deselect. */ | ||
426 | f_imap->selected = NULL; | ||
427 | |||
428 | f_imap->state = IMAP_NO_STATE; | ||
429 | return folder_close (mailbox->folder); | ||
430 | } | ||
431 | |||
432 | /* Construction of the message_t, nothing else is done then this setup. To | ||
433 | clarify this is different from say message_get_part(). This call is for the | ||
434 | mailbox and we are setting up the message_t structure. */ | ||
435 | static int | ||
436 | imap_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg) | ||
437 | { | ||
438 | m_imap_t m_imap = mailbox->data; | ||
439 | msg_imap_t msg_imap; | ||
440 | int status = 0; | ||
441 | |||
442 | if (pmsg == NULL || msgno == 0 || msgno > m_imap->messages_count) | ||
443 | return EINVAL; | ||
444 | |||
445 | /* Check to see if we have already this message. */ | ||
446 | monitor_rdlock (mailbox->monitor); | ||
447 | { | ||
448 | size_t i; | ||
449 | for (i = 0; i < m_imap->imessages_count; i++) | ||
450 | { | ||
451 | if (m_imap->imessages[i]) | ||
452 | { | ||
453 | if (m_imap->imessages[i]->num == msgno) | ||
454 | { | ||
455 | *pmsg = m_imap->imessages[i]->message; | ||
456 | monitor_unlock (mailbox->monitor); | ||
457 | return 0; | ||
458 | } | ||
459 | } | ||
460 | } | ||
461 | } | ||
462 | monitor_unlock (mailbox->monitor); | ||
463 | |||
464 | /* Allocate a concrete imap message. */ | ||
465 | msg_imap = calloc (1, sizeof *msg_imap); | ||
466 | if (msg_imap == NULL) | ||
467 | return ENOMEM; | ||
468 | /* Back pointer. */ | ||
469 | msg_imap->m_imap = m_imap; | ||
470 | msg_imap->num = msgno; | ||
471 | status = imap_get_message0 (msg_imap, pmsg); | ||
472 | if (status == 0) | ||
473 | { | ||
474 | /* Add it to the list. */ | ||
475 | monitor_wrlock (mailbox->monitor); | ||
476 | { | ||
477 | msg_imap_t *m ; | ||
478 | m = realloc (m_imap->imessages, | ||
479 | (m_imap->imessages_count + 1) * sizeof *m); | ||
480 | if (m == NULL) | ||
481 | { | ||
482 | message_destroy (pmsg, msg_imap); | ||
483 | monitor_unlock (mailbox->monitor); | ||
484 | return ENOMEM; | ||
485 | } | ||
486 | m_imap->imessages = m; | ||
487 | m_imap->imessages[m_imap->imessages_count] = msg_imap; | ||
488 | m_imap->imessages_count++; | ||
489 | } | ||
490 | monitor_unlock (mailbox->monitor); | ||
491 | |||
492 | msg_imap->message = *pmsg; | ||
493 | } | ||
494 | else | ||
495 | free (msg_imap); | ||
496 | return status; | ||
497 | } | ||
498 | |||
499 | /* Set all the message_t functions and parts. */ | ||
500 | static int | ||
501 | imap_get_message0 (msg_imap_t msg_imap, message_t *pmsg) | ||
502 | { | ||
503 | int status = 0; | ||
504 | message_t msg = NULL; | ||
505 | mailbox_t mailbox = msg_imap->m_imap->mailbox; | ||
506 | |||
507 | /* Create the message and its stream. */ | ||
508 | { | ||
509 | stream_t stream = NULL; | ||
510 | if ((status = message_create (&msg, msg_imap)) != 0 | ||
511 | || (status = stream_create (&stream, mailbox->flags, msg)) != 0) | ||
512 | { | ||
513 | stream_destroy (&stream, msg); | ||
514 | message_destroy (&msg, msg_imap); | ||
515 | return status; | ||
516 | } | ||
517 | stream_setbufsiz (stream, 128); | ||
518 | stream_set_read (stream, imap_message_read, msg); | ||
519 | stream_set_fd (stream, imap_message_fd, msg); | ||
520 | message_set_stream (msg, stream, msg_imap); | ||
521 | message_set_size (msg, imap_message_size, msg_imap); | ||
522 | message_set_lines (msg, imap_message_lines, msg_imap); | ||
523 | } | ||
524 | |||
525 | /* Create the header. */ | ||
526 | { | ||
527 | header_t header = NULL; | ||
528 | if ((status = header_create (&header, NULL, 0, msg)) != 0) | ||
529 | { | ||
530 | message_destroy (&msg, msg_imap); | ||
531 | return status; | ||
532 | } | ||
533 | header_set_fill (header, imap_header_read, msg); | ||
534 | header_set_get_value (header, imap_header_get_value, msg); | ||
535 | header_set_get_fvalue (header, imap_header_get_fvalue, msg); | ||
536 | message_set_header (msg, header, msg_imap); | ||
537 | } | ||
538 | |||
539 | /* Create the attribute. */ | ||
540 | { | ||
541 | attribute_t attribute; | ||
542 | status = attribute_create (&attribute, msg); | ||
543 | if (status != 0) | ||
544 | { | ||
545 | message_destroy (&msg, msg_imap); | ||
546 | return status; | ||
547 | } | ||
548 | attribute_set_get_flags (attribute, imap_attr_get_flags, msg); | ||
549 | attribute_set_set_flags (attribute, imap_attr_set_flags, msg); | ||
550 | attribute_set_unset_flags (attribute, imap_attr_unset_flags, msg); | ||
551 | message_set_attribute (msg, attribute, msg_imap); | ||
552 | } | ||
553 | |||
554 | /* Create the body and its stream. */ | ||
555 | { | ||
556 | body_t body = NULL; | ||
557 | stream_t stream = NULL; | ||
558 | if ((status = body_create (&body, msg)) != 0 | ||
559 | || (status = stream_create (&stream, mailbox->flags, body)) != 0) | ||
560 | { | ||
561 | body_destroy (&body, msg); | ||
562 | stream_destroy (&stream, body); | ||
563 | message_destroy (&msg, msg_imap); | ||
564 | return status; | ||
565 | } | ||
566 | stream_setbufsiz (stream, 128); | ||
567 | stream_set_read (stream, imap_body_read, body); | ||
568 | stream_set_fd (stream, imap_body_fd, body); | ||
569 | body_set_size (body, imap_body_size, msg); | ||
570 | body_set_lines (body, imap_body_lines, msg); | ||
571 | body_set_stream (body, stream, msg); | ||
572 | message_set_body (msg, body, msg_imap); | ||
573 | } | ||
574 | |||
575 | /* Set the envelope. */ | ||
576 | { | ||
577 | envelope_t envelope= NULL; | ||
578 | status = envelope_create (&envelope, msg); | ||
579 | if (status != 0) | ||
580 | { | ||
581 | message_destroy (&msg, msg_imap); | ||
582 | return status; | ||
583 | } | ||
584 | envelope_set_sender (envelope, imap_envelope_sender, msg); | ||
585 | envelope_set_date (envelope, imap_envelope_date, msg); | ||
586 | message_set_envelope (msg, envelope, msg_imap); | ||
587 | } | ||
588 | |||
589 | /* Set the mime handling. */ | ||
590 | message_set_is_multipart (msg, imap_is_multipart, msg_imap); | ||
591 | message_set_get_num_parts (msg, imap_get_num_parts, msg_imap); | ||
592 | message_set_get_part (msg, imap_get_part, msg_imap); | ||
593 | |||
594 | /* Set the UID on the message. */ | ||
595 | message_set_uid (msg, imap_message_uid, msg_imap); | ||
596 | message_set_mailbox (msg, mailbox, msg_imap); | ||
597 | |||
598 | /* We are done here. */ | ||
599 | *pmsg = msg; | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | static int | ||
604 | imap_message_unseen (mailbox_t mailbox, size_t *punseen) | ||
605 | { | ||
606 | m_imap_t m_imap = mailbox->data; | ||
607 | *punseen = m_imap->unseen; | ||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | static int | ||
612 | imap_messages_recent (mailbox_t mailbox, size_t *precent) | ||
613 | { | ||
614 | m_imap_t m_imap = mailbox->data; | ||
615 | *precent = m_imap->recent; | ||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int | ||
620 | imap_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity) | ||
621 | { | ||
622 | m_imap_t m_imap = mailbox->data; | ||
623 | *puidvalidity = m_imap->uidvalidity; | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static int | ||
628 | imap_uidnext (mailbox_t mailbox, size_t *puidnext) | ||
629 | { | ||
630 | m_imap_t m_imap = mailbox->data; | ||
631 | *puidnext = m_imap->uidnext; | ||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | /* There is no explicit call to get the message count. The count is send on | ||
636 | a SELECT/EXAMINE command it is also sent async, meaning it will be piggy | ||
637 | back on other server response as an untag "EXIST" response. The | ||
638 | function is also use as a way to select mailbox by other functions. */ | ||
639 | static int | ||
640 | imap_messages_count (mailbox_t mailbox, size_t *pnum) | ||
641 | { | ||
642 | m_imap_t m_imap = mailbox->data; | ||
643 | f_imap_t f_imap = m_imap->f_imap; | ||
644 | int status = 0; | ||
645 | |||
646 | /* FIXME: It is debatable if we should reconnect when the connection | ||
647 | timeout or die. Probably for timeout client should ping i.e. send | ||
648 | a NOOP via imap_is_updated() function to keep the connection alive. */ | ||
649 | status = folder_open (mailbox->folder, mailbox->flags); | ||
650 | if (status != 0) | ||
651 | return status; | ||
652 | |||
653 | /* Are we already selected ? */ | ||
654 | if (m_imap == (f_imap->selected)) | ||
655 | { | ||
656 | if (pnum) | ||
657 | *pnum = m_imap->messages_count; | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | /* Put the mailbox as selected. */ | ||
662 | f_imap->selected = m_imap; | ||
663 | |||
664 | switch (f_imap->state) | ||
665 | { | ||
666 | case IMAP_NO_STATE: | ||
667 | status = imap_writeline (f_imap, "g%d SELECT %s\r\n", | ||
668 | f_imap->seq++, m_imap->name); | ||
669 | CHECK_ERROR (f_imap, status); | ||
670 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
671 | f_imap->state = IMAP_SELECT; | ||
672 | |||
673 | case IMAP_SELECT: | ||
674 | status = imap_send (f_imap); | ||
675 | CHECK_EAGAIN (f_imap, status); | ||
676 | f_imap->state = IMAP_SELECT_ACK; | ||
677 | |||
678 | case IMAP_SELECT_ACK: | ||
679 | status = imap_parse (f_imap); | ||
680 | CHECK_EAGAIN (f_imap, status); | ||
681 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
682 | break; | ||
683 | |||
684 | default: | ||
685 | /*mu_error ("imap_message_count unknown state: reconnect\n");*/ | ||
686 | break; | ||
687 | } | ||
688 | |||
689 | if (pnum) | ||
690 | *pnum = m_imap->messages_count; | ||
691 | |||
692 | f_imap->state = IMAP_NO_STATE; | ||
693 | return status; | ||
694 | } | ||
695 | |||
696 | static int | ||
697 | imap_scan (mailbox_t mailbox, size_t msgno, size_t *pcount) | ||
698 | { | ||
699 | return imap_scan0 (mailbox, msgno, pcount , 1); | ||
700 | } | ||
701 | |||
702 | /* Usually when this function is call it is because there is an oberver | ||
703 | attach an the client is try to build some sort of list/tree header | ||
704 | as the scanning progress. But doing this for each message can be | ||
705 | time consuming and inefficient. So we bundle all the request | ||
706 | into one and ask the server for everything "FETCH 1:*". The good | ||
707 | side is that everything will be faster and we do not do lot of small | ||
708 | transcation but rather a big one. The bad thing is that every thing | ||
709 | will be cache in the structure using a lot of memory. */ | ||
710 | static int | ||
711 | imap_scan0 (mailbox_t mailbox, size_t msgno, size_t *pcount, int notif) | ||
712 | { | ||
713 | int status; | ||
714 | size_t i; | ||
715 | size_t count = 0; | ||
716 | m_imap_t m_imap = mailbox->data; | ||
717 | f_imap_t f_imap = m_imap->f_imap; | ||
718 | |||
719 | /* Selected. */ | ||
720 | status = imap_messages_count (mailbox, &count); | ||
721 | if (pcount) | ||
722 | *pcount = count; | ||
723 | if (status != 0) | ||
724 | return status; | ||
725 | |||
726 | /* No need to scan, there is no messages. */ | ||
727 | if (count == 0) | ||
728 | return 0; | ||
729 | |||
730 | switch (f_imap->state) | ||
731 | { | ||
732 | case IMAP_NO_STATE: | ||
733 | status = imap_writeline (f_imap, | ||
734 | "g%d FETCH 1:* (FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])\r\n", | ||
735 | f_imap->seq++, MU_IMAP_CACHE_HEADERS); | ||
736 | CHECK_ERROR (f_imap, status); | ||
737 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
738 | f_imap->state = IMAP_SCAN; | ||
739 | |||
740 | case IMAP_SCAN: | ||
741 | status = imap_send (f_imap); | ||
742 | CHECK_EAGAIN (f_imap, status); | ||
743 | f_imap->state = IMAP_SCAN_ACK; | ||
744 | /* Clear the callback string structure. */ | ||
745 | stream_truncate (f_imap->string.stream, 0); | ||
746 | f_imap->string.offset = 0; | ||
747 | f_imap->string.nleft = 0; | ||
748 | f_imap->string.type = IMAP_NO_STATE; | ||
749 | f_imap->string.msg_imap = NULL; | ||
750 | |||
751 | case IMAP_SCAN_ACK: | ||
752 | status = imap_parse (f_imap); | ||
753 | CHECK_EAGAIN (f_imap, status); | ||
754 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
755 | /* Clear the callback string structure. */ | ||
756 | stream_truncate (f_imap->string.stream, 0); | ||
757 | f_imap->string.offset = 0; | ||
758 | f_imap->string.nleft = 0; | ||
759 | f_imap->string.type = IMAP_NO_STATE; | ||
760 | f_imap->string.msg_imap = NULL; | ||
761 | break; | ||
762 | |||
763 | default: | ||
764 | /*mu_error ("imap_scan unknown state: reconnect\n");*/ | ||
765 | return EINVAL; | ||
766 | } | ||
767 | |||
768 | f_imap->state = IMAP_NO_STATE; | ||
769 | |||
770 | /* Do not send notifications. */ | ||
771 | if (!notif) | ||
772 | return 0; | ||
773 | |||
774 | /* If no callbacks bail out early. */ | ||
775 | if (mailbox->observable == NULL) | ||
776 | return 0; | ||
777 | |||
778 | for (i = msgno; i <= count; i++) | ||
779 | { | ||
780 | if (observable_notify (mailbox->observable, MU_EVT_MESSAGE_ADD) != 0) | ||
781 | break; | ||
782 | if (((i + 1) % 100) == 0) | ||
783 | { | ||
784 | observable_notify (mailbox->observable, MU_EVT_MAILBOX_PROGRESS); | ||
785 | } | ||
786 | } | ||
787 | return 0; | ||
788 | } | ||
789 | |||
790 | /* Send a NOOP and see if the count has changed. */ | ||
791 | static int | ||
792 | imap_is_updated (mailbox_t mailbox) | ||
793 | { | ||
794 | m_imap_t m_imap = mailbox->data; | ||
795 | size_t oldcount = m_imap->messages_count; | ||
796 | f_imap_t f_imap = m_imap->f_imap; | ||
797 | int status = 0; | ||
798 | |||
799 | /* Selected. */ | ||
800 | status = imap_messages_count (mailbox, &oldcount); | ||
801 | if (status != 0) | ||
802 | return status; | ||
803 | |||
804 | /* Send a noop, and let imap piggy pack the information. */ | ||
805 | switch (f_imap->state) | ||
806 | { | ||
807 | case IMAP_NO_STATE: | ||
808 | status = imap_writeline (f_imap, "g%d NOOP\r\n", f_imap->seq++); | ||
809 | CHECK_ERROR (f_imap, status); | ||
810 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
811 | f_imap->state = IMAP_NOOP; | ||
812 | |||
813 | case IMAP_NOOP: | ||
814 | status = imap_send (f_imap); | ||
815 | CHECK_EAGAIN (f_imap, status); | ||
816 | f_imap->state = IMAP_NOOP_ACK; | ||
817 | |||
818 | case IMAP_NOOP_ACK: | ||
819 | status = imap_parse (f_imap); | ||
820 | CHECK_EAGAIN (f_imap, status); | ||
821 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
822 | break; | ||
823 | |||
824 | default: | ||
825 | /*mu_error ("imap_noop unknown state: reconnect\n"); */ | ||
826 | break; | ||
827 | } | ||
828 | f_imap->state = IMAP_NO_STATE; | ||
829 | return (oldcount == m_imap->messages_count); | ||
830 | } | ||
831 | |||
832 | |||
833 | /* It is only here that the Deleted flags are sent. Expunge is not | ||
834 | call rather the mailbox is close explicitely, letting the server | ||
835 | do the expunge without sending the notifications. It's faster. */ | ||
836 | static int | ||
837 | imap_expunge (mailbox_t mailbox) | ||
838 | { | ||
839 | int status; | ||
840 | m_imap_t m_imap = mailbox->data; | ||
841 | f_imap_t f_imap = m_imap->f_imap; | ||
842 | |||
843 | /* Select first. */ | ||
844 | status = imap_messages_count (mailbox, NULL); | ||
845 | if (status != 0) | ||
846 | return status; | ||
847 | |||
848 | switch (f_imap->state) | ||
849 | { | ||
850 | case IMAP_NO_STATE: | ||
851 | { | ||
852 | char *set = NULL; | ||
853 | status = delete_to_string (m_imap, &set); | ||
854 | CHECK_ERROR (f_imap, status); | ||
855 | if (set == NULL || *set == '\0') | ||
856 | { | ||
857 | if (set) | ||
858 | free (set); | ||
859 | return 0; | ||
860 | } | ||
861 | status = imap_writeline (f_imap, | ||
862 | "g%d STORE %s +FLAGS.SILENT (\\Deleted)\r\n", | ||
863 | f_imap->seq++, set); | ||
864 | free (set); | ||
865 | CHECK_ERROR (f_imap, status); | ||
866 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
867 | f_imap->state = IMAP_STORE; | ||
868 | } | ||
869 | |||
870 | /* Send DELETE. */ | ||
871 | case IMAP_STORE: | ||
872 | status = imap_send (f_imap); | ||
873 | CHECK_EAGAIN (f_imap, status); | ||
874 | f_imap->state = IMAP_STORE_ACK; | ||
875 | |||
876 | case IMAP_STORE_ACK: | ||
877 | status = imap_parse (f_imap); | ||
878 | CHECK_EAGAIN (f_imap, status); | ||
879 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
880 | f_imap->state = IMAP_NO_STATE; | ||
881 | |||
882 | /* We are not sending EXPUNGE, rather we close the mailbox | ||
883 | which will purge. */ | ||
884 | case IMAP_CLOSE: | ||
885 | case IMAP_CLOSE_ACK: | ||
886 | status = mailbox_imap_close (mailbox); | ||
887 | CHECK_EAGAIN (f_imap, status); | ||
888 | |||
889 | /* Rescan after expunging but do not trigger the observers. */ | ||
890 | case IMAP_SCAN: | ||
891 | case IMAP_SCAN_ACK: | ||
892 | status = imap_scan0 (mailbox, 1, NULL, 0); | ||
893 | CHECK_EAGAIN (f_imap, status); | ||
894 | |||
895 | default: | ||
896 | /* mu_error ("imap_expunge: unknow state\n"); */ | ||
897 | break; | ||
898 | } | ||
899 | |||
900 | return status; | ||
901 | } | ||
902 | |||
903 | /* FIXME: Not ___Nonblocking___ safe. */ | ||
904 | /* DANGER: The message_t object makes no guaranty about the size and the lines | ||
905 | that it returns, if its pointing to non-local file messages, so we | ||
906 | make a local copy. */ | ||
907 | static int | ||
908 | imap_append_message (mailbox_t mailbox, message_t msg) | ||
909 | { | ||
910 | int status = 0; | ||
911 | m_imap_t m_imap = mailbox->data; | ||
912 | f_imap_t f_imap = m_imap->f_imap; | ||
913 | |||
914 | /* FIXME: It is debatable if we should reconnect when the connection | ||
915 | timeout or die. For timeout client should ping i.e. send | ||
916 | a NOOP via imap_is_updated() function to keep the connection alive. */ | ||
917 | status = folder_open (mailbox->folder, mailbox->flags); | ||
918 | if (status != 0) | ||
919 | return status; | ||
920 | |||
921 | /* FIXME: Can we append to self. */ | ||
922 | |||
923 | /* Check to see if we are selected. If the message was not modified | ||
924 | and came from the same imap folder. use COPY.*/ | ||
925 | if (f_imap->selected != m_imap && !message_is_modified (msg) | ||
926 | && is_same_folder (mailbox, msg)) | ||
927 | return imap_copy_message (mailbox, msg); | ||
928 | |||
929 | /* copy the message to local disk by createing a floating message. */ | ||
930 | { | ||
931 | message_t message = NULL; | ||
932 | |||
933 | status = message_create_copy(&message, msg); | ||
934 | |||
935 | if (status == 0) | ||
936 | status = imap_append_message0 (mailbox, message); | ||
937 | message_destroy (&message, NULL); | ||
938 | } | ||
939 | return status; | ||
940 | } | ||
941 | |||
942 | /* Ok this mean that the message is coming from somewhere else. IMAP | ||
943 | is very susceptible on the size, example: | ||
944 | A003 APPEND saved-messages (\Seen) {310} | ||
945 | if the server does not get the right size advertise in the string literal | ||
946 | it will misbehave. Sine we are assuming that the message will be | ||
947 | in native file system format meaning ending with NEWLINE, we will have | ||
948 | to do the calculation. But what is worse; the value return | ||
949 | by message_size () and message_lines () are no mean exact but rather | ||
950 | a gross approximation for certain type of mailbox. So the sane | ||
951 | thing to do is to save the message in temporary file, this we say | ||
952 | we guarantee the size of the message. */ | ||
953 | static int | ||
954 | imap_append_message0 (mailbox_t mailbox, message_t msg) | ||
955 | { | ||
956 | size_t total; | ||
957 | int status = 0; | ||
958 | m_imap_t m_imap = mailbox->data; | ||
959 | f_imap_t f_imap = m_imap->f_imap; | ||
960 | |||
961 | switch (f_imap->state) | ||
962 | { | ||
963 | case IMAP_NO_STATE: | ||
964 | { | ||
965 | size_t lines, size; | ||
966 | char *path; | ||
967 | char *abuf = malloc (1); | ||
968 | /* Get the desired flags attribute. */ | ||
969 | if (abuf == NULL) | ||
970 | return ENOMEM; | ||
971 | *abuf = '\0'; | ||
972 | { | ||
973 | attribute_t attribute = NULL; | ||
974 | int flags = 0; | ||
975 | message_get_attribute (msg, &attribute); | ||
976 | attribute_get_flags (attribute, &flags); | ||
977 | status = flags_to_string (&abuf, flags); | ||
978 | if (status != 0) | ||
979 | return status; | ||
980 | /* Put the surrounding parenthesis, wu-IMAP is sensible to this. */ | ||
981 | { | ||
982 | char *tmp = calloc (strlen (abuf) + 3, 1); | ||
983 | if (tmp == NULL) | ||
984 | { | ||
985 | free (abuf); | ||
986 | return ENOMEM; | ||
987 | } | ||
988 | sprintf (tmp, "(%s)", abuf); | ||
989 | free (abuf); | ||
990 | abuf = tmp; | ||
991 | } | ||
992 | } | ||
993 | |||
994 | /* Get the mailbox filepath. */ | ||
995 | { | ||
996 | size_t n = 0; | ||
997 | url_get_path (mailbox->url, NULL, 0, &n); | ||
998 | if (n == 0) | ||
999 | { | ||
1000 | if (!(path = strdup ("INBOX"))) | ||
1001 | { | ||
1002 | free (abuf); | ||
1003 | return ENOMEM; | ||
1004 | } | ||
1005 | } | ||
1006 | else | ||
1007 | { | ||
1008 | path = calloc (n + 1, sizeof (*path)); | ||
1009 | if (path == NULL) | ||
1010 | { | ||
1011 | free (abuf); | ||
1012 | return ENOMEM; | ||
1013 | } | ||
1014 | url_get_path (mailbox->url, path, n + 1, NULL); | ||
1015 | } | ||
1016 | } | ||
1017 | |||
1018 | /* FIXME: we need to get the envelope_date and use it. | ||
1019 | currently it is ignored. */ | ||
1020 | |||
1021 | /* Get the total size, assuming that it is in UNIX format. */ | ||
1022 | lines = size = 0; | ||
1023 | message_size (msg, &size); | ||
1024 | message_lines (msg, &lines); | ||
1025 | total = size + lines; | ||
1026 | status = imap_writeline (f_imap, "g%d APPEND %s %s {%d}\r\n", | ||
1027 | f_imap->seq++, path, abuf, size + lines); | ||
1028 | free (abuf); | ||
1029 | free (path); | ||
1030 | CHECK_ERROR (f_imap, status); | ||
1031 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1032 | f_imap->state = IMAP_APPEND; | ||
1033 | } | ||
1034 | |||
1035 | case IMAP_APPEND: | ||
1036 | status = imap_send (f_imap); | ||
1037 | CHECK_EAGAIN (f_imap, status); | ||
1038 | f_imap->state = IMAP_APPEND_CONT; | ||
1039 | |||
1040 | case IMAP_APPEND_CONT: | ||
1041 | status = imap_parse (f_imap); | ||
1042 | CHECK_EAGAIN (f_imap, status); | ||
1043 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1044 | /* If we did not receive the continuation token, it is an error | ||
1045 | bail out. */ | ||
1046 | if (f_imap->buffer[0] != '+') | ||
1047 | { | ||
1048 | status = EACCES; | ||
1049 | break; | ||
1050 | } | ||
1051 | f_imap->state = IMAP_APPEND_SEND; | ||
1052 | |||
1053 | case IMAP_APPEND_SEND: | ||
1054 | { | ||
1055 | stream_t stream = NULL; | ||
1056 | off_t off = 0; | ||
1057 | size_t n = 0; | ||
1058 | char buffer[255]; | ||
1059 | message_get_stream (msg, &stream); | ||
1060 | while (stream_readline (stream, buffer, sizeof buffer, off, &n) == 0 | ||
1061 | && n > 0) | ||
1062 | { | ||
1063 | if (buffer[n - 1] == '\n') | ||
1064 | { | ||
1065 | buffer[n - 1] = '\0'; | ||
1066 | status = imap_writeline (f_imap, "%s\r\n", buffer); | ||
1067 | } | ||
1068 | else | ||
1069 | imap_writeline (f_imap, "%s", buffer); | ||
1070 | off += n; | ||
1071 | status = imap_send (f_imap); | ||
1072 | CHECK_EAGAIN (f_imap, status); | ||
1073 | } | ||
1074 | f_imap->state = IMAP_APPEND_ACK; | ||
1075 | } | ||
1076 | /* !@#%$ UW-IMAP server hack: insists on the last line. */ | ||
1077 | imap_writeline (f_imap, "\n"); | ||
1078 | status = imap_send (f_imap); | ||
1079 | CHECK_EAGAIN (f_imap, status); | ||
1080 | |||
1081 | case IMAP_APPEND_ACK: | ||
1082 | status = imap_parse (f_imap); | ||
1083 | CHECK_EAGAIN (f_imap, status); | ||
1084 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1085 | |||
1086 | default: | ||
1087 | /* mu_error ("imap_append: unknown state\n"); */ | ||
1088 | break; | ||
1089 | } | ||
1090 | f_imap->state = IMAP_NO_STATE; | ||
1091 | return status; | ||
1092 | } | ||
1093 | |||
1094 | /* If the message is on the same server. Use the COPY command much more | ||
1095 | efficient. */ | ||
1096 | static int | ||
1097 | imap_copy_message (mailbox_t mailbox, message_t msg) | ||
1098 | { | ||
1099 | m_imap_t m_imap = mailbox->data; | ||
1100 | f_imap_t f_imap = m_imap->f_imap; | ||
1101 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1102 | int status = 0; | ||
1103 | |||
1104 | /* FIXME: It is debatable if we should reconnect when the connection | ||
1105 | timeout or die. For timeout client should ping i.e. send | ||
1106 | a NOOP via imap_is_updated() function to keep the connection alive. */ | ||
1107 | status = folder_open (mailbox->folder, mailbox->flags); | ||
1108 | if (status != 0) | ||
1109 | return status; | ||
1110 | |||
1111 | switch (f_imap->state) | ||
1112 | { | ||
1113 | case IMAP_NO_STATE: | ||
1114 | { | ||
1115 | char *path; | ||
1116 | size_t n = 0; | ||
1117 | /* Check for a valid mailbox name. */ | ||
1118 | url_get_path (mailbox->url, NULL, 0, &n); | ||
1119 | if (n == 0) | ||
1120 | return EINVAL; | ||
1121 | path = calloc (n + 1, sizeof (*path)); | ||
1122 | if (path == NULL) | ||
1123 | return ENOMEM; | ||
1124 | url_get_path (mailbox->url, path, n + 1, NULL); | ||
1125 | status = imap_writeline (f_imap, "g%d COPY %d %s\r\n", f_imap->seq++, | ||
1126 | msg_imap->num, path); | ||
1127 | free (path); | ||
1128 | CHECK_ERROR (f_imap, status); | ||
1129 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1130 | f_imap->state = IMAP_COPY; | ||
1131 | } | ||
1132 | |||
1133 | case IMAP_COPY: | ||
1134 | status = imap_send (f_imap); | ||
1135 | CHECK_EAGAIN (f_imap, status); | ||
1136 | f_imap->state = IMAP_COPY_ACK; | ||
1137 | |||
1138 | case IMAP_COPY_ACK: | ||
1139 | status = imap_parse (f_imap); | ||
1140 | CHECK_EAGAIN (f_imap, status); | ||
1141 | MAILBOX_DEBUG0 (mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1142 | |||
1143 | default: | ||
1144 | break; | ||
1145 | } | ||
1146 | f_imap->state = IMAP_NO_STATE; | ||
1147 | return status; | ||
1148 | } | ||
1149 | |||
1150 | /* Message read overload */ | ||
1151 | static int | ||
1152 | imap_message_read (stream_t stream, char *buffer, size_t buflen, | ||
1153 | off_t offset, size_t *plen) | ||
1154 | { | ||
1155 | message_t msg = stream_get_owner (stream); | ||
1156 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1157 | m_imap_t m_imap = msg_imap->m_imap; | ||
1158 | f_imap_t f_imap = m_imap->f_imap; | ||
1159 | char *oldbuf = NULL; | ||
1160 | char newbuf[2]; | ||
1161 | int status; | ||
1162 | |||
1163 | /* This is so annoying, a buffer len of 1 is a killer. If you have for | ||
1164 | example "\n" to retrieve from the server, IMAP will transform this to | ||
1165 | "\r\n" and since you ask for only 1, the server will send '\r' only. | ||
1166 | And ... '\r' will be stripped by (imap_readline()) the number of char | ||
1167 | read will be 0 which means we're done .... sigh ... So we guard by at | ||
1168 | least ask for 2 chars. */ | ||
1169 | if (buflen == 1) | ||
1170 | { | ||
1171 | oldbuf = buffer; | ||
1172 | buffer = newbuf; | ||
1173 | buflen = 2; | ||
1174 | } | ||
1175 | |||
1176 | /* Start over. */ | ||
1177 | if (offset == 0) | ||
1178 | msg_imap->message_lines = 0; | ||
1179 | |||
1180 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1181 | if (status != 0) | ||
1182 | return status; | ||
1183 | |||
1184 | /* Select first. */ | ||
1185 | if (f_imap->state == IMAP_NO_STATE) | ||
1186 | { | ||
1187 | char *section = NULL; | ||
1188 | |||
1189 | if (msg_imap->part) | ||
1190 | section = section_name (msg_imap); | ||
1191 | |||
1192 | /* We have strip the \r, but the offset on the imap server is with that | ||
1193 | octet(CFLF) so add it in the offset, it's the number of lines. */ | ||
1194 | status = imap_writeline (f_imap, | ||
1195 | "g%d FETCH %d BODY.PEEK[%s]<%d.%d>\r\n", | ||
1196 | f_imap->seq++, msg_imap->num, | ||
1197 | (section) ? section : "", | ||
1198 | offset + msg_imap->message_lines, buflen); | ||
1199 | if (section) | ||
1200 | free (section); | ||
1201 | CHECK_ERROR (f_imap, status); | ||
1202 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1203 | f_imap->state = IMAP_FETCH; | ||
1204 | } | ||
1205 | status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen); | ||
1206 | |||
1207 | if (oldbuf) | ||
1208 | oldbuf[0] = buffer[0]; | ||
1209 | return status; | ||
1210 | } | ||
1211 | |||
1212 | static int | ||
1213 | imap_message_lines (message_t msg, size_t *plines) | ||
1214 | { | ||
1215 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1216 | if (plines && msg_imap) | ||
1217 | { | ||
1218 | if (msg_imap->message_lines == 0) | ||
1219 | *plines = msg_imap->body_lines + msg_imap->header_lines; | ||
1220 | else | ||
1221 | *plines = msg_imap->message_lines; | ||
1222 | } | ||
1223 | return 0; | ||
1224 | } | ||
1225 | |||
1226 | /* Sometimes a message is just a place container for other sub parts. | ||
1227 | In those cases imap bodystructure does not set the message_size aka | ||
1228 | the body_size. But we can calculate it since the message_size | ||
1229 | is the sum of its subparts. */ | ||
1230 | static int | ||
1231 | imap_submessage_size (msg_imap_t msg_imap, size_t *psize) | ||
1232 | { | ||
1233 | if (psize) | ||
1234 | { | ||
1235 | *psize = 0; | ||
1236 | if (msg_imap->message_size == 0) | ||
1237 | { | ||
1238 | size_t i, size; | ||
1239 | for (size = i = 0; i < msg_imap->num_parts; i++, size = 0) | ||
1240 | { | ||
1241 | if (msg_imap->parts[i]) | ||
1242 | imap_submessage_size (msg_imap->parts[i], &size); | ||
1243 | *psize += size; | ||
1244 | } | ||
1245 | } | ||
1246 | else | ||
1247 | *psize = (msg_imap->message_size + msg_imap->header_size) | ||
1248 | - msg_imap->message_lines; | ||
1249 | } | ||
1250 | return 0; | ||
1251 | } | ||
1252 | |||
1253 | static int | ||
1254 | imap_message_size (message_t msg, size_t *psize) | ||
1255 | { | ||
1256 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1257 | m_imap_t m_imap = msg_imap->m_imap; | ||
1258 | f_imap_t f_imap = m_imap->f_imap; | ||
1259 | int status = 0;; | ||
1260 | |||
1261 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1262 | if (status != 0) | ||
1263 | return status; | ||
1264 | |||
1265 | /* If there is a parent it means it is a sub message, IMAP does not give | ||
1266 | the full size of mime messages, so the message_size retrieved from | ||
1267 | doing a bodystructure represents rather the body_size. */ | ||
1268 | if (msg_imap->parent) | ||
1269 | return imap_submessage_size (msg_imap, psize); | ||
1270 | |||
1271 | if (msg_imap->message_size == 0) | ||
1272 | { | ||
1273 | /* Select first. */ | ||
1274 | if (f_imap->state == IMAP_NO_STATE) | ||
1275 | { | ||
1276 | /* We strip the \r, but the offset/size on the imap server is with | ||
1277 | that octet so add it in the offset, since it's the number of | ||
1278 | lines. */ | ||
1279 | status = imap_writeline (f_imap, | ||
1280 | "g%d FETCH %d RFC822.SIZE\r\n", | ||
1281 | f_imap->seq++, msg_imap->num); | ||
1282 | CHECK_ERROR (f_imap, status); | ||
1283 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1284 | f_imap->state = IMAP_FETCH; | ||
1285 | } | ||
1286 | status = fetch_operation (f_imap, msg_imap, 0, 0, 0); | ||
1287 | } | ||
1288 | |||
1289 | if (status == 0) | ||
1290 | { | ||
1291 | if (psize) | ||
1292 | *psize = msg_imap->message_size - msg_imap->message_lines; | ||
1293 | } | ||
1294 | return status; | ||
1295 | } | ||
1296 | |||
1297 | static int | ||
1298 | imap_message_uid (message_t msg, size_t *puid) | ||
1299 | { | ||
1300 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1301 | m_imap_t m_imap = msg_imap->m_imap; | ||
1302 | f_imap_t f_imap = m_imap->f_imap; | ||
1303 | int status; | ||
1304 | |||
1305 | if (puid) | ||
1306 | return 0; | ||
1307 | |||
1308 | /* Select first. */ | ||
1309 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1310 | if (status != 0) | ||
1311 | return status; | ||
1312 | |||
1313 | if (f_imap->state == IMAP_NO_STATE) | ||
1314 | { | ||
1315 | if (msg_imap->uid) | ||
1316 | { | ||
1317 | *puid = msg_imap->uid; | ||
1318 | return 0; | ||
1319 | } | ||
1320 | status = imap_writeline (f_imap, "g%d FETCH %d UID\r\n", | ||
1321 | f_imap->seq++, msg_imap->num); | ||
1322 | CHECK_ERROR (f_imap, status); | ||
1323 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1324 | f_imap->state = IMAP_FETCH; | ||
1325 | } | ||
1326 | status = fetch_operation (f_imap, msg_imap, 0, 0, 0); | ||
1327 | if (status != 0) | ||
1328 | return status; | ||
1329 | *puid = msg_imap->uid; | ||
1330 | return 0; | ||
1331 | } | ||
1332 | |||
1333 | static int | ||
1334 | imap_message_fd (stream_t stream, int * pfd) | ||
1335 | { | ||
1336 | message_t msg = stream_get_owner (stream); | ||
1337 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1338 | return imap_get_fd (msg_imap, pfd); | ||
1339 | } | ||
1340 | |||
1341 | /* Mime. */ | ||
1342 | static int | ||
1343 | imap_is_multipart (message_t msg, int *ismulti) | ||
1344 | { | ||
1345 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1346 | m_imap_t m_imap = msg_imap->m_imap; | ||
1347 | f_imap_t f_imap = m_imap->f_imap; | ||
1348 | int status; | ||
1349 | |||
1350 | /* Select first. */ | ||
1351 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1352 | if (status != 0) | ||
1353 | return status; | ||
1354 | |||
1355 | if (f_imap->state == IMAP_NO_STATE) | ||
1356 | { | ||
1357 | if (msg_imap->num_parts || msg_imap->part) | ||
1358 | { | ||
1359 | if (ismulti) | ||
1360 | *ismulti = (msg_imap->num_parts > 1); | ||
1361 | return 0; | ||
1362 | } | ||
1363 | status = imap_writeline (f_imap, | ||
1364 | "g%d FETCH %d BODYSTRUCTURE\r\n", | ||
1365 | f_imap->seq++, msg_imap->num); | ||
1366 | CHECK_ERROR (f_imap, status); | ||
1367 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1368 | f_imap->state = IMAP_FETCH; | ||
1369 | } | ||
1370 | status = fetch_operation (f_imap, msg_imap, 0, 0, 0); | ||
1371 | if (status != 0) | ||
1372 | return status; | ||
1373 | if (ismulti) | ||
1374 | *ismulti = (msg_imap->num_parts > 1); | ||
1375 | return 0; | ||
1376 | } | ||
1377 | |||
1378 | static int | ||
1379 | imap_get_num_parts (message_t msg, size_t *nparts) | ||
1380 | { | ||
1381 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1382 | if (msg_imap) | ||
1383 | { | ||
1384 | if (msg_imap->num_parts == 0) | ||
1385 | { | ||
1386 | int status = imap_is_multipart (msg, NULL); | ||
1387 | if (status != 0) | ||
1388 | return status; | ||
1389 | } | ||
1390 | if (nparts) | ||
1391 | *nparts = (msg_imap->num_parts == 0) ? 1 : msg_imap->num_parts; | ||
1392 | } | ||
1393 | return 0; | ||
1394 | } | ||
1395 | |||
1396 | static int | ||
1397 | imap_get_part (message_t msg, size_t partno, message_t *pmsg) | ||
1398 | { | ||
1399 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1400 | int status = 0; | ||
1401 | |||
1402 | if (msg_imap->num_parts == 0) | ||
1403 | { | ||
1404 | status = imap_get_num_parts (msg, NULL); | ||
1405 | if (status != 0) | ||
1406 | return status; | ||
1407 | } | ||
1408 | |||
1409 | if (partno <= msg_imap->num_parts) | ||
1410 | { | ||
1411 | if (msg_imap->parts[partno - 1]->message) | ||
1412 | { | ||
1413 | if (pmsg) | ||
1414 | *pmsg = msg_imap->parts[partno - 1]->message; | ||
1415 | } | ||
1416 | else | ||
1417 | { | ||
1418 | message_t message; | ||
1419 | status = imap_get_message0 (msg_imap->parts[partno - 1], &message); | ||
1420 | if (status == 0) | ||
1421 | { | ||
1422 | header_t header; | ||
1423 | message_get_header (message, &header); | ||
1424 | header_set_get_value (header, NULL, message); | ||
1425 | message_set_stream (message, NULL, msg_imap->parts[partno - 1]); | ||
1426 | /* message_set_size (message, NULL, msg_imap->parts[partno - 1]); */ | ||
1427 | msg_imap->parts[partno - 1]->message = message; | ||
1428 | if (pmsg) | ||
1429 | *pmsg = message; | ||
1430 | } | ||
1431 | } | ||
1432 | } | ||
1433 | else | ||
1434 | { | ||
1435 | if (pmsg) | ||
1436 | *pmsg = msg_imap->message; | ||
1437 | } | ||
1438 | return status; | ||
1439 | } | ||
1440 | |||
1441 | /* Envelope. */ | ||
1442 | static int | ||
1443 | imap_envelope_sender (envelope_t envelope, char *buffer, size_t buflen, | ||
1444 | size_t *plen) | ||
1445 | { | ||
1446 | message_t msg = envelope_get_owner (envelope); | ||
1447 | header_t header; | ||
1448 | int status; | ||
1449 | |||
1450 | if (buflen == 0) | ||
1451 | return 0; | ||
1452 | |||
1453 | message_get_header (msg, &header); | ||
1454 | status = header_get_value (header, MU_HEADER_SENDER, buffer, buflen, plen); | ||
1455 | if (status == EAGAIN) | ||
1456 | return status; | ||
1457 | else if (status != 0) | ||
1458 | status = header_get_value (header, MU_HEADER_FROM, buffer, buflen, plen); | ||
1459 | if (status == 0) | ||
1460 | { | ||
1461 | address_t address; | ||
1462 | if (address_create (&address, buffer) == 0) | ||
1463 | { | ||
1464 | address_get_email (address, 1, buffer, buflen, plen); | ||
1465 | address_destroy (&address); | ||
1466 | } | ||
1467 | } | ||
1468 | else if (status != EAGAIN) | ||
1469 | { | ||
1470 | strncpy (buffer, "Unknown", buflen)[buflen - 1] = '0'; | ||
1471 | if (plen) | ||
1472 | *plen = strlen (buffer); | ||
1473 | } | ||
1474 | return status; | ||
1475 | } | ||
1476 | |||
1477 | static int | ||
1478 | imap_envelope_date (envelope_t envelope, char *buffer, size_t buflen, | ||
1479 | size_t *plen) | ||
1480 | { | ||
1481 | message_t msg = envelope_get_owner (envelope); | ||
1482 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1483 | m_imap_t m_imap = msg_imap->m_imap; | ||
1484 | f_imap_t f_imap = m_imap->f_imap; | ||
1485 | struct tm tm; | ||
1486 | mu_timezone tz; | ||
1487 | time_t now; | ||
1488 | char datebuf[] = "mm-dd-yyyy hh:mm:ss +0000"; | ||
1489 | const char* date = datebuf; | ||
1490 | const char** datep = &date; | ||
1491 | /* reserve as much space as we need for internal-date */ | ||
1492 | int status; | ||
1493 | |||
1494 | /* Select first. */ | ||
1495 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1496 | if (status != 0) | ||
1497 | return status; | ||
1498 | if (msg_imap->internal_date == NULL) | ||
1499 | { | ||
1500 | if (f_imap->state == IMAP_NO_STATE) | ||
1501 | { | ||
1502 | status = imap_writeline (f_imap, | ||
1503 | "g%d FETCH %d INTERNALDATE\r\n", | ||
1504 | f_imap->seq++, msg_imap->num); | ||
1505 | CHECK_ERROR (f_imap, status); | ||
1506 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1507 | f_imap->state = IMAP_FETCH; | ||
1508 | } | ||
1509 | status = fetch_operation (f_imap, msg_imap, datebuf, | ||
1510 | sizeof datebuf, NULL); | ||
1511 | if (status != 0) | ||
1512 | return status; | ||
1513 | msg_imap->internal_date = strdup (datebuf); | ||
1514 | } | ||
1515 | else | ||
1516 | { | ||
1517 | date = msg_imap->internal_date; | ||
1518 | datep = &date; | ||
1519 | } | ||
1520 | |||
1521 | if (mu_parse_imap_date_time(datep, &tm, &tz) != 0) | ||
1522 | now = (time_t)-1; | ||
1523 | else | ||
1524 | now = mu_tm2time (&tm, &tz); | ||
1525 | |||
1526 | /* if the time was unparseable, or mktime() didn't like what we | ||
1527 | parsed, use the calendar time. */ | ||
1528 | if (now == (time_t)-1) | ||
1529 | { | ||
1530 | struct tm* gmt; | ||
1531 | |||
1532 | time(&now); | ||
1533 | gmt = gmtime(&now); | ||
1534 | tm = *gmt; | ||
1535 | } | ||
1536 | |||
1537 | { | ||
1538 | int len = strftime (buffer, buflen, " %a %b %d %H:%M:%S %Y", &tm); | ||
1539 | |||
1540 | /* FIXME: I don't know what strftime does if the buflen is too | ||
1541 | short, or it fails. Assuming that it won't fail, this is my guess | ||
1542 | as to the right thing. | ||
1543 | |||
1544 | I think if the buffer is too short, it will fill it as much | ||
1545 | as it can, and nul terminate it. But I'll terminate it anyhow. | ||
1546 | */ | ||
1547 | if(len == 0) | ||
1548 | { | ||
1549 | len = buflen - 1; | ||
1550 | buffer[len] = 0; | ||
1551 | } | ||
1552 | |||
1553 | if(plen) | ||
1554 | *plen = len; | ||
1555 | } | ||
1556 | return 0; | ||
1557 | } | ||
1558 | |||
1559 | /* Attributes. */ | ||
1560 | static int | ||
1561 | imap_attr_get_flags (attribute_t attribute, int *pflags) | ||
1562 | { | ||
1563 | message_t msg = attribute_get_owner (attribute); | ||
1564 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1565 | m_imap_t m_imap = msg_imap->m_imap; | ||
1566 | f_imap_t f_imap = m_imap->f_imap; | ||
1567 | int status = 0; | ||
1568 | |||
1569 | /* Select first. */ | ||
1570 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1571 | if (status != 0) | ||
1572 | return status; | ||
1573 | |||
1574 | /* Did we retrieve it alread ? */ | ||
1575 | if (msg_imap->flags != 0) | ||
1576 | { | ||
1577 | if (pflags) | ||
1578 | *pflags = msg_imap->flags; | ||
1579 | return 0; | ||
1580 | } | ||
1581 | |||
1582 | if (f_imap->state == IMAP_NO_STATE) | ||
1583 | { | ||
1584 | status = imap_writeline (f_imap, "g%d FETCH %d FLAGS\r\n", | ||
1585 | f_imap->seq++, msg_imap->num); | ||
1586 | CHECK_ERROR (f_imap, status); | ||
1587 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1588 | f_imap->state = IMAP_FETCH; | ||
1589 | } | ||
1590 | status = fetch_operation (f_imap, msg_imap, NULL, 0, NULL); | ||
1591 | if (status == 0) | ||
1592 | { | ||
1593 | if (pflags) | ||
1594 | *pflags = msg_imap->flags; | ||
1595 | } | ||
1596 | return status; | ||
1597 | } | ||
1598 | |||
1599 | static int | ||
1600 | imap_attr_set_flags (attribute_t attribute, int flag) | ||
1601 | { | ||
1602 | message_t msg = attribute_get_owner (attribute); | ||
1603 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1604 | m_imap_t m_imap = msg_imap->m_imap; | ||
1605 | f_imap_t f_imap = m_imap->f_imap; | ||
1606 | int status = 0; | ||
1607 | |||
1608 | /* Select first. */ | ||
1609 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1610 | if (status != 0) | ||
1611 | return status; | ||
1612 | |||
1613 | /* If already set don't bother. */ | ||
1614 | if (msg_imap->flags & flag) | ||
1615 | return 0; | ||
1616 | |||
1617 | /* The delete FLAG is not pass yet but only on the expunge. */ | ||
1618 | if (flag & MU_ATTRIBUTE_DELETED) | ||
1619 | { | ||
1620 | msg_imap->flags |= MU_ATTRIBUTE_DELETED; | ||
1621 | flag &= ~MU_ATTRIBUTE_DELETED; | ||
1622 | } | ||
1623 | |||
1624 | if (f_imap->state == IMAP_NO_STATE) | ||
1625 | { | ||
1626 | char *abuf = malloc (1); | ||
1627 | if (abuf == NULL) | ||
1628 | return ENOMEM; | ||
1629 | *abuf = '\0'; | ||
1630 | status = flags_to_string (&abuf, flag); | ||
1631 | if (status != 0) | ||
1632 | return status; | ||
1633 | /* No flags to send?? */ | ||
1634 | if (*abuf == '\0') | ||
1635 | { | ||
1636 | free (abuf); | ||
1637 | return 0; | ||
1638 | } | ||
1639 | status = imap_writeline (f_imap, "g%d STORE %d +FLAGS.SILENT (%s)\r\n", | ||
1640 | f_imap->seq++, msg_imap->num, abuf); | ||
1641 | free (abuf); | ||
1642 | CHECK_ERROR (f_imap, status); | ||
1643 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1644 | msg_imap->flags |= flag; | ||
1645 | f_imap->state = IMAP_FETCH; | ||
1646 | } | ||
1647 | return fetch_operation (f_imap, msg_imap, NULL, 0, NULL); | ||
1648 | } | ||
1649 | |||
1650 | static int | ||
1651 | imap_attr_unset_flags (attribute_t attribute, int flag) | ||
1652 | { | ||
1653 | message_t msg = attribute_get_owner (attribute); | ||
1654 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1655 | m_imap_t m_imap = msg_imap->m_imap; | ||
1656 | f_imap_t f_imap = m_imap->f_imap; | ||
1657 | int status = 0; | ||
1658 | |||
1659 | /* Select first. */ | ||
1660 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1661 | if (status != 0) | ||
1662 | return status; | ||
1663 | |||
1664 | /* The delete FLAG is not pass yet but only on the expunge. */ | ||
1665 | if (flag & MU_ATTRIBUTE_DELETED) | ||
1666 | { | ||
1667 | msg_imap->flags &= ~MU_ATTRIBUTE_DELETED; | ||
1668 | flag &= ~MU_ATTRIBUTE_DELETED; | ||
1669 | } | ||
1670 | |||
1671 | if (f_imap->state == IMAP_NO_STATE) | ||
1672 | { | ||
1673 | char *abuf = malloc (1); | ||
1674 | if (abuf == NULL) | ||
1675 | return ENOMEM; | ||
1676 | *abuf = '\0'; | ||
1677 | status = flags_to_string (&abuf, flag); | ||
1678 | if (status != 0) | ||
1679 | return status; | ||
1680 | /* No flags to send?? */ | ||
1681 | if (*abuf == '\0') | ||
1682 | { | ||
1683 | free (abuf); | ||
1684 | return 0; | ||
1685 | } | ||
1686 | status = imap_writeline (f_imap, "g%d STORE %d -FLAGS.SILENT (%s)\r\n", | ||
1687 | f_imap->seq++, msg_imap->num, abuf); | ||
1688 | free (abuf); | ||
1689 | CHECK_ERROR (f_imap, status); | ||
1690 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1691 | msg_imap->flags &= ~flag; | ||
1692 | f_imap->state = IMAP_FETCH; | ||
1693 | } | ||
1694 | return fetch_operation (f_imap, msg_imap, NULL, 0, NULL); | ||
1695 | } | ||
1696 | |||
1697 | /* Header. */ | ||
1698 | static int | ||
1699 | imap_header_get_value (header_t header, const char *field, char * buffer, | ||
1700 | size_t buflen, size_t *plen) | ||
1701 | { | ||
1702 | message_t msg = header_get_owner (header); | ||
1703 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1704 | m_imap_t m_imap = msg_imap->m_imap; | ||
1705 | f_imap_t f_imap = m_imap->f_imap; | ||
1706 | int status; | ||
1707 | size_t len = 0; | ||
1708 | char *value; | ||
1709 | |||
1710 | /* Select first. */ | ||
1711 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1712 | if (status != 0) | ||
1713 | return status; | ||
1714 | |||
1715 | /* Hack, if buffer == NULL they want to know how big is the field value, | ||
1716 | Unfortunately IMAP does not say, so we take a guess hoping that the | ||
1717 | value will not be over 1024. */ | ||
1718 | if (buffer == NULL || buflen == 0) | ||
1719 | len = 1024; | ||
1720 | else | ||
1721 | len = strlen (field) + buflen + 4; | ||
1722 | |||
1723 | if (f_imap->state == IMAP_NO_STATE) | ||
1724 | { | ||
1725 | /* Select first. */ | ||
1726 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1727 | if (status != 0) | ||
1728 | return status; | ||
1729 | status = imap_writeline (f_imap, | ||
1730 | "g%d FETCH %d BODY.PEEK[HEADER.FIELDS (%s)]<0.%d>\r\n", | ||
1731 | f_imap->seq++, msg_imap->num, field, len); | ||
1732 | CHECK_ERROR (f_imap, status); | ||
1733 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1734 | f_imap->state = IMAP_FETCH; | ||
1735 | |||
1736 | } | ||
1737 | |||
1738 | value = calloc (len, sizeof (*value)); | ||
1739 | status = fetch_operation (f_imap, msg_imap, value, len, &len); | ||
1740 | if (status == 0) | ||
1741 | { | ||
1742 | char *colon; | ||
1743 | /* The field-matching is case-insensitive. In all cases, the | ||
1744 | delimiting newline between the header and the body is always | ||
1745 | included. Nuke it */ | ||
1746 | if (len) | ||
1747 | value[len - 1] = '\0'; | ||
1748 | |||
1749 | /* Move pass the field-name. */ | ||
1750 | colon = strchr (value, ':'); | ||
1751 | if (colon) | ||
1752 | { | ||
1753 | colon++; | ||
1754 | if (*colon == ' ') | ||
1755 | colon++; | ||
1756 | } | ||
1757 | else | ||
1758 | colon = value; | ||
1759 | |||
1760 | while (*colon && colon[strlen (colon) - 1] == '\n') | ||
1761 | colon[strlen (colon) - 1] = '\0'; | ||
1762 | |||
1763 | if (buffer && buflen) | ||
1764 | { | ||
1765 | strncpy (buffer, colon, buflen); | ||
1766 | buffer[buflen - 1] = '\0'; | ||
1767 | } | ||
1768 | len = strlen (buffer); | ||
1769 | if (plen) | ||
1770 | *plen = len; | ||
1771 | if (len == 0) | ||
1772 | status = ENOENT; | ||
1773 | } | ||
1774 | free (value); | ||
1775 | return status; | ||
1776 | } | ||
1777 | |||
1778 | static int | ||
1779 | imap_header_get_fvalue (header_t header, const char *field, char * buffer, | ||
1780 | size_t buflen, size_t *plen) | ||
1781 | { | ||
1782 | message_t msg = header_get_owner (header); | ||
1783 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1784 | m_imap_t m_imap = msg_imap->m_imap; | ||
1785 | f_imap_t f_imap = m_imap->f_imap; | ||
1786 | int status; | ||
1787 | size_t len = 0; | ||
1788 | char *value; | ||
1789 | |||
1790 | /* Select first. */ | ||
1791 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1792 | if (status != 0) | ||
1793 | return status; | ||
1794 | |||
1795 | /* Do we all ready have the headers. */ | ||
1796 | if (msg_imap->fheader) | ||
1797 | return header_get_value (msg_imap->fheader, field, buffer, buflen, plen); | ||
1798 | |||
1799 | /* We are caching the must use headers. */ | ||
1800 | if (f_imap->state == IMAP_NO_STATE) | ||
1801 | { | ||
1802 | /* Select first. */ | ||
1803 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1804 | if (status != 0) | ||
1805 | return status; | ||
1806 | status = imap_writeline (f_imap, | ||
1807 | "g%d FETCH %d (FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])\r\n", | ||
1808 | f_imap->seq++, msg_imap->num, | ||
1809 | MU_IMAP_CACHE_HEADERS); | ||
1810 | CHECK_ERROR (f_imap, status); | ||
1811 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1812 | f_imap->state = IMAP_FETCH; | ||
1813 | |||
1814 | } | ||
1815 | |||
1816 | /* Should be enough for our needs. */ | ||
1817 | len = 2048; | ||
1818 | value = calloc (len, sizeof *value); | ||
1819 | status = fetch_operation (f_imap, msg_imap, value, len, &len); | ||
1820 | if (status == 0) | ||
1821 | { | ||
1822 | status = header_create (&msg_imap->fheader, value, len, NULL); | ||
1823 | if (status == 0) | ||
1824 | status = header_get_value (msg_imap->fheader, field, buffer, | ||
1825 | buflen, plen); | ||
1826 | } | ||
1827 | free (value); | ||
1828 | return status; | ||
1829 | } | ||
1830 | |||
1831 | static int | ||
1832 | imap_header_read (header_t header, char *buffer, size_t buflen, off_t offset, | ||
1833 | size_t *plen) | ||
1834 | { | ||
1835 | message_t msg = header_get_owner (header); | ||
1836 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1837 | m_imap_t m_imap = msg_imap->m_imap; | ||
1838 | f_imap_t f_imap = m_imap->f_imap; | ||
1839 | char *oldbuf = NULL; | ||
1840 | char newbuf[2]; | ||
1841 | int status; | ||
1842 | |||
1843 | /* This is so annoying, a buffer len of 1 is a killer. If you have for | ||
1844 | example "\n" to retrieve from the server, IMAP will transform this to | ||
1845 | "\r\n" and since you ask for only 1, the server will send '\r' only. | ||
1846 | And ... '\r' will be stripped by (imap_readline()) the number of char | ||
1847 | read will be 0 which means we're done .... sigh ... So we guard by at | ||
1848 | least ask for 2 chars. */ | ||
1849 | if (buflen == 1) | ||
1850 | { | ||
1851 | oldbuf = buffer; | ||
1852 | buffer = newbuf; | ||
1853 | buflen = 2; | ||
1854 | } | ||
1855 | |||
1856 | /* Start over. */ | ||
1857 | if (offset == 0) | ||
1858 | msg_imap->header_lines = 0; | ||
1859 | |||
1860 | /* Select first. */ | ||
1861 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1862 | if (status != 0) | ||
1863 | return status; | ||
1864 | |||
1865 | if (f_imap->state == IMAP_NO_STATE) | ||
1866 | { | ||
1867 | /* We strip the \r, but the offset/size on the imap server is with that | ||
1868 | octet so add it in the offset, since it's the number of lines. */ | ||
1869 | if (msg_imap->part) | ||
1870 | { | ||
1871 | char *section = section_name (msg_imap); | ||
1872 | status = imap_writeline (f_imap, | ||
1873 | "g%d FETCH %d BODY.PEEK[%s.MIME]<%d.%d>\r\n", | ||
1874 | f_imap->seq++, msg_imap->num, | ||
1875 | (section) ? section : "", | ||
1876 | offset + msg_imap->header_lines, buflen); | ||
1877 | if (section) | ||
1878 | free (section); | ||
1879 | } | ||
1880 | else | ||
1881 | status = imap_writeline (f_imap, | ||
1882 | "g%d FETCH %d BODY.PEEK[HEADER]<%d.%d>\r\n", | ||
1883 | f_imap->seq++, msg_imap->num, | ||
1884 | offset + msg_imap->header_lines, buflen); | ||
1885 | CHECK_ERROR (f_imap, status); | ||
1886 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1887 | f_imap->state = IMAP_FETCH; | ||
1888 | |||
1889 | } | ||
1890 | status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen); | ||
1891 | if (oldbuf) | ||
1892 | oldbuf[0] = buffer[0]; | ||
1893 | return status; | ||
1894 | } | ||
1895 | |||
1896 | /* Body. */ | ||
1897 | static int | ||
1898 | imap_body_size (body_t body, size_t *psize) | ||
1899 | { | ||
1900 | message_t msg = body_get_owner (body); | ||
1901 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1902 | if (psize && msg_imap) | ||
1903 | { | ||
1904 | /* If there is a parent it means it is a sub message, IMAP does not give | ||
1905 | the full size of mime messages, so the message_size was retrieve from | ||
1906 | doing a bodystructure and represents rather the body_size. */ | ||
1907 | if (msg_imap->parent) | ||
1908 | { | ||
1909 | *psize = msg_imap->message_size - msg_imap->message_lines; | ||
1910 | } | ||
1911 | else | ||
1912 | { | ||
1913 | if (msg_imap->body_size) | ||
1914 | *psize = msg_imap->body_size; | ||
1915 | else if (msg_imap->message_size) | ||
1916 | *psize = msg_imap->message_size | ||
1917 | - (msg_imap->header_size + msg_imap->header_lines); | ||
1918 | else | ||
1919 | *psize = 0; | ||
1920 | } | ||
1921 | } | ||
1922 | return 0; | ||
1923 | } | ||
1924 | |||
1925 | static int | ||
1926 | imap_body_lines (body_t body, size_t *plines) | ||
1927 | { | ||
1928 | message_t msg = body_get_owner (body); | ||
1929 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1930 | if (plines && msg_imap) | ||
1931 | *plines = msg_imap->body_lines; | ||
1932 | return 0; | ||
1933 | } | ||
1934 | |||
1935 | /* FIXME: Send EISPIPE if trying to seek back. */ | ||
1936 | static int | ||
1937 | imap_body_read (stream_t stream, char *buffer, size_t buflen, off_t offset, | ||
1938 | size_t *plen) | ||
1939 | { | ||
1940 | body_t body = stream_get_owner (stream); | ||
1941 | message_t msg = body_get_owner (body); | ||
1942 | msg_imap_t msg_imap = message_get_owner (msg); | ||
1943 | m_imap_t m_imap = msg_imap->m_imap; | ||
1944 | f_imap_t f_imap = m_imap->f_imap; | ||
1945 | char *oldbuf = NULL; | ||
1946 | char newbuf[2]; | ||
1947 | int status; | ||
1948 | |||
1949 | /* This is so annoying, a buffer len of 1 is a killer. If you have for | ||
1950 | example "\n" to retrieve from the server, IMAP will transform this to | ||
1951 | "\r\n" and since you ask for only 1, the server will send '\r' only. | ||
1952 | And ... '\r' will be stripped by (imap_readline()) the number of char | ||
1953 | read will be 0 which means we're done .... sigh ... So we guard by at | ||
1954 | least ask for 2 chars. */ | ||
1955 | if (buflen == 1) | ||
1956 | { | ||
1957 | oldbuf = buffer; | ||
1958 | buffer = newbuf; | ||
1959 | buflen = 2; | ||
1960 | } | ||
1961 | |||
1962 | /* Start over. */ | ||
1963 | if (offset == 0) | ||
1964 | { | ||
1965 | msg_imap->body_lines = 0; | ||
1966 | msg_imap->body_size = 0; | ||
1967 | } | ||
1968 | |||
1969 | /* Select first. */ | ||
1970 | status = imap_messages_count (m_imap->mailbox, NULL); | ||
1971 | if (status != 0) | ||
1972 | return status; | ||
1973 | |||
1974 | if (f_imap->state == IMAP_NO_STATE) | ||
1975 | { | ||
1976 | /* We strip the \r, but the offset/size on the imap server is with the | ||
1977 | octet, so add it since it's the number of lines. */ | ||
1978 | if (msg_imap->part) | ||
1979 | { | ||
1980 | char *section = section_name (msg_imap); | ||
1981 | status = imap_writeline (f_imap, | ||
1982 | "g%d FETCH %d BODY.PEEK[%s]<%d.%d>\r\n", | ||
1983 | f_imap->seq++, msg_imap->num, | ||
1984 | (section) ? section: "", | ||
1985 | offset + msg_imap->body_lines, buflen); | ||
1986 | if (section) | ||
1987 | free (section); | ||
1988 | } | ||
1989 | else | ||
1990 | status = imap_writeline (f_imap, | ||
1991 | "g%d FETCH %d BODY.PEEK[TEXT]<%d.%d>\r\n", | ||
1992 | f_imap->seq++, msg_imap->num, | ||
1993 | offset + msg_imap->body_lines, buflen); | ||
1994 | CHECK_ERROR (f_imap, status); | ||
1995 | MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer); | ||
1996 | f_imap->state = IMAP_FETCH; | ||
1997 | |||
1998 | } | ||
1999 | status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen); | ||
2000 | if (oldbuf) | ||
2001 | oldbuf[0] = buffer[0]; | ||
2002 | return status; | ||
2003 | } | ||
2004 | |||
2005 | static int | ||
2006 | imap_body_fd (stream_t stream, int *pfd) | ||
2007 | { | ||
2008 | body_t body = stream_get_owner (stream); | ||
2009 | message_t msg = body_get_owner (body); | ||
2010 | msg_imap_t msg_imap = message_get_owner (msg); | ||
2011 | return imap_get_fd (msg_imap, pfd); | ||
2012 | } | ||
2013 | |||
2014 | |||
2015 | static int | ||
2016 | imap_get_fd (msg_imap_t msg_imap, int *pfd) | ||
2017 | { | ||
2018 | if ( msg_imap | ||
2019 | && msg_imap->m_imap | ||
2020 | && msg_imap->m_imap->f_imap | ||
2021 | && msg_imap->m_imap->f_imap->folder) | ||
2022 | return stream_get_fd (msg_imap->m_imap->f_imap->folder->stream, pfd); | ||
2023 | return EINVAL; | ||
2024 | } | ||
2025 | |||
2026 | /* Since so many operations are fetch, we regoup this into one function. */ | ||
2027 | static int | ||
2028 | fetch_operation (f_imap_t f_imap, msg_imap_t msg_imap, char *buffer, | ||
2029 | size_t buflen, size_t *plen) | ||
2030 | { | ||
2031 | int status = 0; | ||
2032 | |||
2033 | switch (f_imap->state) | ||
2034 | { | ||
2035 | case IMAP_FETCH: | ||
2036 | status = imap_send (f_imap); | ||
2037 | CHECK_EAGAIN (f_imap, status); | ||
2038 | stream_truncate (f_imap->string.stream, 0); | ||
2039 | f_imap->string.offset = 0; | ||
2040 | f_imap->string.nleft = 0; | ||
2041 | f_imap->string.type = IMAP_NO_STATE; | ||
2042 | f_imap->string.msg_imap = msg_imap; | ||
2043 | f_imap->state = IMAP_FETCH_ACK; | ||
2044 | |||
2045 | case IMAP_FETCH_ACK: | ||
2046 | status = imap_parse (f_imap); | ||
2047 | CHECK_EAGAIN (f_imap, status); | ||
2048 | if (f_imap->selected) | ||
2049 | MAILBOX_DEBUG0 (f_imap->selected->mailbox, MU_DEBUG_PROT, | ||
2050 | f_imap->buffer); | ||
2051 | |||
2052 | default: | ||
2053 | break; | ||
2054 | } | ||
2055 | |||
2056 | f_imap->state = IMAP_NO_STATE; | ||
2057 | |||
2058 | /* The server may have timeout any case connection is gone away. */ | ||
2059 | if (status == 0 && f_imap->isopen == 0 && f_imap->string.offset == 0) | ||
2060 | status = EBADF; | ||
2061 | |||
2062 | if (buffer) | ||
2063 | stream_read (f_imap->string.stream, buffer, buflen, 0, plen); | ||
2064 | else if (plen) | ||
2065 | *plen = 0; | ||
2066 | stream_truncate (f_imap->string.stream, 0); | ||
2067 | f_imap->string.offset = 0; | ||
2068 | f_imap->string.nleft = 0; | ||
2069 | f_imap->string.type = IMAP_NO_STATE; | ||
2070 | f_imap->string.msg_imap = NULL; | ||
2071 | return status; | ||
2072 | } | ||
2073 | |||
2074 | /* Decide whether the message came from the same folder as the mailbox. */ | ||
2075 | static int | ||
2076 | is_same_folder (mailbox_t mailbox, message_t msg) | ||
2077 | { | ||
2078 | mailbox_t mbox = NULL; | ||
2079 | message_get_mailbox (msg, &mbox); | ||
2080 | return (mbox != NULL && mbox->url != NULL | ||
2081 | && url_is_same_scheme (mbox->url, mailbox->url) | ||
2082 | && url_is_same_host (mbox->url, mailbox->url) | ||
2083 | && url_is_same_port (mbox->url, mailbox->url)); | ||
2084 | } | ||
2085 | |||
2086 | /* Convert flag attribute to IMAP String attributes. */ | ||
2087 | static int | ||
2088 | flags_to_string (char **pbuf, int flag) | ||
2089 | { | ||
2090 | char *abuf = *pbuf; | ||
2091 | if (flag & MU_ATTRIBUTE_DELETED) | ||
2092 | { | ||
2093 | char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Deleted") + 2); | ||
2094 | if (tmp == NULL) | ||
2095 | { | ||
2096 | free (abuf); | ||
2097 | return ENOMEM; | ||
2098 | } | ||
2099 | abuf = tmp; | ||
2100 | if (*abuf) | ||
2101 | strcat (abuf, " "); | ||
2102 | strcat (abuf, "\\Deleted"); | ||
2103 | } | ||
2104 | if (flag & MU_ATTRIBUTE_READ) | ||
2105 | { | ||
2106 | char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Seen") + 2); | ||
2107 | if (tmp == NULL) | ||
2108 | { | ||
2109 | free (abuf); | ||
2110 | return ENOMEM; | ||
2111 | } | ||
2112 | abuf = tmp; | ||
2113 | if (*abuf) | ||
2114 | strcat (abuf, " "); | ||
2115 | strcat (abuf, "\\Seen"); | ||
2116 | } | ||
2117 | if (flag & MU_ATTRIBUTE_ANSWERED) | ||
2118 | { | ||
2119 | char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Answered") + 2); | ||
2120 | if (tmp == NULL) | ||
2121 | { | ||
2122 | free (abuf); | ||
2123 | return ENOMEM; | ||
2124 | } | ||
2125 | abuf = tmp; | ||
2126 | if (*abuf) | ||
2127 | strcat (abuf, " "); | ||
2128 | strcat (abuf, "\\Answered"); | ||
2129 | } | ||
2130 | if (flag & MU_ATTRIBUTE_DRAFT) | ||
2131 | { | ||
2132 | char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Draft") + 2); | ||
2133 | if (tmp == NULL) | ||
2134 | { | ||
2135 | free (abuf); | ||
2136 | return ENOMEM; | ||
2137 | } | ||
2138 | abuf = tmp; | ||
2139 | if (*abuf) | ||
2140 | strcat (abuf, " "); | ||
2141 | strcat (abuf, "\\Draft"); | ||
2142 | } | ||
2143 | if (flag & MU_ATTRIBUTE_FLAGGED) | ||
2144 | { | ||
2145 | char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Flagged") + 2); | ||
2146 | if (tmp == NULL) | ||
2147 | { | ||
2148 | free (abuf); | ||
2149 | return ENOMEM; | ||
2150 | } | ||
2151 | abuf = tmp; | ||
2152 | if (*abuf) | ||
2153 | strcat (abuf, " "); | ||
2154 | strcat (abuf, "\\Flagged"); | ||
2155 | } | ||
2156 | *pbuf = abuf; | ||
2157 | return 0; | ||
2158 | } | ||
2159 | |||
2160 | /* Convert a suite of number to IMAP message number. */ | ||
2161 | static int | ||
2162 | add_number (char **pset, size_t start, size_t end) | ||
2163 | { | ||
2164 | char buf[128]; | ||
2165 | char *set; | ||
2166 | char *tmp; | ||
2167 | size_t set_len = 0; | ||
2168 | |||
2169 | if (pset == NULL) | ||
2170 | return 0; | ||
2171 | |||
2172 | set = *pset; | ||
2173 | |||
2174 | if (set) | ||
2175 | set_len = strlen (set); | ||
2176 | |||
2177 | /* We had a previous seqence. */ | ||
2178 | if (start == 0) | ||
2179 | *buf = '\0'; | ||
2180 | else if (start != end) | ||
2181 | snprintf (buf, sizeof buf, "%lu:%lu", | ||
2182 | (unsigned long) start, | ||
2183 | (unsigned long) end); | ||
2184 | else | ||
2185 | snprintf (buf, sizeof buf, "%lu", (unsigned long) start); | ||
2186 | |||
2187 | if (set_len) | ||
2188 | tmp = realloc (set, set_len + strlen (buf) + 2 /* null and comma */); | ||
2189 | else | ||
2190 | tmp = calloc (strlen (buf) + 1, 1); | ||
2191 | |||
2192 | if (tmp == NULL) | ||
2193 | { | ||
2194 | free (set); | ||
2195 | return ENOMEM; | ||
2196 | } | ||
2197 | set = tmp; | ||
2198 | |||
2199 | /* If we had something add a comma separator. */ | ||
2200 | if (set_len) | ||
2201 | strcat (set, ","); | ||
2202 | strcat (set, buf); | ||
2203 | |||
2204 | *pset = set; | ||
2205 | return 0; | ||
2206 | } | ||
2207 | |||
2208 | static int | ||
2209 | delete_to_string (m_imap_t m_imap, char **pset) | ||
2210 | { | ||
2211 | int status; | ||
2212 | size_t i, prev = 0, is_range = 0; | ||
2213 | size_t start = 0, cur = 0; | ||
2214 | char *set = NULL; | ||
2215 | |||
2216 | /* Reformat the number for IMAP. */ | ||
2217 | for (i = 0; i < m_imap->imessages_count; ++i) | ||
2218 | { | ||
2219 | if (m_imap->imessages[i] | ||
2220 | && (m_imap->imessages[i]->flags & MU_ATTRIBUTE_DELETED)) | ||
2221 | { | ||
2222 | cur = m_imap->imessages[i]->num; | ||
2223 | /* The first number. */ | ||
2224 | if (start == 0) | ||
2225 | { | ||
2226 | start = prev = cur; | ||
2227 | } | ||
2228 | /* Is it a sequence? */ | ||
2229 | else if ((prev + 1) == cur) | ||
2230 | { | ||
2231 | prev = cur; | ||
2232 | is_range = 1; | ||
2233 | } | ||
2234 | continue; | ||
2235 | } | ||
2236 | |||
2237 | if (start) | ||
2238 | { | ||
2239 | status = add_number (&set, start, cur); | ||
2240 | if (status != 0) | ||
2241 | return status; | ||
2242 | start = 0; | ||
2243 | prev = 0; | ||
2244 | cur = 0; | ||
2245 | is_range = 0; | ||
2246 | } | ||
2247 | } /* for () */ | ||
2248 | |||
2249 | status = add_number (&set, start, cur); | ||
2250 | if (status != 0) | ||
2251 | return status; | ||
2252 | *pset = set; | ||
2253 | return 0; | ||
2254 | } | ||
2255 | |||
2256 | #endif |
mailbox/mbx_maildir.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | /* First draft by Jeff Bailey based on mbox by Alain Magloire */ | ||
19 | |||
20 | #ifdef HAVE_CONFIG_H | ||
21 | # include <config.h> | ||
22 | #endif | ||
23 | |||
24 | #include <stdlib.h> | ||
25 | #include <stdio.h> | ||
26 | #include <time.h> | ||
27 | #include <sys/stat.h> | ||
28 | #include <fcntl.h> | ||
29 | #include <unistd.h> | ||
30 | #include <signal.h> | ||
31 | #include <time.h> | ||
32 | #include <string.h> | ||
33 | #include <ctype.h> | ||
34 | #include <limits.h> | ||
35 | #include <errno.h> | ||
36 | |||
37 | #ifdef WITH_PTHREAD | ||
38 | # ifdef HAVE_PTHREAD_H | ||
39 | # define _XOPEN_SOURCE 500 | ||
40 | # include <pthread.h> | ||
41 | # endif | ||
42 | #endif | ||
43 | |||
44 | #ifdef HAVE_ALLOCA_H | ||
45 | # include <alloca.h> | ||
46 | #endif | ||
47 | |||
48 | #ifdef HAVE_STRINGS_H | ||
49 | # include <strings.h> | ||
50 | #endif | ||
51 | |||
52 | #include <mailbox0.h> | ||
53 | #include <registrar0.h> | ||
54 | |||
55 | #include <mailutils/address.h> | ||
56 | #include <mailutils/attribute.h> | ||
57 | #include <mailutils/body.h> | ||
58 | #include <mailutils/debug.h> | ||
59 | #include <mailutils/envelope.h> | ||
60 | #include <mailutils/errno.h> | ||
61 | #include <mailutils/error.h> | ||
62 | #include <mailutils/header.h> | ||
63 | #include <mailutils/locker.h> | ||
64 | #include <mailutils/message.h> | ||
65 | #include <mailutils/mutil.h> | ||
66 | #include <mailutils/observer.h> | ||
67 | #include <mailutils/property.h> | ||
68 | #include <mailutils/stream.h> | ||
69 | #include <mailutils/url.h> | ||
70 | |||
71 | /* Mailbox concrete implementation. */ | ||
72 | static int maildir_open __P ((mailbox_t, int)); | ||
73 | static int maildir_close __P ((mailbox_t)); | ||
74 | static void maildir_destroy __P ((mailbox_t)); | ||
75 | static int maildir_get_message __P ((mailbox_t, size_t, message_t *)); | ||
76 | /* static int maildir_get_message_by_uid __P ((mailbox_t, size_t, message_t *)); */ | ||
77 | static int maildir_append_message __P ((mailbox_t, message_t)); | ||
78 | static int maildir_messages_count __P ((mailbox_t, size_t *)); | ||
79 | static int maildir_messages_recent __P ((mailbox_t, size_t *)); | ||
80 | static int maildir_message_unseen __P ((mailbox_t, size_t *)); | ||
81 | static int maildir_expunge __P ((mailbox_t)); | ||
82 | static int maildir_save_attributes __P ((mailbox_t)); | ||
83 | static int maildir_uidvalidity __P ((mailbox_t, unsigned long *)); | ||
84 | static int maildir_uidnext __P ((mailbox_t, size_t *)); | ||
85 | static int maildir_scan __P ((mailbox_t, size_t, size_t *)); | ||
86 | static int maildir_is_updated __P ((mailbox_t)); | ||
87 | static int maildir_get_size __P ((mailbox_t, off_t *)); | ||
88 | |||
89 | int | ||
90 | _mailbox_maildir_init (mailbox_t mailbox) | ||
91 | { | ||
92 | |||
93 | if (mailbox == NULL) | ||
94 | return EINVAL; | ||
95 | |||
96 | /* Overloading the defaults. */ | ||
97 | mailbox->_destroy = maildir_destroy; | ||
98 | |||
99 | mailbox->_open = maildir_open; | ||
100 | mailbox->_close = maildir_close; | ||
101 | |||
102 | /* Overloading of the entire mailbox object methods. */ | ||
103 | mailbox->_get_message = maildir_get_message; | ||
104 | mailbox->_append_message = maildir_append_message; | ||
105 | mailbox->_messages_count = maildir_messages_count; | ||
106 | mailbox->_messages_recent = maildir_messages_recent; | ||
107 | mailbox->_message_unseen = maildir_message_unseen; | ||
108 | mailbox->_expunge = maildir_expunge; | ||
109 | mailbox->_save_attributes = maildir_save_attributes; | ||
110 | mailbox->_uidvalidity = maildir_uidvalidity; | ||
111 | mailbox->_uidnext = maildir_uidnext; | ||
112 | |||
113 | mailbox->_scan = maildir_scan; | ||
114 | mailbox->_is_updated = maildir_is_updated; | ||
115 | |||
116 | mailbox->_get_size = maildir_get_size; | ||
117 | |||
118 | return 0; /* okdoke */ | ||
119 | } | ||
120 | |||
121 | /* Destruct maildir setup */ | ||
122 | static void | ||
123 | maildir_destroy (mailbox_t mailbox) | ||
124 | { | ||
125 | return; | ||
126 | } | ||
127 | |||
128 | /* Open the file. For MU_STREAM_READ, the code tries mmap() first and fall | ||
129 | back to normal file. */ | ||
130 | static int | ||
131 | maildir_open (mailbox_t mailbox, int flags) | ||
132 | { | ||
133 | return -1; | ||
134 | } | ||
135 | |||
136 | static int | ||
137 | maildir_close (mailbox_t mailbox) | ||
138 | { | ||
139 | return -1; | ||
140 | } | ||
141 | |||
142 | /* Cover function that call the real thing, maildir_scan(), with | ||
143 | notification set. */ | ||
144 | static int | ||
145 | maildir_scan (mailbox_t mailbox, size_t msgno, size_t *pcount) | ||
146 | { | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | /* FIXME: How to handle a shrink ? meaning, the &^$^@%#@^& user start two | ||
151 | browsers and deleted emails in one session. My views is that we should | ||
152 | scream bloody murder and hunt them with a machette. But for now just play | ||
153 | dumb, but maybe the best approach is to pack our things and leave | ||
154 | .i.e exit()/abort(). */ | ||
155 | static int | ||
156 | maildir_is_updated (mailbox_t mailbox) | ||
157 | { | ||
158 | return -1; | ||
159 | } | ||
160 | |||
161 | static int | ||
162 | maildir_expunge (mailbox_t mailbox) | ||
163 | { | ||
164 | return -1; | ||
165 | } | ||
166 | |||
167 | static int | ||
168 | maildir_get_size (mailbox_t mailbox, off_t *psize) | ||
169 | { | ||
170 | return -1; | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | maildir_save_attributes (mailbox_t mailbox) | ||
175 | { | ||
176 | return -1; | ||
177 | } | ||
178 | |||
179 | static int | ||
180 | maildir_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg) | ||
181 | { | ||
182 | return -1; | ||
183 | } | ||
184 | |||
185 | static int | ||
186 | maildir_append_message (mailbox_t mailbox, message_t msg) | ||
187 | { | ||
188 | return -1; | ||
189 | } | ||
190 | |||
191 | static int | ||
192 | maildir_messages_count (mailbox_t mailbox, size_t *pcount) | ||
193 | { | ||
194 | return -1; | ||
195 | } | ||
196 | |||
197 | /* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN | ||
198 | ('O' in the Status header), i.e. a message that is first seen | ||
199 | by the current session (see attributes.h) */ | ||
200 | static int | ||
201 | maildir_messages_recent (mailbox_t mailbox, size_t *pcount) | ||
202 | { | ||
203 | return -1; | ||
204 | } | ||
205 | |||
206 | /* An "unseen" message is the one that has not been read yet */ | ||
207 | static int | ||
208 | maildir_message_unseen (mailbox_t mailbox, size_t *pmsgno) | ||
209 | { | ||
210 | return -1; | ||
211 | } | ||
212 | |||
213 | static int | ||
214 | maildir_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity) | ||
215 | { | ||
216 | return -1; | ||
217 | } | ||
218 | |||
219 | static int | ||
220 | maildir_uidnext (mailbox_t mailbox, size_t *puidnext) | ||
221 | { | ||
222 | return -1; | ||
223 | } | ||
224 |
mailbox/mbx_mbox.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | /* First draft by Alain Magloire */ | ||
19 | |||
20 | #ifdef HAVE_CONFIG_H | ||
21 | # include <config.h> | ||
22 | #endif | ||
23 | |||
24 | #include <stdlib.h> | ||
25 | #include <stdio.h> | ||
26 | #include <time.h> | ||
27 | #include <sys/stat.h> | ||
28 | #include <fcntl.h> | ||
29 | #include <unistd.h> | ||
30 | #include <signal.h> | ||
31 | #include <time.h> | ||
32 | #include <string.h> | ||
33 | #include <ctype.h> | ||
34 | #include <limits.h> | ||
35 | #include <errno.h> | ||
36 | |||
37 | #ifdef WITH_PTHREAD | ||
38 | # ifdef HAVE_PTHREAD_H | ||
39 | # define _XOPEN_SOURCE 500 | ||
40 | # include <pthread.h> | ||
41 | # endif | ||
42 | #endif | ||
43 | |||
44 | #ifdef HAVE_ALLOCA_H | ||
45 | # include <alloca.h> | ||
46 | #endif | ||
47 | |||
48 | #ifdef HAVE_STRINGS_H | ||
49 | # include <strings.h> | ||
50 | #endif | ||
51 | |||
52 | #include <mailbox0.h> | ||
53 | #include <registrar0.h> | ||
54 | |||
55 | #include <mailutils/address.h> | ||
56 | #include <mailutils/attribute.h> | ||
57 | #include <mailutils/body.h> | ||
58 | #include <mailutils/debug.h> | ||
59 | #include <mailutils/envelope.h> | ||
60 | #include <mailutils/errno.h> | ||
61 | #include <mailutils/error.h> | ||
62 | #include <mailutils/header.h> | ||
63 | #include <mailutils/locker.h> | ||
64 | #include <mailutils/message.h> | ||
65 | #include <mailutils/mutil.h> | ||
66 | #include <mailutils/observer.h> | ||
67 | #include <mailutils/property.h> | ||
68 | #include <mailutils/stream.h> | ||
69 | #include <mailutils/url.h> | ||
70 | |||
71 | #define ATTRIBUTE_IS_DELETED(flag) (flag & MU_ATTRIBUTE_DELETED) | ||
72 | #define ATTRIBUTE_IS_EQUAL(flag1, flag2) (flag1 == flag2) | ||
73 | |||
74 | static void mbox_destroy (mailbox_t); | ||
75 | |||
76 | struct _mbox_message; | ||
77 | struct _mbox_data; | ||
78 | |||
79 | typedef struct _mbox_data* mbox_data_t; | ||
80 | typedef struct _mbox_message* mbox_message_t; | ||
81 | |||
82 | /* Below are the headers field-names that we are caching for speed, it is | ||
83 | more or less the list of headers in ENVELOPE command from IMAP. */ | ||
84 | #define HDRSIZE 15 | ||
85 | |||
86 | const char *fhdr_table[] = | ||
87 | { | ||
88 | #define H_BCC 0 | ||
89 | "Bcc", | ||
90 | #define H_CC 1 | ||
91 | "Cc", | ||
92 | #define H_CONTENT_LANGUAGE 2 | ||
93 | "Content-Language", | ||
94 | #define H_CONTENT_TRANSFER_ENCODING 3 | ||
95 | "Content-Transfer-Encoding", | ||
96 | #define H_CONTENT_TYPE 4 | ||
97 | "Content-Type", | ||
98 | #define H_DATE 5 | ||
99 | "Date", | ||
100 | #define H_FROM 6 | ||
101 | "From", | ||
102 | #define H_IN_REPLY_TO 7 | ||
103 | "In-Reply-To", | ||
104 | #define H_MESSAGE_ID 8 | ||
105 | "Message-ID", | ||
106 | #define H_REFERENCE 9 | ||
107 | "Reference", | ||
108 | #define H_REPLY_TO 10 | ||
109 | "Reply-To", | ||
110 | #define H_SENDER 11 | ||
111 | "Sender", | ||
112 | #define H_SUBJECT 12 | ||
113 | "Subject", | ||
114 | #define H_TO 13 | ||
115 | "To", | ||
116 | #define H_X_UIDL 14 | ||
117 | "X-UIDL" | ||
118 | }; | ||
119 | |||
120 | /* Keep the file positions of where the headers and bodies start and end. | ||
121 | attr_flags is the "Status:" message. */ | ||
122 | struct _mbox_message | ||
123 | { | ||
124 | /* Offset of the messages in the mailbox. */ | ||
125 | off_t header_from; | ||
126 | off_t header_from_end; | ||
127 | off_t body; | ||
128 | off_t body_end; | ||
129 | |||
130 | /* Fast header retrieve, we save here the most common headers. This will | ||
131 | speed the header search. The entire headers are copied, when modified, | ||
132 | by the header_t object, we do not have to worry about updating them. */ | ||
133 | char *fhdr[HDRSIZE]; | ||
134 | |||
135 | size_t uid; /* IMAP uid. */ | ||
136 | |||
137 | int attr_flags; /* The attr_flags contains the "Status:" attribute */ | ||
138 | |||
139 | size_t header_lines; | ||
140 | size_t body_lines; | ||
141 | |||
142 | message_t message; /* A message attach to it. */ | ||
143 | mbox_data_t mud; /* Back pointer. */ | ||
144 | }; | ||
145 | |||
146 | /* The umessages is an array of pointers that contains umessages_count of | ||
147 | mbox_message_t*; umessages[umessages_count]. We do it this way because | ||
148 | realloc() can move everything to a new memory region and invalidate all | ||
149 | the pointers. Thanks to <Dave Inglis> for pointing this out. The | ||
150 | messages_count is the count number of messages parsed so far. */ | ||
151 | struct _mbox_data | ||
152 | { | ||
153 | mbox_message_t *umessages; /* Array. */ | ||
154 | size_t umessages_count; /* How big is the umessages[]. */ | ||
155 | size_t messages_count; /* How many valid entry in umessages[]. */ | ||
156 | off_t size; /* Size of the mailbox. */ | ||
157 | unsigned long uidvalidity; | ||
158 | size_t uidnext; | ||
159 | char *name; | ||
160 | |||
161 | /* The variables below are use to hold the state when appending messages. */ | ||
162 | enum mbox_state | ||
163 | { | ||
164 | MBOX_NO_STATE = 0, | ||
165 | MBOX_STATE_APPEND_SENDER, MBOX_STATE_APPEND_DATE, MBOX_STATE_APPEND_HEADER, | ||
166 | MBOX_STATE_APPEND_ATTRIBUTE, MBOX_STATE_APPEND_UID, MBOX_STATE_APPEND_BODY, | ||
167 | MBOX_STATE_APPEND_MESSAGE | ||
168 | } state ; | ||
169 | char *sender; | ||
170 | char *date; | ||
171 | off_t off; | ||
172 | mailbox_t mailbox; /* Back pointer. */ | ||
173 | }; | ||
174 | |||
175 | /* Mailbox concrete implementation. */ | ||
176 | static int mbox_open __P ((mailbox_t, int)); | ||
177 | static int mbox_close __P ((mailbox_t)); | ||
178 | static int mbox_get_message __P ((mailbox_t, size_t, message_t *)); | ||
179 | /* static int mbox_get_message_by_uid __P ((mailbox_t, size_t, message_t *)); */ | ||
180 | static int mbox_append_message __P ((mailbox_t, message_t)); | ||
181 | static int mbox_messages_count __P ((mailbox_t, size_t *)); | ||
182 | static int mbox_messages_recent __P ((mailbox_t, size_t *)); | ||
183 | static int mbox_message_unseen __P ((mailbox_t, size_t *)); | ||
184 | static int mbox_expunge0 __P ((mailbox_t, int)); | ||
185 | static int mbox_expunge __P ((mailbox_t)); | ||
186 | static int mbox_save_attributes __P ((mailbox_t)); | ||
187 | static int mbox_uidvalidity __P ((mailbox_t, unsigned long *)); | ||
188 | static int mbox_uidnext __P ((mailbox_t, size_t *)); | ||
189 | static int mbox_scan __P ((mailbox_t, size_t, size_t *)); | ||
190 | static int mbox_is_updated __P ((mailbox_t)); | ||
191 | static int mbox_get_size __P ((mailbox_t, off_t *)); | ||
192 | |||
193 | /* private stuff */ | ||
194 | static int mbox_scan0 __P ((mailbox_t, size_t, size_t *, int)); | ||
195 | static int mbox_append_message0 __P ((mailbox_t, message_t, off_t *, | ||
196 | int, int)); | ||
197 | static int mbox_message_uid __P ((message_t, size_t *)); | ||
198 | static int mbox_header_fill __P ((header_t, char *, size_t, off_t, | ||
199 | size_t *)); | ||
200 | static int mbox_get_body_fd __P ((stream_t, int *)); | ||
201 | static int mbox_get_fd __P ((mbox_message_t, int *)); | ||
202 | static int mbox_get_attr_flags __P ((attribute_t, int *)); | ||
203 | static int mbox_set_attr_flags __P ((attribute_t, int)); | ||
204 | static int mbox_unset_attr_flags __P ((attribute_t, int)); | ||
205 | static int mbox_body_read __P ((stream_t, char *, size_t, off_t, | ||
206 | size_t *)); | ||
207 | static int mbox_body_readline __P ((stream_t, char *, size_t, off_t, | ||
208 | size_t *)); | ||
209 | static int mbox_readstream __P ((mbox_message_t, char *, size_t, | ||
210 | off_t, size_t *, int, off_t, | ||
211 | off_t)); | ||
212 | static int mbox_stream_size __P((stream_t stream, off_t *psize)); | ||
213 | |||
214 | static int mbox_header_size __P ((header_t, size_t *)); | ||
215 | static int mbox_header_lines __P ((header_t, size_t *)); | ||
216 | static int mbox_body_size __P ((body_t, size_t *)); | ||
217 | static int mbox_body_lines __P ((body_t, size_t *)); | ||
218 | static int mbox_envelope_sender __P ((envelope_t, char *, size_t, | ||
219 | size_t *)); | ||
220 | static int mbox_envelope_date __P ((envelope_t, char *, size_t, | ||
221 | size_t *)); | ||
222 | static int mbox_tmpfile __P ((mailbox_t, char **pbox)); | ||
223 | #ifdef WITH_PTHREAD | ||
224 | static void mbox_cleanup __P ((void *)); | ||
225 | #endif | ||
226 | |||
227 | /* Allocate the mbox_data_t struct(concrete mailbox), but don't do any | ||
228 | parsing on the name or even test for existence. However we do strip any | ||
229 | leading "mbox:" part of the name, this is suppose to be the | ||
230 | protocol/scheme name. */ | ||
231 | int | ||
232 | _mailbox_mbox_init (mailbox_t mailbox) | ||
233 | { | ||
234 | mbox_data_t mud; | ||
235 | size_t name_len; | ||
236 | |||
237 | if (mailbox == NULL) | ||
238 | return EINVAL; | ||
239 | |||
240 | /* Allocate specific mbox data. */ | ||
241 | mud = mailbox->data = calloc (1, sizeof (*mud)); | ||
242 | if (mailbox->data == NULL) | ||
243 | return ENOMEM; | ||
244 | |||
245 | /* Back pointer. */ | ||
246 | mud->mailbox = mailbox; | ||
247 | |||
248 | /* Copy the name: | ||
249 | We do not do any further interpretation after the scheme "mbox:" | ||
250 | Because for example on distributed system like QnX4 a file name is | ||
251 | //390/etc/passwd. So the best approach is to let the OS handle it | ||
252 | for example if we receive: "mbox:///var/mail/alain" the mailbox name | ||
253 | will be "///var/mail/alain", we let open() do the right thing. | ||
254 | So it will let things like this "mbox://390/var/mail/alain" where | ||
255 | the "//" _is_ part of the filename, pass correctely. */ | ||
256 | url_get_path (mailbox->url, NULL, 0, &name_len); | ||
257 | mud->name = calloc (name_len + 1, sizeof (char)); | ||
258 | if (mud->name == NULL) | ||
259 | { | ||
260 | free (mud); | ||
261 | mailbox->data = NULL; | ||
262 | return ENOMEM; | ||
263 | } | ||
264 | url_get_path (mailbox->url, mud->name, name_len + 1, NULL); | ||
265 | |||
266 | mud->state = MBOX_NO_STATE; | ||
267 | |||
268 | /* Overloading the defaults. */ | ||
269 | mailbox->_destroy = mbox_destroy; | ||
270 | |||
271 | mailbox->_open = mbox_open; | ||
272 | mailbox->_close = mbox_close; | ||
273 | |||
274 | /* Overloading of the entire mailbox object methods. */ | ||
275 | mailbox->_get_message = mbox_get_message; | ||
276 | mailbox->_append_message = mbox_append_message; | ||
277 | mailbox->_messages_count = mbox_messages_count; | ||
278 | mailbox->_messages_recent = mbox_messages_recent; | ||
279 | mailbox->_message_unseen = mbox_message_unseen; | ||
280 | mailbox->_expunge = mbox_expunge; | ||
281 | mailbox->_save_attributes = mbox_save_attributes; | ||
282 | mailbox->_uidvalidity = mbox_uidvalidity; | ||
283 | mailbox->_uidnext = mbox_uidnext; | ||
284 | |||
285 | mailbox->_scan = mbox_scan; | ||
286 | mailbox->_is_updated = mbox_is_updated; | ||
287 | |||
288 | mailbox->_get_size = mbox_get_size; | ||
289 | |||
290 | /* Set our properties. */ | ||
291 | { | ||
292 | property_t property = NULL; | ||
293 | mailbox_get_property (mailbox, &property); | ||
294 | property_set_value (property, "TYPE", "MBOX", 1); | ||
295 | } | ||
296 | |||
297 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_init(%s)\n", mud->name); | ||
298 | return 0; /* okdoke */ | ||
299 | } | ||
300 | |||
301 | /* Free all ressources associated with Unix concrete mailbox. */ | ||
302 | static void | ||
303 | mbox_destroy (mailbox_t mailbox) | ||
304 | { | ||
305 | if (mailbox->data) | ||
306 | { | ||
307 | size_t i; | ||
308 | mbox_data_t mud = mailbox->data; | ||
309 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, | ||
310 | "mbox_destroy (%s)\n", mud->name); | ||
311 | monitor_wrlock (mailbox->monitor); | ||
312 | for (i = 0; i < mud->umessages_count; i++) | ||
313 | { | ||
314 | mbox_message_t mum = mud->umessages[i]; | ||
315 | if (mum) | ||
316 | { | ||
317 | size_t j; | ||
318 | message_destroy (&(mum->message), mum); | ||
319 | for (j = 0; j < HDRSIZE; j++) | ||
320 | if (mum->fhdr[j]) | ||
321 | free (mum->fhdr[j]); | ||
322 | free (mum); | ||
323 | } | ||
324 | } | ||
325 | if (mud->umessages) | ||
326 | free (mud->umessages); | ||
327 | if (mud->name) | ||
328 | free (mud->name); | ||
329 | free (mud); | ||
330 | mailbox->data = NULL; | ||
331 | monitor_unlock (mailbox->monitor); | ||
332 | } | ||
333 | } | ||
334 | |||
335 | /* Open the file. For MU_STREAM_READ, the code tries mmap() first and fall | ||
336 | back to normal file. */ | ||
337 | static int | ||
338 | mbox_open (mailbox_t mailbox, int flags) | ||
339 | { | ||
340 | mbox_data_t mud = mailbox->data; | ||
341 | int status = 0; | ||
342 | |||
343 | if (mud == NULL) | ||
344 | return EINVAL; | ||
345 | |||
346 | mailbox->flags = flags; | ||
347 | |||
348 | /* Get a stream. */ | ||
349 | if (mailbox->stream == NULL) | ||
350 | { | ||
351 | /* We do not try to mmap for CREAT or APPEND, it is not supported. */ | ||
352 | status = (flags & MU_STREAM_CREAT) | ||
353 | || (mailbox->flags & MU_STREAM_APPEND); | ||
354 | |||
355 | /* Try to mmap () the file first. */ | ||
356 | if (status == 0) | ||
357 | { | ||
358 | status = mapfile_stream_create (&mailbox->stream, mud->name, mailbox->flags); | ||
359 | if (status == 0) | ||
360 | { | ||
361 | status = stream_open (mailbox->stream); | ||
362 | } | ||
363 | } | ||
364 | |||
365 | /* Fall back to normal file if mmap() failed. */ | ||
366 | if (status != 0) | ||
367 | { | ||
368 | status = file_stream_create (&mailbox->stream, mud->name, mailbox->flags); | ||
369 | if (status != 0) | ||
370 | return status; | ||
371 | status = stream_open (mailbox->stream); | ||
372 | } | ||
373 | /* All failed, bail out. */ | ||
374 | if (status != 0) | ||
375 | { | ||
376 | stream_destroy (&mailbox->stream, NULL); | ||
377 | return status; | ||
378 | } | ||
379 | /* Even on top, of normal FILE *, lets agressively cache. But this | ||
380 | may not be suitable for system tight on memory. */ | ||
381 | stream_setbufsiz (mailbox->stream, BUFSIZ); | ||
382 | } | ||
383 | else | ||
384 | { | ||
385 | status = stream_open (mailbox->stream); | ||
386 | if (status != 0) | ||
387 | return status; | ||
388 | } | ||
389 | |||
390 | MAILBOX_DEBUG2 (mailbox, MU_DEBUG_TRACE, "mbox_open(%s, 0x%x)\n", | ||
391 | mud->name, mailbox->flags); | ||
392 | |||
393 | if (mailbox->locker == NULL) | ||
394 | status = locker_create (&(mailbox->locker), mud->name, 0); | ||
395 | return status; | ||
396 | } | ||
397 | |||
398 | static int | ||
399 | mbox_close (mailbox_t mailbox) | ||
400 | { | ||
401 | mbox_data_t mud = mailbox->data; | ||
402 | /* size_t i; */ | ||
403 | |||
404 | if (mud == NULL) | ||
405 | return EINVAL; | ||
406 | |||
407 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_close(%s)\n", mud->name); | ||
408 | |||
409 | /* Make sure that we do not hold any file locking. */ | ||
410 | locker_unlock (mailbox->locker); | ||
411 | |||
412 | #if 0 | ||
413 | /* RFC: I'm not sure on the right approach especially if the client is | ||
414 | working in disconnected mode, where it can mailbox_close/mailbox_open | ||
415 | for each request, maybe we should keep them for a while. */ | ||
416 | monitor_wrlock (mailbox->monitor); | ||
417 | /* Before closing we need to remove all the messages | ||
418 | - to reclaim the memory | ||
419 | - to prepare for another scan. */ | ||
420 | for (i = 0; i < mud->umessages_count; i++) | ||
421 | { | ||
422 | mbox_message_t mum = mud->umessages[i]; | ||
423 | /* Destroy the attach messages. */ | ||
424 | if (mum) | ||
425 | { | ||
426 | size_t j; | ||
427 | message_destroy (&(mum->message), mum); | ||
428 | for (j = 0; j < HDRSIZE; j++) | ||
429 | if (mum->fhdr[j]) | ||
430 | free (mum->fhdr[j]); | ||
431 | free (mum); | ||
432 | } | ||
433 | } | ||
434 | if (mud->umessages) | ||
435 | free (mud->umessages); | ||
436 | mud->umessages = NULL; | ||
437 | mud->messages_count = mud->umessages_count = 0; | ||
438 | mud->size = 0; | ||
439 | mud->uidvalidity = 0; | ||
440 | mud->uidnext = 0; | ||
441 | monitor_unlock (mailbox->monitor); | ||
442 | #endif | ||
443 | return stream_close (mailbox->stream); | ||
444 | } | ||
445 | |||
446 | /* Mailbox Parsing. Routing was way to ugly to put here. */ | ||
447 | #include "mbx_mboxscan.c" | ||
448 | |||
449 | /* Cover function that call the real thing, mbox_scan(), with | ||
450 | notification set. */ | ||
451 | static int | ||
452 | mbox_scan (mailbox_t mailbox, size_t msgno, size_t *pcount) | ||
453 | { | ||
454 | size_t i; | ||
455 | mbox_data_t mud = mailbox->data; | ||
456 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_scan(%s)\n", mud->name); | ||
457 | if (! mbox_is_updated (mailbox)) | ||
458 | return mbox_scan0 (mailbox, msgno, pcount, 1); | ||
459 | /* Since the mailbox is already updated fake the scan. */ | ||
460 | if (msgno > 0) | ||
461 | msgno--; /* The fist message is number "1", decremente for the C array. */ | ||
462 | for (i = msgno; i < mud->messages_count; i++) | ||
463 | { | ||
464 | if (observable_notify (mailbox->observable, MU_EVT_MESSAGE_ADD) != 0) | ||
465 | break; | ||
466 | if (((i +1) % 50) == 0) | ||
467 | { | ||
468 | observable_notify (mailbox->observable, MU_EVT_MAILBOX_PROGRESS); | ||
469 | } | ||
470 | } | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | /* FIXME: How to handle a shrink ? meaning, the &^$^@%#@^& user start two | ||
475 | browsers and deleted emails in one session. My views is that we should | ||
476 | scream bloody murder and hunt them with a machette. But for now just play | ||
477 | dumb, but maybe the best approach is to pack our things and leave | ||
478 | .i.e exit()/abort(). */ | ||
479 | static int | ||
480 | mbox_is_updated (mailbox_t mailbox) | ||
481 | { | ||
482 | off_t size = 0; | ||
483 | mbox_data_t mud = mailbox->data; | ||
484 | if (stream_size (mailbox->stream, &size) != 0) | ||
485 | return 0; | ||
486 | if (size < mud->size) | ||
487 | { | ||
488 | observable_notify (mailbox->observable, MU_EVT_MAILBOX_CORRUPT); | ||
489 | /* And be verbose. ? */ | ||
490 | mu_error ("* BAD : Mailbox corrupted, shrank size\n"); | ||
491 | /* FIXME: should I crash. */ | ||
492 | return 0; | ||
493 | } | ||
494 | return (mud->size == size); | ||
495 | } | ||
496 | |||
497 | /* Try to create an uniq file, we no race conditions. */ | ||
498 | static int | ||
499 | mbox_tmpfile (mailbox_t mailbox, char **pbox) | ||
500 | { | ||
501 | const char *tmpdir; | ||
502 | int fd; | ||
503 | const char *basename; | ||
504 | mbox_data_t mud = mailbox->data; | ||
505 | |||
506 | /* P_tmpdir should be in <stdio.h>. */ | ||
507 | #ifndef P_tmpdir | ||
508 | # define P_tmpdir "/tmp" | ||
509 | #endif | ||
510 | |||
511 | basename = strrchr (mud->name, '/'); | ||
512 | if (basename) | ||
513 | basename++; | ||
514 | else | ||
515 | basename = mud->name; | ||
516 | |||
517 | tmpdir = getenv ("TMPDIR") ? getenv ("TMPDIR") : P_tmpdir; | ||
518 | /* (separator + null) == 2 + XXXXXX == 6 + ... */ | ||
519 | *pbox = calloc (strlen (tmpdir) + /* '/' */ 1 + /*strlen ("MU_")*/ 3 + | ||
520 | strlen (basename) + /*strlen ("_XXXXXX")*/ 7 + /*null*/1, | ||
521 | sizeof (**pbox)); | ||
522 | if (*pbox == NULL) | ||
523 | return -1; | ||
524 | sprintf (*pbox, "%s/MU_%s_XXXXXX", tmpdir, basename); | ||
525 | #ifdef HAVE_MKSTEMP | ||
526 | fd = mkstemp (*pbox); | ||
527 | #else | ||
528 | /* Create the file. It must not exist. If it does exist, fail. */ | ||
529 | if (mktemp (*pbox)) | ||
530 | { | ||
531 | fd = open (*pbox, O_RDWR|O_CREAT|O_EXCL, 0600); | ||
532 | } | ||
533 | else | ||
534 | { | ||
535 | free (*pbox); | ||
536 | fd = -1; | ||
537 | } | ||
538 | #endif | ||
539 | return fd; | ||
540 | } | ||
541 | |||
542 | /* For the expunge bits we took a very cautionnary approach, meaning | ||
543 | we create a temporary mailbox in the tmpdir copy all the message not mark | ||
544 | deleted(Actually we copy all the message that may have been modified | ||
545 | i.e new header values set; UIDL or UID or etc .... | ||
546 | and skip the deleted ones, truncate the real mailbox to the desired size | ||
547 | and overwrite with the tmp mailbox. The approach to do everyting | ||
548 | in core is tempting but require | ||
549 | - to much memory, it is not rare nowadays to have 30 Megs mailbox, | ||
550 | - also there is danger for filesystems with quotas, | ||
551 | - if we run out of disk space everything is lost. | ||
552 | - or some program may not respect the advisory lock and decide to append | ||
553 | a new message while your expunging etc ... | ||
554 | The real downside to the approach is that when things go wrong | ||
555 | the temporary file may be left in /tmp, which is not all that bad | ||
556 | because at least, we have something to recuperate when failure. */ | ||
557 | static int | ||
558 | mbox_expunge0 (mailbox_t mailbox, int remove_deleted) | ||
559 | { | ||
560 | mbox_data_t mud = mailbox->data; | ||
561 | mbox_message_t mum; | ||
562 | int status = 0; | ||
563 | sigset_t signalset; | ||
564 | int tempfile; | ||
565 | size_t i, j, dirty; /* dirty will indicate the first modified message. */ | ||
566 | off_t marker = 0; /* marker will be the position to truncate. */ | ||
567 | off_t total = 0; | ||
568 | char *tmpmboxname = NULL; | ||
569 | mailbox_t tmpmailbox = NULL; | ||
570 | size_t save_imapbase = 0; /* uidvalidity is save in the first message. */ | ||
571 | #ifdef WITH_PTHREAD | ||
572 | int state; | ||
573 | #endif | ||
574 | |||
575 | if (mud == NULL) | ||
576 | return EINVAL; | ||
577 | |||
578 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_expunge (%s)\n", mud->name); | ||
579 | |||
580 | /* Noop. */ | ||
581 | if (mud->messages_count == 0) | ||
582 | return 0; | ||
583 | |||
584 | /* Find the first dirty(modified) message. */ | ||
585 | for (dirty = 0; dirty < mud->messages_count; dirty++) | ||
586 | { | ||
587 | mum = mud->umessages[dirty]; | ||
588 | /* Message may have been tampered, break here. */ | ||
589 | if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) || | ||
590 | (mum->attr_flags & MU_ATTRIBUTE_DELETED) || | ||
591 | (mum->message && message_is_modified (mum->message))) | ||
592 | break; | ||
593 | } | ||
594 | |||
595 | /* Did something change ? */ | ||
596 | if (dirty == mud->messages_count) | ||
597 | return 0; /* Nothing change, bail out. */ | ||
598 | |||
599 | /* Create a temporary file. */ | ||
600 | tempfile = mbox_tmpfile (mailbox, &tmpmboxname); | ||
601 | if (tempfile == -1) | ||
602 | { | ||
603 | if (tmpmboxname) | ||
604 | free (tmpmboxname); | ||
605 | mu_error ("Failed to create temporary file when expunging.\n"); | ||
606 | return errno; | ||
607 | } | ||
608 | |||
609 | /* This is redundant, we go to the loop again. But it's more secure here | ||
610 | since we don't want to be disturb when expunging. Destroy all the | ||
611 | messages mark for deletion. */ | ||
612 | if (remove_deleted) | ||
613 | { | ||
614 | for (j = 0; j < mud->messages_count; j++) | ||
615 | { | ||
616 | mum = mud->umessages[j]; | ||
617 | if (mum && mum->message && ATTRIBUTE_IS_DELETED (mum->attr_flags)) | ||
618 | message_destroy (&(mum->message), mum); | ||
619 | } | ||
620 | } | ||
621 | |||
622 | /* Create temporary mailbox_t. */ | ||
623 | { | ||
624 | mbox_data_t tmp_mud; | ||
625 | char *m = alloca (5 + strlen (tmpmboxname) + 1); | ||
626 | /* Try via the mbox: protocol. */ | ||
627 | sprintf (m, "mbox:%s", tmpmboxname); | ||
628 | status = mailbox_create (&tmpmailbox, m); | ||
629 | if (status != 0) | ||
630 | { | ||
631 | /* Do not give up just yet, maybe they register the path_record. */ | ||
632 | sprintf (m, "%s", tmpmboxname); | ||
633 | status = mailbox_create (&tmpmailbox, m); | ||
634 | if (status != 0) | ||
635 | { | ||
636 | /* Ok give up. */ | ||
637 | close (tempfile); | ||
638 | remove (tmpmboxname); | ||
639 | free (tmpmboxname); | ||
640 | return status; | ||
641 | } | ||
642 | } | ||
643 | |||
644 | /* Must be flag CREATE if not the mailbox_open will try to mmap() | ||
645 | the file. */ | ||
646 | status = mailbox_open (tmpmailbox, MU_STREAM_CREAT | MU_STREAM_RDWR); | ||
647 | if (status != 0) | ||
648 | { | ||
649 | close (tempfile); | ||
650 | remove (tmpmboxname); | ||
651 | free (tmpmboxname); | ||
652 | return status; | ||
653 | } | ||
654 | close (tempfile); /* This one is useless the mailbox have its own. */ | ||
655 | tmp_mud = tmpmailbox->data; | ||
656 | /* May need when appending. */ | ||
657 | tmp_mud->uidvalidity = mud->uidvalidity; | ||
658 | tmp_mud->uidnext = mud->uidnext; | ||
659 | } | ||
660 | |||
661 | /* Get the File lock. */ | ||
662 | if ((status = locker_lock (mailbox->locker)) != 0) | ||
663 | { | ||
664 | mailbox_close (tmpmailbox); | ||
665 | mailbox_destroy (&tmpmailbox); | ||
666 | remove (tmpmboxname); | ||
667 | free (tmpmboxname); | ||
668 | mu_error ("Failed to grab the lock: %s\n", mu_strerror(status)); | ||
669 | return status; | ||
670 | } | ||
671 | |||
672 | /* Critical section, we can not allowed signal here. */ | ||
673 | #ifdef WITH_PTHREAD | ||
674 | pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state); | ||
675 | #endif | ||
676 | sigemptyset (&signalset); | ||
677 | sigaddset (&signalset, SIGTERM); | ||
678 | sigaddset (&signalset, SIGHUP); | ||
679 | sigaddset (&signalset, SIGTSTP); | ||
680 | sigaddset (&signalset, SIGINT); | ||
681 | sigaddset (&signalset, SIGWINCH); | ||
682 | sigprocmask (SIG_BLOCK, &signalset, 0); | ||
683 | |||
684 | /* Set the marker position. */ | ||
685 | marker = mud->umessages[dirty]->header_from; | ||
686 | total = 0; | ||
687 | |||
688 | /* Copy to the temporary mailbox emails not mark deleted. */ | ||
689 | for (i = dirty; i < mud->messages_count; i++) | ||
690 | { | ||
691 | mum = mud->umessages[i]; | ||
692 | |||
693 | /* Skip it, if mark for deletion. */ | ||
694 | if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags)) | ||
695 | { | ||
696 | /* We save the uidvalidity in the first message, if it is being | ||
697 | deleted we need to move the uidvalidity to the first available | ||
698 | (non-deleted) message. */ | ||
699 | if (i == save_imapbase) | ||
700 | { | ||
701 | save_imapbase = i + 1; | ||
702 | if (save_imapbase < mud->messages_count) | ||
703 | (mud->umessages[save_imapbase])->attr_flags |= MU_ATTRIBUTE_MODIFIED; | ||
704 | } | ||
705 | continue; | ||
706 | } | ||
707 | |||
708 | /* Do the expensive mbox_append_message0() only if mark dirty. */ | ||
709 | if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) || | ||
710 | (mum->message && message_is_modified (mum->message))) | ||
711 | { | ||
712 | /* The message was not instanciated, probably the dirty flag was | ||
713 | set by mbox_scan(), create one here. */ | ||
714 | if (mum->message == 0) | ||
715 | { | ||
716 | message_t msg; | ||
717 | status = mbox_get_message (mailbox, i + 1, &msg); | ||
718 | if (status != 0) | ||
719 | { | ||
720 | mu_error ("Error expunge:%d: %s", __LINE__, | ||
721 | mu_strerror (status)); | ||
722 | goto bailout0; | ||
723 | } | ||
724 | } | ||
725 | status = mbox_append_message0 (tmpmailbox, mum->message, | ||
726 | &total, 1, (i == save_imapbase)); | ||
727 | if (status != 0) | ||
728 | { | ||
729 | mu_error ("Error expunge:%d: %s", __LINE__, | ||
730 | mu_strerror (status)); | ||
731 | goto bailout0; | ||
732 | } | ||
733 | /* Clear the dirty bits. */ | ||
734 | mum->attr_flags &= ~MU_ATTRIBUTE_MODIFIED; | ||
735 | message_clear_modified (mum->message); | ||
736 | } | ||
737 | else | ||
738 | { | ||
739 | /* Nothing changed copy the message straight. */ | ||
740 | char buffer [1024]; | ||
741 | size_t n; | ||
742 | off_t offset = mum->header_from; | ||
743 | size_t len = mum->body_end - mum->header_from; | ||
744 | while (len > 0) | ||
745 | { | ||
746 | n = (len < sizeof (buffer)) ? len : sizeof (buffer); | ||
747 | if ((status = stream_read (mailbox->stream, buffer, n, offset, | ||
748 | &n) != 0) | ||
749 | || (status = stream_write (tmpmailbox->stream, buffer, n, | ||
750 | total, &n) != 0)) | ||
751 | { | ||
752 | mu_error ("Error expunge:%d: %s", __LINE__, | ||
753 | mu_strerror (status)); | ||
754 | goto bailout0; | ||
755 | } | ||
756 | len -= n; | ||
757 | total += n; | ||
758 | offset += n; | ||
759 | } | ||
760 | /* Add the newline separator. */ | ||
761 | status = stream_write (tmpmailbox->stream, "\n", 1, total, &n); | ||
762 | if (status != 0) | ||
763 | { | ||
764 | mu_error ("Error expunge:%d: %s", __LINE__, | ||
765 | mu_strerror (status)); | ||
766 | goto bailout0; | ||
767 | } | ||
768 | total++; | ||
769 | } | ||
770 | } /* for (;;) */ | ||
771 | |||
772 | /* Caution: before ftruncate()ing the file see | ||
773 | - if we've receive new mails. Some programs may not respect the lock, | ||
774 | - or the lock was held for too long. | ||
775 | - The mailbox may not have been properly updated before expunging. */ | ||
776 | { | ||
777 | off_t size = 0; | ||
778 | if (stream_size (mailbox->stream, &size) == 0) | ||
779 | { | ||
780 | off_t len = size - mud->size; | ||
781 | off_t offset = mud->size; | ||
782 | char buffer [1024]; | ||
783 | size_t n = 0; | ||
784 | if (len > 0 ) | ||
785 | { | ||
786 | while ((status = stream_read (mailbox->stream, buffer, | ||
787 | sizeof (buffer), offset, &n)) == 0 | ||
788 | && n > 0) | ||
789 | { | ||
790 | status = stream_write (tmpmailbox->stream, buffer, n, | ||
791 | total, &n); | ||
792 | if (status != 0) | ||
793 | { | ||
794 | mu_error ("Error expunge:%d: %s", __LINE__, | ||
795 | mu_strerror (status)); | ||
796 | goto bailout0; | ||
797 | } | ||
798 | total += n; | ||
799 | offset += n; | ||
800 | } | ||
801 | } | ||
802 | else if (len < 0) | ||
803 | { | ||
804 | /* Corrupted mailbox. */ | ||
805 | mu_error ("Error expunge:%d: %s", __LINE__, | ||
806 | mu_strerror (status)); | ||
807 | goto bailout0; | ||
808 | } | ||
809 | } | ||
810 | } /* End of precaution. */ | ||
811 | |||
812 | /* Seek and rewrite it. */ | ||
813 | if (total > 0) | ||
814 | { | ||
815 | char buffer [1024]; | ||
816 | size_t n = 0; | ||
817 | off_t off = 0; | ||
818 | off_t offset = marker; | ||
819 | while ((status = stream_read (tmpmailbox->stream, buffer, | ||
820 | sizeof (buffer), off, &n)) == 0 | ||
821 | && n > 0) | ||
822 | { | ||
823 | status = stream_write (mailbox->stream, buffer, n, offset, &n); | ||
824 | if (status != 0) | ||
825 | { | ||
826 | mu_error ("Error expunge:%d: %s\n", __LINE__, | ||
827 | mu_strerror (status)); | ||
828 | goto bailout; | ||
829 | } | ||
830 | off += n; | ||
831 | offset += n; | ||
832 | } | ||
833 | } | ||
834 | |||
835 | /* Flush/truncation. Need to flush before truncate. */ | ||
836 | stream_flush (mailbox->stream); | ||
837 | status = stream_truncate (mailbox->stream, total + marker); | ||
838 | if (status != 0) | ||
839 | { | ||
840 | mu_error ("Error expunging:%d: %s\n", __LINE__, | ||
841 | mu_strerror (status)); | ||
842 | goto bailout; | ||
843 | } | ||
844 | |||
845 | /* Don't remove the tmp mbox in case of errors, when writing back. */ | ||
846 | bailout0: | ||
847 | remove (tmpmboxname); | ||
848 | |||
849 | bailout: | ||
850 | |||
851 | free (tmpmboxname); | ||
852 | /* Release the File lock. */ | ||
853 | locker_unlock (mailbox->locker); | ||
854 | mailbox_close (tmpmailbox); | ||
855 | mailbox_destroy (&tmpmailbox); | ||
856 | |||
857 | /* Reenable signal. */ | ||
858 | #ifdef WITH_PTHREAD | ||
859 | pthread_setcancelstate (state, &state); | ||
860 | #endif | ||
861 | sigprocmask (SIG_UNBLOCK, &signalset, 0); | ||
862 | |||
863 | /* We need to readjust the pointers. | ||
864 | It is a little hairy because we need to keep the message pointers alive | ||
865 | So we are going through the array and "defragmentize". For example | ||
866 | in (1 2 3 4) if 2 was deleted we need to move 3 4 up by one etc .. | ||
867 | */ | ||
868 | if (status == 0) | ||
869 | { | ||
870 | size_t dlast; | ||
871 | monitor_wrlock (mailbox->monitor); | ||
872 | for (j = dirty, dlast = mud->messages_count - 1; | ||
873 | j <= dlast; j++) | ||
874 | { | ||
875 | /* Clear all the references, any attach messages been already | ||
876 | destroy above. */ | ||
877 | mum = mud->umessages[j]; | ||
878 | if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags)) | ||
879 | { | ||
880 | if ((j + 1) <= dlast) | ||
881 | { | ||
882 | /* Move all the pointers up. So the message pointer | ||
883 | part of mum will be at the right position. */ | ||
884 | memmove (mud->umessages + j, mud->umessages + j + 1, | ||
885 | (dlast - j) * sizeof (mum)); | ||
886 | #if 0 | ||
887 | mum->header_from = mum->header_from_end = 0; | ||
888 | mum->body = mum->body_end = 0; | ||
889 | mum->header_lines = mum->body_lines = 0; | ||
890 | #endif | ||
891 | for (i = 0; i < HDRSIZE; i++) | ||
892 | if (mum->fhdr[i]) | ||
893 | { | ||
894 | free (mum->fhdr[i]); | ||
895 | mum->fhdr[i] = NULL; | ||
896 | } | ||
897 | memset (mum, 0, sizeof (*mum)); | ||
898 | /* We are not free()ing the useless mum, but instead | ||
899 | we put it back in the pool, to be reuse. */ | ||
900 | mud->umessages[dlast] = mum; | ||
901 | dlast--; | ||
902 | /* Set mum to the new value after the memmove so it | ||
903 | gets cleared to. */ | ||
904 | mum = mud->umessages[j]; | ||
905 | } | ||
906 | else | ||
907 | { | ||
908 | for (i = 0; i < HDRSIZE; i++) | ||
909 | if (mum->fhdr[i]) | ||
910 | { | ||
911 | free (mum->fhdr[i]); | ||
912 | mum->fhdr[i] = NULL; | ||
913 | } | ||
914 | memset (mum, 0, sizeof (*mum)); | ||
915 | } | ||
916 | } | ||
917 | mum->header_from = mum->header_from_end = 0; | ||
918 | mum->body = mum->body_end = 0; | ||
919 | mum->header_lines = mum->body_lines = 0; | ||
920 | for (i = 0; i < HDRSIZE; i++) | ||
921 | if (mum->fhdr[i]) | ||
922 | { | ||
923 | free (mum->fhdr[i]); | ||
924 | mum->fhdr[i] = NULL; | ||
925 | } | ||
926 | } | ||
927 | monitor_unlock (mailbox->monitor); | ||
928 | /* This is should reset the messages_count, the last argument 0 means | ||
929 | not to send event notification. */ | ||
930 | mbox_scan0 (mailbox, dirty, NULL, 0); | ||
931 | } | ||
932 | return status; | ||
933 | } | ||
934 | |||
935 | static int | ||
936 | mbox_expunge (mailbox_t mailbox) | ||
937 | { | ||
938 | return mbox_expunge0 (mailbox, 1); | ||
939 | } | ||
940 | |||
941 | static int | ||
942 | mbox_save_attributes (mailbox_t mailbox) | ||
943 | { | ||
944 | return mbox_expunge0 (mailbox, 0); | ||
945 | } | ||
946 | |||
947 | static int | ||
948 | mbox_message_uid (message_t msg, size_t *puid) | ||
949 | { | ||
950 | mbox_message_t mum = message_get_owner (msg); | ||
951 | if (puid) | ||
952 | *puid = mum->uid; | ||
953 | return 0; | ||
954 | } | ||
955 | |||
956 | static int | ||
957 | mbox_get_body_fd (stream_t is, int *pfd) | ||
958 | { | ||
959 | body_t body = stream_get_owner (is); | ||
960 | message_t msg = body_get_owner (body); | ||
961 | mbox_message_t mum = message_get_owner (msg); | ||
962 | return mbox_get_fd (mum, pfd); | ||
963 | } | ||
964 | |||
965 | static int | ||
966 | mbox_get_fd (mbox_message_t mum, int *pfd) | ||
967 | { | ||
968 | int status; | ||
969 | if (mum == NULL) | ||
970 | return EINVAL; | ||
971 | status = stream_get_fd (mum->mud->mailbox->stream, pfd); | ||
972 | return status; | ||
973 | } | ||
974 | |||
975 | static int | ||
976 | mbox_get_attr_flags (attribute_t attr, int *pflags) | ||
977 | { | ||
978 | message_t msg = attribute_get_owner (attr); | ||
979 | mbox_message_t mum = message_get_owner (msg); | ||
980 | |||
981 | if (mum == NULL) | ||
982 | return EINVAL; | ||
983 | if (pflags) | ||
984 | *pflags = mum->attr_flags; | ||
985 | return 0; | ||
986 | } | ||
987 | |||
988 | static int | ||
989 | mbox_set_attr_flags (attribute_t attr, int flags) | ||
990 | { | ||
991 | message_t msg = attribute_get_owner (attr); | ||
992 | mbox_message_t mum = message_get_owner (msg); | ||
993 | |||
994 | if (mum == NULL) | ||
995 | return EINVAL; | ||
996 | mum->attr_flags |= flags; | ||
997 | return 0; | ||
998 | } | ||
999 | |||
1000 | static int | ||
1001 | mbox_unset_attr_flags (attribute_t attr, int flags) | ||
1002 | { | ||
1003 | message_t msg = attribute_get_owner (attr); | ||
1004 | mbox_message_t mum = message_get_owner (msg); | ||
1005 | |||
1006 | if (mum == NULL) | ||
1007 | return EINVAL; | ||
1008 | mum->attr_flags &= ~flags; | ||
1009 | return 0; | ||
1010 | } | ||
1011 | |||
1012 | static int | ||
1013 | mbox_body_readline (stream_t is, char *buffer, size_t buflen, | ||
1014 | off_t off, size_t *pnread) | ||
1015 | { | ||
1016 | body_t body = stream_get_owner (is); | ||
1017 | message_t msg = body_get_owner (body); | ||
1018 | mbox_message_t mum = message_get_owner (msg); | ||
1019 | |||
1020 | return mbox_readstream (mum, buffer, buflen, off, pnread, 1, | ||
1021 | mum->body, mum->body_end); | ||
1022 | } | ||
1023 | |||
1024 | static int | ||
1025 | mbox_body_read (stream_t is, char *buffer, size_t buflen, | ||
1026 | off_t off, size_t *pnread) | ||
1027 | { | ||
1028 | body_t body = stream_get_owner (is); | ||
1029 | message_t msg = body_get_owner (body); | ||
1030 | mbox_message_t mum = message_get_owner (msg); | ||
1031 | return mbox_readstream (mum, buffer, buflen, off, pnread, 0, | ||
1032 | mum->body, mum->body_end); | ||
1033 | } | ||
1034 | |||
1035 | static int | ||
1036 | mbox_readstream (mbox_message_t mum, char *buffer, size_t buflen, | ||
1037 | off_t off, size_t *pnread, int isreadline, | ||
1038 | off_t start, off_t end) | ||
1039 | { | ||
1040 | size_t nread = 0; | ||
1041 | int status = 0; | ||
1042 | |||
1043 | if (buffer == NULL || buflen == 0) | ||
1044 | { | ||
1045 | if (pnread) | ||
1046 | *pnread = nread; | ||
1047 | return 0; | ||
1048 | } | ||
1049 | |||
1050 | monitor_rdlock (mum->mud->mailbox->monitor); | ||
1051 | #ifdef WITH_PTHREAD | ||
1052 | /* read() is cancellation point since we're doing a potentially | ||
1053 | long operation. Lets make sure we clean the state. */ | ||
1054 | pthread_cleanup_push (mbox_cleanup, (void *)mum->mud->mailbox); | ||
1055 | #endif | ||
1056 | { | ||
1057 | off_t ln = end - (start + off); | ||
1058 | if (ln > 0) | ||
1059 | { | ||
1060 | /* Position the file pointer and the buffer. */ | ||
1061 | nread = ((size_t)ln < buflen) ? (size_t)ln : buflen; | ||
1062 | if (isreadline) | ||
1063 | status = stream_readline (mum->mud->mailbox->stream, buffer, buflen, | ||
1064 | start + off, &nread); | ||
1065 | else | ||
1066 | status = stream_read (mum->mud->mailbox->stream, buffer, nread, | ||
1067 | start + off, &nread); | ||
1068 | } | ||
1069 | } | ||
1070 | monitor_unlock (mum->mud->mailbox->monitor); | ||
1071 | #ifdef WITH_PTHREAD | ||
1072 | pthread_cleanup_pop (0); | ||
1073 | #endif | ||
1074 | |||
1075 | if (pnread) | ||
1076 | *pnread = nread; | ||
1077 | return status; | ||
1078 | } | ||
1079 | |||
1080 | static int | ||
1081 | mbox_header_fill (header_t header, char *buffer, size_t len, | ||
1082 | off_t off, size_t *pnread) | ||
1083 | { | ||
1084 | message_t msg = header_get_owner (header); | ||
1085 | mbox_message_t mum = message_get_owner (msg); | ||
1086 | size_t j; | ||
1087 | /* Since we are filling the header there is no need for the cache headers | ||
1088 | discard them. */ | ||
1089 | for (j = 0; j < HDRSIZE; j++) | ||
1090 | { | ||
1091 | if (mum->fhdr[j]) | ||
1092 | { | ||
1093 | free (mum->fhdr[j]); | ||
1094 | mum->fhdr[j] = NULL; | ||
1095 | } | ||
1096 | } | ||
1097 | return mbox_readstream (mum, buffer, len, off, pnread, 0, | ||
1098 | mum->header_from_end, mum->body); | ||
1099 | } | ||
1100 | |||
1101 | static int | ||
1102 | mbox_header_get_fvalue (header_t header, const char *name, char *buffer, | ||
1103 | size_t buflen, size_t *pnread) | ||
1104 | { | ||
1105 | size_t i, fv_len = 0; | ||
1106 | message_t msg = header_get_owner (header); | ||
1107 | mbox_message_t mum = message_get_owner (msg); | ||
1108 | int err = ENOENT; | ||
1109 | for (i = 0; i < HDRSIZE; i++) | ||
1110 | { | ||
1111 | if (*name == *(fhdr_table[i]) && strcasecmp (fhdr_table[i], name) == 0) | ||
1112 | { | ||
1113 | if (mum->fhdr[i]) | ||
1114 | { | ||
1115 | fv_len = strlen (mum->fhdr[i]); | ||
1116 | if (buffer && buflen > 0) | ||
1117 | { | ||
1118 | /* For the null. */ | ||
1119 | buflen--; | ||
1120 | fv_len = (fv_len < buflen) ? fv_len : buflen; | ||
1121 | memcpy (buffer, mum->fhdr[i], fv_len); | ||
1122 | buffer[fv_len] = '\0'; | ||
1123 | } | ||
1124 | err = 0; | ||
1125 | } | ||
1126 | else | ||
1127 | err = ENOENT; | ||
1128 | break; | ||
1129 | } | ||
1130 | } | ||
1131 | |||
1132 | if (pnread) | ||
1133 | *pnread = fv_len; | ||
1134 | return err; | ||
1135 | } | ||
1136 | |||
1137 | static int | ||
1138 | mbox_header_size (header_t header, size_t *psize) | ||
1139 | { | ||
1140 | message_t msg = header_get_owner (header); | ||
1141 | mbox_message_t mum = message_get_owner (msg); | ||
1142 | if (mum == NULL) | ||
1143 | return EINVAL; | ||
1144 | if (psize) | ||
1145 | *psize = mum->body - mum->header_from_end; | ||
1146 | return 0; | ||
1147 | } | ||
1148 | |||
1149 | static int | ||
1150 | mbox_header_lines (header_t header, size_t *plines) | ||
1151 | { | ||
1152 | message_t msg = header_get_owner (header); | ||
1153 | mbox_message_t mum = message_get_owner (msg); | ||
1154 | if (mum == NULL) | ||
1155 | return EINVAL; | ||
1156 | if (plines) | ||
1157 | *plines = mum->header_lines; | ||
1158 | return 0; | ||
1159 | } | ||
1160 | |||
1161 | static int | ||
1162 | mbox_body_size (body_t body, size_t *psize) | ||
1163 | { | ||
1164 | message_t msg = body_get_owner (body); | ||
1165 | mbox_message_t mum = message_get_owner (msg); | ||
1166 | if (mum == NULL) | ||
1167 | return EINVAL; | ||
1168 | if (psize) | ||
1169 | *psize = mum->body_end - mum->body; | ||
1170 | return 0; | ||
1171 | } | ||
1172 | |||
1173 | static int | ||
1174 | mbox_stream_size (stream_t stream, off_t *psize) | ||
1175 | { | ||
1176 | body_t body = stream_get_owner (stream); | ||
1177 | return mbox_body_size (body, (size_t*) psize); | ||
1178 | } | ||
1179 | |||
1180 | static int | ||
1181 | mbox_body_lines (body_t body, size_t *plines) | ||
1182 | { | ||
1183 | message_t msg = body_get_owner (body); | ||
1184 | mbox_message_t mum = message_get_owner (msg); | ||
1185 | if (mum == NULL) | ||
1186 | return EINVAL; | ||
1187 | if (plines) | ||
1188 | *plines = mum->body_lines; | ||
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | static int | ||
1193 | mbox_envelope_date (envelope_t envelope, char *buf, size_t len, | ||
1194 | size_t *pnwrite) | ||
1195 | { | ||
1196 | message_t msg = envelope_get_owner (envelope); | ||
1197 | mbox_message_t mum = message_get_owner (msg); | ||
1198 | size_t n = 0; | ||
1199 | int status; | ||
1200 | char buffer[512]; | ||
1201 | char *s; | ||
1202 | |||
1203 | if (mum == NULL) | ||
1204 | return EINVAL; | ||
1205 | |||
1206 | status = stream_readline (mum->mud->mailbox->stream, buffer, sizeof(buffer), | ||
1207 | mum->header_from, &n); | ||
1208 | if (status != 0) | ||
1209 | { | ||
1210 | if (pnwrite) | ||
1211 | *pnwrite = 0; | ||
1212 | return status; | ||
1213 | } | ||
1214 | |||
1215 | /* Format: "From [sender] [date]" */ | ||
1216 | /* strlen ("From ") == 5 */ | ||
1217 | if (n > 5 && (s = strchr (buffer + 5, ' ')) != NULL) | ||
1218 | { | ||
1219 | if (buf && len > 0) | ||
1220 | { | ||
1221 | len--; /* Leave space for the null. */ | ||
1222 | strncpy (buf, s + 1, len)[len] = '\0'; | ||
1223 | len = strlen (buf); | ||
1224 | } | ||
1225 | else | ||
1226 | len = strlen (s + 1); | ||
1227 | } | ||
1228 | else | ||
1229 | len = 0; | ||
1230 | |||
1231 | if (pnwrite) | ||
1232 | *pnwrite = len; | ||
1233 | return 0; | ||
1234 | } | ||
1235 | |||
1236 | static int | ||
1237 | mbox_envelope_sender (envelope_t envelope, char *buf, size_t len, | ||
1238 | size_t *pnwrite) | ||
1239 | { | ||
1240 | message_t msg = envelope_get_owner (envelope); | ||
1241 | mbox_message_t mum = message_get_owner (msg); | ||
1242 | size_t n = 0; | ||
1243 | int status; | ||
1244 | char buffer[512]; | ||
1245 | char *s; | ||
1246 | |||
1247 | if (mum == NULL) | ||
1248 | return EINVAL; | ||
1249 | |||
1250 | status = stream_readline (mum->mud->mailbox->stream, buffer, sizeof(buffer), | ||
1251 | mum->header_from, &n); | ||
1252 | if (status != 0) | ||
1253 | { | ||
1254 | if (pnwrite) | ||
1255 | *pnwrite = 0; | ||
1256 | return status; | ||
1257 | } | ||
1258 | |||
1259 | /* Format: "From [sender] [date]" */ | ||
1260 | /* strlen ("From ") == 5 */ | ||
1261 | if (n > 5 && (s = strchr (buffer + 5, ' ')) != NULL) | ||
1262 | { | ||
1263 | /* Put a NULL to isolate the sender string, make a C string. */ | ||
1264 | *s = '\0'; | ||
1265 | if (buf && len > 0) | ||
1266 | { | ||
1267 | len--; /* leave space for the null */ | ||
1268 | strncpy (buf, buffer + 5, len)[len] = '\0'; | ||
1269 | len = strlen (buf); | ||
1270 | } | ||
1271 | else | ||
1272 | len = strlen (buffer + 5); | ||
1273 | } | ||
1274 | else | ||
1275 | len = 0; | ||
1276 | |||
1277 | if (pnwrite) | ||
1278 | *pnwrite = len; | ||
1279 | return 0; | ||
1280 | } | ||
1281 | |||
1282 | static int | ||
1283 | mbox_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg) | ||
1284 | { | ||
1285 | int status; | ||
1286 | mbox_data_t mud = mailbox->data; | ||
1287 | mbox_message_t mum; | ||
1288 | message_t msg = NULL; | ||
1289 | |||
1290 | /* Sanity checks. */ | ||
1291 | if (pmsg == NULL || mud == NULL) | ||
1292 | return EINVAL; | ||
1293 | |||
1294 | /* If we did not start a scanning yet do it now. */ | ||
1295 | if (mud->messages_count == 0) | ||
1296 | { | ||
1297 | status = mbox_scan0 (mailbox, 1, NULL, 0); | ||
1298 | if (status != 0) | ||
1299 | return status; | ||
1300 | } | ||
1301 | |||
1302 | /* Second sanity: check the message number. */ | ||
1303 | if (!(mud->messages_count > 0 | ||
1304 | && msgno > 0 | ||
1305 | && msgno <= mud->messages_count)) | ||
1306 | return EINVAL; | ||
1307 | |||
1308 | mum = mud->umessages[msgno - 1]; | ||
1309 | |||
1310 | /* Check if we already have it. */ | ||
1311 | if (mum->message) | ||
1312 | { | ||
1313 | if (pmsg) | ||
1314 | *pmsg = mum->message; | ||
1315 | return 0; | ||
1316 | } | ||
1317 | |||
1318 | MAILBOX_DEBUG2 (mailbox, MU_DEBUG_TRACE, "mbox_get_message(%s, %d)\n", | ||
1319 | mud->name, msgno); | ||
1320 | |||
1321 | /* Get an empty message struct. */ | ||
1322 | status = message_create (&msg, mum); | ||
1323 | if (status != 0) | ||
1324 | return status; | ||
1325 | |||
1326 | /* Set the header. */ | ||
1327 | { | ||
1328 | header_t header = NULL; | ||
1329 | status = header_create (&header, NULL, 0, msg); | ||
1330 | if (status != 0) | ||
1331 | { | ||
1332 | message_destroy (&msg, mum); | ||
1333 | return status; | ||
1334 | } | ||
1335 | header_set_fill (header, mbox_header_fill, msg); | ||
1336 | header_set_get_fvalue (header, mbox_header_get_fvalue, msg); | ||
1337 | header_set_size (header, mbox_header_size, msg); | ||
1338 | header_set_lines (header, mbox_header_lines, msg); | ||
1339 | message_set_header (msg, header, mum); | ||
1340 | } | ||
1341 | |||
1342 | /* Set the attribute. */ | ||
1343 | { | ||
1344 | attribute_t attribute; | ||
1345 | status = attribute_create (&attribute, msg); | ||
1346 | if (status != 0) | ||
1347 | { | ||
1348 | message_destroy (&msg, mum); | ||
1349 | return status; | ||
1350 | } | ||
1351 | attribute_set_get_flags (attribute, mbox_get_attr_flags, msg); | ||
1352 | attribute_set_set_flags (attribute, mbox_set_attr_flags, msg); | ||
1353 | attribute_set_unset_flags (attribute, mbox_unset_attr_flags, msg); | ||
1354 | message_set_attribute (msg, attribute, mum); | ||
1355 | } | ||
1356 | |||
1357 | /* Prepare the body. */ | ||
1358 | { | ||
1359 | body_t body = NULL; | ||
1360 | stream_t stream = NULL; | ||
1361 | if ((status = body_create (&body, msg)) != 0 | ||
1362 | || (status = stream_create (&stream, | ||
1363 | mailbox->flags | MU_STREAM_SEEKABLE, | ||
1364 | body)) != 0) | ||
1365 | { | ||
1366 | body_destroy (&body, msg); | ||
1367 | stream_destroy (&stream, body); | ||
1368 | message_destroy (&msg, mum); | ||
1369 | return status; | ||
1370 | } | ||
1371 | stream_set_read (stream, mbox_body_read, body); | ||
1372 | stream_set_readline (stream, mbox_body_readline, body); | ||
1373 | stream_set_fd (stream, mbox_get_body_fd, body); | ||
1374 | stream_set_size (stream, mbox_stream_size, body); | ||
1375 | body_set_stream (body, stream, msg); | ||
1376 | body_set_size (body, mbox_body_size, msg); | ||
1377 | body_set_lines (body, mbox_body_lines, msg); | ||
1378 | message_set_body (msg, body, mum); | ||
1379 | } | ||
1380 | |||
1381 | /* Set the envelope. */ | ||
1382 | { | ||
1383 | envelope_t envelope= NULL; | ||
1384 | status = envelope_create (&envelope, msg); | ||
1385 | if (status != 0) | ||
1386 | { | ||
1387 | message_destroy (&msg, mum); | ||
1388 | return status; | ||
1389 | } | ||
1390 | envelope_set_sender (envelope, mbox_envelope_sender, msg); | ||
1391 | envelope_set_date (envelope, mbox_envelope_date, msg); | ||
1392 | message_set_envelope (msg, envelope, mum); | ||
1393 | } | ||
1394 | |||
1395 | /* Set the UID. */ | ||
1396 | message_set_uid (msg, mbox_message_uid, mum); | ||
1397 | |||
1398 | /* Attach the message to the mailbox mbox data. */ | ||
1399 | mum->message = msg; | ||
1400 | message_set_mailbox (msg, mailbox, mum); | ||
1401 | |||
1402 | *pmsg = msg; | ||
1403 | return 0; | ||
1404 | } | ||
1405 | |||
1406 | static int | ||
1407 | mbox_append_message (mailbox_t mailbox, message_t msg) | ||
1408 | { | ||
1409 | int status = 0; | ||
1410 | mbox_data_t mud = mailbox->data; | ||
1411 | if (msg == NULL || mud == NULL) | ||
1412 | return EINVAL; | ||
1413 | |||
1414 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_append_message (%s)\n", | ||
1415 | mud->name); | ||
1416 | |||
1417 | switch (mud->state) | ||
1418 | { | ||
1419 | case MBOX_NO_STATE: | ||
1420 | if ((status = locker_lock (mailbox->locker)) != 0) | ||
1421 | { | ||
1422 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, | ||
1423 | "mbox_append_message: %s\n", | ||
1424 | mu_strerror(status)); | ||
1425 | return status; | ||
1426 | } | ||
1427 | |||
1428 | default: | ||
1429 | { | ||
1430 | off_t size; | ||
1431 | /* Move to the end of the file, not necesary if _APPEND mode. */ | ||
1432 | if ((status = stream_size (mailbox->stream, &size)) != 0 | ||
1433 | || (status = mbox_append_message0 (mailbox, msg, &size, 0, 0)) != 0) | ||
1434 | { | ||
1435 | if (status != EAGAIN) | ||
1436 | locker_unlock (mailbox->locker); | ||
1437 | return status; | ||
1438 | } | ||
1439 | } | ||
1440 | } | ||
1441 | locker_unlock (mailbox->locker); | ||
1442 | return 0; | ||
1443 | } | ||
1444 | |||
1445 | /* FIXME: We need to escape body line that begins with "From ", this | ||
1446 | will required to read the body by line instead of by chuncks hurting | ||
1447 | perfomance big time when expunging. But should not this be the | ||
1448 | responsability of the client ? */ | ||
1449 | static int | ||
1450 | mbox_append_message0 (mailbox_t mailbox, message_t msg, off_t *psize, | ||
1451 | int is_expunging, int first) | ||
1452 | { | ||
1453 | mbox_data_t mud = mailbox->data; | ||
1454 | int status = 0; | ||
1455 | size_t n = 0; | ||
1456 | char nl = '\n'; | ||
1457 | |||
1458 | switch (mud->state) | ||
1459 | { | ||
1460 | case MBOX_NO_STATE: | ||
1461 | /* Allocate memory for the sender/date buffers. */ | ||
1462 | mud->sender = calloc (128, sizeof (char)); | ||
1463 | if (mud->sender == NULL) | ||
1464 | return ENOMEM; | ||
1465 | mud->date = calloc (128, sizeof (char)); | ||
1466 | if (mud->date == NULL) | ||
1467 | { | ||
1468 | free (mud->sender); | ||
1469 | mud->sender = NULL; | ||
1470 | return ENOMEM; | ||
1471 | } | ||
1472 | mud->off = 0; | ||
1473 | mud->state = MBOX_STATE_APPEND_SENDER; | ||
1474 | |||
1475 | case MBOX_STATE_APPEND_SENDER: | ||
1476 | /* Generate the sender for the "From " separator. */ | ||
1477 | { | ||
1478 | char *s; | ||
1479 | size_t len = 0; | ||
1480 | envelope_t envelope = NULL; | ||
1481 | message_get_envelope (msg, &envelope); | ||
1482 | status = envelope_sender (envelope, mud->sender, 128, &len); | ||
1483 | if (status != 0) | ||
1484 | { | ||
1485 | if (status != EAGAIN) | ||
1486 | { | ||
1487 | free (mud->sender); | ||
1488 | free (mud->date); | ||
1489 | mud->date = mud->sender = NULL; | ||
1490 | mud->state = MBOX_NO_STATE; | ||
1491 | } | ||
1492 | return status; | ||
1493 | } | ||
1494 | |||
1495 | /* Nuke trailing newline. */ | ||
1496 | s = memchr (mud->sender, nl, len); | ||
1497 | if (s) | ||
1498 | *s = '\0'; | ||
1499 | mud->state = MBOX_STATE_APPEND_DATE; | ||
1500 | } | ||
1501 | |||
1502 | case MBOX_STATE_APPEND_DATE: | ||
1503 | /* Generate a date for the "From " separator. */ | ||
1504 | { | ||
1505 | char *s; | ||
1506 | size_t len = 0; | ||
1507 | envelope_t envelope = NULL; | ||
1508 | char buffer[1024]; | ||
1509 | message_get_envelope (msg, &envelope); | ||
1510 | status = envelope_date (envelope, mud->date, 128, &len); | ||
1511 | if (status != 0) | ||
1512 | { | ||
1513 | if (status != EAGAIN) | ||
1514 | { | ||
1515 | free (mud->sender); | ||
1516 | free (mud->date); | ||
1517 | mud->date = mud->sender = NULL; | ||
1518 | mud->state = MBOX_NO_STATE; | ||
1519 | } | ||
1520 | return status; | ||
1521 | } | ||
1522 | |||
1523 | /* Nuke trailing newline. */ | ||
1524 | s = memchr (mud->date, nl, len); | ||
1525 | if (s) | ||
1526 | *s = '\0'; | ||
1527 | |||
1528 | /* Write the separator to the mailbox. */ | ||
1529 | n = snprintf (buffer, sizeof (buffer), "From %s %s", | ||
1530 | mud->sender, mud->date); | ||
1531 | stream_write (mailbox->stream, buffer, n, *psize, &n); | ||
1532 | *psize += n; | ||
1533 | |||
1534 | /* Add the newline, the above may be truncated. */ | ||
1535 | stream_write (mailbox->stream, &nl , 1, *psize, &n); | ||
1536 | *psize += n; | ||
1537 | |||
1538 | free (mud->sender); | ||
1539 | free (mud->date); | ||
1540 | mud->sender = mud->date = NULL; | ||
1541 | /* If we are not expunging get the message in one block via the stream | ||
1542 | message instead of the header/body. This is good for POP where | ||
1543 | there is no separation between header and body(RETR). */ | ||
1544 | if (! is_expunging) | ||
1545 | { | ||
1546 | mud->state = MBOX_STATE_APPEND_MESSAGE; | ||
1547 | break; | ||
1548 | } | ||
1549 | mud->state = MBOX_STATE_APPEND_HEADER; | ||
1550 | } | ||
1551 | |||
1552 | case MBOX_STATE_APPEND_HEADER: | ||
1553 | /* Append the Header. */ | ||
1554 | { | ||
1555 | char buffer[1024]; | ||
1556 | size_t nread = 0; | ||
1557 | stream_t is = NULL; | ||
1558 | header_t header = NULL; | ||
1559 | message_get_header (msg, &header); | ||
1560 | header_get_stream (header, &is); | ||
1561 | do | ||
1562 | { | ||
1563 | status = stream_readline (is, buffer, sizeof (buffer), mud->off, | ||
1564 | &nread); | ||
1565 | if (status != 0) | ||
1566 | { | ||
1567 | if (status != EAGAIN) | ||
1568 | { | ||
1569 | mud->state = MBOX_NO_STATE; | ||
1570 | mud->off = 0; | ||
1571 | } | ||
1572 | return status; | ||
1573 | } | ||
1574 | mud->off += nread; | ||
1575 | if (*buffer == '\n') | ||
1576 | break; | ||
1577 | |||
1578 | /* We do not copy the Status since it is rewritten by the | ||
1579 | attribute code below. Ditto for X-UID and X-IMAPBase. | ||
1580 | FIXME: | ||
1581 | - We have a problem here the header may not fit the buffer. | ||
1582 | - Should we skip the IMAP "X-Status"? */ | ||
1583 | if ((strncasecmp (buffer, "Status", 6) == 0) | ||
1584 | || (strncasecmp (buffer, "X-IMAPbase", 10) == 0) | ||
1585 | /* FIXME: isn't the length of "X-UID" 5, not 4? And | ||
1586 | this will match X-UID and X-UIDL, is this intended? */ | ||
1587 | || (strncasecmp (buffer, "X-UID", 4) == 0 | ||
1588 | && (buffer[5] == ':' || buffer[5] == ' ' | ||
1589 | || buffer[5] == '\t'))) | ||
1590 | continue; | ||
1591 | |||
1592 | status = stream_write (mailbox->stream, buffer, nread, | ||
1593 | *psize, &n); | ||
1594 | if (status != 0) | ||
1595 | break; | ||
1596 | *psize += n; | ||
1597 | } | ||
1598 | while (nread > 0); | ||
1599 | mud->off = 0; | ||
1600 | |||
1601 | /* Rewrite the X-IMAPbase marker. */ | ||
1602 | if (first && is_expunging) | ||
1603 | { | ||
1604 | n = sprintf (buffer, "X-IMAPbase: %lu %u\n", | ||
1605 | (unsigned long) mud->uidvalidity, | ||
1606 | (unsigned) mud->uidnext); | ||
1607 | stream_write (mailbox->stream, buffer, n, *psize, &n); | ||
1608 | *psize += n; | ||
1609 | } | ||
1610 | mud->state = MBOX_STATE_APPEND_ATTRIBUTE; | ||
1611 | } | ||
1612 | |||
1613 | case MBOX_STATE_APPEND_ATTRIBUTE: | ||
1614 | /* Put the new attributes. */ | ||
1615 | { | ||
1616 | char abuf[64]; | ||
1617 | size_t na = 0; | ||
1618 | attribute_t attr = NULL; | ||
1619 | abuf[0] = '\0'; | ||
1620 | message_get_attribute (msg, &attr); | ||
1621 | attribute_to_string (attr, abuf, sizeof(abuf), &na); | ||
1622 | |||
1623 | status = stream_write (mailbox->stream, abuf, na, *psize, &n); | ||
1624 | if (status != 0) | ||
1625 | break; | ||
1626 | *psize += n; | ||
1627 | |||
1628 | mud->state = MBOX_STATE_APPEND_UID; | ||
1629 | } | ||
1630 | |||
1631 | case MBOX_STATE_APPEND_UID: | ||
1632 | /* The new X-UID. */ | ||
1633 | { | ||
1634 | char suid[64]; | ||
1635 | size_t uid = 0; | ||
1636 | if (is_expunging) | ||
1637 | { | ||
1638 | status = message_get_uid (msg, &uid); | ||
1639 | if (status == EAGAIN) | ||
1640 | return status; | ||
1641 | } | ||
1642 | else | ||
1643 | uid = mud->uidnext++; | ||
1644 | |||
1645 | if (status == 0 || uid != 0) | ||
1646 | { | ||
1647 | n = sprintf (suid, "X-UID: %u\n", (unsigned) uid); | ||
1648 | /* Put the UID. */ | ||
1649 | status = stream_write (mailbox->stream, suid, n, *psize, &n); | ||
1650 | if (status != 0) | ||
1651 | break; | ||
1652 | *psize += n; | ||
1653 | } | ||
1654 | |||
1655 | /* New line separator of the Header. */ | ||
1656 | status = stream_write (mailbox->stream, &nl , 1, *psize, &n); | ||
1657 | if (status != 0) | ||
1658 | break; | ||
1659 | *psize += n; | ||
1660 | mud->state = MBOX_STATE_APPEND_BODY; | ||
1661 | } | ||
1662 | |||
1663 | case MBOX_STATE_APPEND_BODY: | ||
1664 | /* Append the Body. */ | ||
1665 | { | ||
1666 | char buffer[1024]; | ||
1667 | size_t nread = 0; | ||
1668 | stream_t is = NULL; | ||
1669 | body_t body = NULL; | ||
1670 | message_get_body (msg, &body); | ||
1671 | body_get_stream (body, &is); | ||
1672 | do | ||
1673 | { | ||
1674 | status = stream_read (is, buffer, sizeof (buffer), mud->off, | ||
1675 | &nread); | ||
1676 | if (status != 0) | ||
1677 | { | ||
1678 | if (status != EAGAIN) | ||
1679 | { | ||
1680 | mud->state = MBOX_NO_STATE; | ||
1681 | mud->off = 0; | ||
1682 | } | ||
1683 | return status; | ||
1684 | } | ||
1685 | mud->off += nread; | ||
1686 | status = stream_write (mailbox->stream, buffer, nread, *psize, &n); | ||
1687 | if (status != 0) | ||
1688 | break; | ||
1689 | *psize += n; | ||
1690 | } | ||
1691 | while (nread > 0); | ||
1692 | mud->off = 0; | ||
1693 | n = 0; | ||
1694 | stream_write (mailbox->stream, &nl, 1, *psize, &n); | ||
1695 | *psize += n; | ||
1696 | } | ||
1697 | |||
1698 | default: | ||
1699 | break; | ||
1700 | } | ||
1701 | |||
1702 | /* If not expunging we are taking the stream message. */ | ||
1703 | if (!is_expunging) | ||
1704 | { | ||
1705 | switch (mud->state) | ||
1706 | { | ||
1707 | case MBOX_STATE_APPEND_MESSAGE: | ||
1708 | { | ||
1709 | /* Append the Message. */ | ||
1710 | char buffer[1024]; | ||
1711 | size_t nread = 0; | ||
1712 | stream_t is = NULL; | ||
1713 | message_get_stream (msg, &is); | ||
1714 | do | ||
1715 | { | ||
1716 | status = stream_read (is, buffer, sizeof (buffer), mud->off, | ||
1717 | &nread); | ||
1718 | if (status != 0) | ||
1719 | { | ||
1720 | if (status != EAGAIN) | ||
1721 | { | ||
1722 | mud->state = MBOX_NO_STATE; | ||
1723 | mud->off = 0; | ||
1724 | } | ||
1725 | stream_flush (mailbox->stream); | ||
1726 | return status; | ||
1727 | } | ||
1728 | stream_write (mailbox->stream, buffer, nread, *psize, &n); | ||
1729 | mud->off += nread; | ||
1730 | *psize += n; | ||
1731 | } | ||
1732 | while (nread > 0); | ||
1733 | n = 0; | ||
1734 | stream_write (mailbox->stream, &nl, 1, *psize, &n); | ||
1735 | *psize += n; | ||
1736 | } | ||
1737 | |||
1738 | default: | ||
1739 | break; | ||
1740 | } | ||
1741 | } /* is_expunging */ | ||
1742 | mud->state = MBOX_NO_STATE; | ||
1743 | stream_flush (mailbox->stream); | ||
1744 | return status; | ||
1745 | } | ||
1746 | |||
1747 | static int | ||
1748 | mbox_get_size (mailbox_t mailbox, off_t *psize) | ||
1749 | { | ||
1750 | off_t size; | ||
1751 | int status; | ||
1752 | |||
1753 | /* Maybe was not open yet ?? */ | ||
1754 | status = stream_size (mailbox->stream, &size); | ||
1755 | if (status != 0) | ||
1756 | return status; | ||
1757 | if (psize) | ||
1758 | *psize = size; | ||
1759 | return 0; | ||
1760 | } | ||
1761 | |||
1762 | static int | ||
1763 | mbox_messages_count (mailbox_t mailbox, size_t *pcount) | ||
1764 | { | ||
1765 | mbox_data_t mud = mailbox->data; | ||
1766 | if (mud == NULL) | ||
1767 | return EINVAL; | ||
1768 | |||
1769 | if (! mbox_is_updated (mailbox)) | ||
1770 | return mbox_scan0 (mailbox, mud->messages_count, pcount, 0); | ||
1771 | |||
1772 | if (pcount) | ||
1773 | *pcount = mud->messages_count; | ||
1774 | |||
1775 | return 0; | ||
1776 | } | ||
1777 | |||
1778 | /* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN | ||
1779 | ('O' in the Status header), i.e. a message that is first seen | ||
1780 | by the current session (see attributes.h) */ | ||
1781 | static int | ||
1782 | mbox_messages_recent (mailbox_t mailbox, size_t *pcount) | ||
1783 | { | ||
1784 | mbox_data_t mud = mailbox->data; | ||
1785 | mbox_message_t mum; | ||
1786 | size_t j, recent; | ||
1787 | |||
1788 | /* If we did not start a scanning yet do it now. */ | ||
1789 | if (mud->messages_count == 0) | ||
1790 | { | ||
1791 | int status = mbox_scan0 (mailbox, 1, NULL, 0); | ||
1792 | if (status != 0) | ||
1793 | return status; | ||
1794 | } | ||
1795 | for (recent = j = 0; j < mud->messages_count; j++) | ||
1796 | { | ||
1797 | mum = mud->umessages[j]; | ||
1798 | if (mum && MU_ATTRIBUTE_IS_UNSEEN(mum->attr_flags)) | ||
1799 | recent++; | ||
1800 | } | ||
1801 | *pcount = recent; | ||
1802 | return 0; | ||
1803 | } | ||
1804 | |||
1805 | /* An "unseen" message is the one that has not been read yet */ | ||
1806 | static int | ||
1807 | mbox_message_unseen (mailbox_t mailbox, size_t *pmsgno) | ||
1808 | { | ||
1809 | mbox_data_t mud = mailbox->data; | ||
1810 | mbox_message_t mum; | ||
1811 | size_t j, unseen; | ||
1812 | |||
1813 | /* If we did not start a scanning yet do it now. */ | ||
1814 | if (mud->messages_count == 0) | ||
1815 | { | ||
1816 | int status = mbox_scan0 (mailbox, 1, NULL, 0); | ||
1817 | if (status != 0) | ||
1818 | return status; | ||
1819 | } | ||
1820 | for (unseen = j = 0; j < mud->messages_count; j++) | ||
1821 | { | ||
1822 | mum = mud->umessages[j]; | ||
1823 | if (mum && MU_ATTRIBUTE_IS_UNREAD(mum->attr_flags)) | ||
1824 | { | ||
1825 | unseen = j + 1; | ||
1826 | break; | ||
1827 | } | ||
1828 | } | ||
1829 | *pmsgno = unseen; | ||
1830 | return 0; | ||
1831 | } | ||
1832 | |||
1833 | static int | ||
1834 | mbox_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity) | ||
1835 | { | ||
1836 | mbox_data_t mud = mailbox->data; | ||
1837 | int status = mbox_messages_count (mailbox, NULL); | ||
1838 | if (status != 0) | ||
1839 | return status; | ||
1840 | /* If we did not start a scanning yet do it now. */ | ||
1841 | if (mud->messages_count == 0) | ||
1842 | { | ||
1843 | status = mbox_scan0 (mailbox, 1, NULL, 0); | ||
1844 | if (status != 0) | ||
1845 | return status; | ||
1846 | } | ||
1847 | if (puidvalidity) | ||
1848 | *puidvalidity = mud->uidvalidity; | ||
1849 | return 0; | ||
1850 | } | ||
1851 | |||
1852 | static int | ||
1853 | mbox_uidnext (mailbox_t mailbox, size_t *puidnext) | ||
1854 | { | ||
1855 | mbox_data_t mud = mailbox->data; | ||
1856 | int status = mbox_messages_count (mailbox, NULL); | ||
1857 | if (status != 0) | ||
1858 | return status; | ||
1859 | /* If we did not start a scanning yet do it now. */ | ||
1860 | if (mud->messages_count == 0) | ||
1861 | { | ||
1862 | status = mbox_scan0 (mailbox, 1, NULL, 0); | ||
1863 | if (status != 0) | ||
1864 | return status; | ||
1865 | } | ||
1866 | if (puidnext) | ||
1867 | *puidnext = mud->uidnext; | ||
1868 | return 0; | ||
1869 | } | ||
1870 | |||
1871 | #ifdef WITH_PTHREAD | ||
1872 | static void | ||
1873 | mbox_cleanup (void *arg) | ||
1874 | { | ||
1875 | mailbox_t mailbox = arg; | ||
1876 | monitor_unlock (mailbox->monitor); | ||
1877 | locker_unlock (mailbox->locker); | ||
1878 | } | ||
1879 | #endif |
mailbox/mbx_mboxscan.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | /* Credits to the c-client and its Authors | ||
19 | * The notorius c-client VALID() macro, was written by Mark Crispin. | ||
20 | */ | ||
21 | |||
22 | /* Parsing. | ||
23 | The approach is to detect the "From " as start of a new message, give the | ||
24 | position of the header and scan until "\n" then set header_end, set body | ||
25 | position, scan until we it another "From " and set body_end. | ||
26 | ************************************ | ||
27 | This is a classic case of premature optimisation being the root of all | ||
28 | Evil(Donald E. Knuth). But I'm under "pressure" ;-) to come with | ||
29 | something "faster". I think it's wastefull * to spend time to gain a few | ||
30 | seconds on 30Megs mailboxes ... but then again ... in computer time, 60 | ||
31 | seconds, is eternity. If they use the event notification stuff to get | ||
32 | some headers/messages early ... it's like pissing in the wind(sorry don't | ||
33 | have the english equivalent). The worst is progress_bar it should be ... | ||
34 | &*($^ nuke. For the events, we have to remove the *.LCK file, release the | ||
35 | locks, flush the stream save the pointers etc ... hurry and wait... | ||
36 | I this point I'm pretty much ranting. */ | ||
37 | |||
38 | |||
39 | /* From the C-Client, part of pine */ | ||
40 | /* You are not expected to understand this macro, but read the next page if | ||
41 | * you are not faint of heart. | ||
42 | * | ||
43 | * Known formats to the VALID macro are: | ||
44 | * From user Wed Dec 2 05:53 1992 | ||
45 | * BSD From user Wed Dec 2 05:53:22 1992 | ||
46 | * SysV From user Wed Dec 2 05:53 PST 1992 | ||
47 | * rn From user Wed Dec 2 05:53:22 PST 1992 | ||
48 | * From user Wed Dec 2 05:53 -0700 1992 | ||
49 | * From user Wed Dec 2 05:53:22 -0700 1992 | ||
50 | * From user Wed Dec 2 05:53 1992 PST | ||
51 | * From user Wed Dec 2 05:53:22 1992 PST | ||
52 | * From user Wed Dec 2 05:53 1992 -0700 | ||
53 | * Solaris From user Wed Dec 2 05:53:22 1992 -0700 | ||
54 | * | ||
55 | * Plus all of the above with `` remote from xxx'' after it. Thank you very | ||
56 | * much, smail and Solaris, for making my life considerably more complicated. | ||
57 | */ | ||
58 | /* | ||
59 | * What? You want to understand the VALID macro anyway? Alright, since you | ||
60 | * insist. Actually, it isn't really all that difficult, provided that you | ||
61 | * take it step by step. | ||
62 | * | ||
63 | * Line 1 Initializes the return ti value to failure (0); | ||
64 | * Lines 2-3 Validates that the 1st-5th characters are ``From ''. | ||
65 | * Lines 4-6 Validates that there is an end of line and points x at it. | ||
66 | * Lines 7-14 First checks to see if the line is at least 41 characters long | ||
67 | . | ||
68 | * If so, it scans backwards to find the rightmost space. From | ||
69 | * that point, it scans backwards to see if the string matches | ||
70 | * `` remote from''. If so, it sets x to point to the space at | ||
71 | * the start of the string. | ||
72 | * Line 15 Makes sure that there are at least 27 characters in the line. | ||
73 | * Lines 16-21 Checks if the date/time ends with the year (there is a space | ||
74 | * five characters back). If there is a colon three characters | ||
75 | * further back, there is no timezone field, so zn is set to 0 | ||
76 | * and ti is set in front of the year. Otherwise, there must | ||
77 | * either to be a space four characters back for a three-letter | ||
78 | * timezone, or a space six characters back followed by a + or - | ||
79 | * for a numeric timezone; in either case, zn and ti become the | ||
80 | * offset of the space immediately before it. | ||
81 | * Lines 22-24 Are the failure case for line 14. If there is a space four | ||
82 | * characters back, it is a three-letter timezone; there must be | ||
83 | a | ||
84 | * space for the year nine characters back. zn is the zone | ||
85 | * offset; ti is the offset of the space. | ||
86 | * Lines 25-28 Are the failure case for line 20. If there is a space six | ||
87 | * characters back, it is a numeric timezone; there must be a | ||
88 | * space eleven characters back and a + or - five characters back | ||
89 | . | ||
90 | * zn is the zone offset; ti is the offset of the space. | ||
91 | * Line 29-32 If ti is valid, make sure that the string before ti is of the | ||
92 | * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise | ||
93 | * invalidate ti. There must be a colon three characters back | ||
94 | * and a space six or nine characters back (depending upon | ||
95 | * whether or not the character six characters back is a colon). | ||
96 | * There must be a space three characters further back (in front | ||
97 | * of the day), one seven characters back (in front of the month) | ||
98 | , | ||
99 | * and one eleven characters back (in front of the day of week). | ||
100 | * ti is set to be the offset of the space before the time. | ||
101 | * | ||
102 | * Why a macro? It gets invoked a *lot* in a tight loop. On some of the | ||
103 | * newer pipelined machines it is faster being open-coded than it would be if | ||
104 | * subroutines are called. | ||
105 | * | ||
106 | * Why does it scan backwards from the end of the line, instead of doing the | ||
107 | * much easier forward scan? There is no deterministic way to parse the | ||
108 | * ``user'' field, because it may contain unquoted spaces! Yes, I tested it t | ||
109 | o | ||
110 | * see if unquoted spaces were possible. They are, and I've encountered enoug | ||
111 | h | ||
112 | * evil mail to be totally unwilling to trust that ``it will never happen''. | ||
113 | */ | ||
114 | #define VALID(s,x,ti,zn) { \ | ||
115 | ti = 0; \ | ||
116 | if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \ | ||
117 | (s[4] == ' ')) { \ | ||
118 | for (x = s + 5; *x && *x != '\n'; x++); \ | ||
119 | if (x) { \ | ||
120 | if (x - s >= 41) { \ | ||
121 | for (zn = -1; x[zn] != ' '; zn--); \ | ||
122 | if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \ | ||
123 | (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \ | ||
124 | (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \ | ||
125 | (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\ | ||
126 | x += zn - 12; \ | ||
127 | } \ | ||
128 | if (x - s >= 27) { \ | ||
129 | if (x[-5] == ' ') { \ | ||
130 | if (x[-8] == ':') zn = 0,ti = -5; \ | ||
131 | else if (x[-9] == ' ') ti = zn = -9; \ | ||
132 | else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \ | ||
133 | ti = zn = -11; \ | ||
134 | } \ | ||
135 | else if (x[-4] == ' ') { \ | ||
136 | if (x[-9] == ' ') zn = -4,ti = -9; \ | ||
137 | } \ | ||
138 | else if (x[-6] == ' ') { \ | ||
139 | if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \ | ||
140 | zn = -6,ti = -11; \ | ||
141 | } \ | ||
142 | if (ti && !((x[ti - 3] == ':') && \ | ||
143 | (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \ | ||
144 | (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \ | ||
145 | (x[ti - 11] == ' '))) ti = 0; \ | ||
146 | } \ | ||
147 | } \ | ||
148 | } \ | ||
149 | } | ||
150 | |||
151 | #define ATTRIBUTE_SET(buf,mum,c0,c1,type) \ | ||
152 | do \ | ||
153 | { \ | ||
154 | char *s; \ | ||
155 | for (s = (buf) + 7; *s; s++) \ | ||
156 | { \ | ||
157 | if (*s == c0 || *s == c1) \ | ||
158 | { \ | ||
159 | (mum)->attr_flags |= (type); \ | ||
160 | break; \ | ||
161 | } \ | ||
162 | } \ | ||
163 | } while (0) | ||
164 | |||
165 | #define ISBCC(buf) (\ | ||
166 | (buf[0] == 'B' || buf[0] == 'b') \ | ||
167 | && (buf[1] == 'C' || buf[1] == 'c') \ | ||
168 | && (buf[2] == 'C' || buf[2] == 'c') \ | ||
169 | && (buf[3] == ':' || buf[3] == ' ' || buf[3] == '\t')) | ||
170 | |||
171 | #define ISCC(buf) (\ | ||
172 | (buf[0] == 'C' || buf[0] == 'c') \ | ||
173 | && (buf[1] == 'C' || buf[1] == 'c') \ | ||
174 | && (buf[2] == ':' || buf[2] == ' ' || buf[2] == '\t')) | ||
175 | |||
176 | #define ISCONTENT_LANGUAGE(buf) (\ | ||
177 | (buf[0] == 'C' || buf[0] == 'c') \ | ||
178 | && (buf[1] == 'O' || buf[1] == 'o') \ | ||
179 | && (buf[2] == 'N' || buf[2] == 'n') \ | ||
180 | && (buf[3] == 'T' || buf[3] == 't') \ | ||
181 | && (buf[4] == 'E' || buf[4] == 'e') \ | ||
182 | && (buf[5] == 'N' || buf[5] == 'n') \ | ||
183 | && (buf[6] == 'T' || buf[6] == 't') \ | ||
184 | && (buf[7] == '-') \ | ||
185 | && (buf[8] == 'L' || buf[8] == 'l') \ | ||
186 | && (buf[9] == 'A' || buf[9] == 'a') \ | ||
187 | && (buf[10] == 'N' || buf[10] == 'n') \ | ||
188 | && (buf[11] == 'G' || buf[11] == 'g') \ | ||
189 | && (buf[12] == 'U' || buf[12] == 'u') \ | ||
190 | && (buf[13] == 'A' || buf[13] == 'a') \ | ||
191 | && (buf[14] == 'G' || buf[14] == 'g') \ | ||
192 | && (buf[15] == 'E' || buf[15] == 'e') \ | ||
193 | && (buf[16] == ':' || buf[16] == ' ' || buf[16] == '\t')) | ||
194 | |||
195 | #define ISCONTENT_TRANSFER_ENCODING(buf) (\ | ||
196 | (buf[0] == 'C' || buf[0] == 'c') \ | ||
197 | && (buf[1] == 'O' || buf[1] == 'o') \ | ||
198 | && (buf[2] == 'N' || buf[2] == 'n') \ | ||
199 | && (buf[3] == 'T' || buf[3] == 't') \ | ||
200 | && (buf[4] == 'E' || buf[4] == 'e') \ | ||
201 | && (buf[5] == 'N' || buf[5] == 'n') \ | ||
202 | && (buf[6] == 'T' || buf[6] == 't') \ | ||
203 | && (buf[7] == '-') \ | ||
204 | && (buf[8] == 'T' || buf[8] == 't') \ | ||
205 | && (buf[9] == 'R' || buf[9] == 'r') \ | ||
206 | && (buf[10] == 'A' || buf[10] == 'a') \ | ||
207 | && (buf[11] == 'N' || buf[11] == 'n') \ | ||
208 | && (buf[12] == 'S' || buf[12] == 's') \ | ||
209 | && (buf[13] == 'F' || buf[13] == 'f') \ | ||
210 | && (buf[14] == 'E' || buf[14] == 'e') \ | ||
211 | && (buf[15] == 'R' || buf[15] == 'r') \ | ||
212 | && (buf[16] == '-') \ | ||
213 | && (buf[17] == 'E' || buf[17] == 'e') \ | ||
214 | && (buf[18] == 'N' || buf[18] == 'n') \ | ||
215 | && (buf[19] == 'C' || buf[19] == 'c') \ | ||
216 | && (buf[20] == 'O' || buf[20] == 'o') \ | ||
217 | && (buf[21] == 'D' || buf[21] == 'd') \ | ||
218 | && (buf[22] == 'I' || buf[22] == 'i') \ | ||
219 | && (buf[23] == 'N' || buf[23] == 'n') \ | ||
220 | && (buf[24] == 'G' || buf[24] == 'g') \ | ||
221 | && (buf[25] == ':' || buf[25] == ' ' || buf[25] == '\t')) | ||
222 | |||
223 | #define ISCONTENT_TYPE(buf) (\ | ||
224 | (buf[0] == 'C' || buf[0] == 'c') \ | ||
225 | && (buf[1] == 'O' || buf[1] == 'o') \ | ||
226 | && (buf[2] == 'N' || buf[2] == 'n') \ | ||
227 | && (buf[3] == 'T' || buf[3] == 't') \ | ||
228 | && (buf[4] == 'E' || buf[4] == 'e') \ | ||
229 | && (buf[5] == 'N' || buf[5] == 'n') \ | ||
230 | && (buf[6] == 'T' || buf[6] == 't') \ | ||
231 | && (buf[7] == '-') \ | ||
232 | && (buf[8] == 'T' || buf[8] == 't') \ | ||
233 | && (buf[9] == 'Y' || buf[9] == 'y') \ | ||
234 | && (buf[10] == 'P' || buf[10] == 'p') \ | ||
235 | && (buf[11] == 'E' || buf[11] == 'e') \ | ||
236 | && (buf[12] == ':' || buf[12] == ' ' || buf[12] == '\t')) | ||
237 | |||
238 | #define ISDATE(buf) (\ | ||
239 | (buf[0] == 'D' || buf[0] == 'd') \ | ||
240 | && (buf[1] == 'A' || buf[1] == 'a') \ | ||
241 | && (buf[2] == 'T' || buf[2] == 't') \ | ||
242 | && (buf[3] == 'E' || buf[3] == 'e') \ | ||
243 | && (buf[4] == ':' || buf[4] == ' ' || buf[4] == '\t')) | ||
244 | |||
245 | #define ISFROM(buf) (\ | ||
246 | (buf[0] == 'F' || buf[0] == 'f') \ | ||
247 | && (buf[1] == 'R' || buf[1] == 'r') \ | ||
248 | && (buf[2] == 'O' || buf[2] == 'o') \ | ||
249 | && (buf[3] == 'M' || buf[3] == 'm') \ | ||
250 | && (buf[4] == ':' || buf[4] == ' ' || buf[4] == '\t')) | ||
251 | |||
252 | #define ISIN_REPLY_TO(buf) (\ | ||
253 | (buf[0] == 'I' || buf[0] == 'i') \ | ||
254 | && (buf[1] == 'N' || buf[1] == 'n') \ | ||
255 | && (buf[2] == '-' || buf[2] == '-') \ | ||
256 | && (buf[3] == 'R' || buf[3] == 'r') \ | ||
257 | && (buf[4] == 'E' || buf[4] == 'e') \ | ||
258 | && (buf[5] == 'P' || buf[5] == 'p') \ | ||
259 | && (buf[6] == 'L' || buf[6] == 'l') \ | ||
260 | && (buf[7] == 'Y' || buf[7] == 'y') \ | ||
261 | && (buf[8] == '-') \ | ||
262 | && (buf[9] == 'T' || buf[9] == 't') \ | ||
263 | && (buf[10] == 'O' || buf[10] == 'o') \ | ||
264 | && (buf[11] == ':' || buf[11] == ' ' || buf[11] == '\t')) | ||
265 | |||
266 | #define ISMESSAGE_ID(buf) (\ | ||
267 | (buf[0] == 'M' || buf[0] == 'm') \ | ||
268 | && (buf[1] == 'E' || buf[1] == 'e') \ | ||
269 | && (buf[2] == 'S' || buf[2] == 's') \ | ||
270 | && (buf[3] == 'S' || buf[3] == 's') \ | ||
271 | && (buf[4] == 'A' || buf[4] == 'a') \ | ||
272 | && (buf[5] == 'G' || buf[5] == 'g') \ | ||
273 | && (buf[6] == 'E' || buf[6] == 'e') \ | ||
274 | && (buf[7] == '-') \ | ||
275 | && (buf[8] == 'I' || buf[8] == 'i') \ | ||
276 | && (buf[9] == 'D' || buf[9] == 'd') \ | ||
277 | && (buf[10] == ':' || buf[10] == ' ' || buf[10] == '\t')) | ||
278 | |||
279 | #define ISREFERENCE(buf) (\ | ||
280 | (buf[0] == 'R' || buf[0] == 'r') \ | ||
281 | && (buf[1] == 'E' || buf[1] == 'e') \ | ||
282 | && (buf[2] == 'F' || buf[2] == 'f') \ | ||
283 | && (buf[3] == 'E' || buf[3] == 'e') \ | ||
284 | && (buf[4] == 'R' || buf[4] == 'r') \ | ||
285 | && (buf[5] == 'E' || buf[5] == 'e') \ | ||
286 | && (buf[6] == 'n' || buf[6] == 'n') \ | ||
287 | && (buf[7] == 'C' || buf[7] == 'c') \ | ||
288 | && (buf[8] == 'E' || buf[8] == 'e') \ | ||
289 | && (buf[9] == ':' || buf[9] == ' ' || buf[9] == '\t')) | ||
290 | |||
291 | #define ISREPLY_TO(buf) (\ | ||
292 | (buf[0] == 'R' || buf[0] == 'r') \ | ||
293 | && (buf[1] == 'E' || buf[1] == 'e') \ | ||
294 | && (buf[2] == 'P' || buf[2] == 'p') \ | ||
295 | && (buf[3] == 'L' || buf[3] == 'l') \ | ||
296 | && (buf[4] == 'Y' || buf[4] == 'y') \ | ||
297 | && (buf[5] == '-') \ | ||
298 | && (buf[6] == 'T' || buf[6] == 't') \ | ||
299 | && (buf[7] == 'O' || buf[7] == 'o') \ | ||
300 | && (buf[8] == ':' || buf[8] == ' ' || buf[8] == '\t')) | ||
301 | |||
302 | #define ISSENDER(buf) (\ | ||
303 | (buf[0] == 'S' || buf[0] == 's') \ | ||
304 | && (buf[1] == 'E' || buf[1] == 'e') \ | ||
305 | && (buf[2] == 'N' || buf[2] == 'n') \ | ||
306 | && (buf[3] == 'D' || buf[3] == 'd') \ | ||
307 | && (buf[4] == 'E' || buf[4] == 'e') \ | ||
308 | && (buf[5] == 'R' || buf[5] == 'r') \ | ||
309 | && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t')) | ||
310 | |||
311 | #define ISSTATUS(buf) (\ | ||
312 | (buf[0] == 'S' || buf[0] == 's') \ | ||
313 | && (buf[1] == 'T' || buf[1] == 't') \ | ||
314 | && (buf[2] == 'A' || buf[2] == 'a') \ | ||
315 | && (buf[3] == 'T' || buf[3] == 't') \ | ||
316 | && (buf[4] == 'U' || buf[4] == 'u') \ | ||
317 | && (buf[5] == 'S' || buf[5] == 's') \ | ||
318 | && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t')) | ||
319 | |||
320 | #define ISSUBJECT(buf) (\ | ||
321 | (buf[0] == 'S' || buf[0] == 's') \ | ||
322 | && (buf[1] == 'U' || buf[1] == 'u') \ | ||
323 | && (buf[2] == 'B' || buf[2] == 'b') \ | ||
324 | && (buf[3] == 'J' || buf[3] == 'j') \ | ||
325 | && (buf[4] == 'E' || buf[4] == 'e') \ | ||
326 | && (buf[5] == 'C' || buf[5] == 'c') \ | ||
327 | && (buf[6] == 'T' || buf[6] == 't') \ | ||
328 | && (buf[7] == ':' || buf[7] == ' ' || buf[7] == '\t')) | ||
329 | |||
330 | #define ISTO(buf) (\ | ||
331 | (buf[0] == 'T' || buf[0] == 't') \ | ||
332 | && (buf[1] == 'O' || buf[1] == 'o') \ | ||
333 | && (buf[2] == ':' || buf[2] == ' ' || buf[2] == '\t')) | ||
334 | |||
335 | #define ISX_IMAPBASE(buf) (\ | ||
336 | (buf[0] == 'X' || buf[0] == 'x') \ | ||
337 | && (buf[1] == '-') \ | ||
338 | && (buf[2] == 'I' || buf[2] == 'i') \ | ||
339 | && (buf[3] == 'M' || buf[3] == 'm') \ | ||
340 | && (buf[4] == 'A' || buf[4] == 'a') \ | ||
341 | && (buf[5] == 'P' || buf[5] == 'p') \ | ||
342 | && (buf[6] == 'B' || buf[6] == 'b') \ | ||
343 | && (buf[7] == 'A' || buf[7] == 'a') \ | ||
344 | && (buf[8] == 'S' || buf[8] == 's') \ | ||
345 | && (buf[9] == 'E' || buf[9] == 'e') \ | ||
346 | && (buf[10] == ':' || buf[10] == ' ' || buf[10] == '\t')) | ||
347 | |||
348 | #define ISX_UIDL(buf) (\ | ||
349 | (buf[0] == 'X' || buf[0] == 'x') \ | ||
350 | && (buf[1] == '-') \ | ||
351 | && (buf[2] == 'U' || buf[2] == 'u') \ | ||
352 | && (buf[3] == 'I' || buf[3] == 'i') \ | ||
353 | && (buf[4] == 'D' || buf[4] == 'd') \ | ||
354 | && (buf[5] == 'L' || buf[5] == 'l') \ | ||
355 | && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t')) | ||
356 | |||
357 | #define ISX_UID(buf) (\ | ||
358 | (buf[0] == 'X' || buf[0] == 'x') \ | ||
359 | && (buf[1] == '-') \ | ||
360 | && (buf[2] == 'U' || buf[2] == 'u') \ | ||
361 | && (buf[3] == 'I' || buf[3] == 'i') \ | ||
362 | && (buf[4] == 'D' || buf[4] == 'd') \ | ||
363 | && (buf[5] == ':' || buf[5] == ' ' || buf[5] == '\t')) | ||
364 | |||
365 | /* Skip prepend spaces. */ | ||
366 | #define SKIPSPACE(p) while (*p == ' ') p++ | ||
367 | |||
368 | #define ATOI(a,i) \ | ||
369 | do {\ | ||
370 | SKIPSPACE(a); \ | ||
371 | for (i = 0; *a >= '0' && *a <= '9'; a++) \ | ||
372 | i = 10 * i + (*a - '0'); \ | ||
373 | } while (0) | ||
374 | |||
375 | /* Save/concatenate the field-value in the fast header(fhd) field. */ | ||
376 | #define FAST_HEADER(field,buf,n) \ | ||
377 | do { \ | ||
378 | int i = 0; \ | ||
379 | char *s = field; \ | ||
380 | char *p = buf; \ | ||
381 | if (s) \ | ||
382 | while (*s++) i++; \ | ||
383 | else \ | ||
384 | p = memchr (buf, ':', n); \ | ||
385 | if (p) \ | ||
386 | { \ | ||
387 | int l; \ | ||
388 | char *tmp; \ | ||
389 | buf[n - 1] = '\0'; \ | ||
390 | p++; \ | ||
391 | if (!field) \ | ||
392 | SKIPSPACE(p); \ | ||
393 | l = n - (p - buf); \ | ||
394 | tmp = realloc (field, (l + i + 1) * sizeof (char)); \ | ||
395 | if (tmp) \ | ||
396 | { \ | ||
397 | field = tmp; \ | ||
398 | memcpy (field + i, p, l); \ | ||
399 | } \ | ||
400 | } \ | ||
401 | } while (0) | ||
402 | |||
403 | #define FAST_H_BCC(mum,save_field,buf,n) \ | ||
404 | FAST_HEADER(mum->fhdr[H_BCC],buf,n); \ | ||
405 | save_field = &(mum->fhdr[H_BCC]) | ||
406 | |||
407 | #define FAST_H_CC(mum,save_field,buf,n) \ | ||
408 | FAST_HEADER(mum->fhdr[H_CC],buf,n); \ | ||
409 | save_field = &(mum->fhdr[H_CC]) | ||
410 | |||
411 | #define FAST_H_CONTENT_LANGUAGE(mum,save_field,buf,n) \ | ||
412 | FAST_HEADER(mum->fhdr[H_CONTENT_LANGUAGE],buf,n); \ | ||
413 | save_field = &(mum->fhdr[H_CONTENT_LANGUAGE]) | ||
414 | |||
415 | #define FAST_H_CONTENT_TRANSFER_ENCODING(mum,save_field,buf,n) \ | ||
416 | FAST_HEADER(mum->fhdr[H_CONTENT_TRANSFER_ENCODING],buf,n); \ | ||
417 | save_field = &(mum->fhdr[H_CONTENT_TRANSFER_ENCODING]) | ||
418 | |||
419 | #define FAST_H_CONTENT_TYPE(mum,save_field,buf,n) \ | ||
420 | FAST_HEADER(mum->fhdr[H_CONTENT_TYPE],buf,n); \ | ||
421 | save_field = &(mum->fhdr[H_CONTENT_TYPE]) | ||
422 | |||
423 | #define FAST_H_DATE(mum,save_field,buf,n) \ | ||
424 | FAST_HEADER(mum->fhdr[H_DATE],buf,n); \ | ||
425 | save_field = &(mum->fhdr[H_DATE]) | ||
426 | |||
427 | #define FAST_H_FROM(mum,save_field,buf,n) \ | ||
428 | FAST_HEADER(mum->fhdr[H_FROM],buf,n); \ | ||
429 | save_field = &(mum->fhdr[H_FROM]) | ||
430 | |||
431 | #define FAST_H_IN_REPLY_TO(mum,save_field,buf,n) \ | ||
432 | FAST_HEADER(mum->fhdr[H_IN_REPLY_TO],buf,n); \ | ||
433 | save_field = &(mum->fhdr[H_IN_REPLY_TO]) | ||
434 | |||
435 | #define FAST_H_MESSAGE_ID(mum,save_field,buf,n) \ | ||
436 | FAST_HEADER(mum->fhdr[H_MESSAGE_ID],buf,n); \ | ||
437 | save_field = &(mum->fhdr[H_MESSAGE_ID]) | ||
438 | |||
439 | #define FAST_H_REFERENCE(mum,save_field,buf,n) \ | ||
440 | FAST_HEADER(mum->fhdr[H_REFERENCE],buf,n); \ | ||
441 | save_field = &(mum->fhdr[H_REFERENCE]) | ||
442 | |||
443 | #define FAST_H_REPLY_TO(mum,save_field,buf,n) \ | ||
444 | FAST_HEADER(mum->fhdr[H_REPLY_TO],buf,n); \ | ||
445 | save_field = &(mum->fhdr[H_REPLY_TO]) | ||
446 | |||
447 | #define FAST_H_SENDER(mum,save_field,buf,n) \ | ||
448 | FAST_HEADER(mum->fhdr[H_SENDER],buf,n); \ | ||
449 | save_field = &(mum->fhdr[H_SENDER]) | ||
450 | |||
451 | #define FAST_H_SUBJECT(mum,save_field,buf,n) \ | ||
452 | FAST_HEADER(mum->fhdr[H_SUBJECT],buf,n); \ | ||
453 | save_field = &(mum->fhdr[H_SUBJECT]) | ||
454 | |||
455 | #define FAST_H_TO(mum,save_field,buf,n) \ | ||
456 | FAST_HEADER(mum->fhdr[H_TO],buf,n); \ | ||
457 | save_field = &(mum->fhdr[H_TO]) | ||
458 | |||
459 | #define FAST_H_X_UIDL(mum,save_field,buf,n) \ | ||
460 | FAST_HEADER(mum->fhdr[H_X_UIDL],buf,n); \ | ||
461 | save_field = &(mum->fhdr[H_X_UIDL]) | ||
462 | |||
463 | /* Notifications ADD_MESG. */ | ||
464 | #define DISPATCH_ADD_MSG(mbox,mud) \ | ||
465 | do \ | ||
466 | { \ | ||
467 | int bailing = 0; \ | ||
468 | monitor_unlock (mbox->monitor); \ | ||
469 | if (mbox->observable) \ | ||
470 | bailing = observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD); \ | ||
471 | if (bailing != 0) \ | ||
472 | { \ | ||
473 | if (pcount) \ | ||
474 | *pcount = (mud)->messages_count; \ | ||
475 | locker_unlock (mbox->locker); \ | ||
476 | return EINTR; \ | ||
477 | } \ | ||
478 | monitor_wrlock (mbox->monitor); \ | ||
479 | } while (0); | ||
480 | |||
481 | /* Notification MBX_PROGRESS | ||
482 | We do not want to fire up the progress notification every line, it will be | ||
483 | too expensive, so we do it arbitrarely every 10 000 Lines. | ||
484 | FIXME: maybe this should be configurable. */ | ||
485 | /* This is more tricky we can not leave the mum struct incomplete. So we | ||
486 | only tell them about the complete messages. */ | ||
487 | #define DISPATCH_PROGRESS(mbox,mud) \ | ||
488 | do \ | ||
489 | { \ | ||
490 | { \ | ||
491 | int bailing = 0; \ | ||
492 | monitor_unlock (mbox->monitor); \ | ||
493 | mud->messages_count--; \ | ||
494 | if (mbox->observable) \ | ||
495 | bailing = observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS); \ | ||
496 | if (bailing != 0) \ | ||
497 | { \ | ||
498 | if (pcount) \ | ||
499 | *pcount = (mud)->messages_count; \ | ||
500 | locker_unlock (mbox->locker); \ | ||
501 | return EINTR; \ | ||
502 | } \ | ||
503 | mud->messages_count++; \ | ||
504 | monitor_wrlock (mbox->monitor); \ | ||
505 | } \ | ||
506 | } while (0) | ||
507 | |||
508 | /* Allocate slots for the new messages. */ | ||
509 | /* size_t num = 2 * ((mud)->messages_count) + 10; */ | ||
510 | #define ALLOCATE_MSGS(mbox,mud) \ | ||
511 | do \ | ||
512 | { \ | ||
513 | if ((mud)->messages_count >= (mud)->umessages_count) \ | ||
514 | { \ | ||
515 | mbox_message_t *m; \ | ||
516 | size_t num = ((mud)->umessages_count) + 1; \ | ||
517 | m = realloc ((mud)->umessages, num * sizeof (*m)); \ | ||
518 | if (m == NULL) \ | ||
519 | { \ | ||
520 | locker_unlock (mbox->locker); \ | ||
521 | monitor_unlock (mbox->monitor); \ | ||
522 | return ENOMEM; \ | ||
523 | } \ | ||
524 | (mud)->umessages = m; \ | ||
525 | (mud)->umessages[num - 1] = calloc (1, sizeof (*(mum))); \ | ||
526 | if ((mud)->umessages[num - 1] == NULL) \ | ||
527 | { \ | ||
528 | locker_unlock (mbox->locker); \ | ||
529 | monitor_unlock (mbox->monitor); \ | ||
530 | return ENOMEM; \ | ||
531 | } \ | ||
532 | (mud)->umessages_count = num; \ | ||
533 | } \ | ||
534 | } while (0) | ||
535 | |||
536 | static int | ||
537 | mbox_scan0 (mailbox_t mailbox, size_t msgno, size_t *pcount, int do_notif) | ||
538 | { | ||
539 | #define MSGLINELEN 1024 | ||
540 | char buf[MSGLINELEN]; | ||
541 | int inheader; | ||
542 | int inbody; | ||
543 | off_t total = 0; | ||
544 | mbox_data_t mud = mailbox->data; | ||
545 | mbox_message_t mum = NULL; | ||
546 | int status = 0; | ||
547 | size_t lines; | ||
548 | int newline; | ||
549 | size_t n = 0; | ||
550 | stream_t stream; | ||
551 | char **sfield = NULL; | ||
552 | size_t min_uid = 0; | ||
553 | int zn, isfrom = 0; | ||
554 | char *temp; | ||
555 | |||
556 | /* Sanity. */ | ||
557 | if (mud == NULL) | ||
558 | return EINVAL; | ||
559 | |||
560 | /* Grab the lock. */ | ||
561 | monitor_wrlock (mailbox->monitor); | ||
562 | |||
563 | #ifdef WITH_PTHREAD | ||
564 | /* read() is cancellation point since we're doing a potentially | ||
565 | long operation. Lets make sure we clean the state. */ | ||
566 | pthread_cleanup_push (mbox_cleanup, (void *)mailbox); | ||
567 | #endif | ||
568 | |||
569 | /* Save the timestamp and size. */ | ||
570 | status = stream_size (mailbox->stream, &(mud->size)); | ||
571 | if (status != 0) | ||
572 | { | ||
573 | monitor_unlock (mailbox->monitor); | ||
574 | return status; | ||
575 | } | ||
576 | |||
577 | if((status = locker_lock (mailbox->locker))) | ||
578 | { | ||
579 | monitor_unlock (mailbox->monitor); | ||
580 | return status; | ||
581 | } | ||
582 | |||
583 | /* Seek to the starting point. */ | ||
584 | if (mud->umessages && msgno > 0 && mud->messages_count > 0 | ||
585 | && msgno <= mud->messages_count) | ||
586 | { | ||
587 | mum = mud->umessages[msgno - 1]; | ||
588 | if (mum) | ||
589 | total = mum->header_from; | ||
590 | mud->messages_count = msgno - 1; | ||
591 | } | ||
592 | else | ||
593 | mud->messages_count = 0; | ||
594 | |||
595 | #if 0 | ||
596 | { | ||
597 | size_t j, k; | ||
598 | for (j = 0; j < mud->umessages_count; j++) | ||
599 | { | ||
600 | mum = mud->umessages[j]; | ||
601 | for (k = 0; k < HDRSIZE; k++) | ||
602 | if (mum->fhdr[k]) | ||
603 | free (mum->fhdr[k]); | ||
604 | } | ||
605 | } | ||
606 | #endif | ||
607 | newline = 1; | ||
608 | errno = lines = inheader = inbody = 0; | ||
609 | |||
610 | stream = mailbox->stream; | ||
611 | while ((status = stream_readline (mailbox->stream, buf, sizeof (buf), | ||
612 | total, &n)) == 0 && n != 0) | ||
613 | { | ||
614 | int nl; | ||
615 | total += n; | ||
616 | |||
617 | nl = (*buf == '\n') ? 1 : 0; | ||
618 | VALID(buf, temp, isfrom, zn); | ||
619 | isfrom = (isfrom) ? 1 : 0; | ||
620 | |||
621 | /* Which part of the message are we in ? */ | ||
622 | inheader = isfrom | ((!nl) & inheader); | ||
623 | inbody = (!isfrom) & (!inheader); | ||
624 | |||
625 | if (buf[n - 1] == '\n') | ||
626 | lines++; | ||
627 | |||
628 | if (inheader) | ||
629 | { | ||
630 | /* New message. */ | ||
631 | if (isfrom) | ||
632 | { | ||
633 | size_t j; | ||
634 | /* Signal the end of the body. */ | ||
635 | if (mum && !mum->body_end) | ||
636 | { | ||
637 | mum->body_end = total - n - newline; | ||
638 | mum->body_lines = --lines - newline; | ||
639 | |||
640 | if (mum->uid <= min_uid) | ||
641 | { | ||
642 | mum->uid = ++min_uid; | ||
643 | /* Note that modification for when expunging. */ | ||
644 | mum->attr_flags |= MU_ATTRIBUTE_MODIFIED; | ||
645 | } | ||
646 | else | ||
647 | min_uid = mum->uid; | ||
648 | |||
649 | if (do_notif) | ||
650 | DISPATCH_ADD_MSG(mailbox, mud); | ||
651 | |||
652 | } | ||
653 | /* Allocate_msgs will initialize mum. */ | ||
654 | ALLOCATE_MSGS(mailbox, mud); | ||
655 | mud->messages_count++; | ||
656 | mum = mud->umessages[mud->messages_count - 1]; | ||
657 | mum->mud = mud; | ||
658 | mum->header_from = total - n; | ||
659 | mum->header_from_end = total; | ||
660 | mum->body_end = mum->body = 0; | ||
661 | mum->attr_flags = 0; | ||
662 | lines = 0; | ||
663 | sfield = NULL; | ||
664 | for (j = 0; j < HDRSIZE; j++) | ||
665 | if (mum->fhdr[j]) | ||
666 | { | ||
667 | free (mum->fhdr[j]); | ||
668 | mum->fhdr[j] = NULL; | ||
669 | } | ||
670 | } | ||
671 | else if (ISSTATUS(buf)) | ||
672 | { | ||
673 | ATTRIBUTE_SET(buf, mum, 'r', 'R', MU_ATTRIBUTE_READ); | ||
674 | ATTRIBUTE_SET(buf, mum, 'o', 'O', MU_ATTRIBUTE_SEEN); | ||
675 | ATTRIBUTE_SET(buf, mum, 'a', 'A', MU_ATTRIBUTE_ANSWERED); | ||
676 | ATTRIBUTE_SET(buf, mum, 'd', 'D', MU_ATTRIBUTE_DELETED); | ||
677 | sfield = NULL; | ||
678 | } | ||
679 | else if (ISBCC(buf)) | ||
680 | { | ||
681 | FAST_H_BCC(mum, sfield, buf, n); | ||
682 | } | ||
683 | else if (ISCC(buf)) | ||
684 | { | ||
685 | FAST_H_CC(mum, sfield, buf, n); | ||
686 | } | ||
687 | else if (ISCONTENT_LANGUAGE(buf)) | ||
688 | { | ||
689 | FAST_H_CONTENT_LANGUAGE(mum, sfield, buf, n); | ||
690 | } | ||
691 | else if (ISCONTENT_TRANSFER_ENCODING(buf)) | ||
692 | { | ||
693 | FAST_H_CONTENT_TRANSFER_ENCODING(mum, sfield, buf, n); | ||
694 | } | ||
695 | else if (ISCONTENT_TYPE(buf)) | ||
696 | { | ||
697 | FAST_H_CONTENT_TYPE(mum, sfield, buf, n); | ||
698 | } | ||
699 | else if (ISDATE(buf)) | ||
700 | { | ||
701 | FAST_H_DATE(mum, sfield, buf, n); | ||
702 | } | ||
703 | else if (ISFROM(buf)) | ||
704 | { | ||
705 | FAST_H_FROM(mum, sfield, buf, n); | ||
706 | } | ||
707 | else if (ISIN_REPLY_TO(buf)) | ||
708 | { | ||
709 | FAST_H_IN_REPLY_TO(mum, sfield, buf, n); | ||
710 | } | ||
711 | else if (ISMESSAGE_ID(buf)) | ||
712 | { | ||
713 | FAST_H_MESSAGE_ID(mum, sfield, buf, n); | ||
714 | } | ||
715 | else if (ISREFERENCE(buf)) | ||
716 | { | ||
717 | FAST_H_REFERENCE(mum, sfield, buf, n); | ||
718 | } | ||
719 | else if (ISREPLY_TO(buf)) | ||
720 | { | ||
721 | FAST_H_REPLY_TO(mum, sfield, buf, n); | ||
722 | } | ||
723 | else if (ISSENDER(buf)) | ||
724 | { | ||
725 | FAST_H_SENDER (mum, sfield, buf, n); | ||
726 | } | ||
727 | else if (ISSUBJECT(buf)) | ||
728 | { | ||
729 | FAST_H_SUBJECT (mum, sfield, buf, n); | ||
730 | } | ||
731 | else if (ISTO(buf)) | ||
732 | { | ||
733 | FAST_H_TO (mum, sfield, buf, n); | ||
734 | } | ||
735 | else if (ISX_UIDL(buf)) | ||
736 | { | ||
737 | FAST_H_X_UIDL (mum, sfield, buf, n); | ||
738 | } | ||
739 | else if (ISX_IMAPBASE(buf)) | ||
740 | { | ||
741 | char *s = memchr (buf, ':', n); | ||
742 | if (s) | ||
743 | { | ||
744 | s++; | ||
745 | ATOI(s, mud->uidvalidity); | ||
746 | ATOI(s, mud->uidnext); | ||
747 | } | ||
748 | } | ||
749 | else if (ISX_UID(buf)) | ||
750 | { | ||
751 | char *s = memchr (buf, ':', n); | ||
752 | if (s) | ||
753 | { | ||
754 | s++; | ||
755 | ATOI(s, mum->uid); | ||
756 | } | ||
757 | } | ||
758 | else if (sfield && (buf[0] == ' ' || buf[0] == '\t')) | ||
759 | { | ||
760 | char *save = *sfield; | ||
761 | FAST_HEADER (save, buf, n); | ||
762 | *sfield = save; | ||
763 | } | ||
764 | else | ||
765 | { | ||
766 | sfield = NULL; | ||
767 | } | ||
768 | } | ||
769 | |||
770 | /* Body. */ | ||
771 | if (inbody) | ||
772 | { | ||
773 | /* Set the body position. */ | ||
774 | if (mum && !mum->body) | ||
775 | { | ||
776 | mum->body = total - n + nl; | ||
777 | mum->header_lines = lines; | ||
778 | lines = 0; | ||
779 | } | ||
780 | } | ||
781 | |||
782 | newline = nl; | ||
783 | |||
784 | /* Every 100 mesgs update the lock, it should be every minute. */ | ||
785 | if ((mud->messages_count % 100) == 0) | ||
786 | locker_touchlock (mailbox->locker); | ||
787 | |||
788 | /* Ping them every 1000 lines. Should be tunable. */ | ||
789 | if (do_notif) | ||
790 | if (((lines +1) % 1000) == 0) | ||
791 | DISPATCH_PROGRESS(mailbox, mud); | ||
792 | |||
793 | } /* while */ | ||
794 | |||
795 | if (mum) | ||
796 | { | ||
797 | mum->body_end = total - newline; | ||
798 | mum->body_lines = lines - newline; | ||
799 | |||
800 | if (mum->uid <= min_uid) | ||
801 | { | ||
802 | mum->uid = ++min_uid; | ||
803 | /* Note that modification for when expunging. */ | ||
804 | mum->attr_flags |= MU_ATTRIBUTE_MODIFIED; | ||
805 | } | ||
806 | else | ||
807 | min_uid = mum->uid; | ||
808 | |||
809 | if (do_notif) | ||
810 | DISPATCH_ADD_MSG(mailbox, mud); | ||
811 | } | ||
812 | if (pcount) | ||
813 | *pcount = mud->messages_count; | ||
814 | locker_unlock (mailbox->locker); | ||
815 | monitor_unlock (mailbox->monitor); | ||
816 | |||
817 | /* Reset the uidvalidity. */ | ||
818 | if (mud->messages_count > 0) | ||
819 | { | ||
820 | mum = mud->umessages[0]; | ||
821 | if (mud->uidvalidity == 0) | ||
822 | { | ||
823 | mud->uidvalidity = (unsigned long)time (NULL); | ||
824 | mud->uidnext = mud->messages_count + 1; | ||
825 | /* Tell that we have been modified for expunging. */ | ||
826 | mum->attr_flags |= MU_ATTRIBUTE_MODIFIED; | ||
827 | } | ||
828 | } | ||
829 | |||
830 | if (mud->messages_count > 0 && min_uid >= mud->uidnext) | ||
831 | { | ||
832 | mum = mud->umessages[0]; | ||
833 | mud->uidnext = min_uid + 1; | ||
834 | mum->attr_flags |= MU_ATTRIBUTE_MODIFIED; | ||
835 | } | ||
836 | |||
837 | #ifdef WITH_PTHREAD | ||
838 | pthread_cleanup_pop (0); | ||
839 | #endif | ||
840 | return status; | ||
841 | } |
mailbox/mbx_mh.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | /* First draft by Sergey Poznyakoff */ | ||
19 | |||
20 | #ifdef HAVE_CONFIG_H | ||
21 | # include <config.h> | ||
22 | #endif | ||
23 | |||
24 | #ifdef ENABLE_MH | ||
25 | |||
26 | #include <sys/types.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <stdio.h> | ||
29 | #include <time.h> | ||
30 | #include <sys/stat.h> | ||
31 | #include <fcntl.h> | ||
32 | #include <unistd.h> | ||
33 | #include <errno.h> | ||
34 | #include <ctype.h> | ||
35 | #include <dirent.h> | ||
36 | |||
37 | #ifdef WITH_PTHREAD | ||
38 | # ifdef HAVE_PTHREAD_H | ||
39 | # define _XOPEN_SOURCE 500 | ||
40 | # include <pthread.h> | ||
41 | # endif | ||
42 | #endif | ||
43 | |||
44 | #include <string.h> | ||
45 | #ifdef HAVE_STRINGS_H | ||
46 | # include <strings.h> | ||
47 | #endif | ||
48 | |||
49 | #include <mailutils/attribute.h> | ||
50 | #include <mailutils/body.h> | ||
51 | #include <mailutils/debug.h> | ||
52 | #include <mailutils/envelope.h> | ||
53 | #include <mailutils/error.h> | ||
54 | #include <mailutils/header.h> | ||
55 | #include <mailutils/locker.h> | ||
56 | #include <mailutils/message.h> | ||
57 | #include <mailutils/mutil.h> | ||
58 | #include <mailutils/property.h> | ||
59 | #include <mailutils/stream.h> | ||
60 | #include <mailutils/url.h> | ||
61 | #include <mailutils/observer.h> | ||
62 | #include <mailbox0.h> | ||
63 | #include <registrar0.h> | ||
64 | |||
65 | #define MAX_OPEN_STREAMS 16 | ||
66 | |||
67 | /* Notifications ADD_MESG. */ | ||
68 | #define DISPATCH_ADD_MSG(mbox,mhd) \ | ||
69 | do \ | ||
70 | { \ | ||
71 | int bailing = 0; \ | ||
72 | monitor_unlock (mbox->monitor); \ | ||
73 | if (mbox->observable) \ | ||
74 | bailing = observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD); \ | ||
75 | if (bailing != 0) \ | ||
76 | { \ | ||
77 | if (pcount) \ | ||
78 | *pcount = (mhd)->msg_count; \ | ||
79 | locker_unlock (mbox->locker); \ | ||
80 | return EINTR; \ | ||
81 | } \ | ||
82 | monitor_wrlock (mbox->monitor); \ | ||
83 | } while (0); | ||
84 | |||
85 | /* Note: In this particular implementation the message sequence number | ||
86 | serves also as its UID. This allows to avoid many problems related | ||
87 | to keeping the uids in the headers of the messages. */ | ||
88 | |||
89 | struct _mh_data; | ||
90 | struct _mh_message | ||
91 | { | ||
92 | struct _mh_message *next; | ||
93 | struct _mh_message *prev; | ||
94 | |||
95 | stream_t stream; /* Associated file stream */ | ||
96 | off_t body_start; /* Offset of body start in the message file */ | ||
97 | off_t body_end; /* Offset of body end (size of file, effectively)*/ | ||
98 | |||
99 | size_t seq_number; /* message sequence number */ | ||
100 | |||
101 | int attr_flags; /* Attribute flags */ | ||
102 | int deleted; /* Was the message originally deleted */ | ||
103 | |||
104 | time_t mtime; /* Time of last modification */ | ||
105 | size_t header_lines; /* Number of lines in the header part */ | ||
106 | size_t body_lines; /* Number of lines in the body */ | ||
107 | |||
108 | message_t message; /* Corresponding message_t */ | ||
109 | struct _mh_data *mhd; /* Back pointer. */ | ||
110 | }; | ||
111 | |||
112 | struct _mh_data | ||
113 | { | ||
114 | /* List of messages: */ | ||
115 | size_t msg_count; /* number of messages in the list */ | ||
116 | struct _mh_message *msg_head; /* First */ | ||
117 | struct _mh_message *msg_tail; /* Last */ | ||
118 | |||
119 | unsigned long uidvalidity; | ||
120 | |||
121 | char *name; /* Directory name */ | ||
122 | |||
123 | /* Pool of open message streams */ | ||
124 | struct _mh_message *msg_pool[MAX_OPEN_STREAMS]; | ||
125 | int pool_first; /* Index to the first used entry in msg_pool */ | ||
126 | int pool_last; /* Index to the first free entry in msg_pool */ | ||
127 | |||
128 | time_t mtime; /* Time of last modification */ | ||
129 | |||
130 | mailbox_t mailbox; /* Back pointer. */ | ||
131 | }; | ||
132 | |||
133 | static void mh_destroy __P((mailbox_t mailbox)); | ||
134 | static int mh_open __P ((mailbox_t, int)); | ||
135 | static int mh_close __P ((mailbox_t)); | ||
136 | static int mh_get_message __P ((mailbox_t, size_t, message_t *)); | ||
137 | static int mh_append_message __P ((mailbox_t, message_t)); | ||
138 | static int mh_messages_count __P ((mailbox_t, size_t *)); | ||
139 | static int mh_messages_recent __P ((mailbox_t, size_t *)); | ||
140 | static int mh_message_unseen __P ((mailbox_t, size_t *)); | ||
141 | static int mh_expunge __P ((mailbox_t)); | ||
142 | static int mh_save_attributes __P ((mailbox_t)); | ||
143 | static int mh_uidvalidity __P ((mailbox_t, unsigned long *)); | ||
144 | static int mh_uidnext __P ((mailbox_t, size_t *)); | ||
145 | static int mh_scan __P ((mailbox_t, size_t, size_t *)); | ||
146 | static int mh_scan0 __P ((mailbox_t mailbox, size_t msgno, size_t *pcount, | ||
147 | int do_notify)); | ||
148 | static int mh_is_updated __P ((mailbox_t)); | ||
149 | static int mh_get_size __P ((mailbox_t, off_t *)); | ||
150 | |||
151 | static int mh_body_read __P ((stream_t, char *, size_t, off_t, size_t *)); | ||
152 | static int mh_body_readline __P ((stream_t, char *, size_t, off_t, size_t *)); | ||
153 | static int mh_stream_size __P ((stream_t stream, off_t *psize)); | ||
154 | |||
155 | static int mh_body_size __P ((body_t body, size_t *psize)); | ||
156 | static int mh_body_lines __P ((body_t body, size_t *plines)); | ||
157 | |||
158 | static int mh_message_uid __P ((message_t msg, size_t *puid)); | ||
159 | |||
160 | static int mh_message_stream_open __P((struct _mh_message *mhm)); | ||
161 | static void mh_message_stream_close __P((struct _mh_message *mhm)); | ||
162 | |||
163 | static int mh_header_fill __P((header_t header, char *buffer, size_t len, | ||
164 | off_t off, size_t *pnread)); | ||
165 | static int mh_header_size __P((header_t header, size_t *psize)); | ||
166 | static int mh_header_lines __P((header_t header, size_t *plines)); | ||
167 | |||
168 | static int mh_get_attr_flags __P((attribute_t attr, int *pflags)); | ||
169 | static int mh_set_attr_flags __P((attribute_t attr, int flags)); | ||
170 | static int mh_unset_attr_flags __P((attribute_t attr, int flags)); | ||
171 | |||
172 | static void _mh_message_insert __P((struct _mh_data *mhd, | ||
173 | struct _mh_message *msg)); | ||
174 | static void _mh_message_delete __P((struct _mh_data *mhd, | ||
175 | struct _mh_message *msg)); | ||
176 | static int mh_pool_open __P((struct _mh_message *mhm)); | ||
177 | |||
178 | static int mh_envelope_date __P((envelope_t envelope, char *buf, size_t len, | ||
179 | size_t *psize)); | ||
180 | static int mh_envelope_sender __P((envelope_t envelope, char *buf, size_t len, | ||
181 | size_t *psize)); | ||
182 | |||
183 | /* Should be in an other header file. */ | ||
184 | extern int mh_message_number __P ((message_t msg, size_t *pnum)); | ||
185 | |||
186 | /* Return filename for the message. | ||
187 | NOTE: Allocates memory. */ | ||
188 | static char * | ||
189 | _mh_message_name (struct _mh_message *mhm, int deleted) | ||
190 | { | ||
191 | char *filename; | ||
192 | size_t len = strlen (mhm->mhd->name) + 32; | ||
193 | filename = malloc (len); | ||
194 | if (deleted) | ||
195 | snprintf (filename, len, "%s/,%lu", mhm->mhd->name, | ||
196 | (unsigned long) mhm->seq_number); | ||
197 | else | ||
198 | snprintf (filename, len, "%s/%lu", mhm->mhd->name, | ||
199 | (unsigned long) mhm->seq_number); | ||
200 | return filename; | ||
201 | } | ||
202 | |||
203 | int | ||
204 | _mailbox_mh_init (mailbox_t mailbox) | ||
205 | { | ||
206 | struct _mh_data *mhd; | ||
207 | size_t name_len; | ||
208 | |||
209 | if (mailbox == NULL) | ||
210 | return EINVAL; | ||
211 | |||
212 | mhd = mailbox->data = calloc (1, sizeof (*mhd)); | ||
213 | if (mailbox->data == NULL) | ||
214 | return ENOMEM; | ||
215 | |||
216 | /* Back pointer. */ | ||
217 | mhd->mailbox = mailbox; | ||
218 | |||
219 | url_get_path (mailbox->url, NULL, 0, &name_len); | ||
220 | mhd->name = calloc (name_len + 1, sizeof (char)); | ||
221 | if (mhd->name == NULL) | ||
222 | { | ||
223 | free (mhd); | ||
224 | mailbox->data = NULL; | ||
225 | return ENOMEM; | ||
226 | } | ||
227 | url_get_path (mailbox->url, mhd->name, name_len + 1, NULL); | ||
228 | |||
229 | /* Overloading the defaults. */ | ||
230 | mailbox->_destroy = mh_destroy; | ||
231 | |||
232 | mailbox->_open = mh_open; | ||
233 | mailbox->_close = mh_close; | ||
234 | |||
235 | /* Overloading of the entire mailbox object methods. */ | ||
236 | mailbox->_get_message = mh_get_message; | ||
237 | mailbox->_append_message = mh_append_message; | ||
238 | mailbox->_messages_count = mh_messages_count; | ||
239 | mailbox->_messages_recent = mh_messages_recent; | ||
240 | mailbox->_message_unseen = mh_message_unseen; | ||
241 | mailbox->_expunge = mh_expunge; | ||
242 | mailbox->_save_attributes = mh_save_attributes; | ||
243 | mailbox->_uidvalidity = mh_uidvalidity; | ||
244 | mailbox->_uidnext = mh_uidnext; | ||
245 | |||
246 | mailbox->_scan = mh_scan; | ||
247 | mailbox->_is_updated = mh_is_updated; | ||
248 | |||
249 | mailbox->_get_size = mh_get_size; | ||
250 | |||
251 | /* Set our properties. */ | ||
252 | { | ||
253 | property_t property = NULL; | ||
254 | mailbox_get_property (mailbox, &property); | ||
255 | property_set_value (property, "TYPE", "MH", 1); | ||
256 | } | ||
257 | |||
258 | MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mh_init(%s)\n", mhd->name); | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static void | ||
263 | mh_destroy (mailbox_t mailbox) | ||
264 | { | ||
265 | struct _mh_data *mhd = mailbox->data; | ||
266 | struct _mh_message *msg, *next; | ||
267 | |||
268 | if (!mhd) | ||
269 | return; | ||
270 | |||
271 | monitor_wrlock (mailbox->monitor); | ||
272 | msg = mhd->msg_head; | ||
273 | while (msg) | ||
274 | { | ||
275 | next = msg->next; | ||
276 | message_destroy (&msg->message, msg); | ||
277 | free (msg); | ||
278 | msg = next; | ||
279 | } | ||
280 | |||
281 | if (mhd->name) | ||
282 | free (mhd->name); | ||
283 | |||
284 | free (mhd); | ||
285 | mailbox->data = NULL; | ||
286 | monitor_unlock (mailbox->monitor); | ||
287 | } | ||
288 | |||
289 | static int | ||
290 | mh_open (mailbox_t mailbox, int flags) | ||
291 | { | ||
292 | struct _mh_data *mhd = mailbox->data; | ||
293 | int status = 0; | ||
294 | struct stat st; | ||
295 | |||
296 | mailbox->flags = flags; | ||
297 | if (stat (mhd->name, &st) < 0) | ||
298 | return errno; | ||
299 | |||
300 | if (!S_ISDIR (st.st_mode)) | ||
301 | return EINVAL; | ||
302 | |||
303 | mhd->mtime = st.st_mtime; | ||
304 | |||
305 | /* FIXME: is this the right kind of locking for mh folders? */ | ||
306 | if (mailbox->locker == NULL) | ||
307 | status = locker_create (&mailbox->locker, mhd->name, 0); | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int | ||
312 | mh_close (mailbox_t mailbox) | ||
313 | { | ||
314 | if (!mailbox) | ||
315 | return EINVAL; | ||
316 | return locker_unlock (mailbox->locker); | ||
317 | } | ||
318 | |||
319 | static struct _mh_message * | ||
320 | _mh_get_message (struct _mh_data *mhd, size_t msgno) | ||
321 | { | ||
322 | size_t n; | ||
323 | struct _mh_message *msg; | ||
324 | |||
325 | for (n = 1, msg = mhd->msg_head; msg && n < msgno; n++, msg = msg->next) | ||
326 | ; | ||
327 | |||
328 | return msg; | ||
329 | } | ||
330 | |||
331 | /* Find the message with the given sequence number */ | ||
332 | static struct _mh_message * | ||
333 | _mh_get_message_seq (struct _mh_data *mhd, size_t seq) | ||
334 | { | ||
335 | struct _mh_message *msg; | ||
336 | |||
337 | for (msg = mhd->msg_head; msg && msg->seq_number < seq; msg = msg->next) | ||
338 | ; | ||
339 | |||
340 | if (msg) | ||
341 | return msg->seq_number == seq ? msg : NULL; | ||
342 | return NULL; | ||
343 | } | ||
344 | |||
345 | static int | ||
346 | _mh_attach_message (mailbox_t mailbox, struct _mh_message *mhm, | ||
347 | message_t *pmsg) | ||
348 | { | ||
349 | int status; | ||
350 | message_t msg; | ||
351 | |||
352 | /* Check if we already have it. */ | ||
353 | if (mhm->message) | ||
354 | { | ||
355 | if (pmsg) | ||
356 | *pmsg = mhm->message; | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | /* Get an empty message struct. */ | ||
361 | status = message_create (&msg, mhm); | ||
362 | if (status != 0) | ||
363 | return status; | ||
364 | |||
365 | /* Set the header. */ | ||
366 | { | ||
367 | header_t header = NULL; | ||
368 | status = header_create (&header, NULL, 0, msg); | ||
369 | if (status != 0) | ||
370 | { | ||
371 | message_destroy (&msg, mhm); | ||
372 | return status; | ||
373 | } | ||
374 | header_set_fill (header, mh_header_fill, msg); | ||
375 | header_set_size (header, mh_header_size, msg); | ||
376 | header_set_lines (header, mh_header_lines, msg); | ||
377 | /*FIXME: | ||
378 | header_set_get_fvalue (header, mh_header_get_fvalue, msg); | ||
379 | */ | ||
380 | message_set_header (msg, header, mhm); | ||
381 | } | ||
382 | |||
383 | /* Set the attribute. */ | ||
384 | { | ||
385 | attribute_t attribute; | ||
386 | status = attribute_create (&attribute, msg); | ||
387 | if (status != 0) | ||
388 | { | ||
389 | message_destroy (&msg, mhm); | ||
390 | return status; | ||
391 | } | ||
392 | attribute_set_get_flags (attribute, mh_get_attr_flags, msg); | ||
393 | attribute_set_set_flags (attribute, mh_set_attr_flags, msg); | ||
394 | attribute_set_unset_flags (attribute, mh_unset_attr_flags, msg); | ||
395 | message_set_attribute (msg, attribute, mhm); | ||
396 | } | ||
397 | |||
398 | /* Prepare the body. */ | ||
399 | { | ||
400 | body_t body = NULL; | ||
401 | stream_t stream = NULL; | ||
402 | if ((status = body_create (&body, msg)) != 0 | ||
403 | || (status = stream_create (&stream, | ||
404 | mailbox->flags | MU_STREAM_SEEKABLE, | ||
405 | body)) != 0) | ||
406 | { | ||
407 | body_destroy (&body, msg); | ||
408 | stream_destroy (&stream, body); | ||
409 | message_destroy (&msg, mhm); | ||
410 | return status; | ||
411 | } | ||
412 | stream_set_read (stream, mh_body_read, body); | ||
413 | stream_set_readline (stream, mh_body_readline, body); | ||
414 | stream_set_size (stream, mh_stream_size, body); | ||
415 | body_set_stream (body, stream, msg); | ||
416 | body_set_size (body, mh_body_size, msg); | ||
417 | body_set_lines (body, mh_body_lines, msg); | ||
418 | message_set_body (msg, body, mhm); | ||
419 | } | ||
420 | |||
421 | /* Set the envelope. */ | ||
422 | { | ||
423 | envelope_t envelope = NULL; | ||
424 | status = envelope_create (&envelope, msg); | ||
425 | if (status != 0) | ||
426 | { | ||
427 | message_destroy (&msg, mhm); | ||
428 | return status; | ||
429 | } | ||
430 | envelope_set_sender (envelope, mh_envelope_sender, msg); | ||
431 | envelope_set_date (envelope, mh_envelope_date, msg); | ||
432 | message_set_envelope (msg, envelope, mhm); | ||
433 | } | ||
434 | |||
435 | /* Set the UID. */ | ||
436 | message_set_uid (msg, mh_message_uid, mhm); | ||
437 | |||
438 | /* Attach the message to the mailbox mbox data. */ | ||
439 | mhm->message = msg; | ||
440 | message_set_mailbox (msg, mailbox, mhm); | ||
441 | |||
442 | if (pmsg) | ||
443 | *pmsg = msg; | ||
444 | |||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | static int | ||
449 | mh_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg) | ||
450 | { | ||
451 | int status; | ||
452 | struct _mh_data *mhd = mailbox->data; | ||
453 | struct _mh_message *mhm; | ||
454 | |||
455 | /* Sanity checks. */ | ||
456 | if (pmsg == NULL || mhd == NULL) | ||
457 | return EINVAL; | ||
458 | |||
459 | /* If we did not start a scanning yet do it now. */ | ||
460 | if (mhd->msg_count == 0) | ||
461 | { | ||
462 | status = mh_scan0 (mailbox, 1, NULL, 0); | ||
463 | if (status != 0) | ||
464 | return status; | ||
465 | } | ||
466 | |||
467 | if ((mhm = _mh_get_message (mhd, msgno)) == NULL) | ||
468 | return EINVAL; | ||
469 | return _mh_attach_message (mailbox, mhm, pmsg); | ||
470 | } | ||
471 | |||
472 | static size_t | ||
473 | _mh_next_seq (struct _mh_data *mhd) | ||
474 | { | ||
475 | return (mhd->msg_tail ? mhd->msg_tail->seq_number : 0) + 1; | ||
476 | } | ||
477 | |||
478 | static FILE * | ||
479 | _mh_tempfile(struct _mh_data *mhd, char **namep) | ||
480 | { | ||
481 | int fd = mu_tempfile (mhd->name, namep); | ||
482 | if (fd == -1) | ||
483 | return NULL; | ||
484 | return fdopen (fd, "w"); | ||
485 | } | ||
486 | |||
487 | static int | ||
488 | _mh_delim (char *str) | ||
489 | { | ||
490 | if (str[0] == '-') | ||
491 | { | ||
492 | for (; *str == '-'; str++) | ||
493 | ; | ||
494 | for (; *str == ' ' || *str == '\t'; str++) | ||
495 | ; | ||
496 | } | ||
497 | return str[0] == '\n'; | ||
498 | } | ||
499 | |||
500 | static int | ||
501 | _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge) | ||
502 | { | ||
503 | stream_t stream = NULL; | ||
504 | char *name = NULL, *buf = NULL, *msg_name; | ||
505 | size_t n, off = 0; | ||
506 | size_t bsize; | ||
507 | size_t nlines, nbytes; | ||
508 | size_t new_body_start, new_header_lines; | ||
509 | FILE *fp; | ||
510 | message_t msg = mhm->message; | ||
511 | header_t hdr; | ||
512 | int status; | ||
513 | attribute_t attr; | ||
514 | body_t body; | ||
515 | char buffer[512]; | ||
516 | envelope_t env = NULL; | ||
517 | |||
518 | fp = _mh_tempfile (mhm->mhd, &name); | ||
519 | if (!fp) | ||
520 | return errno; | ||
521 | |||
522 | message_size (msg, &bsize); | ||
523 | |||
524 | /* Try to allocate large buffer */ | ||
525 | for (; bsize > 1; bsize /= 2) | ||
526 | if ((buf = malloc (bsize))) | ||
527 | break; | ||
528 | |||
529 | if (!bsize) | ||
530 | return ENOMEM; | ||
531 | |||
532 | /* Copy flags */ | ||
533 | message_get_header (msg, &hdr); | ||
534 | header_get_stream (hdr, &stream); | ||
535 | off = 0; | ||
536 | nlines = nbytes = 0; | ||
537 | while ((status = stream_readline (stream, buf, bsize, off, &n)) == 0 | ||
538 | && n != 0) | ||
539 | { | ||
540 | if (_mh_delim(buf)) | ||
541 | break; | ||
542 | |||
543 | if (!(strncasecmp (buf, "status:", 7) == 0 | ||
544 | || strncasecmp (buf, "x-imapbase:", 11) == 0 | ||
545 | || strncasecmp (buf, "x-uid:", 6) == 0 | ||
546 | || strncasecmp (buf, MU_HEADER_ENV_DATE ":", sizeof (MU_HEADER_ENV_DATE)) == 0 | ||
547 | || strncasecmp (buf, MU_HEADER_ENV_SENDER ":", sizeof (MU_HEADER_ENV_SENDER)) == 0)) | ||
548 | { | ||
549 | nlines++; | ||
550 | nbytes += fprintf (fp, "%s", buf); | ||
551 | } | ||
552 | |||
553 | off += n; | ||
554 | } | ||
555 | |||
556 | /* Add imapbase */ | ||
557 | if (!mhd->msg_head || (mhd->msg_head == mhm)) /*FIXME*/ | ||
558 | { | ||
559 | nbytes += fprintf (fp, "X-IMAPbase: %lu %u\n", | ||
560 | (unsigned long) mhd->uidvalidity, | ||
561 | (unsigned) _mh_next_seq(mhd)); | ||
562 | nlines++; | ||
563 | } | ||
564 | |||
565 | message_get_envelope (msg, &env); | ||
566 | if (envelope_date (env, buffer, sizeof buffer, &n) == 0 && n > 0) | ||
567 | { | ||
568 | /* NOTE: buffer is terminated with \n */ | ||
569 | char *p = buffer; | ||
570 | while (isspace (*p)) | ||
571 | p++; | ||
572 | nbytes += fprintf (fp, "%s: %s", MU_HEADER_ENV_DATE, p); | ||
573 | |||
574 | if (*p && p[strlen (p) - 1] != '\n') | ||
575 | nbytes += fprintf (fp, "\n"); | ||
576 | |||
577 | nlines++; | ||
578 | } | ||
579 | |||
580 | if (envelope_sender (env, buffer, sizeof buffer, &n) == 0 && n > 0) | ||
581 | { | ||
582 | fprintf (fp, "%s: %s\n", MU_HEADER_ENV_SENDER, buffer); | ||
583 | nlines++; | ||
584 | } | ||
585 | |||
586 | /* Add status */ | ||
587 | message_get_attribute (msg, &attr); | ||
588 | attribute_to_string (attr, buf, bsize, &n); | ||
589 | if (n) | ||
590 | { | ||
591 | nbytes += fprintf (fp, "%s", buf); | ||
592 | nlines++; | ||
593 | } | ||
594 | nbytes += fprintf (fp, "\n"); | ||
595 | nlines++; | ||
596 | |||
597 | new_header_lines = nlines; | ||
598 | new_body_start = nbytes; | ||
599 | |||
600 | /* Copy message body */ | ||
601 | |||
602 | message_get_body (msg, &body); | ||
603 | body_get_stream (body, &stream); | ||
604 | off = 0; | ||
605 | nlines = 0; | ||
606 | while (stream_read (stream, buf, bsize, off, &n) == 0 && n != 0) | ||
607 | { | ||
608 | char *p; | ||
609 | for (p = buf; p < buf + n; p++) | ||
610 | if (*p == '\n') | ||
611 | nlines++; | ||
612 | fwrite (buf, 1, n, fp); | ||
613 | off += n; | ||
614 | nbytes += n; | ||
615 | } | ||
616 | |||
617 | mhm->header_lines = new_header_lines; | ||
618 | mhm->body_start = new_body_start; | ||
619 | mhm->body_lines = nlines; | ||
620 | mhm->body_end = nbytes; | ||
621 | |||
622 | free (buf); | ||
623 | fclose (fp); | ||
624 | |||
625 | msg_name = _mh_message_name (mhm, mhm->deleted); | ||
626 | rename (name, msg_name); | ||
627 | free (name); | ||
628 | free (msg_name); | ||
629 | |||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | static int | ||
634 | mh_append_message (mailbox_t mailbox, message_t msg) | ||
635 | { | ||
636 | int status; | ||
637 | struct _mh_data *mhd = mailbox->data; | ||
638 | struct _mh_message *mhm; | ||
639 | |||
640 | if (!mailbox || !msg) | ||
641 | return EINVAL; | ||
642 | |||
643 | mhm = calloc (1, sizeof(*mhm)); | ||
644 | if (!mhm) | ||
645 | return ENOMEM; | ||
646 | |||
647 | /* If we did not start a scanning yet do it now. */ | ||
648 | if (mhd->msg_count == 0) | ||
649 | { | ||
650 | status = mh_scan0 (mailbox, 1, NULL, 0); | ||
651 | if (status != 0) | ||
652 | return status; | ||
653 | } | ||
654 | |||
655 | mhm->mhd = mhd; | ||
656 | mhm->seq_number = _mh_next_seq (mhd); | ||
657 | mhm->message = msg; | ||
658 | status = _mh_message_save (mhd, mhm, 0); | ||
659 | mhm->message = NULL; | ||
660 | /* Insert and re-scan the message */ | ||
661 | _mh_message_insert (mhd, mhm); | ||
662 | return status; | ||
663 | } | ||
664 | |||
665 | static int | ||
666 | mh_messages_count (mailbox_t mailbox, size_t *pcount) | ||
667 | { | ||
668 | struct _mh_data *mhd = mailbox->data; | ||
669 | |||
670 | if (mhd == NULL) | ||
671 | return EINVAL; | ||
672 | |||
673 | if (!mh_is_updated (mailbox)) | ||
674 | return mh_scan0 (mailbox, mhd->msg_count, pcount, 0); | ||
675 | |||
676 | if (pcount) | ||
677 | *pcount = mhd->msg_count; | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | /* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN | ||
683 | ('O' in the Status header), i.e. a message that is first seen | ||
684 | by the current session (see attributes.h) */ | ||
685 | static int | ||
686 | mh_messages_recent (mailbox_t mailbox, size_t *pcount) | ||
687 | { | ||
688 | struct _mh_data *mhd = mailbox->data; | ||
689 | struct _mh_message *mhm; | ||
690 | size_t count; | ||
691 | |||
692 | /* If we did not start a scanning yet do it now. */ | ||
693 | if (mhd->msg_count == 0) | ||
694 | { | ||
695 | int status = mh_scan0 (mailbox, 1, NULL, 0); | ||
696 | if (status != 0) | ||
697 | return status; | ||
698 | } | ||
699 | count = 0; | ||
700 | for (mhm = mhd->msg_head; mhm; mhm = mhm->next) | ||
701 | { | ||
702 | if (MU_ATTRIBUTE_IS_UNSEEN(mhm->attr_flags)) | ||
703 | count++; | ||
704 | } | ||
705 | *pcount = count; | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | /* An "unseen" message is the one that has not been read yet */ | ||
710 | static int | ||
711 | mh_message_unseen (mailbox_t mailbox, size_t *pmsgno) | ||
712 | { | ||
713 | struct _mh_data *mhd = mailbox->data; | ||
714 | struct _mh_message *mhm; | ||
715 | size_t i, unseen; | ||
716 | |||
717 | /* If we did not start a scanning yet do it now. */ | ||
718 | if (mhd->msg_count == 0) | ||
719 | { | ||
720 | int status = mh_scan0 (mailbox, 1, NULL, 0); | ||
721 | if (status != 0) | ||
722 | return status; | ||
723 | } | ||
724 | for (unseen = i = 1, mhm = mhd->msg_head; mhm; i++, mhm = mhm->next) | ||
725 | { | ||
726 | if (MU_ATTRIBUTE_IS_UNREAD(mhm->attr_flags)) | ||
727 | { | ||
728 | unseen = i; | ||
729 | break; | ||
730 | } | ||
731 | } | ||
732 | *pmsgno = unseen; | ||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | static int | ||
737 | mh_expunge (mailbox_t mailbox) | ||
738 | { | ||
739 | struct _mh_data *mhd = mailbox->data; | ||
740 | struct _mh_message *mhm; | ||
741 | |||
742 | if (mhd == NULL) | ||
743 | return EINVAL; | ||
744 | |||
745 | if (mhd->msg_count == 0) | ||
746 | return 0; | ||
747 | |||
748 | /* Find the first dirty(modified) message. */ | ||
749 | for (mhm = mhd->msg_head; mhm; mhm = mhm->next) | ||
750 | { | ||
751 | if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) || | ||
752 | (mhm->attr_flags & MU_ATTRIBUTE_DELETED) || | ||
753 | (mhm->message && message_is_modified (mhm->message))) | ||
754 | break; | ||
755 | } | ||
756 | |||
757 | if (!mhm) | ||
758 | return 0; /* Nothing changed, just return. */ | ||
759 | |||
760 | while (mhm) | ||
761 | { | ||
762 | struct _mh_message *next = mhm->next; | ||
763 | |||
764 | if (mhm->attr_flags & MU_ATTRIBUTE_DELETED) | ||
765 | { | ||
766 | if (!mhm->deleted) | ||
767 | { | ||
768 | char *old_name, *new_name; | ||
769 | /* Rename original message */ | ||
770 | old_name = _mh_message_name (mhm, 0); | ||
771 | new_name = _mh_message_name (mhm, 1); | ||
772 | rename (old_name, new_name); | ||
773 | free (old_name); | ||
774 | free (new_name); | ||
775 | } | ||
776 | _mh_message_delete (mhd, mhm); | ||
777 | } | ||
778 | else if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) | ||
779 | || (mhm->message && message_is_modified (mhm->message))) | ||
780 | { | ||
781 | _mh_attach_message (mailbox, mhm, NULL); | ||
782 | mhm->deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED; | ||
783 | _mh_message_save (mhd, mhm, 1); | ||
784 | } | ||
785 | mhm = next; | ||
786 | } | ||
787 | |||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | static int | ||
792 | mh_save_attributes (mailbox_t mailbox) | ||
793 | { | ||
794 | struct _mh_data *mhd = mailbox->data; | ||
795 | struct _mh_message *mhm; | ||
796 | |||
797 | if (mhd == NULL) | ||
798 | return EINVAL; | ||
799 | |||
800 | if (mhd->msg_count == 0) | ||
801 | return 0; | ||
802 | |||
803 | /* Find the first dirty(modified) message. */ | ||
804 | for (mhm = mhd->msg_head; mhm; mhm = mhm->next) | ||
805 | { | ||
806 | if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) | ||
807 | || (mhm->message && message_is_modified (mhm->message))) | ||
808 | break; | ||
809 | } | ||
810 | |||
811 | if (!mhm) | ||
812 | return 0; /* Nothing changed, just return. */ | ||
813 | |||
814 | while (mhm) | ||
815 | { | ||
816 | struct _mh_message *next = mhm->next; | ||
817 | |||
818 | if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) | ||
819 | || (mhm->message && message_is_modified (mhm->message))) | ||
820 | { | ||
821 | _mh_attach_message (mailbox, mhm, NULL); | ||
822 | mhm->deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED; | ||
823 | _mh_message_save (mhd, mhm, 0); | ||
824 | } | ||
825 | mhm = next; | ||
826 | } | ||
827 | |||
828 | return 0; | ||
829 | } | ||
830 | |||
831 | static int | ||
832 | mh_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity) | ||
833 | { | ||
834 | struct _mh_data *mhd = mailbox->data; | ||
835 | int status = mh_messages_count (mailbox, NULL); | ||
836 | if (status != 0) | ||
837 | return status; | ||
838 | /* If we did not start a scanning yet do it now. */ | ||
839 | if (mhd->msg_count == 0) | ||
840 | { | ||
841 | status = mh_scan0 (mailbox, 1, NULL, 0); | ||
842 | if (status != 0) | ||
843 | return status; | ||
844 | } | ||
845 | if (puidvalidity) | ||
846 | *puidvalidity = mhd->uidvalidity; | ||
847 | return 0; | ||
848 | } | ||
849 | |||
850 | static int | ||
851 | mh_uidnext (mailbox_t mailbox, size_t *puidnext) | ||
852 | { | ||
853 | struct _mh_data *mhd = mailbox->data; | ||
854 | int status = mh_messages_count (mailbox, NULL); | ||
855 | if (status != 0) | ||
856 | return status; | ||
857 | /* If we did not start a scanning yet do it now. */ | ||
858 | if (mhd->msg_count == 0) | ||
859 | { | ||
860 | status = mh_scan0 (mailbox, 1, NULL, 0); | ||
861 | if (status != 0) | ||
862 | return status; | ||
863 | } | ||
864 | if (puidnext) | ||
865 | *puidnext = _mh_next_seq(mhd); | ||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | /* FIXME: effectively the same as mbox_cleanup */ | ||
870 | static void | ||
871 | mh_cleanup (void *arg) | ||
872 | { | ||
873 | mailbox_t mailbox = arg; | ||
874 | monitor_unlock (mailbox->monitor); | ||
875 | locker_unlock (mailbox->locker); | ||
876 | } | ||
877 | |||
878 | /* Insert message msg into the message list on the appropriate position */ | ||
879 | static void | ||
880 | _mh_message_insert (struct _mh_data *mhd, struct _mh_message *msg) | ||
881 | { | ||
882 | struct _mh_message *p; | ||
883 | struct _mh_message *prev; | ||
884 | size_t n = msg->seq_number; | ||
885 | |||
886 | for (p = mhd->msg_head; p && p->seq_number < n; p = p->next) | ||
887 | ; | ||
888 | |||
889 | if (!p) | ||
890 | { | ||
891 | msg->next = NULL; | ||
892 | msg->prev = mhd->msg_tail; | ||
893 | mhd->msg_tail = msg; | ||
894 | if (!mhd->msg_head) | ||
895 | mhd->msg_head = msg; | ||
896 | } | ||
897 | else | ||
898 | { | ||
899 | msg->next = p; | ||
900 | msg->prev = p->prev; | ||
901 | p->prev = msg; | ||
902 | } | ||
903 | if ((prev = msg->prev) != NULL) | ||
904 | prev->next = msg; | ||
905 | else | ||
906 | mhd->msg_head = msg; | ||
907 | msg->mhd = mhd; | ||
908 | mhd->msg_count++; | ||
909 | } | ||
910 | |||
911 | static void | ||
912 | _mh_message_delete (struct _mh_data *mhd, struct _mh_message *msg) | ||
913 | { | ||
914 | struct _mh_message *p; | ||
915 | |||
916 | if ((p = msg->next) != NULL) | ||
917 | p->prev = msg->prev; | ||
918 | else | ||
919 | mhd->msg_tail = msg->prev; | ||
920 | |||
921 | if ((p = msg->prev) != NULL) | ||
922 | p->next = msg->next; | ||
923 | else | ||
924 | mhd->msg_head = msg->next; | ||
925 | |||
926 | message_destroy (&msg->message, msg); | ||
927 | free (msg); | ||
928 | mhd->msg_count--; | ||
929 | } | ||
930 | |||
931 | /* Scan given message and fill mh_message_t fields. | ||
932 | NOTE: the function assumes mhm->stream != NULL. */ | ||
933 | static int | ||
934 | mh_scan_message (struct _mh_message *mhm) | ||
935 | { | ||
936 | stream_t stream = mhm->stream; | ||
937 | char buf[1024]; | ||
938 | off_t off = 0; | ||
939 | size_t n; | ||
940 | int status; | ||
941 | int in_header = 1; | ||
942 | size_t hlines = 0; | ||
943 | size_t blines = 0; | ||
944 | size_t body_start = 0; | ||
945 | |||
946 | /* Check if the message was modified after the last scan */ | ||
947 | if (mhm->mtime) | ||
948 | { | ||
949 | struct stat st; | ||
950 | char *msg_name = _mh_message_name (mhm, mhm->deleted); | ||
951 | |||
952 | if (stat (msg_name, &st) == 0 && st.st_mtime == mhm->mtime) | ||
953 | { | ||
954 | /* Nothing to do */ | ||
955 | free (msg_name); | ||
956 | return 0; | ||
957 | } | ||
958 | free (msg_name); | ||
959 | } | ||
960 | |||
961 | while ((status = stream_readline (stream, buf, sizeof (buf), off, &n) == 0) | ||
962 | && n != 0) | ||
963 | { | ||
964 | if (in_header) | ||
965 | { | ||
966 | if (buf[0] == '\n') | ||
967 | { | ||
968 | in_header = 0; | ||
969 | body_start = off+1; | ||
970 | } | ||
971 | if (buf[n - 1] == '\n') | ||
972 | hlines++; | ||
973 | |||
974 | /* Process particular attributes */ | ||
975 | if (strncasecmp (buf, "status:", 7) == 0) | ||
976 | { | ||
977 | int deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED; | ||
978 | string_to_flags (buf, &mhm->attr_flags); | ||
979 | mhm->attr_flags |= deleted; | ||
980 | } | ||
981 | else if (strncasecmp (buf, "x-imapbase:", 11) == 0) | ||
982 | { | ||
983 | char *p; | ||
984 | mhm->mhd->uidvalidity = strtoul (buf + 11, &p, 10); | ||
985 | /* second number is next uid. Ignored */ | ||
986 | } | ||
987 | } | ||
988 | else | ||
989 | { | ||
990 | if (buf[n - 1] == '\n') | ||
991 | blines++; | ||
992 | } | ||
993 | off += n; | ||
994 | } | ||
995 | |||
996 | if (!body_start) | ||
997 | body_start = off; | ||
998 | mhm->header_lines = hlines; | ||
999 | mhm->body_lines = blines; | ||
1000 | mhm->body_start = body_start; | ||
1001 | mhm->body_end = off; | ||
1002 | return 0; | ||
1003 | } | ||
1004 | |||
1005 | /* Scan the mailbox */ | ||
1006 | static int | ||
1007 | mh_scan0 (mailbox_t mailbox, size_t msgno, size_t *pcount, int do_notify) | ||
1008 | { | ||
1009 | struct _mh_data *mhd = mailbox->data; | ||
1010 | struct _mh_message *msg; | ||
1011 | DIR *dir; | ||
1012 | struct dirent *entry; | ||
1013 | int status = 0; | ||
1014 | struct stat st; | ||
1015 | (void)msgno; | ||
1016 | |||
1017 | if (mhd == NULL) | ||
1018 | return EINVAL; | ||
1019 | |||
1020 | dir = opendir (mhd->name); | ||
1021 | if (!dir) | ||
1022 | return errno; | ||
1023 | |||
1024 | monitor_wrlock (mailbox->monitor); | ||
1025 | |||
1026 | #ifdef WITH_PTHREAD | ||
1027 | pthread_cleanup_push (mh_cleanup, (void *)mailbox); | ||
1028 | #endif | ||
1029 | |||
1030 | locker_lock (mailbox->locker); | ||
1031 | |||
1032 | /* Do actual work. */ | ||
1033 | |||
1034 | while ((entry = readdir (dir))) | ||
1035 | { | ||
1036 | char *namep; | ||
1037 | int attr_flags; | ||
1038 | size_t num; | ||
1039 | |||
1040 | attr_flags = 0; | ||
1041 | switch (entry->d_name[0]) | ||
1042 | { | ||
1043 | case '.': | ||
1044 | /* FIXME: .mh_sequences */ | ||
1045 | continue; | ||
1046 | case ',': | ||
1047 | continue; | ||
1048 | #if 0 | ||
1049 | attr_flags |= MU_ATTRIBUTE_DELETED; | ||
1050 | namep = entry->d_name+1; | ||
1051 | break; | ||
1052 | #endif | ||
1053 | case '0':case '1':case '2':case '3':case '4': | ||
1054 | case '5':case '6':case '7':case '8':case '9': | ||
1055 | namep = entry->d_name; | ||
1056 | break; | ||
1057 | default: | ||
1058 | /*FIXME: Invalid entry. Report? */ | ||
1059 | continue; | ||
1060 | } | ||
1061 | |||
1062 | num = strtoul (namep, &namep, 10); | ||
1063 | if (namep[0]) | ||
1064 | continue; | ||
1065 | |||
1066 | msg = _mh_get_message_seq (mhd, num); | ||
1067 | if (!msg) | ||
1068 | { | ||
1069 | msg = calloc (1, sizeof(*msg)); | ||
1070 | |||
1071 | msg->seq_number = num; | ||
1072 | msg->attr_flags = attr_flags; | ||
1073 | msg->deleted = attr_flags & MU_ATTRIBUTE_DELETED; | ||
1074 | |||
1075 | _mh_message_insert (mhd, msg); | ||
1076 | |||
1077 | } | ||
1078 | else | ||
1079 | { | ||
1080 | msg->attr_flags = attr_flags; | ||
1081 | } | ||
1082 | } | ||
1083 | |||
1084 | closedir (dir); | ||
1085 | |||
1086 | if (do_notify) | ||
1087 | for (msg = mhd->msg_head; msg; msg = msg->next) | ||
1088 | { | ||
1089 | DISPATCH_ADD_MSG(mailbox, mhd); | ||
1090 | } | ||
1091 | |||
1092 | if (stat (mhd->name, &st) == 0) | ||
1093 | mhd->mtime = st.st_mtime; | ||
1094 | |||
1095 | if (pcount) | ||
1096 | *pcount = mhd->msg_count; | ||
1097 | |||
1098 | /* Reset the uidvalidity. */ | ||
1099 | if (mhd->msg_count > 0) | ||
1100 | { | ||
1101 | if (mhd->uidvalidity == 0) | ||
1102 | { | ||
1103 | mhd->uidvalidity = (unsigned long)time (NULL); | ||
1104 | /* FIXME mhd->uidnext = mhd->msg_count + 1;*/ | ||
1105 | /* Tell that we have been modified for expunging. */ | ||
1106 | if (mhd->msg_head) | ||
1107 | { | ||
1108 | mh_message_stream_open (mhd->msg_head); | ||
1109 | mh_message_stream_close (mhd->msg_head); | ||
1110 | mhd->msg_head->attr_flags |= MU_ATTRIBUTE_MODIFIED; | ||
1111 | } | ||
1112 | } | ||
1113 | } | ||
1114 | |||
1115 | /* Clean up the things */ | ||
1116 | |||
1117 | mh_cleanup (mailbox); | ||
1118 | #ifdef WITH_PTHREAD | ||
1119 | pthread_cleanup_pop (0); | ||
1120 | #endif | ||
1121 | return status; | ||
1122 | } | ||
1123 | |||
1124 | |||
1125 | static int | ||
1126 | mh_scan (mailbox_t mailbox, size_t msgno, size_t *pcount) | ||
1127 | { | ||
1128 | struct _mh_data *mhd = mailbox->data; | ||
1129 | |||
1130 | if (! mh_is_updated (mailbox)) | ||
1131 | return mh_scan0 (mailbox, msgno, pcount, 1); | ||
1132 | |||
1133 | if (pcount) | ||
1134 | *pcount = mhd->msg_count; | ||
1135 | |||
1136 | return 0; | ||
1137 | } | ||
1138 | |||
1139 | /* Is the internal representation of the mailbox up to date. | ||
1140 | Return 1 if so, 0 otherwise. */ | ||
1141 | static int | ||
1142 | mh_is_updated (mailbox_t mailbox) | ||
1143 | { | ||
1144 | struct stat st; | ||
1145 | struct _mh_data *mhd = mailbox->data; | ||
1146 | |||
1147 | if (!mhd->msg_head) | ||
1148 | return 0; | ||
1149 | |||
1150 | if (stat (mhd->name, &st) < 0) | ||
1151 | return 1; | ||
1152 | |||
1153 | return mhd->mtime == st.st_mtime; | ||
1154 | } | ||
1155 | |||
1156 | static int | ||
1157 | mh_get_size (mailbox_t mailbox, off_t *psize) | ||
1158 | { | ||
1159 | /*FIXME*/ | ||
1160 | (void)mailbox; | ||
1161 | (void)psize; | ||
1162 | return ENOSYS; | ||
1163 | } | ||
1164 | |||
1165 | /* Return number of open streams residing in a message pool */ | ||
1166 | static int | ||
1167 | mh_pool_open_count(struct _mh_data *mhd) | ||
1168 | { | ||
1169 | int cnt = mhd->pool_last - mhd->pool_first; | ||
1170 | if (cnt < 0) | ||
1171 | cnt += MAX_OPEN_STREAMS; | ||
1172 | return cnt; | ||
1173 | } | ||
1174 | |||
1175 | /* Look up a _mh_message in the pool of open messages. | ||
1176 | Returns 1 if the message is found in the pool, and 0 otherwise. */ | ||
1177 | static int | ||
1178 | mh_pool_lookup (struct _mh_message *mhm) | ||
1179 | { | ||
1180 | struct _mh_data *mhd = mhm->mhd; | ||
1181 | int i; | ||
1182 | |||
1183 | for (i = mhd->pool_first; i != mhd->pool_last; ) | ||
1184 | { | ||
1185 | if (mhd->msg_pool[i] == mhm) | ||
1186 | return 1; | ||
1187 | if (++i == MAX_OPEN_STREAMS) | ||
1188 | i = 0; | ||
1189 | } | ||
1190 | return 0; | ||
1191 | } | ||
1192 | |||
1193 | /* Open a stream associated with the message mhm. If the stream is | ||
1194 | already open, do nothing */ | ||
1195 | static int | ||
1196 | mh_pool_open (struct _mh_message *mhm) | ||
1197 | { | ||
1198 | struct _mh_data *mhd = mhm->mhd; | ||
1199 | if (mh_pool_lookup (mhm)) | ||
1200 | return 0; | ||
1201 | if (mh_pool_open_count(mhd) == MAX_OPEN_STREAMS-1) | ||
1202 | { | ||
1203 | mh_message_stream_close (mhd->msg_pool[mhd->pool_first++]); | ||
1204 | mhd->pool_first %= MAX_OPEN_STREAMS; | ||
1205 | } | ||
1206 | mh_message_stream_open (mhm); | ||
1207 | mhd->msg_pool[mhd->pool_last++] = mhm; | ||
1208 | mhd->pool_last %= MAX_OPEN_STREAMS; | ||
1209 | return 0; | ||
1210 | } | ||
1211 | |||
1212 | /* Attach a stream to a given message structure. The latter is supposed | ||
1213 | to be already added to the open message pool. */ | ||
1214 | int | ||
1215 | mh_message_stream_open (struct _mh_message *mhm) | ||
1216 | { | ||
1217 | struct _mh_data *mhd = mhm->mhd; | ||
1218 | char *filename = NULL; | ||
1219 | int status; | ||
1220 | int flags = MU_STREAM_ALLOW_LINKS; | ||
1221 | filename = _mh_message_name (mhm, mhm->deleted); | ||
1222 | |||
1223 | if (!filename) | ||
1224 | return ENOMEM; | ||
1225 | |||
1226 | /* The message should be at least readable */ | ||
1227 | if (mhd->mailbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND)) | ||
1228 | flags |= MU_STREAM_RDWR; | ||
1229 | else | ||
1230 | flags |= MU_STREAM_READ; | ||
1231 | status = file_stream_create (&mhm->stream, filename, flags); | ||
1232 | |||
1233 | free (filename); | ||
1234 | |||
1235 | if (status != 0) | ||
1236 | return status; | ||
1237 | |||
1238 | status = stream_open (mhm->stream); | ||
1239 | |||
1240 | if (status != 0) | ||
1241 | stream_destroy (&mhm->stream, NULL); | ||
1242 | |||
1243 | if (status == 0) | ||
1244 | status = mh_scan_message (mhm); | ||
1245 | |||
1246 | return status; | ||
1247 | } | ||
1248 | |||
1249 | /* Close the stream associated with the given message. */ | ||
1250 | void | ||
1251 | mh_message_stream_close (struct _mh_message *mhm) | ||
1252 | { | ||
1253 | if (mhm) | ||
1254 | { | ||
1255 | stream_close (mhm->stream); | ||
1256 | mhm->stream = NULL; | ||
1257 | } | ||
1258 | } | ||
1259 | |||
1260 | void | ||
1261 | mh_check_message (struct _mh_message *mhm) | ||
1262 | { | ||
1263 | if (mhm->body_end == 0) | ||
1264 | mh_pool_open (mhm); | ||
1265 | } | ||
1266 | |||
1267 | /* Reading functions */ | ||
1268 | |||
1269 | static int | ||
1270 | mh_readstream (struct _mh_message *mhm, char *buffer, size_t buflen, | ||
1271 | off_t off, size_t *pnread, int isreadline, | ||
1272 | off_t start, off_t end) | ||
1273 | { | ||
1274 | size_t nread = 0; | ||
1275 | int status = 0; | ||
1276 | off_t ln; | ||
1277 | |||
1278 | if (buffer == NULL || buflen == 0) | ||
1279 | { | ||
1280 | if (pnread) | ||
1281 | *pnread = nread; | ||
1282 | return 0; | ||
1283 | } | ||
1284 | |||
1285 | monitor_rdlock (mhm->mhd->mailbox->monitor); | ||
1286 | #ifdef WITH_PTHREAD | ||
1287 | /* read() is cancellation point since we're doing a potentially | ||
1288 | long operation. Lets make sure we clean the state. */ | ||
1289 | pthread_cleanup_push (mh_cleanup, (void *)mhm->mhd->mailbox); | ||
1290 | #endif | ||
1291 | |||
1292 | ln = end - (start + off); | ||
1293 | if (ln > 0) | ||
1294 | { | ||
1295 | /* Position the file pointer and the buffer. */ | ||
1296 | nread = ((size_t)ln < buflen) ? (size_t)ln : buflen; | ||
1297 | if (isreadline) | ||
1298 | status = stream_readline (mhm->stream, buffer, buflen, | ||
1299 | start + off, &nread); | ||
1300 | else | ||
1301 | status = stream_read (mhm->stream, buffer, nread, | ||
1302 | start + off, &nread); | ||
1303 | } | ||
1304 | |||
1305 | monitor_unlock (mhm->mhd->mailbox->monitor); | ||
1306 | #ifdef WITH_PTHREAD | ||
1307 | pthread_cleanup_pop (0); | ||
1308 | #endif | ||
1309 | |||
1310 | if (pnread) | ||
1311 | *pnread = nread; | ||
1312 | return status; | ||
1313 | } | ||
1314 | |||
1315 | static int | ||
1316 | mh_body_read (stream_t is, char *buffer, size_t buflen, off_t off, | ||
1317 | size_t *pnread) | ||
1318 | { | ||
1319 | body_t body = stream_get_owner (is); | ||
1320 | message_t msg = body_get_owner (body); | ||
1321 | struct _mh_message *mhm = message_get_owner (msg); | ||
1322 | mh_pool_open (mhm); | ||
1323 | return mh_readstream (mhm, buffer, buflen, off, pnread, 0, | ||
1324 | mhm->body_start, mhm->body_end); | ||
1325 | } | ||
1326 | |||
1327 | static int | ||
1328 | mh_body_readline (stream_t is, char *buffer, size_t buflen, | ||
1329 | off_t off, size_t *pnread) | ||
1330 | { | ||
1331 | body_t body = stream_get_owner (is); | ||
1332 | message_t msg = body_get_owner (body); | ||
1333 | struct _mh_message *mhm = message_get_owner (msg); | ||
1334 | mh_pool_open (mhm); | ||
1335 | return mh_readstream (mhm, buffer, buflen, off, pnread, 1, | ||
1336 | mhm->body_start, mhm->body_end); | ||
1337 | } | ||
1338 | |||
1339 | /* Return corresponding sizes */ | ||
1340 | |||
1341 | static int | ||
1342 | mh_stream_size (stream_t stream, off_t *psize) | ||
1343 | { | ||
1344 | body_t body = stream_get_owner (stream); | ||
1345 | return mh_body_size (body, (size_t*) psize); | ||
1346 | } | ||
1347 | |||
1348 | static int | ||
1349 | mh_body_size (body_t body, size_t *psize) | ||
1350 | { | ||
1351 | message_t msg = body_get_owner (body); | ||
1352 | struct _mh_message *mhm = message_get_owner (msg); | ||
1353 | if (mhm == NULL) | ||
1354 | return EINVAL; | ||
1355 | mh_check_message (mhm); | ||
1356 | if (psize) | ||
1357 | *psize = mhm->body_end - mhm->body_start; | ||
1358 | return 0; | ||
1359 | } | ||
1360 | |||
1361 | static int | ||
1362 | mh_body_lines (body_t body, size_t *plines) | ||
1363 | { | ||
1364 | message_t msg = body_get_owner (body); | ||
1365 | struct _mh_message *mhm = message_get_owner (msg); | ||
1366 | if (mhm == NULL) | ||
1367 | return EINVAL; | ||
1368 | mh_check_message (mhm); | ||
1369 | if (plines) | ||
1370 | *plines = mhm->body_lines; | ||
1371 | return 0; | ||
1372 | } | ||
1373 | |||
1374 | static int | ||
1375 | mh_message_uid (message_t msg, size_t *puid) | ||
1376 | { | ||
1377 | struct _mh_message *mhm = message_get_owner (msg); | ||
1378 | if (puid) | ||
1379 | *puid = mhm->seq_number; | ||
1380 | return 0; | ||
1381 | } | ||
1382 | |||
1383 | /* Headers */ | ||
1384 | static int | ||
1385 | mh_header_fill (header_t header, char *buffer, size_t len, | ||
1386 | off_t off, size_t *pnread) | ||
1387 | { | ||
1388 | message_t msg = header_get_owner (header); | ||
1389 | struct _mh_message *mhm = message_get_owner (msg); | ||
1390 | |||
1391 | mh_pool_open (mhm); | ||
1392 | return mh_readstream (mhm, buffer, len, off, pnread, 0, | ||
1393 | 0, mhm->body_start); | ||
1394 | } | ||
1395 | |||
1396 | static int | ||
1397 | mh_header_size (header_t header, size_t *psize) | ||
1398 | { | ||
1399 | message_t msg = header_get_owner (header); | ||
1400 | struct _mh_message *mhm = message_get_owner (msg); | ||
1401 | if (mhm == NULL) | ||
1402 | return EINVAL; | ||
1403 | mh_check_message (mhm); | ||
1404 | if (psize) | ||
1405 | *psize = mhm->body_start; | ||
1406 | return 0; | ||
1407 | } | ||
1408 | |||
1409 | static int | ||
1410 | mh_header_lines (header_t header, size_t *plines) | ||
1411 | { | ||
1412 | message_t msg = header_get_owner (header); | ||
1413 | struct _mh_message *mhm = message_get_owner (msg); | ||
1414 | if (mhm == NULL) | ||
1415 | return EINVAL; | ||
1416 | mh_check_message (mhm); | ||
1417 | if (plines) | ||
1418 | *plines = mhm->header_lines; | ||
1419 | return 0; | ||
1420 | } | ||
1421 | |||
1422 | /* Attributes */ | ||
1423 | static int | ||
1424 | mh_get_attr_flags (attribute_t attr, int *pflags) | ||
1425 | { | ||
1426 | message_t msg = attribute_get_owner (attr); | ||
1427 | struct _mh_message *mhm = message_get_owner (msg); | ||
1428 | |||
1429 | if (mhm == NULL) | ||
1430 | return EINVAL; | ||
1431 | if (pflags) | ||
1432 | *pflags = mhm->attr_flags; | ||
1433 | return 0; | ||
1434 | } | ||
1435 | |||
1436 | static int | ||
1437 | mh_set_attr_flags (attribute_t attr, int flags) | ||
1438 | { | ||
1439 | message_t msg = attribute_get_owner (attr); | ||
1440 | struct _mh_message *mhm = message_get_owner (msg); | ||
1441 | |||
1442 | if (mhm == NULL) | ||
1443 | return EINVAL; | ||
1444 | mhm->attr_flags |= flags; | ||
1445 | return 0; | ||
1446 | } | ||
1447 | |||
1448 | static int | ||
1449 | mh_unset_attr_flags (attribute_t attr, int flags) | ||
1450 | { | ||
1451 | message_t msg = attribute_get_owner (attr); | ||
1452 | struct _mh_message *mhm = message_get_owner (msg); | ||
1453 | |||
1454 | if (mhm == NULL) | ||
1455 | return EINVAL; | ||
1456 | mhm->attr_flags &= ~flags; | ||
1457 | return 0; | ||
1458 | } | ||
1459 | |||
1460 | /* Envelope */ | ||
1461 | static int | ||
1462 | mh_envelope_date (envelope_t envelope, char *buf, size_t len, | ||
1463 | size_t *psize) | ||
1464 | { | ||
1465 | message_t msg = envelope_get_owner (envelope); | ||
1466 | struct _mh_message *mhm = message_get_owner (msg); | ||
1467 | header_t hdr = NULL; | ||
1468 | char *from; | ||
1469 | int status; | ||
1470 | |||
1471 | if (mhm == NULL) | ||
1472 | return EINVAL; | ||
1473 | |||
1474 | if ((status = message_get_header (msg, &hdr)) != 0) | ||
1475 | return status; | ||
1476 | if (header_aget_value (hdr, MU_HEADER_ENV_DATE, &from)) | ||
1477 | return ENOSYS; | ||
1478 | |||
1479 | /* Format: "sender date" */ | ||
1480 | if (buf && len > 0) | ||
1481 | { | ||
1482 | len--; /* Leave space for the null. */ | ||
1483 | strncpy (buf, from, len); | ||
1484 | if (strlen (from) < len) | ||
1485 | { | ||
1486 | len = strlen (buf); | ||
1487 | buf[len++] = '\n'; | ||
1488 | } | ||
1489 | buf[len] = '\0'; | ||
1490 | } | ||
1491 | else | ||
1492 | len = 0; | ||
1493 | |||
1494 | if (psize) | ||
1495 | *psize = len; | ||
1496 | return 0; | ||
1497 | } | ||
1498 | |||
1499 | static int | ||
1500 | mh_envelope_sender (envelope_t envelope, char *buf, size_t len, size_t *psize) | ||
1501 | { | ||
1502 | message_t msg = envelope_get_owner (envelope); | ||
1503 | struct _mh_message *mhm = message_get_owner (msg); | ||
1504 | header_t hdr = NULL; | ||
1505 | char *from; | ||
1506 | int status; | ||
1507 | |||
1508 | if (mhm == NULL) | ||
1509 | return EINVAL; | ||
1510 | |||
1511 | if ((status = message_get_header (msg, &hdr)) != 0) | ||
1512 | return status; | ||
1513 | if (header_aget_value (hdr, MU_HEADER_ENV_SENDER, &from)) | ||
1514 | return ENOSYS; | ||
1515 | |||
1516 | if (buf && len > 0) | ||
1517 | { | ||
1518 | int slen = strlen (from); | ||
1519 | |||
1520 | if (len < slen + 1) | ||
1521 | slen = len - 1; | ||
1522 | memcpy (buf, from, slen); | ||
1523 | buf[slen] = 0; | ||
1524 | } | ||
1525 | else | ||
1526 | len = 0; | ||
1527 | |||
1528 | if (psize) | ||
1529 | *psize = len; | ||
1530 | return 0; | ||
1531 | } | ||
1532 | |||
1533 | #endif |
mailbox/mbx_pop.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef ENABLE_POP | ||
23 | |||
24 | #include <termios.h> | ||
25 | #include <errno.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <stdio.h> | ||
28 | #include <string.h> | ||
29 | #include <sys/time.h> | ||
30 | #include <sys/types.h> | ||
31 | #include <unistd.h> | ||
32 | #include <fcntl.h> | ||
33 | #include <stdarg.h> | ||
34 | |||
35 | #ifdef HAVE_ALLOCA_H | ||
36 | # include <alloca.h> | ||
37 | #endif | ||
38 | |||
39 | #ifdef HAVE_STRINGS_H | ||
40 | # include <strings.h> | ||
41 | #endif | ||
42 | |||
43 | #include <md5.h> | ||
44 | |||
45 | #include <mailutils/attribute.h> | ||
46 | #include <mailutils/auth.h> | ||
47 | #include <mailutils/body.h> | ||
48 | #include <mailutils/debug.h> | ||
49 | #include <mailutils/errno.h> | ||
50 | #include <mailutils/error.h> | ||
51 | #include <mailutils/header.h> | ||
52 | #include <mailutils/message.h> | ||
53 | #include <mailutils/observer.h> | ||
54 | #include <mailutils/property.h> | ||
55 | #include <mailutils/stream.h> | ||
56 | #include <mailutils/url.h> | ||
57 | |||
58 | #include <folder0.h> | ||
59 | #include <mailbox0.h> | ||
60 | #include <registrar0.h> | ||
61 | #include <url0.h> | ||
62 | |||
63 | #define PROP_RFC822 1 | ||
64 | |||
65 | /* Advance declarations. */ | ||
66 | struct _pop_data; | ||
67 | struct _pop_message; | ||
68 | |||
69 | typedef struct _pop_data * pop_data_t; | ||
70 | typedef struct _pop_message * pop_message_t; | ||
71 | |||
72 | /* The different possible states of a Pop client, Note that POP3 is not | ||
73 | reentrant i.e. it is only one channel, so it is not possible to start | ||
74 | Another operation while one is running. The only resort is to close the | ||
75 | connection and reopen it again. This is what we do, the downside is that | ||
76 | the client as to get the authentication again user/pass. */ | ||
77 | enum pop_state | ||
78 | { | ||
79 | POP_NO_STATE, POP_STATE_DONE, | ||
80 | POP_OPEN_CONNECTION, | ||
81 | POP_GREETINGS, | ||
82 | POP_APOP, POP_APOP_ACK, | ||
83 | POP_DELE, POP_DELE_ACK, | ||
84 | POP_LIST, POP_LIST_ACK, POP_LIST_RX, | ||
85 | POP_QUIT, POP_QUIT_ACK, | ||
86 | POP_NOOP, POP_NOOP_ACK, | ||
87 | POP_RETR, POP_RETR_ACK, POP_RETR_RX_HDR, POP_RETR_RX_BODY, | ||
88 | POP_RSET, POP_RSET_ACK, | ||
89 | POP_STAT, POP_STAT_ACK, | ||
90 | POP_TOP, POP_TOP_ACK, POP_TOP_RX, | ||
91 | POP_UIDL, POP_UIDL_ACK, | ||
92 | POP_AUTH, POP_AUTH_DONE, | ||
93 | POP_AUTH_USER, POP_AUTH_USER_ACK, | ||
94 | POP_AUTH_PASS, POP_AUTH_PASS_ACK | ||
95 | }; | ||
96 | |||
97 | static void pop_destroy __P ((mailbox_t)); | ||
98 | |||
99 | /* Functions/Methods that implements the mailbox_t API. */ | ||
100 | static int pop_open __P ((mailbox_t, int)); | ||
101 | static int pop_close __P ((mailbox_t)); | ||
102 | static int pop_get_message __P ((mailbox_t, size_t, message_t *)); | ||
103 | static int pop_messages_count __P ((mailbox_t, size_t *)); | ||
104 | static int pop_messages_recent __P ((mailbox_t, size_t *)); | ||
105 | static int pop_message_unseen __P ((mailbox_t, size_t *)); | ||
106 | static int pop_expunge __P ((mailbox_t)); | ||
107 | static int pop_scan __P ((mailbox_t, size_t, size_t *)); | ||
108 | static int pop_is_updated __P ((mailbox_t)); | ||
109 | |||
110 | /* The implementation of message_t */ | ||
111 | int _pop_user __P ((authority_t)); | ||
112 | int _pop_apop __P ((authority_t)); | ||
113 | static int pop_get_size __P ((mailbox_t, off_t *)); | ||
114 | /* We use pop_top for retreiving headers. */ | ||
115 | /* static int pop_header_read (header_t, char *, size_t, off_t, size_t *); */ | ||
116 | static int pop_body_fd __P ((stream_t, int *)); | ||
117 | static int pop_body_size __P ((body_t, size_t *)); | ||
118 | static int pop_body_lines __P ((body_t, size_t *)); | ||
119 | static int pop_body_read __P ((stream_t, char *, size_t, off_t, size_t *)); | ||
120 | static int pop_message_read __P ((stream_t, char *, size_t, off_t, size_t *)); | ||
121 | static int pop_message_size __P ((message_t, size_t *)); | ||
122 | static int pop_message_fd __P ((stream_t, int *)); | ||
123 | static int pop_top __P ((header_t, char *, size_t, off_t, size_t *)); | ||
124 | static int pop_retr __P ((pop_message_t, char *, size_t, off_t, size_t *)); | ||
125 | static int pop_get_fd __P ((pop_message_t, int *)); | ||
126 | static int pop_get_attribute __P ((attribute_t, int *)); | ||
127 | static int pop_set_attribute __P ((attribute_t, int)); | ||
128 | static int pop_unset_attribute __P ((attribute_t, int)); | ||
129 | static int pop_uidl __P ((message_t, char *, size_t, size_t *)); | ||
130 | static int pop_uid __P ((message_t, size_t *)); | ||
131 | static int fill_buffer __P ((pop_data_t, char *, size_t)); | ||
132 | static int pop_sleep __P ((int)); | ||
133 | static int pop_readline __P ((pop_data_t)); | ||
134 | static int pop_read_ack __P ((pop_data_t)); | ||
135 | static int pop_writeline __P ((pop_data_t, const char *, ...)); | ||
136 | static int pop_write __P ((pop_data_t)); | ||
137 | static int pop_get_user __P ((authority_t)); | ||
138 | static int pop_get_passwd __P ((authority_t)); | ||
139 | static char *pop_get_timestamp __P ((pop_data_t)); | ||
140 | static int pop_get_md5 __P ((pop_data_t)); | ||
141 | |||
142 | /* This structure holds the info for a message. The pop_message_t | ||
143 | type, will serve as the owner of the message_t and contains the command to | ||
144 | send to "RETR"eive the specify message. The problem comes from the header. | ||
145 | If the POP server supports TOP, we can cleanly fetch the header. | ||
146 | But otherwise we use the clumsy approach. .i.e for the header we read 'til | ||
147 | ^\n then discard the rest, for the body we read after ^\n and discard the | ||
148 | beginning. This is a waste, Pop was not conceive for this obviously. */ | ||
149 | struct _pop_message | ||
150 | { | ||
151 | int inbody; | ||
152 | int skip_header; | ||
153 | int skip_body; | ||
154 | size_t body_size; | ||
155 | size_t header_size; | ||
156 | size_t body_lines; | ||
157 | size_t header_lines; | ||
158 | size_t message_size; | ||
159 | size_t num; | ||
160 | char *uidl; /* Cache the uidl string. */ | ||
161 | int attr_flags; | ||
162 | message_t message; | ||
163 | pop_data_t mpd; /* Back pointer. */ | ||
164 | }; | ||
165 | |||
166 | /* Structure to hold things general to the POP mailbox, like its state, how | ||
167 | many messages we have so far etc ... */ | ||
168 | struct _pop_data | ||
169 | { | ||
170 | void *func; /* Indicate a command is in operation, busy. */ | ||
171 | size_t id; /* A second level of distincion, we maybe in the same function | ||
172 | but working on a different message. */ | ||
173 | enum pop_state state; | ||
174 | pop_message_t *pmessages; | ||
175 | size_t pmessages_count; | ||
176 | size_t messages_count; | ||
177 | size_t size; | ||
178 | |||
179 | /* Working I/O buffers. */ | ||
180 | char *buffer; | ||
181 | size_t buflen; /* Len of buffer. */ | ||
182 | char *ptr; /* Points to the end of the buffer i.e the non consume chars. */ | ||
183 | char *nl; /* Points to the '\n' char in te string. */ | ||
184 | off_t offset; /* Dummy, this is use because of the stream buffering. | ||
185 | The stream_t maintains and offset and the offset we use must | ||
186 | be in sync. */ | ||
187 | |||
188 | int is_updated; | ||
189 | char *user; /* Temporary holders for user and passwd. */ | ||
190 | char *passwd; /* Temporary holders for passwd memset (0) when finish. */ | ||
191 | mailbox_t mbox; /* Back pointer. */ | ||
192 | } ; | ||
193 | |||
194 | /* Usefull little Macros, since these are very repetitive. */ | ||
195 | |||
196 | /* Check if we're busy ? */ | ||
197 | /* POP is a one channel download protocol, so if someone | ||
198 | is trying to execute a command while another is running | ||
199 | something is seriously incorrect, So the best course | ||
200 | of action is to close down the connection and start a new one. | ||
201 | For example mime_t only reads part of the message. If a client | ||
202 | wants to read different part of the message via mime it should | ||
203 | download it first. POP does not have the features of IMAP for | ||
204 | multipart messages. | ||
205 | Let see a concrete example: | ||
206 | { | ||
207 | mailbox_t mbox; message_t msg; stream_t stream; char buffer[105]; | ||
208 | mailbox_create (&mbox, "pop://qnx.com"); | ||
209 | mailbox_get_message (mbox, 1, &msg); | ||
210 | message_get_stream (msg, &stream); | ||
211 | while (stream_readline (stream, buffer, sizeof(buffer), NULL) != 0) { ..} | ||
212 | } | ||
213 | if in the while of the readline, one try to get another email. The pop | ||
214 | server will get seriously confused, and the second message will still | ||
215 | be the first one, There is no way to tell POP servers yo! stop/abort. | ||
216 | The approach is to close the stream and reopen again. So every time | ||
217 | we go in to a function our state is preserve by the triplets | ||
218 | mpd->{func,state,id}. The macro CHECK_BUSY checks if we are not | ||
219 | in another operation if not you get access if yes the stream is close | ||
220 | and pop_open() is recall again for a new connection. | ||
221 | */ | ||
222 | #define CHECK_BUSY(mbox, mpd, function, identity) \ | ||
223 | do \ | ||
224 | { \ | ||
225 | int err = monitor_wrlock (mbox->monitor); \ | ||
226 | if (err != 0) \ | ||
227 | return err; \ | ||
228 | if ((mpd->func && mpd->func != function) \ | ||
229 | || (mpd->id && mpd->id != (size_t)identity)) \ | ||
230 | { \ | ||
231 | mpd->id = 0; \ | ||
232 | mpd->func = (void *)pop_open; \ | ||
233 | mpd->state = POP_NO_STATE; \ | ||
234 | monitor_unlock (mbox->monitor); \ | ||
235 | err = pop_open (mbox, mbox->flags); \ | ||
236 | if (err != 0) \ | ||
237 | { \ | ||
238 | return err; \ | ||
239 | } \ | ||
240 | } \ | ||
241 | else \ | ||
242 | { \ | ||
243 | mpd->id = (size_t)identity; \ | ||
244 | mpd->func = func; \ | ||
245 | monitor_unlock (mbox->monitor); \ | ||
246 | } \ | ||
247 | } \ | ||
248 | while (0) | ||
249 | |||
250 | /* Clear the state. */ | ||
251 | #define CLEAR_STATE(mpd) \ | ||
252 | mpd->id = 0, mpd->func = NULL, mpd->state = POP_NO_STATE | ||
253 | |||
254 | /* Clear the state and close the stream. */ | ||
255 | #define CHECK_ERROR_CLOSE(mbox, mpd, status) \ | ||
256 | do \ | ||
257 | { \ | ||
258 | if (status != 0) \ | ||
259 | { \ | ||
260 | stream_close (mbox->stream); \ | ||
261 | CLEAR_STATE (mpd); \ | ||
262 | mpd->func = (void *)-1; \ | ||
263 | MAILBOX_DEBUG1(mbox, MU_DEBUG_PROT, "CHECK_ERROR_CLOSE: %s\n", mu_strerror (status));\ | ||
264 | return status; \ | ||
265 | } \ | ||
266 | } \ | ||
267 | while (0) | ||
268 | |||
269 | /* If error, clear the state and return. */ | ||
270 | #define CHECK_ERROR(mpd, status) \ | ||
271 | do \ | ||
272 | { \ | ||
273 | if (status != 0) \ | ||
274 | { \ | ||
275 | CLEAR_STATE (mpd); \ | ||
276 | mpd->func = (void*)-1; \ | ||
277 | MAILBOX_DEBUG1(mpd->mbox, MU_DEBUG_PROT, "CHECK_ERROR: %s\n", mu_strerror (status));\ | ||
278 | return status; \ | ||
279 | } \ | ||
280 | } \ | ||
281 | while (0) | ||
282 | |||
283 | /* Clear the state for non recoverable error. */ | ||
284 | #define CHECK_EAGAIN(mpd, status) \ | ||
285 | do \ | ||
286 | { \ | ||
287 | if (status != 0) \ | ||
288 | { \ | ||
289 | if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \ | ||
290 | { \ | ||
291 | CLEAR_STATE (mpd); \ | ||
292 | mpd->func = (void *)-1; \ | ||
293 | MAILBOX_DEBUG1(mpd->mbox, MU_DEBUG_PROT, "CHECK_EAGAIN: %s\n", mu_strerror (status));\ | ||
294 | } \ | ||
295 | return status; \ | ||
296 | } \ | ||
297 | } \ | ||
298 | while (0) | ||
299 | |||
300 | |||
301 | /* Allocate mailbox_t, allocate pop internal structures. */ | ||
302 | int | ||
303 | _mailbox_pop_init (mailbox_t mbox) | ||
304 | { | ||
305 | pop_data_t mpd; | ||
306 | int status = 0; | ||
307 | |||
308 | /* Allocate specifics for pop data. */ | ||
309 | mpd = mbox->data = calloc (1, sizeof (*mpd)); | ||
310 | if (mbox->data == NULL) | ||
311 | return ENOMEM; | ||
312 | |||
313 | mpd->mbox = mbox; /* Back pointer. */ | ||
314 | |||
315 | mpd->state = POP_NO_STATE; /* Init with no state. */ | ||
316 | |||
317 | /* Initialize the structure. */ | ||
318 | mbox->_destroy = pop_destroy; | ||
319 | |||
320 | mbox->_open = pop_open; | ||
321 | mbox->_close = pop_close; | ||
322 | |||
323 | /* Messages. */ | ||
324 | mbox->_get_message = pop_get_message; | ||
325 | mbox->_messages_count = pop_messages_count; | ||
326 | mbox->_messages_recent = pop_messages_recent; | ||
327 | mbox->_message_unseen = pop_message_unseen; | ||
328 | mbox->_expunge = pop_expunge; | ||
329 | |||
330 | mbox->_scan = pop_scan; | ||
331 | mbox->_is_updated = pop_is_updated; | ||
332 | |||
333 | mbox->_get_size = pop_get_size; | ||
334 | |||
335 | /* Set our properties. */ | ||
336 | { | ||
337 | property_t property = NULL; | ||
338 | mailbox_get_property (mbox, &property); | ||
339 | property_set_value (property, "TYPE", "POP3", 1); | ||
340 | } | ||
341 | |||
342 | /* Hack! POP does not really have a folder. */ | ||
343 | mbox->folder->data = mbox; | ||
344 | |||
345 | return status; | ||
346 | } | ||
347 | |||
348 | /* Cleaning up all the ressources associate with a pop mailbox. */ | ||
349 | static void | ||
350 | pop_destroy (mailbox_t mbox) | ||
351 | { | ||
352 | if (mbox->data) | ||
353 | { | ||
354 | pop_data_t mpd = mbox->data; | ||
355 | size_t i; | ||
356 | monitor_wrlock (mbox->monitor); | ||
357 | /* Destroy the pop messages and ressources associated to them. */ | ||
358 | for (i = 0; i < mpd->pmessages_count; i++) | ||
359 | { | ||
360 | if (mpd->pmessages[i]) | ||
361 | { | ||
362 | message_destroy (&(mpd->pmessages[i]->message), | ||
363 | mpd->pmessages[i]); | ||
364 | if (mpd->pmessages[i]->uidl) | ||
365 | free (mpd->pmessages[i]->uidl); | ||
366 | free (mpd->pmessages[i]); | ||
367 | mpd->pmessages[i] = NULL; | ||
368 | } | ||
369 | } | ||
370 | if (mpd->buffer) | ||
371 | free (mpd->buffer); | ||
372 | if (mpd->pmessages) | ||
373 | free (mpd->pmessages); | ||
374 | free (mpd); | ||
375 | mbox->data = NULL; | ||
376 | monitor_unlock (mbox->monitor); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | /* Simple User/pass authentication for pop. We ask for the info | ||
381 | from the standard input. */ | ||
382 | int | ||
383 | _pop_user (authority_t auth) | ||
384 | { | ||
385 | folder_t folder = authority_get_owner (auth); | ||
386 | mailbox_t mbox = folder->data; | ||
387 | pop_data_t mpd = mbox->data; | ||
388 | int status; | ||
389 | |||
390 | switch (mpd->state) | ||
391 | { | ||
392 | case POP_AUTH: | ||
393 | /* Fetch the user from them. */ | ||
394 | status = pop_get_user (auth); | ||
395 | if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0') | ||
396 | { | ||
397 | CHECK_ERROR_CLOSE (mbox, mpd, EINVAL); | ||
398 | } | ||
399 | status = pop_writeline (mpd, "USER %s\r\n", mpd->user); | ||
400 | CHECK_ERROR_CLOSE(mbox, mpd, status); | ||
401 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
402 | free (mpd->user); | ||
403 | mpd->user = NULL; | ||
404 | mpd->state = POP_AUTH_USER; | ||
405 | |||
406 | case POP_AUTH_USER: | ||
407 | /* Send username. */ | ||
408 | status = pop_write (mpd); | ||
409 | CHECK_EAGAIN (mpd, status); | ||
410 | mpd->state = POP_AUTH_USER_ACK; | ||
411 | |||
412 | case POP_AUTH_USER_ACK: | ||
413 | /* Get the user ack. */ | ||
414 | status = pop_read_ack (mpd); | ||
415 | CHECK_EAGAIN (mpd, status); | ||
416 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
417 | if (strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
418 | { | ||
419 | observable_t observable = NULL; | ||
420 | mailbox_get_observable (mbox, &observable); | ||
421 | CLEAR_STATE (mpd); | ||
422 | observable_notify (observable, MU_EVT_AUTHORITY_FAILED); | ||
423 | CHECK_ERROR_CLOSE (mbox, mpd, EACCES); | ||
424 | } | ||
425 | status = pop_get_passwd (auth); | ||
426 | if (status != 0 || mpd->passwd == NULL || mpd->passwd[0] == '\0') | ||
427 | { | ||
428 | CHECK_ERROR_CLOSE (mbox, mpd, EINVAL); | ||
429 | } | ||
430 | status = pop_writeline (mpd, "PASS %s\r\n", mpd->passwd); | ||
431 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
432 | /* Leave not trail of the passwd. */ | ||
433 | memset (mpd->passwd, '\0', strlen (mpd->passwd)); | ||
434 | free (mpd->passwd); | ||
435 | mpd->passwd = NULL; | ||
436 | CHECK_ERROR_CLOSE (mbox, mpd, status); | ||
437 | mpd->state = POP_AUTH_PASS; | ||
438 | |||
439 | case POP_AUTH_PASS: | ||
440 | /* Send passwd. */ | ||
441 | status = pop_write (mpd); | ||
442 | CHECK_EAGAIN (mpd, status); | ||
443 | /* Clear the buffer it contains the passwd. */ | ||
444 | memset (mpd->buffer, '\0', mpd->buflen); | ||
445 | mpd->state = POP_AUTH_PASS_ACK; | ||
446 | |||
447 | case POP_AUTH_PASS_ACK: | ||
448 | /* Get the ack from passwd. */ | ||
449 | status = pop_read_ack (mpd); | ||
450 | CHECK_EAGAIN (mpd, status); | ||
451 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
452 | if (strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
453 | { | ||
454 | observable_t observable = NULL; | ||
455 | mailbox_get_observable (mbox, &observable); | ||
456 | CLEAR_STATE (mpd); | ||
457 | observable_notify (observable, MU_EVT_AUTHORITY_FAILED); | ||
458 | CHECK_ERROR_CLOSE (mbox, mpd, EACCES); | ||
459 | } | ||
460 | mpd->state = POP_AUTH_DONE; | ||
461 | break; /* We're outta here. */ | ||
462 | |||
463 | default: | ||
464 | break; | ||
465 | } | ||
466 | CLEAR_STATE (mpd); | ||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | int | ||
471 | _pop_apop (authority_t auth) | ||
472 | { | ||
473 | folder_t folder = authority_get_owner (auth); | ||
474 | mailbox_t mbox = folder->data; | ||
475 | pop_data_t mpd = mbox->data; | ||
476 | int status; | ||
477 | |||
478 | switch (mpd->state) | ||
479 | { | ||
480 | case POP_AUTH: | ||
481 | /* Fetch the user from them. */ | ||
482 | status = pop_get_user (auth); | ||
483 | if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0') | ||
484 | { | ||
485 | CHECK_ERROR_CLOSE (mbox, mpd, EINVAL); | ||
486 | } | ||
487 | |||
488 | /* Fetch the secret from them. */ | ||
489 | status = pop_get_passwd (auth); | ||
490 | if (status != 0 || mpd->passwd == NULL || mpd->passwd[0] == '\0') | ||
491 | { | ||
492 | CHECK_ERROR_CLOSE (mbox, mpd, EINVAL); | ||
493 | } | ||
494 | |||
495 | /* Make the MD5 digest string. */ | ||
496 | status = pop_get_md5 (mpd); | ||
497 | if (status != 0) | ||
498 | { | ||
499 | CHECK_ERROR_CLOSE (mbox, mpd, status); | ||
500 | } | ||
501 | status = pop_writeline (mpd, "APOP %s %s\r\n", mpd->user, mpd->passwd); | ||
502 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
503 | /* We have to obscure the md5 string. */ | ||
504 | memset (mpd->passwd, '\0', strlen (mpd->passwd)); | ||
505 | free (mpd->user); | ||
506 | free (mpd->passwd); | ||
507 | mpd->user = NULL; | ||
508 | mpd->passwd = NULL; | ||
509 | CHECK_ERROR_CLOSE (mbox, mpd, status); | ||
510 | mpd->state = POP_APOP; | ||
511 | |||
512 | case POP_APOP: | ||
513 | /* Send apop. */ | ||
514 | status = pop_write (mpd); | ||
515 | CHECK_EAGAIN (mpd, status); | ||
516 | /* Clear the buffer it contains the md5. */ | ||
517 | memset (mpd->buffer, '\0', mpd->buflen); | ||
518 | mpd->state = POP_APOP_ACK; | ||
519 | |||
520 | case POP_APOP_ACK: | ||
521 | status = pop_read_ack (mpd); | ||
522 | CHECK_EAGAIN (mpd, status); | ||
523 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
524 | if (strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
525 | { | ||
526 | observable_t observable = NULL; | ||
527 | mailbox_get_observable (mbox, &observable); | ||
528 | CLEAR_STATE (mpd); | ||
529 | observable_notify (observable, MU_EVT_AUTHORITY_FAILED); | ||
530 | CHECK_ERROR_CLOSE (mbox, mpd, EACCES); | ||
531 | } | ||
532 | mpd->state = POP_AUTH_DONE; | ||
533 | break; /* We're outta here. */ | ||
534 | |||
535 | default: | ||
536 | break; | ||
537 | } | ||
538 | CLEAR_STATE (mpd); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | |||
543 | /* Open the connection to the sever, and send the authentication. | ||
544 | FIXME: Should also send the CAPA command to detect for example the suport | ||
545 | for TOP, APOP, ... and DTRT(Do The Right Thing). */ | ||
546 | static int | ||
547 | pop_open (mailbox_t mbox, int flags) | ||
548 | { | ||
549 | pop_data_t mpd = mbox->data; | ||
550 | int status; | ||
551 | char *host; | ||
552 | size_t hostlen = 0; | ||
553 | long port = 110; | ||
554 | |||
555 | /* Sanity checks. */ | ||
556 | if (mpd == NULL) | ||
557 | return EINVAL; | ||
558 | |||
559 | /* Fetch the pop server name and the port in the url_t. */ | ||
560 | status = url_get_host (mbox->url, NULL, 0, &hostlen); | ||
561 | if (status != 0) | ||
562 | return status; | ||
563 | host = alloca (hostlen + 1); | ||
564 | url_get_host (mbox->url, host, hostlen + 1, NULL); | ||
565 | url_get_port (mbox->url, &port); | ||
566 | |||
567 | mbox->flags = flags; | ||
568 | |||
569 | /* Do not check for reconnect here. */ | ||
570 | /* CHECK_BUSY (mbox, mpd, func, 0); */ | ||
571 | |||
572 | /* Enter the pop state machine, and boogy: AUTHORISATION State. */ | ||
573 | switch (mpd->state) | ||
574 | { | ||
575 | case POP_NO_STATE: | ||
576 | /* Allocate a working io buffer. */ | ||
577 | if (mpd->buffer == NULL) | ||
578 | { | ||
579 | /* 255 is the limit lenght of a POP3 command according to RFCs. */ | ||
580 | mpd->buflen = 255; | ||
581 | mpd->buffer = calloc (mpd->buflen + 1, sizeof (char)); | ||
582 | if (mpd->buffer == NULL) | ||
583 | { | ||
584 | CHECK_ERROR (mpd, ENOMEM); | ||
585 | } | ||
586 | } | ||
587 | else | ||
588 | { | ||
589 | /* Clear any residual from a previous connection. */ | ||
590 | memset (mpd->buffer, '\0', mpd->buflen); | ||
591 | } | ||
592 | mpd->ptr = mpd->buffer; | ||
593 | |||
594 | /* Create the networking stack. */ | ||
595 | if (mbox->stream == NULL) | ||
596 | { | ||
597 | status = tcp_stream_create (&mbox->stream, host, port, mbox->flags); | ||
598 | CHECK_ERROR(mpd, status); | ||
599 | /* Using the awkward stream_t buffering. */ | ||
600 | stream_setbufsiz (mbox->stream, BUFSIZ); | ||
601 | } | ||
602 | else | ||
603 | { | ||
604 | /* This is sudden death: for many pop servers, it is important to | ||
605 | let them time to remove locks or move the .user.pop files. This | ||
606 | happen when we do BUSY_CHECK(). For example, the user does not | ||
607 | want to read the entire file, and wants start to read a new | ||
608 | message, closing the connection and immediately contact the | ||
609 | server again, and we'll end up having "-ERR Mail Lock busy" or | ||
610 | something similar. To prevent this race condition we sleep 2 | ||
611 | seconds. */ | ||
612 | stream_close (mbox->stream); | ||
613 | pop_sleep (2); | ||
614 | } | ||
615 | mpd->state = POP_OPEN_CONNECTION; | ||
616 | |||
617 | case POP_OPEN_CONNECTION: | ||
618 | /* Establish the connection. */ | ||
619 | MAILBOX_DEBUG2 (mbox, MU_DEBUG_PROT, "open (%s:%d)\n", host, port); | ||
620 | status = stream_open (mbox->stream); | ||
621 | CHECK_EAGAIN (mpd, status); | ||
622 | /* Can't recover bailout. */ | ||
623 | CHECK_ERROR_CLOSE (mbox, mpd, status); | ||
624 | mpd->state = POP_GREETINGS; | ||
625 | |||
626 | case POP_GREETINGS: | ||
627 | { | ||
628 | /* Swallow the greetings. */ | ||
629 | status = pop_read_ack (mpd); | ||
630 | CHECK_EAGAIN (mpd, status); | ||
631 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
632 | if (strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
633 | { | ||
634 | CHECK_ERROR_CLOSE (mbox, mpd, EACCES); | ||
635 | } | ||
636 | mpd->state = POP_AUTH; | ||
637 | } | ||
638 | |||
639 | case POP_AUTH: | ||
640 | case POP_AUTH_USER: | ||
641 | case POP_AUTH_USER_ACK: | ||
642 | case POP_AUTH_PASS: | ||
643 | case POP_AUTH_PASS_ACK: | ||
644 | case POP_APOP: | ||
645 | case POP_APOP_ACK: | ||
646 | /* Authenticate. */ | ||
647 | status = authority_authenticate (mbox->folder->authority); | ||
648 | CHECK_EAGAIN (mpd, status); | ||
649 | |||
650 | case POP_AUTH_DONE: | ||
651 | break; | ||
652 | |||
653 | default: | ||
654 | /* | ||
655 | mu_error ("pop_open unknown state\n"); | ||
656 | */ | ||
657 | break; | ||
658 | }/* End AUTHORISATION state. */ | ||
659 | |||
660 | /* Clear any state. */ | ||
661 | CLEAR_STATE (mpd); | ||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | /* Send the QUIT and close the socket. */ | ||
666 | static int | ||
667 | pop_close (mailbox_t mbox) | ||
668 | { | ||
669 | pop_data_t mpd = mbox->data; | ||
670 | void *func = (void *)pop_close; | ||
671 | int status; | ||
672 | size_t i; | ||
673 | |||
674 | if (mpd == NULL) | ||
675 | return EINVAL; | ||
676 | |||
677 | /* Should not check for Busy, we're shuting down anyway. */ | ||
678 | /* CHECK_BUSY (mbox, mpd, func, 0); */ | ||
679 | monitor_wrlock (mbox->monitor); | ||
680 | if (mpd->func && mpd->func != func) | ||
681 | mpd->state = POP_NO_STATE; | ||
682 | mpd->id = 0; | ||
683 | mpd->func = func; | ||
684 | monitor_unlock (mbox->monitor); | ||
685 | |||
686 | /* Ok boys, it's a wrap: UPDATE State. */ | ||
687 | switch (mpd->state) | ||
688 | { | ||
689 | case POP_NO_STATE: | ||
690 | /* Initiate the close. */ | ||
691 | status = pop_writeline (mpd, "QUIT\r\n"); | ||
692 | CHECK_ERROR (mpd, status); | ||
693 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
694 | mpd->state = POP_QUIT; | ||
695 | |||
696 | case POP_QUIT: | ||
697 | /* Send the quit. */ | ||
698 | status = pop_write (mpd); | ||
699 | CHECK_EAGAIN (mpd, status); | ||
700 | mpd->state = POP_QUIT_ACK; | ||
701 | |||
702 | case POP_QUIT_ACK: | ||
703 | /* Glob the acknowledge. */ | ||
704 | status = pop_read_ack (mpd); | ||
705 | CHECK_EAGAIN (mpd, status); | ||
706 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
707 | /* Now what ! and how can we tell them about errors ? So far now | ||
708 | lets just be verbose about the error but close the connection | ||
709 | anyway. */ | ||
710 | if (strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
711 | mu_error ("pop_close: %s\n", mpd->buffer); | ||
712 | stream_close (mbox->stream); | ||
713 | break; | ||
714 | |||
715 | default: | ||
716 | /* | ||
717 | mu_error ("pop_close unknow state"); | ||
718 | */ | ||
719 | break; | ||
720 | } /* UPDATE state. */ | ||
721 | |||
722 | /* Free the messages. */ | ||
723 | for (i = 0; i < mpd->pmessages_count; i++) | ||
724 | { | ||
725 | if (mpd->pmessages[i]) | ||
726 | { | ||
727 | message_destroy (&(mpd->pmessages[i]->message), | ||
728 | mpd->pmessages[i]); | ||
729 | if (mpd->pmessages[i]->uidl) | ||
730 | free (mpd->pmessages[i]->uidl); | ||
731 | free (mpd->pmessages[i]); | ||
732 | mpd->pmessages[i] = NULL; | ||
733 | } | ||
734 | } | ||
735 | /* And clear any residue. */ | ||
736 | if (mpd->pmessages) | ||
737 | free (mpd->pmessages); | ||
738 | mpd->pmessages = NULL; | ||
739 | mpd->pmessages_count = 0; | ||
740 | mpd->is_updated = 0; | ||
741 | if (mpd->buffer) | ||
742 | free (mpd->buffer); | ||
743 | mpd->buffer = NULL; | ||
744 | |||
745 | CLEAR_STATE (mpd); | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | /* Only build/setup the message_t structure for a mesgno. pop_message_t, | ||
750 | will act as the owner of messages. */ | ||
751 | static int | ||
752 | pop_get_message (mailbox_t mbox, size_t msgno, message_t *pmsg) | ||
753 | { | ||
754 | pop_data_t mpd = mbox->data; | ||
755 | message_t msg = NULL; | ||
756 | pop_message_t mpm; | ||
757 | int status; | ||
758 | size_t i; | ||
759 | |||
760 | /* Sanity. */ | ||
761 | if (pmsg == NULL || mpd == NULL || msgno > mpd->messages_count) | ||
762 | return EINVAL; | ||
763 | |||
764 | monitor_rdlock (mbox->monitor); | ||
765 | /* See if we have already this message. */ | ||
766 | for (i = 0; i < mpd->pmessages_count; i++) | ||
767 | { | ||
768 | if (mpd->pmessages[i]) | ||
769 | { | ||
770 | if (mpd->pmessages[i]->num == msgno) | ||
771 | { | ||
772 | *pmsg = mpd->pmessages[i]->message; | ||
773 | monitor_unlock (mbox->monitor); | ||
774 | return 0; | ||
775 | } | ||
776 | } | ||
777 | } | ||
778 | monitor_unlock (mbox->monitor); | ||
779 | |||
780 | mpm = calloc (1, sizeof (*mpm)); | ||
781 | if (mpm == NULL) | ||
782 | return ENOMEM; | ||
783 | |||
784 | /* Back pointer. */ | ||
785 | mpm->mpd = mpd; | ||
786 | mpm->num = msgno; | ||
787 | |||
788 | /* Create the message. */ | ||
789 | { | ||
790 | stream_t stream = NULL; | ||
791 | if ((status = message_create (&msg, mpm)) != 0 | ||
792 | || (status = stream_create (&stream, mbox->flags, msg)) != 0) | ||
793 | { | ||
794 | stream_destroy (&stream, msg); | ||
795 | message_destroy (&msg, mpm); | ||
796 | free (mpm); | ||
797 | return status; | ||
798 | } | ||
799 | /* Help for the readline()s */ | ||
800 | stream_setbufsiz (stream, 128); | ||
801 | stream_set_read (stream, pop_message_read, msg); | ||
802 | stream_set_fd (stream, pop_message_fd, msg); | ||
803 | message_set_stream (msg, stream, mpm); | ||
804 | message_set_size (msg, pop_message_size, mpm); | ||
805 | } | ||
806 | |||
807 | /* Create the header. */ | ||
808 | { | ||
809 | header_t header = NULL; | ||
810 | if ((status = header_create (&header, NULL, 0, msg)) != 0) | ||
811 | { | ||
812 | message_destroy (&msg, mpm); | ||
813 | free (mpm); | ||
814 | return status; | ||
815 | } | ||
816 | header_set_fill (header, pop_top, msg); | ||
817 | message_set_header (msg, header, mpm); | ||
818 | } | ||
819 | |||
820 | /* Create the attribute. */ | ||
821 | { | ||
822 | attribute_t attribute; | ||
823 | status = attribute_create (&attribute, msg); | ||
824 | if (status != 0) | ||
825 | { | ||
826 | message_destroy (&msg, mpm); | ||
827 | free (mpm); | ||
828 | return status; | ||
829 | } | ||
830 | attribute_set_get_flags (attribute, pop_get_attribute, msg); | ||
831 | attribute_set_set_flags (attribute, pop_set_attribute, msg); | ||
832 | attribute_set_unset_flags (attribute, pop_unset_attribute, msg); | ||
833 | message_set_attribute (msg, attribute, mpm); | ||
834 | } | ||
835 | |||
836 | /* Create the body and its stream. */ | ||
837 | { | ||
838 | body_t body = NULL; | ||
839 | stream_t stream = NULL; | ||
840 | if ((status = body_create (&body, msg)) != 0 | ||
841 | || (status = stream_create (&stream, mbox->flags, body)) != 0) | ||
842 | { | ||
843 | body_destroy (&body, msg); | ||
844 | stream_destroy (&stream, body); | ||
845 | message_destroy (&msg, mpm); | ||
846 | free (mpm); | ||
847 | return status; | ||
848 | } | ||
849 | /* Helps for the readline()s */ | ||
850 | stream_setbufsiz (stream, 128); | ||
851 | stream_set_read (stream, pop_body_read, body); | ||
852 | stream_set_fd (stream, pop_body_fd, body); | ||
853 | body_set_size (body, pop_body_size, msg); | ||
854 | body_set_lines (body, pop_body_lines, msg); | ||
855 | body_set_stream (body, stream, msg); | ||
856 | message_set_body (msg, body, mpm); | ||
857 | } | ||
858 | |||
859 | /* Set the UIDL call on the message. */ | ||
860 | message_set_uidl (msg, pop_uidl, mpm); | ||
861 | |||
862 | /* Set the UID on the message. */ | ||
863 | message_set_uid (msg, pop_uid, mpm); | ||
864 | |||
865 | /* Add it to the list. */ | ||
866 | monitor_wrlock (mbox->monitor); | ||
867 | { | ||
868 | pop_message_t *m ; | ||
869 | m = realloc (mpd->pmessages, (mpd->pmessages_count + 1)*sizeof (*m)); | ||
870 | if (m == NULL) | ||
871 | { | ||
872 | message_destroy (&msg, mpm); | ||
873 | free (mpm); | ||
874 | monitor_unlock (mbox->monitor); | ||
875 | return ENOMEM; | ||
876 | } | ||
877 | mpd->pmessages = m; | ||
878 | mpd->pmessages[mpd->pmessages_count] = mpm; | ||
879 | mpd->pmessages_count++; | ||
880 | } | ||
881 | monitor_unlock (mbox->monitor); | ||
882 | |||
883 | /* Save The message pointer. */ | ||
884 | message_set_mailbox (msg, mbox, mpm); | ||
885 | *pmsg = mpm->message = msg; | ||
886 | |||
887 | return 0; | ||
888 | } | ||
889 | |||
890 | /* There is no such thing in pop all messages should be consider recent. | ||
891 | FIXME: We could cheat and peek at the status if it was not strip | ||
892 | by the server ... */ | ||
893 | static int | ||
894 | pop_messages_recent (mailbox_t mbox, size_t *precent) | ||
895 | { | ||
896 | return pop_messages_count (mbox, precent); | ||
897 | } | ||
898 | |||
899 | /* There is no such thing in pop all messages should be consider unseen. | ||
900 | FIXME: We could cheat and peek at the status if it was not strip | ||
901 | by the server ... */ | ||
902 | static int | ||
903 | pop_message_unseen (mailbox_t mbox, size_t *punseen) | ||
904 | { | ||
905 | size_t count = 0; | ||
906 | int status = pop_messages_count (mbox, &count); | ||
907 | if (status != 0) | ||
908 | return status; | ||
909 | if (punseen) | ||
910 | *punseen = (count > 0) ? 1 : 0; | ||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | /* How many messages we have. Done with STAT. */ | ||
915 | static int | ||
916 | pop_messages_count (mailbox_t mbox, size_t *pcount) | ||
917 | { | ||
918 | pop_data_t mpd = mbox->data; | ||
919 | int status; | ||
920 | void *func = (void *)pop_messages_count; | ||
921 | |||
922 | if (mpd == NULL) | ||
923 | return EINVAL; | ||
924 | |||
925 | /* Do not send a STAT if we know the answer. */ | ||
926 | if (pop_is_updated (mbox)) | ||
927 | { | ||
928 | if (pcount) | ||
929 | *pcount = mpd->messages_count; | ||
930 | return 0; | ||
931 | } | ||
932 | |||
933 | /* Flag busy. */ | ||
934 | CHECK_BUSY (mbox, mpd, func, 0); | ||
935 | |||
936 | /* TRANSACTION state. */ | ||
937 | switch (mpd->state) | ||
938 | { | ||
939 | case POP_NO_STATE: | ||
940 | status = pop_writeline (mpd, "STAT\r\n"); | ||
941 | CHECK_ERROR (mpd, status); | ||
942 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
943 | mpd->state = POP_STAT; | ||
944 | |||
945 | case POP_STAT: | ||
946 | /* Send the STAT. */ | ||
947 | status = pop_write (mpd); | ||
948 | CHECK_EAGAIN (mpd, status); | ||
949 | mpd->state = POP_STAT_ACK; | ||
950 | |||
951 | case POP_STAT_ACK: | ||
952 | /* Get the ACK. */ | ||
953 | status = pop_read_ack (mpd); | ||
954 | CHECK_EAGAIN (mpd, status); | ||
955 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
956 | break; | ||
957 | |||
958 | default: | ||
959 | /* | ||
960 | mu_error ("pop_messages_count: unknow state\n"); | ||
961 | */ | ||
962 | break; | ||
963 | } | ||
964 | |||
965 | |||
966 | /* Parse the answer. */ | ||
967 | status = sscanf (mpd->buffer, "+OK %d %d", &(mpd->messages_count), | ||
968 | &(mpd->size)); | ||
969 | |||
970 | /* Clear the state _after_ the scanf, since another thread could | ||
971 | start writing over mpd->buffer. */ | ||
972 | CLEAR_STATE (mpd); | ||
973 | |||
974 | if (status == EOF || status != 2) | ||
975 | return EIO; | ||
976 | |||
977 | if (pcount) | ||
978 | *pcount = mpd->messages_count; | ||
979 | mpd->is_updated = 1; | ||
980 | return 0; | ||
981 | } | ||
982 | |||
983 | /* Update and scanning. */ | ||
984 | static int | ||
985 | pop_is_updated (mailbox_t mbox) | ||
986 | { | ||
987 | pop_data_t mpd = mbox->data; | ||
988 | if (mpd == NULL) | ||
989 | return 0; | ||
990 | return mpd->is_updated; | ||
991 | } | ||
992 | |||
993 | /* We just simulate by sending a notification for the total msgno. */ | ||
994 | /* FIXME is message is set deleted should we sent a notif ? */ | ||
995 | static int | ||
996 | pop_scan (mailbox_t mbox, size_t msgno, size_t *pcount) | ||
997 | { | ||
998 | int status; | ||
999 | size_t i; | ||
1000 | size_t count = 0; | ||
1001 | |||
1002 | status = pop_messages_count (mbox, &count); | ||
1003 | if (pcount) | ||
1004 | *pcount = count; | ||
1005 | if (status != 0) | ||
1006 | return status; | ||
1007 | if (mbox->observable == NULL) | ||
1008 | return 0; | ||
1009 | for (i = msgno; i <= count; i++) | ||
1010 | { | ||
1011 | if (observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD) != 0) | ||
1012 | break; | ||
1013 | if (((i +1) % 10) == 0) | ||
1014 | { | ||
1015 | observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS); | ||
1016 | } | ||
1017 | } | ||
1018 | return 0; | ||
1019 | } | ||
1020 | |||
1021 | /* This is where we actually send the DELE command. Meaning that when | ||
1022 | the attribute on the message is set deleted the comand DELE is not | ||
1023 | sent right away and if we did there is no way to mark a message undeleted | ||
1024 | beside closing down the connection without going to the update state via | ||
1025 | QUIT. So DELE is send only when in expunge. */ | ||
1026 | static int | ||
1027 | pop_expunge (mailbox_t mbox) | ||
1028 | { | ||
1029 | pop_data_t mpd = mbox->data; | ||
1030 | size_t i; | ||
1031 | attribute_t attr; | ||
1032 | int status; | ||
1033 | void *func = (void *)pop_expunge; | ||
1034 | |||
1035 | if (mpd == NULL) | ||
1036 | return EINVAL; | ||
1037 | |||
1038 | /* Busy ? */ | ||
1039 | CHECK_BUSY (mbox, mpd, func, 0); | ||
1040 | |||
1041 | for (i = (int)mpd->id; i < mpd->pmessages_count; mpd->id = ++i) | ||
1042 | { | ||
1043 | if (message_get_attribute (mpd->pmessages[i]->message, &attr) == 0) | ||
1044 | { | ||
1045 | if (attribute_is_deleted (attr)) | ||
1046 | { | ||
1047 | switch (mpd->state) | ||
1048 | { | ||
1049 | case POP_NO_STATE: | ||
1050 | status = pop_writeline (mpd, "DELE %d\r\n", | ||
1051 | mpd->pmessages[i]->num); | ||
1052 | CHECK_ERROR (mpd, status); | ||
1053 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1054 | mpd->state = POP_DELE; | ||
1055 | |||
1056 | case POP_DELE: | ||
1057 | /* Send DELETE. */ | ||
1058 | status = pop_write (mpd); | ||
1059 | CHECK_EAGAIN (mpd, status); | ||
1060 | mpd->state = POP_DELE_ACK; | ||
1061 | |||
1062 | case POP_DELE_ACK: | ||
1063 | /* Ack Delete. */ | ||
1064 | status = pop_read_ack (mpd); | ||
1065 | CHECK_EAGAIN (mpd, status); | ||
1066 | MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1067 | if (strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
1068 | { | ||
1069 | CHECK_ERROR (mpd, ERANGE); | ||
1070 | } | ||
1071 | mpd->state = POP_NO_STATE; | ||
1072 | break; | ||
1073 | |||
1074 | default: | ||
1075 | /* mu_error ("pop_expunge: unknow state\n"); */ | ||
1076 | break; | ||
1077 | } /* switch (state) */ | ||
1078 | } /* if attribute_is_deleted() */ | ||
1079 | } /* message_get_attribute() */ | ||
1080 | } /* for */ | ||
1081 | CLEAR_STATE (mpd); | ||
1082 | /* Invalidate. But Really they should shutdown the channel POP protocol | ||
1083 | is not meant for this like IMAP. */ | ||
1084 | mpd->is_updated = 0; | ||
1085 | return 0; | ||
1086 | } | ||
1087 | |||
1088 | /* Mailbox size ? It is part of the STAT command */ | ||
1089 | static int | ||
1090 | pop_get_size (mailbox_t mbox, off_t *psize) | ||
1091 | { | ||
1092 | pop_data_t mpd = mbox->data; | ||
1093 | int status = 0; | ||
1094 | |||
1095 | if (mpd == NULL) | ||
1096 | return EINVAL; | ||
1097 | |||
1098 | if (! pop_is_updated (mbox)) | ||
1099 | status = pop_messages_count (mbox, &mpd->size); | ||
1100 | if (psize) | ||
1101 | *psize = mpd->size; | ||
1102 | return status; | ||
1103 | } | ||
1104 | |||
1105 | /* Form the RFC: | ||
1106 | "It is important to note that the octet count for a message on the | ||
1107 | server host may differ from the octet count assigned to that message | ||
1108 | due to local conventions for designating end-of-line. Usually, | ||
1109 | during the AUTHORIZATION state of the POP3 session, the POP3 server | ||
1110 | can calculate the size of each message in octets when it opens the | ||
1111 | maildrop. For example, if the POP3 server host internally represents | ||
1112 | end-of-line as a single character, then the POP3 server simply counts | ||
1113 | each occurrence of this character in a message as two octets." | ||
1114 | |||
1115 | This is not perfect if we do not know the number of lines in the message | ||
1116 | then the octets returned will not be correct so we do our best. | ||
1117 | */ | ||
1118 | static int | ||
1119 | pop_message_size (message_t msg, size_t *psize) | ||
1120 | { | ||
1121 | pop_message_t mpm = message_get_owner (msg); | ||
1122 | pop_data_t mpd; | ||
1123 | int status = 0; | ||
1124 | void *func = (void *)pop_message_size; | ||
1125 | size_t num; | ||
1126 | |||
1127 | if (mpm == NULL) | ||
1128 | return EINVAL; | ||
1129 | |||
1130 | /* Did we have it already ? */ | ||
1131 | if (mpm->message_size != 0) | ||
1132 | { | ||
1133 | *psize = mpm->message_size; | ||
1134 | return 0; | ||
1135 | } | ||
1136 | |||
1137 | mpd = mpm->mpd; | ||
1138 | /* Busy ? */ | ||
1139 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1140 | |||
1141 | /* Get the size. */ | ||
1142 | switch (mpd->state) | ||
1143 | { | ||
1144 | case POP_NO_STATE: | ||
1145 | status = pop_writeline (mpd, "LIST %d\r\n", mpm->num); | ||
1146 | CHECK_ERROR (mpd, status); | ||
1147 | MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1148 | mpd->state = POP_LIST; | ||
1149 | |||
1150 | case POP_LIST: | ||
1151 | /* Send the LIST. */ | ||
1152 | status = pop_write (mpd); | ||
1153 | CHECK_EAGAIN (mpd, status); | ||
1154 | mpd->state = POP_LIST_ACK; | ||
1155 | |||
1156 | case POP_LIST_ACK: | ||
1157 | /* Resp from LIST. */ | ||
1158 | status = pop_read_ack (mpd); | ||
1159 | CHECK_EAGAIN (mpd, status); | ||
1160 | MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1161 | break; | ||
1162 | |||
1163 | default: | ||
1164 | /* | ||
1165 | mu_error ("pop_message_size state\n"); | ||
1166 | */ | ||
1167 | break; | ||
1168 | } | ||
1169 | |||
1170 | status = sscanf (mpd->buffer, "+OK %d %d\n", &num, &mpm->message_size); | ||
1171 | CLEAR_STATE (mpd); | ||
1172 | |||
1173 | if (status != 2) | ||
1174 | status = EINVAL; | ||
1175 | |||
1176 | /* The size of the message is with the extra '\r' octet for everyline. | ||
1177 | Substract to get, hopefully, a good count. */ | ||
1178 | if (psize) | ||
1179 | *psize = mpm->message_size - (mpm->header_lines + mpm->body_lines); | ||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | /* Another source of trouble, POP only gives the size of the message | ||
1184 | not the size of subparts like headers, body etc .. Again we're doing | ||
1185 | our best with what we know but the only way to get a precise number | ||
1186 | is by dowloading the whole message. */ | ||
1187 | static int | ||
1188 | pop_body_size (body_t body, size_t *psize) | ||
1189 | { | ||
1190 | message_t msg = body_get_owner (body); | ||
1191 | pop_message_t mpm = message_get_owner (msg); | ||
1192 | |||
1193 | if (mpm == NULL) | ||
1194 | return EINVAL; | ||
1195 | |||
1196 | /* Did we have it already ? */ | ||
1197 | if (mpm->body_size != 0) | ||
1198 | { | ||
1199 | *psize = mpm->body_size; | ||
1200 | } | ||
1201 | else if (mpm->message_size != 0) | ||
1202 | { | ||
1203 | /* Take a guest. */ | ||
1204 | *psize = mpm->message_size - mpm->header_size - mpm->body_lines; | ||
1205 | } | ||
1206 | else | ||
1207 | *psize = 0; | ||
1208 | |||
1209 | return 0; | ||
1210 | } | ||
1211 | |||
1212 | /* Not know until the whole message get downloaded. */ | ||
1213 | static int | ||
1214 | pop_body_lines (body_t body, size_t *plines) | ||
1215 | { | ||
1216 | message_t msg = body_get_owner (body); | ||
1217 | pop_message_t mpm = message_get_owner (msg); | ||
1218 | if (mpm == NULL) | ||
1219 | return EINVAL; | ||
1220 | if (plines) | ||
1221 | *plines = mpm->body_lines; | ||
1222 | return 0; | ||
1223 | } | ||
1224 | |||
1225 | /* Pop does not have any command for this, We fake by reading the "Status: " | ||
1226 | header. But this is hackish some POP server(Qpopper) skip it. Also | ||
1227 | because we call header_get_value the function may return EAGAIN... uncool. | ||
1228 | To put it another way, many servers simply remove the "Status:" header | ||
1229 | field, when you dowload a message, so a message will always look like | ||
1230 | new even if you already read it. There is also no way to set an attribute | ||
1231 | on remote mailbox via the POP server and many server once you do a RETR | ||
1232 | and in some cases a TOP will mark the message as read; "Status: RO" | ||
1233 | or maybe worst some ISP configure there servers to delete after | ||
1234 | the RETR some go as much as deleting after the TOP, since technicaly | ||
1235 | you can download a message via TOP without RET'reiving it. */ | ||
1236 | static int | ||
1237 | pop_get_attribute (attribute_t attr, int *pflags) | ||
1238 | { | ||
1239 | message_t msg = attribute_get_owner (attr); | ||
1240 | pop_message_t mpm = message_get_owner (msg); | ||
1241 | char hdr_status[64]; | ||
1242 | header_t header = NULL; | ||
1243 | |||
1244 | if (mpm == NULL || pflags == NULL) | ||
1245 | return EINVAL; | ||
1246 | if (mpm->attr_flags == 0) | ||
1247 | { | ||
1248 | hdr_status[0] = '\0'; | ||
1249 | message_get_header (mpm->message, &header); | ||
1250 | header_get_value (header, "Status", hdr_status, sizeof hdr_status, NULL); | ||
1251 | string_to_flags (hdr_status, &(mpm->attr_flags)); | ||
1252 | } | ||
1253 | *pflags = mpm->attr_flags; | ||
1254 | return 0; | ||
1255 | } | ||
1256 | |||
1257 | static int | ||
1258 | pop_set_attribute (attribute_t attr, int flags) | ||
1259 | { | ||
1260 | message_t msg = attribute_get_owner (attr); | ||
1261 | pop_message_t mpm = message_get_owner (msg); | ||
1262 | |||
1263 | if (mpm == NULL) | ||
1264 | return EINVAL; | ||
1265 | mpm->attr_flags |= flags; | ||
1266 | return 0; | ||
1267 | } | ||
1268 | |||
1269 | static int | ||
1270 | pop_unset_attribute (attribute_t attr, int flags) | ||
1271 | { | ||
1272 | message_t msg = attribute_get_owner (attr); | ||
1273 | pop_message_t mpm = message_get_owner (msg); | ||
1274 | |||
1275 | if (mpm == NULL) | ||
1276 | return EINVAL; | ||
1277 | mpm->attr_flags &= ~flags; | ||
1278 | return 0; | ||
1279 | } | ||
1280 | |||
1281 | /* Stub to call the fd from body object. */ | ||
1282 | static int | ||
1283 | pop_body_fd (stream_t stream, int *pfd) | ||
1284 | { | ||
1285 | body_t body = stream_get_owner (stream); | ||
1286 | message_t msg = body_get_owner (body); | ||
1287 | pop_message_t mpm = message_get_owner (msg); | ||
1288 | return pop_get_fd (mpm, pfd); | ||
1289 | } | ||
1290 | |||
1291 | /* Stub to call the fd from message object. */ | ||
1292 | static int | ||
1293 | pop_message_fd (stream_t stream, int *pfd) | ||
1294 | { | ||
1295 | message_t msg = stream_get_owner (stream); | ||
1296 | pop_message_t mpm = message_get_owner (msg); | ||
1297 | return pop_get_fd (mpm, pfd); | ||
1298 | } | ||
1299 | |||
1300 | /* Finally return the fd. */ | ||
1301 | static int | ||
1302 | pop_get_fd (pop_message_t mpm, int *pfd) | ||
1303 | { | ||
1304 | if (mpm && mpm->mpd && mpm->mpd->mbox) | ||
1305 | return stream_get_fd (mpm->mpd->mbox->stream, pfd); | ||
1306 | return EINVAL; | ||
1307 | } | ||
1308 | |||
1309 | static int | ||
1310 | pop_uid (message_t msg, size_t *puid) | ||
1311 | { | ||
1312 | pop_message_t mpm = message_get_owner (msg); | ||
1313 | if (puid) | ||
1314 | *puid = mpm->num; | ||
1315 | return 0; | ||
1316 | } | ||
1317 | |||
1318 | /* Get the UIDL. Client should be prepare since it may fail. UIDL is | ||
1319 | optional on many POP servers. | ||
1320 | FIXME: We should check this with CAPA and fall back to a md5 scheme ? | ||
1321 | Or maybe check for "X-UIDL" a la Qpopper ? */ | ||
1322 | static int | ||
1323 | pop_uidl (message_t msg, char *buffer, size_t buflen, size_t *pnwriten) | ||
1324 | { | ||
1325 | pop_message_t mpm = message_get_owner (msg); | ||
1326 | pop_data_t mpd; | ||
1327 | int status = 0; | ||
1328 | void *func = (void *)pop_uidl; | ||
1329 | size_t num; | ||
1330 | /* According to the RFC uidl's are no longer then 70 chars. Still playit | ||
1331 | safe */ | ||
1332 | char uniq[128]; | ||
1333 | |||
1334 | if (mpm == NULL) | ||
1335 | return EINVAL; | ||
1336 | |||
1337 | /* Is it cache ? */ | ||
1338 | if (mpm->uidl) | ||
1339 | { | ||
1340 | size_t len = strlen (mpm->uidl); | ||
1341 | if (buffer) | ||
1342 | { | ||
1343 | buflen--; /* Leave space for the null. */ | ||
1344 | buflen = (len > buflen) ? buflen : len; | ||
1345 | memcpy (buffer, mpm->uidl, buflen); | ||
1346 | buffer[buflen] = '\0'; | ||
1347 | } | ||
1348 | else | ||
1349 | buflen = len; | ||
1350 | if (pnwriten) | ||
1351 | *pnwriten = buflen; | ||
1352 | return 0; | ||
1353 | } | ||
1354 | |||
1355 | mpd = mpm->mpd; | ||
1356 | |||
1357 | /* Busy ? */ | ||
1358 | CHECK_BUSY (mpd->mbox, mpd, func, 0); | ||
1359 | |||
1360 | /* Get the UIDL. */ | ||
1361 | switch (mpd->state) | ||
1362 | { | ||
1363 | case POP_NO_STATE: | ||
1364 | status = pop_writeline (mpd, "UIDL %d\r\n", mpm->num); | ||
1365 | CHECK_ERROR (mpd, status); | ||
1366 | MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1367 | mpd->state = POP_UIDL; | ||
1368 | |||
1369 | case POP_UIDL: | ||
1370 | /* Send the UIDL. */ | ||
1371 | status = pop_write (mpd); | ||
1372 | CHECK_EAGAIN (mpd, status); | ||
1373 | mpd->state = POP_UIDL_ACK; | ||
1374 | |||
1375 | case POP_UIDL_ACK: | ||
1376 | /* Resp from UIDL. */ | ||
1377 | status = pop_read_ack (mpd); | ||
1378 | CHECK_EAGAIN (mpd, status); | ||
1379 | MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1380 | break; | ||
1381 | |||
1382 | default: | ||
1383 | /* | ||
1384 | mu_error ("pop_uidl state\n"); | ||
1385 | */ | ||
1386 | break; | ||
1387 | } | ||
1388 | |||
1389 | /* FIXME: I should cache the result. */ | ||
1390 | *uniq = '\0'; | ||
1391 | status = sscanf (mpd->buffer, "+OK %d %127s\n", &num, uniq); | ||
1392 | if (status != 2) | ||
1393 | { | ||
1394 | status = EINVAL; | ||
1395 | buflen = 0; | ||
1396 | } | ||
1397 | else | ||
1398 | { | ||
1399 | num = strlen (uniq); | ||
1400 | uniq[num - 1] = '\0'; /* Nuke newline. */ | ||
1401 | if (buffer) | ||
1402 | { | ||
1403 | buflen--; /* Leave space for the null. */ | ||
1404 | buflen = (buflen < num) ? buflen : num; | ||
1405 | memcpy (buffer, uniq, buflen); | ||
1406 | buffer [buflen] = '\0'; | ||
1407 | } | ||
1408 | else | ||
1409 | buflen = num - 1; /* Do not count newline. */ | ||
1410 | mpm->uidl = strdup (uniq); | ||
1411 | status = 0; | ||
1412 | } | ||
1413 | |||
1414 | CLEAR_STATE (mpd); | ||
1415 | |||
1416 | if (pnwriten) | ||
1417 | *pnwriten = buflen; | ||
1418 | return status; | ||
1419 | } | ||
1420 | |||
1421 | /* How we retrieve the headers. If it fails we jump to the pop_retr() | ||
1422 | code .i.e send a RETR and skip the body, ugly. | ||
1423 | NOTE: if the offset is different, flag an error, offset is meaningless | ||
1424 | on a socket but we better warn them, some stuff like mime_t may try to | ||
1425 | read ahead, for example for the headers. */ | ||
1426 | static int | ||
1427 | pop_top (header_t header, char *buffer, size_t buflen, | ||
1428 | off_t offset, size_t *pnread) | ||
1429 | { | ||
1430 | message_t msg = header_get_owner (header); | ||
1431 | pop_message_t mpm = message_get_owner (msg); | ||
1432 | pop_data_t mpd; | ||
1433 | size_t nread = 0; | ||
1434 | int status = 0; | ||
1435 | void *func = (void *)pop_top; | ||
1436 | |||
1437 | if (mpm == NULL) | ||
1438 | return EINVAL; | ||
1439 | |||
1440 | mpd = mpm->mpd; | ||
1441 | |||
1442 | /* Busy ? */ | ||
1443 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1444 | |||
1445 | /* We start fresh then reset the sizes. */ | ||
1446 | if (mpd->state == POP_NO_STATE) | ||
1447 | mpm->header_size = 0; | ||
1448 | |||
1449 | /* Throw an error if trying to seek back. */ | ||
1450 | if ((size_t)offset < mpm->header_size) | ||
1451 | return ESPIPE; | ||
1452 | |||
1453 | /* Get the header. */ | ||
1454 | switch (mpd->state) | ||
1455 | { | ||
1456 | case POP_NO_STATE: | ||
1457 | /* TOP is an optionnal command, if we want to be compliant we can not | ||
1458 | count on it to exists. So we should be prepare when it fails and | ||
1459 | fall to a second scheme. */ | ||
1460 | status = pop_writeline (mpd, "TOP %d 0\r\n", mpm->num); | ||
1461 | CHECK_ERROR (mpd, status); | ||
1462 | MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1463 | mpd->state = POP_TOP; | ||
1464 | |||
1465 | case POP_TOP: | ||
1466 | /* Send the TOP. */ | ||
1467 | status = pop_write (mpd); | ||
1468 | CHECK_EAGAIN (mpd, status); | ||
1469 | mpd->state = POP_TOP_ACK; | ||
1470 | |||
1471 | case POP_TOP_ACK: | ||
1472 | /* Ack from TOP. */ | ||
1473 | status = pop_read_ack (mpd); | ||
1474 | CHECK_EAGAIN (mpd, status); | ||
1475 | MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1476 | if (strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
1477 | { | ||
1478 | /* mu_error ("TOP not implemented\n"); */ | ||
1479 | /* Fall back to RETR call. */ | ||
1480 | mpd->state = POP_NO_STATE; | ||
1481 | mpm->skip_header = 0; | ||
1482 | mpm->skip_body = 1; | ||
1483 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1484 | } | ||
1485 | mpd->state = POP_TOP_RX; | ||
1486 | |||
1487 | case POP_TOP_RX: | ||
1488 | /* Get the header. */ | ||
1489 | do | ||
1490 | { | ||
1491 | /* Seek in position. */ | ||
1492 | ssize_t pos = offset - mpm->header_size; | ||
1493 | /* Do we need to fill up. */ | ||
1494 | if (mpd->nl == NULL || mpd->ptr == mpd->buffer) | ||
1495 | { | ||
1496 | status = pop_readline (mpd); | ||
1497 | CHECK_EAGAIN (mpd, status); | ||
1498 | mpm->header_lines++; | ||
1499 | } | ||
1500 | /* If we have to skip some data to get to the offset. */ | ||
1501 | if (pos > 0) | ||
1502 | nread = fill_buffer (mpd, NULL, pos); | ||
1503 | else | ||
1504 | nread = fill_buffer (mpd, buffer, buflen); | ||
1505 | mpm->header_size += nread; | ||
1506 | } | ||
1507 | while (nread > 0 && (size_t)offset > mpm->header_size); | ||
1508 | break; | ||
1509 | |||
1510 | default: | ||
1511 | /* Probaly TOP was not supported so we have fall back to RETR. */ | ||
1512 | mpm->skip_header = 0; | ||
1513 | mpm->skip_body = 1; | ||
1514 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1515 | } /* switch (state) */ | ||
1516 | |||
1517 | if (nread == 0) | ||
1518 | { | ||
1519 | CLEAR_STATE (mpd); | ||
1520 | } | ||
1521 | if (pnread) | ||
1522 | *pnread = nread; | ||
1523 | return 0; | ||
1524 | } | ||
1525 | |||
1526 | /* This is no longer use, see pop_top to retreive headers, we still | ||
1527 | keep it around for debugging purposes. */ | ||
1528 | #if 0 | ||
1529 | /* Stub to call pop_retr (). Call form the stream object of the header. */ | ||
1530 | static int | ||
1531 | pop_header_read (header_t header, char *buffer, size_t buflen, off_t offset, | ||
1532 | size_t *pnread) | ||
1533 | { | ||
1534 | message_t msg = header_get_owner (header); | ||
1535 | pop_message_t mpm = message_get_owner (msg); | ||
1536 | pop_data_t mpd; | ||
1537 | void *func = (void *)pop_header_read; | ||
1538 | |||
1539 | if (mpm == NULL) | ||
1540 | return EINVAL; | ||
1541 | |||
1542 | mpd = mpm->mpd; | ||
1543 | |||
1544 | /* Busy ? */ | ||
1545 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1546 | |||
1547 | /* We start fresh then reset the sizes. */ | ||
1548 | if (mpd->state == POP_NO_STATE) | ||
1549 | mpm->header_size = mpm->inbody = 0; | ||
1550 | |||
1551 | /* Throw an error if trying to seek back. */ | ||
1552 | if ((size_t)offset < mpm->header_size) | ||
1553 | return ESPIPE; | ||
1554 | |||
1555 | mpm->skip_header = 0; | ||
1556 | mpm->skip_body = 1; | ||
1557 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1558 | } | ||
1559 | #endif | ||
1560 | |||
1561 | /* Stub to call pop_retr (). Call from the stream object of the body. */ | ||
1562 | static int | ||
1563 | pop_body_read (stream_t is, char *buffer, size_t buflen, off_t offset, | ||
1564 | size_t *pnread) | ||
1565 | { | ||
1566 | body_t body = stream_get_owner (is); | ||
1567 | message_t msg = body_get_owner (body); | ||
1568 | pop_message_t mpm = message_get_owner (msg); | ||
1569 | pop_data_t mpd; | ||
1570 | void *func = (void *)pop_body_read; | ||
1571 | |||
1572 | if (mpm == NULL) | ||
1573 | return EINVAL; | ||
1574 | |||
1575 | mpd = mpm->mpd; | ||
1576 | |||
1577 | /* Busy ? */ | ||
1578 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1579 | |||
1580 | /* We start fresh then reset the sizes. */ | ||
1581 | if (mpd->state == POP_NO_STATE) | ||
1582 | mpm->body_size = mpm->inbody = 0; | ||
1583 | |||
1584 | /* Can not seek back this a stream socket. */ | ||
1585 | if ((size_t)offset < mpm->body_size) | ||
1586 | return ESPIPE; | ||
1587 | |||
1588 | mpm->skip_header = 1; | ||
1589 | mpm->skip_body = 0; | ||
1590 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1591 | } | ||
1592 | |||
1593 | /* Stub to call pop_retr (), calling from the stream object of a message. */ | ||
1594 | static int | ||
1595 | pop_message_read (stream_t is, char *buffer, size_t buflen, off_t offset, | ||
1596 | size_t *pnread) | ||
1597 | { | ||
1598 | message_t msg = stream_get_owner (is); | ||
1599 | pop_message_t mpm = message_get_owner (msg); | ||
1600 | pop_data_t mpd; | ||
1601 | void *func = (void *)pop_message_read; | ||
1602 | |||
1603 | if (mpm == NULL) | ||
1604 | return EINVAL; | ||
1605 | |||
1606 | mpd = mpm->mpd; | ||
1607 | |||
1608 | /* Busy ? */ | ||
1609 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1610 | |||
1611 | /* We start fresh then reset the sizes. */ | ||
1612 | if (mpd->state == POP_NO_STATE) | ||
1613 | mpm->header_size = mpm->body_size = mpm->inbody = 0; | ||
1614 | |||
1615 | /* Can not seek back this is a stream socket. */ | ||
1616 | if ((size_t)offset < (mpm->body_size + mpm->header_size)) | ||
1617 | return ESPIPE; | ||
1618 | |||
1619 | mpm->skip_header = mpm->skip_body = 0; | ||
1620 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1621 | } | ||
1622 | |||
1623 | /* Little helper to fill the buffer without overflow. */ | ||
1624 | static int | ||
1625 | fill_buffer (pop_data_t mpd, char *buffer, size_t buflen) | ||
1626 | { | ||
1627 | int nleft, n, nread = 0; | ||
1628 | |||
1629 | /* How much we can copy ? */ | ||
1630 | n = mpd->ptr - mpd->buffer; | ||
1631 | nleft = buflen - n; | ||
1632 | |||
1633 | /* We got more then requested. */ | ||
1634 | if (nleft < 0) | ||
1635 | { | ||
1636 | size_t sentinel; | ||
1637 | nread = buflen; | ||
1638 | sentinel = mpd->ptr - (mpd->buffer + nread); | ||
1639 | if (buffer) | ||
1640 | memcpy (buffer, mpd->buffer, nread); | ||
1641 | memmove (mpd->buffer, mpd->buffer + nread, sentinel); | ||
1642 | mpd->ptr = mpd->buffer + sentinel; | ||
1643 | } | ||
1644 | else | ||
1645 | { | ||
1646 | /* Drain our buffer. */; | ||
1647 | nread = n; | ||
1648 | if (buffer) | ||
1649 | memcpy (buffer, mpd->buffer, nread); | ||
1650 | mpd->ptr = mpd->buffer; | ||
1651 | } | ||
1652 | |||
1653 | return nread; | ||
1654 | } | ||
1655 | |||
1656 | /* The heart of most funtions. Send the RETR and skip different parts. */ | ||
1657 | static int | ||
1658 | pop_retr (pop_message_t mpm, char *buffer, size_t buflen, off_t offset, | ||
1659 | size_t *pnread) | ||
1660 | { | ||
1661 | pop_data_t mpd; | ||
1662 | size_t nread = 0; | ||
1663 | int status = 0; | ||
1664 | size_t oldbuflen = buflen; | ||
1665 | |||
1666 | /* Meaningless. */ | ||
1667 | (void)offset; | ||
1668 | |||
1669 | mpd = mpm->mpd; | ||
1670 | |||
1671 | if (pnread) | ||
1672 | *pnread = nread; | ||
1673 | |||
1674 | /* Take care of the obvious. */ | ||
1675 | if (buffer == NULL || buflen == 0) | ||
1676 | { | ||
1677 | CLEAR_STATE (mpd); | ||
1678 | return 0; | ||
1679 | } | ||
1680 | |||
1681 | /* pop_retr() is not call directly so we assume that the locks were set. */ | ||
1682 | |||
1683 | switch (mpd->state) | ||
1684 | { | ||
1685 | case POP_NO_STATE: | ||
1686 | mpm->body_lines = mpm->body_size = 0; | ||
1687 | status = pop_writeline (mpd, "RETR %d\r\n", mpm->num); | ||
1688 | MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1689 | CHECK_ERROR (mpd, status); | ||
1690 | mpd->state = POP_RETR; | ||
1691 | |||
1692 | case POP_RETR: | ||
1693 | /* Send the RETR command. */ | ||
1694 | status = pop_write (mpd); | ||
1695 | CHECK_EAGAIN (mpd, status); | ||
1696 | mpd->state = POP_RETR_ACK; | ||
1697 | |||
1698 | case POP_RETR_ACK: | ||
1699 | /* RETR ACK. */ | ||
1700 | status = pop_read_ack (mpd); | ||
1701 | CHECK_EAGAIN (mpd, status); | ||
1702 | MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer); | ||
1703 | |||
1704 | if (strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
1705 | { | ||
1706 | CHECK_ERROR (mpd, EACCES); | ||
1707 | } | ||
1708 | mpd->state = POP_RETR_RX_HDR; | ||
1709 | |||
1710 | case POP_RETR_RX_HDR: | ||
1711 | /* Skip/Take the header. */ | ||
1712 | while (!mpm->inbody) | ||
1713 | { | ||
1714 | /* Do we need to fill up. */ | ||
1715 | if (mpd->nl == NULL || mpd->ptr == mpd->buffer) | ||
1716 | { | ||
1717 | status = pop_readline (mpd); | ||
1718 | if (status != 0) | ||
1719 | { | ||
1720 | /* Do we have something in the buffer flush it first. */ | ||
1721 | if (buflen != oldbuflen) | ||
1722 | return 0; | ||
1723 | CHECK_EAGAIN (mpd, status); | ||
1724 | } | ||
1725 | mpm->header_lines++; | ||
1726 | } | ||
1727 | /* Oops !! Hello houston we have a major problem here. */ | ||
1728 | if (mpd->buffer[0] == '\0') | ||
1729 | { | ||
1730 | /* Still Do the right thing. */ | ||
1731 | if (buflen != oldbuflen) | ||
1732 | { | ||
1733 | CLEAR_STATE (mpd); | ||
1734 | } | ||
1735 | else | ||
1736 | mpd->state = POP_STATE_DONE; | ||
1737 | return 0; | ||
1738 | } | ||
1739 | /* The problem is that we are using RETR instead of TOP to retreive | ||
1740 | headers, i.e the server contacted does not support it. So we | ||
1741 | have to make sure that the next line really means end of the | ||
1742 | headers. Still some cases we may loose. But 99.9% of POPD | ||
1743 | encounter support TOP. In the 0.1% case get GNU pop3d, or the | ||
1744 | hack below will suffice. */ | ||
1745 | if (mpd->buffer[0] == '\n' && mpd->buffer[1] == '\0') | ||
1746 | mpm->inbody = 1; /* break out of the while. */ | ||
1747 | if (!mpm->skip_header) | ||
1748 | { | ||
1749 | ssize_t pos = offset - mpm->header_size; | ||
1750 | if (pos > 0) | ||
1751 | { | ||
1752 | nread = fill_buffer (mpd, NULL, pos); | ||
1753 | mpm->header_size += nread; | ||
1754 | } | ||
1755 | else | ||
1756 | { | ||
1757 | nread = fill_buffer (mpd, buffer, buflen); | ||
1758 | mpm->header_size += nread; | ||
1759 | if (pnread) | ||
1760 | *pnread += nread; | ||
1761 | buflen -= nread ; | ||
1762 | if (buflen > 0) | ||
1763 | buffer += nread; | ||
1764 | else | ||
1765 | return 0; | ||
1766 | } | ||
1767 | } | ||
1768 | else | ||
1769 | mpd->ptr = mpd->buffer; | ||
1770 | } | ||
1771 | mpd->state = POP_RETR_RX_BODY; | ||
1772 | |||
1773 | case POP_RETR_RX_BODY: | ||
1774 | /* Start/Take the body. */ | ||
1775 | while (mpm->inbody) | ||
1776 | { | ||
1777 | /* Do we need to fill up. */ | ||
1778 | if (mpd->nl == NULL || mpd->ptr == mpd->buffer) | ||
1779 | { | ||
1780 | status = pop_readline (mpd); | ||
1781 | if (status != 0) | ||
1782 | { | ||
1783 | /* Flush The Buffer ? */ | ||
1784 | if (buflen != oldbuflen) | ||
1785 | return 0; | ||
1786 | CHECK_EAGAIN (mpd, status); | ||
1787 | } | ||
1788 | mpm->body_lines++; | ||
1789 | } | ||
1790 | |||
1791 | if (mpd->buffer[0] == '\0') | ||
1792 | mpm->inbody = 0; /* Breakout of the while. */ | ||
1793 | |||
1794 | if (!mpm->skip_body) | ||
1795 | { | ||
1796 | /* If we did not skip the header, it means that we are | ||
1797 | downloading the entire message and the header_size should be | ||
1798 | part of the offset count. */ | ||
1799 | ssize_t pos = offset - (mpm->body_size + ((mpm->skip_header) ? | ||
1800 | 0 : mpm->header_size)); | ||
1801 | if (pos > 0) | ||
1802 | { | ||
1803 | nread = fill_buffer (mpd, NULL, pos); | ||
1804 | mpm->body_size += nread; | ||
1805 | } | ||
1806 | else | ||
1807 | { | ||
1808 | nread = fill_buffer (mpd, buffer, buflen); | ||
1809 | mpm->body_size += nread; | ||
1810 | if (pnread) | ||
1811 | *pnread += nread; | ||
1812 | buflen -= nread ; | ||
1813 | if (buflen > 0) | ||
1814 | buffer += nread; | ||
1815 | else | ||
1816 | return 0; | ||
1817 | } | ||
1818 | } | ||
1819 | else | ||
1820 | { | ||
1821 | mpm->body_size += (mpd->ptr - mpd->buffer); | ||
1822 | mpd->ptr = mpd->buffer; | ||
1823 | } | ||
1824 | } | ||
1825 | mpm->message_size = mpm->body_size + mpm->header_size; | ||
1826 | mpd->state = POP_STATE_DONE; | ||
1827 | /* Return here earlier, because we want to return nread = 0 to notify | ||
1828 | the callee that we've finish, since there is already data | ||
1829 | we have to return them first and _then_ tell them its finish. If we | ||
1830 | don't we will start over again by sending another RETR. */ | ||
1831 | if (buflen != oldbuflen) | ||
1832 | return 0; | ||
1833 | |||
1834 | case POP_STATE_DONE: | ||
1835 | /* A convenient break, this is here we can return 0, we're done. */ | ||
1836 | |||
1837 | default: | ||
1838 | /* mu_error ("pop_retr unknow state\n"); */ | ||
1839 | break; | ||
1840 | } /* Switch state. */ | ||
1841 | |||
1842 | CLEAR_STATE (mpd); | ||
1843 | mpm->skip_header = mpm->skip_body = 0; | ||
1844 | return 0; | ||
1845 | } | ||
1846 | |||
1847 | /* Extract the User from the URL or the ticket. */ | ||
1848 | static int | ||
1849 | pop_get_user (authority_t auth) | ||
1850 | { | ||
1851 | folder_t folder = authority_get_owner (auth); | ||
1852 | mailbox_t mbox = folder->data; | ||
1853 | pop_data_t mpd = mbox->data; | ||
1854 | ticket_t ticket = NULL; | ||
1855 | int status; | ||
1856 | /* Fetch the user from them. */ | ||
1857 | size_t n = 0; | ||
1858 | |||
1859 | authority_get_ticket (auth, &ticket); | ||
1860 | if (mpd->user) | ||
1861 | { | ||
1862 | free (mpd->user); | ||
1863 | mpd->user = NULL; | ||
1864 | } | ||
1865 | /* Was it in the URL? */ | ||
1866 | status = url_get_user (mbox->url, NULL, 0, &n); | ||
1867 | if (status != 0 || n == 0) | ||
1868 | ticket_pop (ticket, mbox->url, "Pop User: ", &mpd->user); | ||
1869 | else | ||
1870 | { | ||
1871 | mpd->user = calloc (1, n + 1); | ||
1872 | url_get_user (mbox->url, mpd->user, n + 1, NULL); | ||
1873 | } | ||
1874 | return 0; | ||
1875 | } | ||
1876 | |||
1877 | /* Extract the User from the URL or the ticket. */ | ||
1878 | static int | ||
1879 | pop_get_passwd (authority_t auth) | ||
1880 | { | ||
1881 | folder_t folder = authority_get_owner (auth); | ||
1882 | mailbox_t mbox = folder->data; | ||
1883 | pop_data_t mpd = mbox->data; | ||
1884 | ticket_t ticket = NULL; | ||
1885 | int status; | ||
1886 | /* Fetch the user from them. */ | ||
1887 | size_t n = 0; | ||
1888 | |||
1889 | authority_get_ticket (auth, &ticket); | ||
1890 | if (mpd->passwd) | ||
1891 | { | ||
1892 | free (mpd->passwd); | ||
1893 | mpd->passwd = NULL; | ||
1894 | } | ||
1895 | /* Was it in the URL? */ | ||
1896 | status = url_get_passwd (mbox->url, NULL, 0, &n); | ||
1897 | if (status != 0 || n == 0) | ||
1898 | ticket_pop (ticket, mbox->url, "Pop Passwd: ", &mpd->passwd); | ||
1899 | else | ||
1900 | { | ||
1901 | mpd->passwd = calloc (1, n + 1); | ||
1902 | url_get_passwd (mbox->url, mpd->passwd, n + 1, NULL); | ||
1903 | } | ||
1904 | return 0; | ||
1905 | } | ||
1906 | |||
1907 | |||
1908 | static char * | ||
1909 | pop_get_timestamp (pop_data_t mpd) | ||
1910 | { | ||
1911 | char *right, *left; | ||
1912 | char *timestamp = NULL; | ||
1913 | size_t len; | ||
1914 | |||
1915 | len = strlen (mpd->buffer); | ||
1916 | right = memchr (mpd->buffer, '<', len); | ||
1917 | if (right) | ||
1918 | { | ||
1919 | len = len - (right - mpd->buffer); | ||
1920 | left = memchr (right, '>', len); | ||
1921 | if (left) | ||
1922 | { | ||
1923 | len = left - right + 1; | ||
1924 | timestamp = calloc (len + 1, 1); | ||
1925 | if (timestamp != NULL) | ||
1926 | { | ||
1927 | memcpy (timestamp, right, len); | ||
1928 | } | ||
1929 | } | ||
1930 | } | ||
1931 | return timestamp; | ||
1932 | } | ||
1933 | |||
1934 | /* Make the MD5 string. */ | ||
1935 | static int | ||
1936 | pop_get_md5 (pop_data_t mpd) | ||
1937 | { | ||
1938 | MD5_CTX md5context; | ||
1939 | unsigned char md5digest[16]; | ||
1940 | char digest[64]; /* Really it just has to be 32 + 1(null). */ | ||
1941 | char *tmp; | ||
1942 | size_t n; | ||
1943 | char *timestamp; | ||
1944 | |||
1945 | timestamp = pop_get_timestamp (mpd); | ||
1946 | if (timestamp == NULL) | ||
1947 | return EINVAL; | ||
1948 | |||
1949 | MD5Init (&md5context); | ||
1950 | MD5Update (&md5context, (unsigned char *)timestamp, strlen (timestamp)); | ||
1951 | MD5Update (&md5context, (unsigned char *)mpd->passwd, strlen (mpd->passwd)); | ||
1952 | MD5Final (md5digest, &md5context); | ||
1953 | for (tmp = digest, n = 0; n < 16; n++, tmp += 2) | ||
1954 | sprintf (tmp, "%02x", md5digest[n]); | ||
1955 | *tmp = '\0'; | ||
1956 | free (timestamp); | ||
1957 | free (mpd->passwd); | ||
1958 | mpd->passwd = strdup (digest); | ||
1959 | return 0; | ||
1960 | } | ||
1961 | |||
1962 | /* GRRRRR!! We can not use sleep in the library since this we'll | ||
1963 | muck up any alarm() done by the user. */ | ||
1964 | static int | ||
1965 | pop_sleep (int seconds) | ||
1966 | { | ||
1967 | struct timeval tval; | ||
1968 | tval.tv_sec = seconds; | ||
1969 | tval.tv_usec = 0; | ||
1970 | return select (1, NULL, NULL, NULL, &tval); | ||
1971 | } | ||
1972 | |||
1973 | /* C99 says that a conforming implementation of snprintf () should return the | ||
1974 | number of char that would have been call but many old GNU/Linux && BSD | ||
1975 | implementations return -1 on error. Worse QnX/Neutrino actually does not | ||
1976 | put the terminal null char. So let's try to cope. */ | ||
1977 | static int | ||
1978 | pop_writeline (pop_data_t mpd, const char *format, ...) | ||
1979 | { | ||
1980 | int len; | ||
1981 | va_list ap; | ||
1982 | int done = 1; | ||
1983 | |||
1984 | if (mpd->buffer == NULL) | ||
1985 | return EINVAL; | ||
1986 | va_start(ap, format); | ||
1987 | do | ||
1988 | { | ||
1989 | len = vsnprintf (mpd->buffer, mpd->buflen - 1, format, ap); | ||
1990 | if (len < 0 || len >= (int)mpd->buflen | ||
1991 | || !memchr (mpd->buffer, '\0', len + 1)) | ||
1992 | { | ||
1993 | mpd->buflen *= 2; | ||
1994 | mpd->buffer = realloc (mpd->buffer, mpd->buflen); | ||
1995 | if (mpd->buffer == NULL) | ||
1996 | return ENOMEM; | ||
1997 | done = 0; | ||
1998 | } | ||
1999 | else | ||
2000 | done = 1; | ||
2001 | } | ||
2002 | while (!done); | ||
2003 | va_end(ap); | ||
2004 | mpd->ptr = mpd->buffer + len; | ||
2005 | return 0; | ||
2006 | } | ||
2007 | |||
2008 | /* A socket may write less then expected and we have to cope with nonblocking. | ||
2009 | if the write failed we keep track and restart where left. */ | ||
2010 | static int | ||
2011 | pop_write (pop_data_t mpd) | ||
2012 | { | ||
2013 | int status = 0; | ||
2014 | if (mpd->ptr > mpd->buffer) | ||
2015 | { | ||
2016 | size_t len; | ||
2017 | size_t n = 0; | ||
2018 | len = mpd->ptr - mpd->buffer; | ||
2019 | status = stream_write (mpd->mbox->stream, mpd->buffer, len, 0, &n); | ||
2020 | if (status == 0) | ||
2021 | { | ||
2022 | memmove (mpd->buffer, mpd->buffer + n, len - n); | ||
2023 | mpd->ptr -= n; | ||
2024 | } | ||
2025 | } | ||
2026 | else | ||
2027 | mpd->ptr = mpd->buffer; | ||
2028 | return status; | ||
2029 | } | ||
2030 | |||
2031 | /* Call readline and reset the mpd->ptr to the buffer, signalling that we have | ||
2032 | done the read to completion. */ | ||
2033 | static int | ||
2034 | pop_read_ack (pop_data_t mpd) | ||
2035 | { | ||
2036 | int status = pop_readline (mpd); | ||
2037 | if (status == 0) | ||
2038 | mpd->ptr = mpd->buffer; | ||
2039 | return status; | ||
2040 | } | ||
2041 | |||
2042 | /* Read a complete line form the pop server. Transform CRLF to LF, remove | ||
2043 | the stuff byte termination octet ".", put a null in the buffer | ||
2044 | when done. */ | ||
2045 | static int | ||
2046 | pop_readline (pop_data_t mpd) | ||
2047 | { | ||
2048 | size_t n = 0; | ||
2049 | size_t total = mpd->ptr - mpd->buffer; | ||
2050 | int status; | ||
2051 | |||
2052 | /* Must get a full line before bailing out. */ | ||
2053 | do | ||
2054 | { | ||
2055 | status = stream_readline (mpd->mbox->stream, mpd->buffer + total, | ||
2056 | mpd->buflen - total, mpd->offset, &n); | ||
2057 | if (status != 0) | ||
2058 | return status; | ||
2059 | |||
2060 | /* The server went away: It maybe a timeout and some pop server | ||
2061 | does not send the -ERR. Consider this like an error. */ | ||
2062 | if (n == 0) | ||
2063 | return EIO; | ||
2064 | |||
2065 | total += n; | ||
2066 | mpd->offset += n; | ||
2067 | mpd->nl = memchr (mpd->buffer, '\n', total); | ||
2068 | if (mpd->nl == NULL) /* Do we have a full line. */ | ||
2069 | { | ||
2070 | /* Allocate a bigger buffer ? */ | ||
2071 | if (total >= mpd->buflen -1) | ||
2072 | { | ||
2073 | mpd->buflen *= 2; | ||
2074 | mpd->buffer = realloc (mpd->buffer, mpd->buflen + 1); | ||
2075 | if (mpd->buffer == NULL) | ||
2076 | return ENOMEM; | ||
2077 | } | ||
2078 | } | ||
2079 | mpd->ptr = mpd->buffer + total; | ||
2080 | } | ||
2081 | while (mpd->nl == NULL); | ||
2082 | |||
2083 | /* When examining a multi-line response, the client checks to see if the | ||
2084 | line begins with the termination octet "."(DOT). If yes and if octets | ||
2085 | other than CRLF follow, the first octet of the line (the termination | ||
2086 | octet) is stripped away. */ | ||
2087 | if (total >= 3 && mpd->buffer[0] == '.') | ||
2088 | { | ||
2089 | if (mpd->buffer[1] != '\r' && mpd->buffer[2] != '\n') | ||
2090 | { | ||
2091 | memmove (mpd->buffer, mpd->buffer + 1, total - 1); | ||
2092 | mpd->ptr--; | ||
2093 | mpd->nl--; | ||
2094 | } | ||
2095 | /* And if CRLF immediately follows the termination character, then the | ||
2096 | response from the POP server is ended and the line containing | ||
2097 | ".CRLF" is not considered part of the multi-line response. */ | ||
2098 | else if (mpd->buffer[1] == '\r' && mpd->buffer[2] == '\n') | ||
2099 | { | ||
2100 | mpd->buffer[0] = '\0'; | ||
2101 | mpd->ptr = mpd->buffer; | ||
2102 | mpd->nl = NULL; | ||
2103 | } | ||
2104 | } | ||
2105 | /* \r\n --> \n\0, conversion. */ | ||
2106 | if (mpd->nl > mpd->buffer) | ||
2107 | { | ||
2108 | *(mpd->nl - 1) = '\n'; | ||
2109 | *(mpd->nl) = '\0'; | ||
2110 | mpd->ptr = mpd->nl; | ||
2111 | } | ||
2112 | return 0; | ||
2113 | } | ||
2114 | |||
2115 | #endif |
mailbox/url_imap.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef ENABLE_IMAP | ||
23 | |||
24 | #include <errno.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include <errno.h> | ||
28 | #ifdef HAVE_STRINGS_H | ||
29 | # include <strings.h> | ||
30 | #endif | ||
31 | |||
32 | #include <registrar0.h> | ||
33 | #include <url0.h> | ||
34 | |||
35 | static void url_imap_destroy (url_t url); | ||
36 | |||
37 | static void | ||
38 | url_imap_destroy (url_t url) | ||
39 | { | ||
40 | (void)url; | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | IMAP URL: | ||
45 | imap://[<user>[;AUTH=<auth>]@]<host>[/<mailbox>] | ||
46 | else | ||
47 | imap://[<user>[:<pass>]@]<host>[/<mailbox>] | ||
48 | */ | ||
49 | |||
50 | int | ||
51 | _url_imap_init (url_t url) | ||
52 | { | ||
53 | int status = 0; | ||
54 | |||
55 | url->_destroy = url_imap_destroy; | ||
56 | |||
57 | status = url_parse (url); | ||
58 | |||
59 | if (status) | ||
60 | return status; | ||
61 | |||
62 | if(!url->host || url->query) | ||
63 | return EINVAL; | ||
64 | |||
65 | /* is it pop? */ | ||
66 | if (strcmp ("imap", url->scheme) != 0) | ||
67 | return EINVAL; | ||
68 | |||
69 | /* fill in default port, if necesary */ | ||
70 | if (url->port == 0) | ||
71 | url->port = MU_IMAP_PORT; | ||
72 | |||
73 | /* fill in default auth, if necessary */ | ||
74 | if (!url->auth) | ||
75 | { | ||
76 | url->auth = malloc (1 + 1); | ||
77 | if (!url->auth) | ||
78 | return ENOMEM; | ||
79 | |||
80 | url->auth[0] = '*'; | ||
81 | url->auth[1] = '\0'; | ||
82 | } | ||
83 | |||
84 | return status; | ||
85 | } | ||
86 | |||
87 | #endif |
mailbox/url_mbox.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #include <errno.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <stdio.h> | ||
25 | #include <string.h> | ||
26 | |||
27 | #ifdef HAVE_STRINGS_H | ||
28 | # include <strings.h> | ||
29 | #endif | ||
30 | |||
31 | #include <registrar0.h> | ||
32 | #include <url0.h> | ||
33 | |||
34 | static void url_mbox_destroy (url_t purl); | ||
35 | |||
36 | static void | ||
37 | url_mbox_destroy (url_t url) | ||
38 | { | ||
39 | (void) url; | ||
40 | } | ||
41 | |||
42 | /* Default mailbox path generator */ | ||
43 | static char * | ||
44 | _url_path_default (const char *spooldir, const char *user, int unused) | ||
45 | { | ||
46 | char *mbox = malloc (sizeof(spooldir) + strlen(user) + 2); | ||
47 | if (!mbox) | ||
48 | errno = ENOMEM; | ||
49 | else | ||
50 | sprintf (mbox, "%s/%s", spooldir, user); | ||
51 | return mbox; | ||
52 | } | ||
53 | |||
54 | /* Hashed indexing */ | ||
55 | static char * | ||
56 | _url_path_hashed (const char *spooldir, const char *user, int param) | ||
57 | { | ||
58 | int i; | ||
59 | int ulen = strlen (user); | ||
60 | char *mbox; | ||
61 | unsigned hash; | ||
62 | |||
63 | if (param > ulen) | ||
64 | param = ulen; | ||
65 | for (i = 0, hash = 0; i < param; i++) | ||
66 | hash += user[i]; | ||
67 | |||
68 | mbox = malloc (ulen + strlen (spooldir) + 5); | ||
69 | sprintf (mbox, "%s/%02X/%s", spooldir, hash % 256, user); | ||
70 | return mbox; | ||
71 | } | ||
72 | |||
73 | static int transtab[] = { | ||
74 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', | ||
75 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', | ||
76 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', | ||
77 | 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', | ||
78 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', | ||
79 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', | ||
80 | 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', | ||
81 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', | ||
82 | 'm', 'a', 'b', 'c', 'd', 'e', 'f', 'g', | ||
83 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | ||
84 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', | ||
85 | 'x', 'y', 'z', 'b', 'c', 'd', 'e', 'f', | ||
86 | 'g', 'a', 'b', 'c', 'd', 'e', 'f', 'g', | ||
87 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | ||
88 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', | ||
89 | 'x', 'y', 'z', 'b', 'c', 'd', 'e', 'f', | ||
90 | 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', | ||
91 | 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', | ||
92 | 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', | ||
93 | 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', | ||
94 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', | ||
95 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', | ||
96 | 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', | ||
97 | 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', | ||
98 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', | ||
99 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', | ||
100 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', | ||
101 | 'y', 'z', 'b', 'c', 'd', 'e', 'f', 'g', | ||
102 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', | ||
103 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', | ||
104 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', | ||
105 | 'y', 'z', 'b', 'c', 'd', 'e', 'f', 'g' | ||
106 | }; | ||
107 | |||
108 | /* Forward Indexing */ | ||
109 | static char * | ||
110 | _url_path_index (const char *spooldir, const char *iuser, int index_depth) | ||
111 | { | ||
112 | const unsigned char* user = (const unsigned char*) iuser; | ||
113 | int i, ulen = strlen (user); | ||
114 | char *mbox, *p; | ||
115 | |||
116 | if (ulen == 0) | ||
117 | return NULL; | ||
118 | |||
119 | mbox = malloc (ulen + strlen (spooldir) + 2*index_depth + 2); | ||
120 | strcpy (mbox, spooldir); | ||
121 | p = mbox + strlen (mbox); | ||
122 | for (i = 0; i < index_depth && i < ulen; i++) | ||
123 | { | ||
124 | *p++ = '/'; | ||
125 | *p++ = transtab[ user[i] ]; | ||
126 | } | ||
127 | for (; i < index_depth; i++) | ||
128 | { | ||
129 | *p++ = '/'; | ||
130 | *p++ = transtab[ user[ulen-1] ]; | ||
131 | } | ||
132 | *p++ = '/'; | ||
133 | strcpy (p, user); | ||
134 | return mbox; | ||
135 | } | ||
136 | |||
137 | /* Reverse Indexing */ | ||
138 | static char * | ||
139 | _url_path_rev_index (const char *spooldir, const char *iuser, int index_depth) | ||
140 | { | ||
141 | const unsigned char* user = (const unsigned char*) iuser; | ||
142 | int i, ulen = strlen (user); | ||
143 | char *mbox, *p; | ||
144 | |||
145 | if (ulen == 0) | ||
146 | return NULL; | ||
147 | |||
148 | mbox = malloc (ulen + strlen (spooldir) + 2*index_depth + 1); | ||
149 | strcpy (mbox, spooldir); | ||
150 | p = mbox + strlen (mbox); | ||
151 | for (i = 0; i < index_depth && i < ulen; i++) | ||
152 | { | ||
153 | *p++ = '/'; | ||
154 | *p++ = transtab[ user[ulen - i - 1] ]; | ||
155 | } | ||
156 | for (; i < index_depth; i++) | ||
157 | { | ||
158 | *p++ = '/'; | ||
159 | *p++ = transtab[ user[0] ]; | ||
160 | } | ||
161 | *p++ = '/'; | ||
162 | strcpy (p, user); | ||
163 | return mbox; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | UNIX Mbox | ||
168 | mbox:path[;type=TYPE][;param=PARAM][;user=USERNAME] | ||
169 | */ | ||
170 | int | ||
171 | _url_mbox_init (url_t url) | ||
172 | { | ||
173 | const char *name = url_to_string (url); | ||
174 | size_t len = strlen (name); | ||
175 | char *p; | ||
176 | |||
177 | /* reject the obvious */ | ||
178 | if (name == NULL || strncmp (MU_MBOX_SCHEME, name, MU_MBOX_SCHEME_LEN) != 0 | ||
179 | || len < (MU_MBOX_SCHEME_LEN + 1) /* (scheme)+1(path)*/) | ||
180 | return EINVAL; | ||
181 | |||
182 | /* do I need to decode url encoding '% hex hex' ? */ | ||
183 | |||
184 | /* TYPE */ | ||
185 | url->_destroy = url_mbox_destroy; | ||
186 | |||
187 | /* SCHEME */ | ||
188 | url->scheme = strdup (MU_MBOX_SCHEME); | ||
189 | if (url->scheme == NULL) | ||
190 | { | ||
191 | url_mbox_destroy (url); | ||
192 | return ENOMEM; | ||
193 | } | ||
194 | |||
195 | /* PATH */ | ||
196 | name += MU_MBOX_SCHEME_LEN; /* pass the scheme */ | ||
197 | url->path = strdup (name); | ||
198 | if (url->path == NULL) | ||
199 | { | ||
200 | url_mbox_destroy (url); | ||
201 | return ENOMEM; | ||
202 | } | ||
203 | p = strchr (url->path, ';'); | ||
204 | if (p) | ||
205 | { | ||
206 | char *(*fun)() = _url_path_default; | ||
207 | char *user = NULL; | ||
208 | int param = 0; | ||
209 | |||
210 | *p++ = 0; | ||
211 | while (p) | ||
212 | { | ||
213 | char *q = strchr (p, ';'); | ||
214 | if (q) | ||
215 | *q++ = 0; | ||
216 | if (strncasecmp (p, "type=", 5) == 0) | ||
217 | { | ||
218 | char *type = p + 5; | ||
219 | |||
220 | if (strcmp (type, "hash") == 0) | ||
221 | fun = _url_path_hashed; | ||
222 | else if (strcmp (type, "index") == 0) | ||
223 | fun = _url_path_index; | ||
224 | else if (strcmp (type, "rev-index") == 0) | ||
225 | fun = _url_path_rev_index; | ||
226 | else | ||
227 | { | ||
228 | url_mbox_destroy (url); | ||
229 | return ENOENT; | ||
230 | } | ||
231 | } | ||
232 | else if (strncasecmp (p, "user=", 5) == 0) | ||
233 | { | ||
234 | user = p + 5; | ||
235 | } | ||
236 | else if (strncasecmp (p, "param=", 6) == 0) | ||
237 | { | ||
238 | param = strtoul (p+6, NULL, 0); | ||
239 | } | ||
240 | p = q; | ||
241 | } | ||
242 | |||
243 | if (user) | ||
244 | { | ||
245 | p = fun (url->path, user, param); | ||
246 | free (url->path); | ||
247 | url->path = p; | ||
248 | } | ||
249 | else | ||
250 | { | ||
251 | url_mbox_destroy (url); | ||
252 | return ENOENT; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 |
mailbox/url_mh.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef ENABLE_MH | ||
23 | |||
24 | #include <errno.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | |||
28 | #include <url0.h> | ||
29 | #include <registrar0.h> | ||
30 | |||
31 | static void | ||
32 | url_mh_destroy (url_t url) | ||
33 | { | ||
34 | (void) url; | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | MH url | ||
39 | mh:path | ||
40 | */ | ||
41 | int | ||
42 | _url_mh_init (url_t url) | ||
43 | { | ||
44 | const char *name = url_to_string (url); | ||
45 | size_t len = strlen (name); | ||
46 | |||
47 | /* reject the obvious */ | ||
48 | if (name == NULL || strncmp (MU_MH_SCHEME, name, MU_MH_SCHEME_LEN) != 0 | ||
49 | || len < (MU_MH_SCHEME_LEN + 1) /* (scheme)+1(path)*/) | ||
50 | return EINVAL; | ||
51 | |||
52 | /* do I need to decode url encoding '% hex hex' ? */ | ||
53 | |||
54 | /* TYPE */ | ||
55 | url->_destroy = url_mh_destroy; | ||
56 | |||
57 | /* SCHEME */ | ||
58 | url->scheme = strdup (MU_MH_SCHEME); | ||
59 | if (url->scheme == NULL) | ||
60 | { | ||
61 | url_mh_destroy (url); | ||
62 | return ENOMEM; | ||
63 | } | ||
64 | |||
65 | /* PATH */ | ||
66 | name += MU_MH_SCHEME_LEN; /* pass the scheme */ | ||
67 | url->path = strdup (name); | ||
68 | if (url->path == NULL) | ||
69 | { | ||
70 | url_mh_destroy (url); | ||
71 | return ENOMEM; | ||
72 | } | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | #endif |
mailbox/url_pop.c
deleted
100644 → 0
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ | ||
17 | |||
18 | #ifdef HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #ifdef ENABLE_POP | ||
23 | |||
24 | #include <errno.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include <errno.h> | ||
28 | #ifdef HAVE_STRINGS_H | ||
29 | # include <strings.h> | ||
30 | #endif | ||
31 | |||
32 | #include <url0.h> | ||
33 | #include <registrar0.h> | ||
34 | |||
35 | static void url_pop_destroy (url_t url); | ||
36 | |||
37 | static void | ||
38 | url_pop_destroy (url_t url) | ||
39 | { | ||
40 | (void)url; | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | POP URL: | ||
45 | pop://[<user>[;AUTH=<auth>]@]<host>[:<port>] | ||
46 | or: | ||
47 | pop://[<user>[:pass]@]<host>[:<port>] | ||
48 | */ | ||
49 | |||
50 | int | ||
51 | _url_pop_init (url_t url) | ||
52 | { | ||
53 | int status = 0; | ||
54 | |||
55 | url->_destroy = url_pop_destroy; | ||
56 | |||
57 | status = url_parse(url); | ||
58 | |||
59 | if(status) | ||
60 | return status; | ||
61 | |||
62 | /* is it pop? */ | ||
63 | if (strcmp ("pop", url->scheme) != 0) | ||
64 | return EINVAL; | ||
65 | |||
66 | /* not valid in a pop url */ | ||
67 | if (url->path || url->query || !url->host) | ||
68 | return EINVAL; | ||
69 | |||
70 | if (url->port == 0) | ||
71 | url->port = MU_POP_PORT; | ||
72 | |||
73 | return status; | ||
74 | } | ||
75 | |||
76 | #endif |
-
Please register or sign in to post a comment