Commit 11bc148b 11bc148b1877b7c692f28d90847bc74737068416 by Sergey Poznyakoff

'Moved from ../'

1 parent 887e7b3b
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2002, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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 <mbox0.h>
25
26 #define ATTRIBUTE_IS_DELETED(flag) (flag & MU_ATTRIBUTE_DELETED)
27 #define ATTRIBUTE_IS_EQUAL(flag1, flag2) (flag1 == flag2)
28
29 static void mbox_destroy (mailbox_t);
30
31 /* Below are the headers field-names that we are caching for speed, it is
32 more or less the list of headers in ENVELOPE command from IMAP.
33
34 NOTE: These are indexed with H_.* macros defined in mbox0.h. Keep them
35 in sync if you add or remove something. */
36
37 const char *fhdr_table[HDRSIZE] =
38 {
39 "Bcc",
40 "Cc",
41 "Content-Language",
42 "Content-Transfer-Encoding",
43 "Content-Type",
44 "Date",
45 "From",
46 "In-Reply-To",
47 "Message-ID",
48 "Reference",
49 "Reply-To",
50 "Sender",
51 "Subject",
52 "To",
53 "X-UIDL"
54 };
55
56 /* Mailbox concrete implementation. */
57 static int mbox_open __P ((mailbox_t, int));
58 static int mbox_close __P ((mailbox_t));
59 static int mbox_get_message __P ((mailbox_t, size_t, message_t *));
60 /* static int mbox_get_message_by_uid __P ((mailbox_t, size_t, message_t *)); */
61 static int mbox_append_message __P ((mailbox_t, message_t));
62 static int mbox_messages_count __P ((mailbox_t, size_t *));
63 static int mbox_messages_recent __P ((mailbox_t, size_t *));
64 static int mbox_message_unseen __P ((mailbox_t, size_t *));
65 static int mbox_expunge0 __P ((mailbox_t, int));
66 static int mbox_expunge __P ((mailbox_t));
67 static int mbox_save_attributes __P ((mailbox_t));
68 static int mbox_uidvalidity __P ((mailbox_t, unsigned long *));
69 static int mbox_uidnext __P ((mailbox_t, size_t *));
70 static int mbox_scan __P ((mailbox_t, size_t, size_t *));
71 static int mbox_is_updated __P ((mailbox_t));
72 static int mbox_get_size __P ((mailbox_t, off_t *));
73
74 /* private stuff */
75 static int mbox_append_message0 __P ((mailbox_t, message_t, off_t *,
76 int, int));
77 static int mbox_message_uid __P ((message_t, size_t *));
78 static int mbox_header_fill __P ((header_t, char *, size_t, off_t,
79 size_t *));
80 static int mbox_get_body_fd __P ((stream_t, int *));
81 static int mbox_get_fd __P ((mbox_message_t, int *));
82 static int mbox_get_attr_flags __P ((attribute_t, int *));
83 static int mbox_set_attr_flags __P ((attribute_t, int));
84 static int mbox_unset_attr_flags __P ((attribute_t, int));
85 static int mbox_body_read __P ((stream_t, char *, size_t, off_t,
86 size_t *));
87 static int mbox_body_readline __P ((stream_t, char *, size_t, off_t,
88 size_t *));
89 static int mbox_readstream __P ((mbox_message_t, char *, size_t,
90 off_t, size_t *, int, off_t,
91 off_t));
92 static int mbox_stream_size __P((stream_t stream, off_t *psize));
93
94 static int mbox_header_size __P ((header_t, size_t *));
95 static int mbox_header_lines __P ((header_t, size_t *));
96 static int mbox_body_size __P ((body_t, size_t *));
97 static int mbox_body_lines __P ((body_t, size_t *));
98 static int mbox_envelope_sender __P ((envelope_t, char *, size_t,
99 size_t *));
100 static int mbox_envelope_date __P ((envelope_t, char *, size_t,
101 size_t *));
102 static int mbox_tmpfile __P ((mailbox_t, char **pbox));
103
104 /* Allocate the mbox_data_t struct(concrete mailbox), but don't do any
105 parsing on the name or even test for existence. However we do strip any
106 leading "mbox:" part of the name, this is suppose to be the
107 protocol/scheme name. */
108 int
109 _mailbox_mbox_init (mailbox_t mailbox)
110 {
111 mbox_data_t mud;
112 size_t name_len;
113
114 if (mailbox == NULL)
115 return EINVAL;
116
117 /* Allocate specific mbox data. */
118 mud = mailbox->data = calloc (1, sizeof (*mud));
119 if (mailbox->data == NULL)
120 return ENOMEM;
121
122 /* Back pointer. */
123 mud->mailbox = mailbox;
124
125 /* Copy the name:
126 We do not do any further interpretation after the scheme "mbox:"
127 Because for example on distributed system like QnX4 a file name is
128 //390/etc/passwd. So the best approach is to let the OS handle it
129 for example if we receive: "mbox:///var/mail/alain" the mailbox name
130 will be "///var/mail/alain", we let open() do the right thing.
131 So it will let things like this "mbox://390/var/mail/alain" where
132 the "//" _is_ part of the filename, pass correctely. */
133 url_get_path (mailbox->url, NULL, 0, &name_len);
134 mud->name = calloc (name_len + 1, sizeof (char));
135 if (mud->name == NULL)
136 {
137 free (mud);
138 mailbox->data = NULL;
139 return ENOMEM;
140 }
141 url_get_path (mailbox->url, mud->name, name_len + 1, NULL);
142
143 mud->state = MBOX_NO_STATE;
144
145 /* Overloading the defaults. */
146 mailbox->_destroy = mbox_destroy;
147
148 mailbox->_open = mbox_open;
149 mailbox->_close = mbox_close;
150
151 /* Overloading of the entire mailbox object methods. */
152 mailbox->_get_message = mbox_get_message;
153 mailbox->_append_message = mbox_append_message;
154 mailbox->_messages_count = mbox_messages_count;
155 mailbox->_messages_recent = mbox_messages_recent;
156 mailbox->_message_unseen = mbox_message_unseen;
157 mailbox->_expunge = mbox_expunge;
158 mailbox->_save_attributes = mbox_save_attributes;
159 mailbox->_uidvalidity = mbox_uidvalidity;
160 mailbox->_uidnext = mbox_uidnext;
161
162 mailbox->_scan = mbox_scan;
163 mailbox->_is_updated = mbox_is_updated;
164
165 mailbox->_get_size = mbox_get_size;
166
167 /* Set our properties. */
168 {
169 property_t property = NULL;
170 mailbox_get_property (mailbox, &property);
171 property_set_value (property, "TYPE", "MBOX", 1);
172 }
173
174 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_init(%s)\n", mud->name);
175 return 0; /* okdoke */
176 }
177
178 /* Free all ressources associated with Unix concrete mailbox. */
179 static void
180 mbox_destroy (mailbox_t mailbox)
181 {
182 if (mailbox->data)
183 {
184 size_t i;
185 mbox_data_t mud = mailbox->data;
186 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE,
187 "mbox_destroy (%s)\n", mud->name);
188 monitor_wrlock (mailbox->monitor);
189 for (i = 0; i < mud->umessages_count; i++)
190 {
191 mbox_message_t mum = mud->umessages[i];
192 if (mum)
193 {
194 size_t j;
195 message_destroy (&(mum->message), mum);
196 for (j = 0; j < HDRSIZE; j++)
197 if (mum->fhdr[j])
198 free (mum->fhdr[j]);
199 free (mum);
200 }
201 }
202 if (mud->umessages)
203 free (mud->umessages);
204 if (mud->name)
205 free (mud->name);
206 free (mud);
207 mailbox->data = NULL;
208 monitor_unlock (mailbox->monitor);
209 }
210 }
211
212 /* Open the file. For MU_STREAM_READ, the code tries mmap() first and fall
213 back to normal file. */
214 static int
215 mbox_open (mailbox_t mailbox, int flags)
216 {
217 mbox_data_t mud = mailbox->data;
218 int status = 0;
219
220 if (mud == NULL)
221 return EINVAL;
222
223 mailbox->flags = flags;
224
225 /* Get a stream. */
226 if (mailbox->stream == NULL)
227 {
228 /* We do not try to mmap for CREAT or APPEND, it is not supported. */
229 status = (flags & MU_STREAM_CREAT)
230 || (mailbox->flags & MU_STREAM_APPEND);
231
232 /* Try to mmap () the file first. */
233 if (status == 0)
234 {
235 status = mapfile_stream_create (&mailbox->stream, mud->name, mailbox->flags);
236 if (status == 0)
237 {
238 status = stream_open (mailbox->stream);
239 }
240 }
241
242 /* Fall back to normal file if mmap() failed. */
243 if (status != 0)
244 {
245 status = file_stream_create (&mailbox->stream, mud->name, mailbox->flags);
246 if (status != 0)
247 return status;
248 status = stream_open (mailbox->stream);
249 }
250 /* All failed, bail out. */
251 if (status != 0)
252 {
253 stream_destroy (&mailbox->stream, NULL);
254 return status;
255 }
256 /* Even on top, of normal FILE *, lets agressively cache. But this
257 may not be suitable for system tight on memory. */
258 stream_setbufsiz (mailbox->stream, BUFSIZ);
259 }
260 else
261 {
262 status = stream_open (mailbox->stream);
263 if (status != 0)
264 return status;
265 }
266
267 MAILBOX_DEBUG2 (mailbox, MU_DEBUG_TRACE, "mbox_open(%s, 0x%x)\n",
268 mud->name, mailbox->flags);
269
270 if (mailbox->locker == NULL)
271 status = locker_create (&(mailbox->locker), mud->name, 0);
272 return status;
273 }
274
275 static int
276 mbox_close (mailbox_t mailbox)
277 {
278 mbox_data_t mud = mailbox->data;
279 /* size_t i; */
280
281 if (mud == NULL)
282 return EINVAL;
283
284 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_close(%s)\n", mud->name);
285
286 /* Make sure that we do not hold any file locking. */
287 locker_unlock (mailbox->locker);
288
289 #if 0
290 /* RFC: I'm not sure on the right approach especially if the client is
291 working in disconnected mode, where it can mailbox_close/mailbox_open
292 for each request, maybe we should keep them for a while. */
293 monitor_wrlock (mailbox->monitor);
294 /* Before closing we need to remove all the messages
295 - to reclaim the memory
296 - to prepare for another scan. */
297 for (i = 0; i < mud->umessages_count; i++)
298 {
299 mbox_message_t mum = mud->umessages[i];
300 /* Destroy the attach messages. */
301 if (mum)
302 {
303 size_t j;
304 message_destroy (&(mum->message), mum);
305 for (j = 0; j < HDRSIZE; j++)
306 if (mum->fhdr[j])
307 free (mum->fhdr[j]);
308 free (mum);
309 }
310 }
311 if (mud->umessages)
312 free (mud->umessages);
313 mud->umessages = NULL;
314 mud->messages_count = mud->umessages_count = 0;
315 mud->size = 0;
316 mud->uidvalidity = 0;
317 mud->uidnext = 0;
318 monitor_unlock (mailbox->monitor);
319 #endif
320 return stream_close (mailbox->stream);
321 }
322
323 /* Cover function that call the real thing, mbox_scan(), with
324 notification set. */
325 static int
326 mbox_scan (mailbox_t mailbox, size_t msgno, size_t *pcount)
327 {
328 size_t i;
329 mbox_data_t mud = mailbox->data;
330 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_scan(%s)\n", mud->name);
331 if (! mbox_is_updated (mailbox))
332 return mbox_scan0 (mailbox, msgno, pcount, 1);
333 /* Since the mailbox is already updated fake the scan. */
334 if (msgno > 0)
335 msgno--; /* The fist message is number "1", decremente for the C array. */
336 for (i = msgno; i < mud->messages_count; i++)
337 {
338 if (observable_notify (mailbox->observable, MU_EVT_MESSAGE_ADD) != 0)
339 break;
340 if (((i +1) % 50) == 0)
341 {
342 observable_notify (mailbox->observable, MU_EVT_MAILBOX_PROGRESS);
343 }
344 }
345 return 0;
346 }
347
348 /* FIXME: How to handle a shrink ? meaning, the &^$^@%#@^& user start two
349 browsers and deleted emails in one session. My views is that we should
350 scream bloody murder and hunt them with a machette. But for now just play
351 dumb, but maybe the best approach is to pack our things and leave
352 .i.e exit()/abort(). */
353 static int
354 mbox_is_updated (mailbox_t mailbox)
355 {
356 off_t size = 0;
357 mbox_data_t mud = mailbox->data;
358 if (stream_size (mailbox->stream, &size) != 0)
359 return 0;
360 if (size < mud->size)
361 {
362 observable_notify (mailbox->observable, MU_EVT_MAILBOX_CORRUPT);
363 /* And be verbose. ? */
364 mu_error ("* BAD : Mailbox corrupted, shrank size\n");
365 /* FIXME: should I crash. */
366 return 0;
367 }
368 return (mud->size == size);
369 }
370
371 /* Try to create an uniq file, we no race conditions. */
372 static int
373 mbox_tmpfile (mailbox_t mailbox, char **pbox)
374 {
375 const char *tmpdir;
376 int fd;
377 const char *basename;
378 mbox_data_t mud = mailbox->data;
379
380 /* P_tmpdir should be in <stdio.h>. */
381 #ifndef P_tmpdir
382 # define P_tmpdir "/tmp"
383 #endif
384
385 basename = strrchr (mud->name, '/');
386 if (basename)
387 basename++;
388 else
389 basename = mud->name;
390
391 tmpdir = getenv ("TMPDIR") ? getenv ("TMPDIR") : P_tmpdir;
392 /* (separator + null) == 2 + XXXXXX == 6 + ... */
393 *pbox = calloc (strlen (tmpdir) + /* '/' */ 1 + /*strlen ("MU_")*/ 3 +
394 strlen (basename) + /*strlen ("_XXXXXX")*/ 7 + /*null*/1,
395 sizeof (**pbox));
396 if (*pbox == NULL)
397 return -1;
398 sprintf (*pbox, "%s/MU_%s_XXXXXX", tmpdir, basename);
399 #ifdef HAVE_MKSTEMP
400 fd = mkstemp (*pbox);
401 #else
402 /* Create the file. It must not exist. If it does exist, fail. */
403 if (mktemp (*pbox))
404 {
405 fd = open (*pbox, O_RDWR|O_CREAT|O_EXCL, 0600);
406 }
407 else
408 {
409 free (*pbox);
410 fd = -1;
411 }
412 #endif
413 return fd;
414 }
415
416 /* For the expunge bits we took a very cautionnary approach, meaning
417 we create a temporary mailbox in the tmpdir copy all the message not mark
418 deleted(Actually we copy all the message that may have been modified
419 i.e new header values set; UIDL or UID or etc ....
420 and skip the deleted ones, truncate the real mailbox to the desired size
421 and overwrite with the tmp mailbox. The approach to do everyting
422 in core is tempting but require
423 - to much memory, it is not rare nowadays to have 30 Megs mailbox,
424 - also there is danger for filesystems with quotas,
425 - if we run out of disk space everything is lost.
426 - or some program may not respect the advisory lock and decide to append
427 a new message while your expunging etc ...
428 The real downside to the approach is that when things go wrong
429 the temporary file may be left in /tmp, which is not all that bad
430 because at least, we have something to recuperate when failure. */
431 static int
432 mbox_expunge0 (mailbox_t mailbox, int remove_deleted)
433 {
434 mbox_data_t mud = mailbox->data;
435 mbox_message_t mum;
436 int status = 0;
437 sigset_t signalset;
438 int tempfile;
439 size_t i, j, dirty; /* dirty will indicate the first modified message. */
440 off_t marker = 0; /* marker will be the position to truncate. */
441 off_t total = 0;
442 char *tmpmboxname = NULL;
443 mailbox_t tmpmailbox = NULL;
444 size_t save_imapbase = 0; /* uidvalidity is save in the first message. */
445 #ifdef WITH_PTHREAD
446 int state;
447 #endif
448
449 if (mud == NULL)
450 return EINVAL;
451
452 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_expunge (%s)\n", mud->name);
453
454 /* Noop. */
455 if (mud->messages_count == 0)
456 return 0;
457
458 /* Find the first dirty(modified) message. */
459 for (dirty = 0; dirty < mud->messages_count; dirty++)
460 {
461 mum = mud->umessages[dirty];
462 /* Message may have been tampered, break here. */
463 if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) ||
464 (mum->attr_flags & MU_ATTRIBUTE_DELETED) ||
465 (mum->message && message_is_modified (mum->message)))
466 break;
467 }
468
469 /* Did something change ? */
470 if (dirty == mud->messages_count)
471 return 0; /* Nothing change, bail out. */
472
473 /* Create a temporary file. */
474 tempfile = mbox_tmpfile (mailbox, &tmpmboxname);
475 if (tempfile == -1)
476 {
477 if (tmpmboxname)
478 free (tmpmboxname);
479 mu_error ("Failed to create temporary file when expunging.\n");
480 return errno;
481 }
482
483 /* This is redundant, we go to the loop again. But it's more secure here
484 since we don't want to be disturb when expunging. Destroy all the
485 messages mark for deletion. */
486 if (remove_deleted)
487 {
488 for (j = 0; j < mud->messages_count; j++)
489 {
490 mum = mud->umessages[j];
491 if (mum && mum->message && ATTRIBUTE_IS_DELETED (mum->attr_flags))
492 message_destroy (&(mum->message), mum);
493 }
494 }
495
496 /* Create temporary mailbox_t. */
497 {
498 mbox_data_t tmp_mud;
499 char *m = alloca (5 + strlen (tmpmboxname) + 1);
500 /* Try via the mbox: protocol. */
501 sprintf (m, "mbox:%s", tmpmboxname);
502 status = mailbox_create (&tmpmailbox, m);
503 if (status != 0)
504 {
505 /* Do not give up just yet, maybe they register the path_record. */
506 sprintf (m, "%s", tmpmboxname);
507 status = mailbox_create (&tmpmailbox, m);
508 if (status != 0)
509 {
510 /* Ok give up. */
511 close (tempfile);
512 remove (tmpmboxname);
513 free (tmpmboxname);
514 return status;
515 }
516 }
517
518 /* Must be flag CREATE if not the mailbox_open will try to mmap()
519 the file. */
520 status = mailbox_open (tmpmailbox, MU_STREAM_CREAT | MU_STREAM_RDWR);
521 if (status != 0)
522 {
523 close (tempfile);
524 remove (tmpmboxname);
525 free (tmpmboxname);
526 return status;
527 }
528 close (tempfile); /* This one is useless the mailbox have its own. */
529 tmp_mud = tmpmailbox->data;
530 /* May need when appending. */
531 tmp_mud->uidvalidity = mud->uidvalidity;
532 tmp_mud->uidnext = mud->uidnext;
533 }
534
535 /* Get the File lock. */
536 if ((status = locker_lock (mailbox->locker)) != 0)
537 {
538 mailbox_close (tmpmailbox);
539 mailbox_destroy (&tmpmailbox);
540 remove (tmpmboxname);
541 free (tmpmboxname);
542 mu_error ("Failed to grab the lock: %s\n", mu_strerror(status));
543 return status;
544 }
545
546 /* Critical section, we can not allowed signal here. */
547 #ifdef WITH_PTHREAD
548 pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
549 #endif
550 sigemptyset (&signalset);
551 sigaddset (&signalset, SIGTERM);
552 sigaddset (&signalset, SIGHUP);
553 sigaddset (&signalset, SIGTSTP);
554 sigaddset (&signalset, SIGINT);
555 sigaddset (&signalset, SIGWINCH);
556 sigprocmask (SIG_BLOCK, &signalset, 0);
557
558 /* Set the marker position. */
559 marker = mud->umessages[dirty]->header_from;
560 total = 0;
561
562 /* Copy to the temporary mailbox emails not mark deleted. */
563 for (i = dirty; i < mud->messages_count; i++)
564 {
565 mum = mud->umessages[i];
566
567 /* Skip it, if mark for deletion. */
568 if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags))
569 {
570 /* We save the uidvalidity in the first message, if it is being
571 deleted we need to move the uidvalidity to the first available
572 (non-deleted) message. */
573 if (i == save_imapbase)
574 {
575 save_imapbase = i + 1;
576 if (save_imapbase < mud->messages_count)
577 (mud->umessages[save_imapbase])->attr_flags |= MU_ATTRIBUTE_MODIFIED;
578 }
579 continue;
580 }
581
582 /* Do the expensive mbox_append_message0() only if mark dirty. */
583 if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) ||
584 (mum->message && message_is_modified (mum->message)))
585 {
586 /* The message was not instanciated, probably the dirty flag was
587 set by mbox_scan(), create one here. */
588 if (mum->message == 0)
589 {
590 message_t msg;
591 status = mbox_get_message (mailbox, i + 1, &msg);
592 if (status != 0)
593 {
594 mu_error ("Error expunge:%d: %s", __LINE__,
595 mu_strerror (status));
596 goto bailout0;
597 }
598 }
599 status = mbox_append_message0 (tmpmailbox, mum->message,
600 &total, 1, (i == save_imapbase));
601 if (status != 0)
602 {
603 mu_error ("Error expunge:%d: %s", __LINE__,
604 mu_strerror (status));
605 goto bailout0;
606 }
607 /* Clear the dirty bits. */
608 mum->attr_flags &= ~MU_ATTRIBUTE_MODIFIED;
609 message_clear_modified (mum->message);
610 }
611 else
612 {
613 /* Nothing changed copy the message straight. */
614 char buffer [1024];
615 size_t n;
616 off_t offset = mum->header_from;
617 size_t len = mum->body_end - mum->header_from;
618 while (len > 0)
619 {
620 n = (len < sizeof (buffer)) ? len : sizeof (buffer);
621 if ((status = stream_read (mailbox->stream, buffer, n, offset,
622 &n) != 0)
623 || (status = stream_write (tmpmailbox->stream, buffer, n,
624 total, &n) != 0))
625 {
626 mu_error ("Error expunge:%d: %s", __LINE__,
627 mu_strerror (status));
628 goto bailout0;
629 }
630 len -= n;
631 total += n;
632 offset += n;
633 }
634 /* Add the newline separator. */
635 status = stream_write (tmpmailbox->stream, "\n", 1, total, &n);
636 if (status != 0)
637 {
638 mu_error ("Error expunge:%d: %s", __LINE__,
639 mu_strerror (status));
640 goto bailout0;
641 }
642 total++;
643 }
644 } /* for (;;) */
645
646 /* Caution: before ftruncate()ing the file see
647 - if we've receive new mails. Some programs may not respect the lock,
648 - or the lock was held for too long.
649 - The mailbox may not have been properly updated before expunging. */
650 {
651 off_t size = 0;
652 if (stream_size (mailbox->stream, &size) == 0)
653 {
654 off_t len = size - mud->size;
655 off_t offset = mud->size;
656 char buffer [1024];
657 size_t n = 0;
658 if (len > 0 )
659 {
660 while ((status = stream_read (mailbox->stream, buffer,
661 sizeof (buffer), offset, &n)) == 0
662 && n > 0)
663 {
664 status = stream_write (tmpmailbox->stream, buffer, n,
665 total, &n);
666 if (status != 0)
667 {
668 mu_error ("Error expunge:%d: %s", __LINE__,
669 mu_strerror (status));
670 goto bailout0;
671 }
672 total += n;
673 offset += n;
674 }
675 }
676 else if (len < 0)
677 {
678 /* Corrupted mailbox. */
679 mu_error ("Error expunge:%d: %s", __LINE__,
680 mu_strerror (status));
681 goto bailout0;
682 }
683 }
684 } /* End of precaution. */
685
686 /* Seek and rewrite it. */
687 if (total > 0)
688 {
689 char buffer [1024];
690 size_t n = 0;
691 off_t off = 0;
692 off_t offset = marker;
693 while ((status = stream_read (tmpmailbox->stream, buffer,
694 sizeof (buffer), off, &n)) == 0
695 && n > 0)
696 {
697 status = stream_write (mailbox->stream, buffer, n, offset, &n);
698 if (status != 0)
699 {
700 mu_error ("Error expunge:%d: %s\n", __LINE__,
701 mu_strerror (status));
702 goto bailout;
703 }
704 off += n;
705 offset += n;
706 }
707 }
708
709 /* Flush/truncation. Need to flush before truncate. */
710 stream_flush (mailbox->stream);
711 status = stream_truncate (mailbox->stream, total + marker);
712 if (status != 0)
713 {
714 mu_error ("Error expunging:%d: %s\n", __LINE__,
715 mu_strerror (status));
716 goto bailout;
717 }
718
719 /* Don't remove the tmp mbox in case of errors, when writing back. */
720 bailout0:
721 remove (tmpmboxname);
722
723 bailout:
724
725 free (tmpmboxname);
726 /* Release the File lock. */
727 locker_unlock (mailbox->locker);
728 mailbox_close (tmpmailbox);
729 mailbox_destroy (&tmpmailbox);
730
731 /* Reenable signal. */
732 #ifdef WITH_PTHREAD
733 pthread_setcancelstate (state, &state);
734 #endif
735 sigprocmask (SIG_UNBLOCK, &signalset, 0);
736
737 /* We need to readjust the pointers.
738 It is a little hairy because we need to keep the message pointers alive
739 So we are going through the array and "defragmentize". For example
740 in (1 2 3 4) if 2 was deleted we need to move 3 4 up by one etc ..
741 */
742 if (status == 0)
743 {
744 size_t dlast;
745 monitor_wrlock (mailbox->monitor);
746 for (j = dirty, dlast = mud->messages_count - 1;
747 j <= dlast; j++)
748 {
749 /* Clear all the references, any attach messages been already
750 destroy above. */
751 mum = mud->umessages[j];
752 if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags))
753 {
754 if ((j + 1) <= dlast)
755 {
756 /* Move all the pointers up. So the message pointer
757 part of mum will be at the right position. */
758 memmove (mud->umessages + j, mud->umessages + j + 1,
759 (dlast - j) * sizeof (mum));
760 #if 0
761 mum->header_from = mum->header_from_end = 0;
762 mum->body = mum->body_end = 0;
763 mum->header_lines = mum->body_lines = 0;
764 #endif
765 for (i = 0; i < HDRSIZE; i++)
766 if (mum->fhdr[i])
767 {
768 free (mum->fhdr[i]);
769 mum->fhdr[i] = NULL;
770 }
771 memset (mum, 0, sizeof (*mum));
772 /* We are not free()ing the useless mum, but instead
773 we put it back in the pool, to be reuse. */
774 mud->umessages[dlast] = mum;
775 dlast--;
776 /* Set mum to the new value after the memmove so it
777 gets cleared to. */
778 mum = mud->umessages[j];
779 }
780 else
781 {
782 for (i = 0; i < HDRSIZE; i++)
783 if (mum->fhdr[i])
784 {
785 free (mum->fhdr[i]);
786 mum->fhdr[i] = NULL;
787 }
788 memset (mum, 0, sizeof (*mum));
789 }
790 }
791 mum->header_from = mum->header_from_end = 0;
792 mum->body = mum->body_end = 0;
793 mum->header_lines = mum->body_lines = 0;
794 for (i = 0; i < HDRSIZE; i++)
795 if (mum->fhdr[i])
796 {
797 free (mum->fhdr[i]);
798 mum->fhdr[i] = NULL;
799 }
800 }
801 monitor_unlock (mailbox->monitor);
802 /* This is should reset the messages_count, the last argument 0 means
803 not to send event notification. */
804 mbox_scan0 (mailbox, dirty, NULL, 0);
805 }
806 return status;
807 }
808
809 static int
810 mbox_expunge (mailbox_t mailbox)
811 {
812 return mbox_expunge0 (mailbox, 1);
813 }
814
815 static int
816 mbox_save_attributes (mailbox_t mailbox)
817 {
818 return mbox_expunge0 (mailbox, 0);
819 }
820
821 static int
822 mbox_message_uid (message_t msg, size_t *puid)
823 {
824 mbox_message_t mum = message_get_owner (msg);
825 if (puid)
826 *puid = mum->uid;
827 return 0;
828 }
829
830 static int
831 mbox_get_body_fd (stream_t is, int *pfd)
832 {
833 body_t body = stream_get_owner (is);
834 message_t msg = body_get_owner (body);
835 mbox_message_t mum = message_get_owner (msg);
836 return mbox_get_fd (mum, pfd);
837 }
838
839 static int
840 mbox_get_fd (mbox_message_t mum, int *pfd)
841 {
842 int status;
843 if (mum == NULL)
844 return EINVAL;
845 status = stream_get_fd (mum->mud->mailbox->stream, pfd);
846 return status;
847 }
848
849 static int
850 mbox_get_attr_flags (attribute_t attr, int *pflags)
851 {
852 message_t msg = attribute_get_owner (attr);
853 mbox_message_t mum = message_get_owner (msg);
854
855 if (mum == NULL)
856 return EINVAL;
857 if (pflags)
858 *pflags = mum->attr_flags;
859 return 0;
860 }
861
862 static int
863 mbox_set_attr_flags (attribute_t attr, int flags)
864 {
865 message_t msg = attribute_get_owner (attr);
866 mbox_message_t mum = message_get_owner (msg);
867
868 if (mum == NULL)
869 return EINVAL;
870 mum->attr_flags |= flags;
871 return 0;
872 }
873
874 static int
875 mbox_unset_attr_flags (attribute_t attr, int flags)
876 {
877 message_t msg = attribute_get_owner (attr);
878 mbox_message_t mum = message_get_owner (msg);
879
880 if (mum == NULL)
881 return EINVAL;
882 mum->attr_flags &= ~flags;
883 return 0;
884 }
885
886 static int
887 mbox_body_readline (stream_t is, char *buffer, size_t buflen,
888 off_t off, size_t *pnread)
889 {
890 body_t body = stream_get_owner (is);
891 message_t msg = body_get_owner (body);
892 mbox_message_t mum = message_get_owner (msg);
893
894 return mbox_readstream (mum, buffer, buflen, off, pnread, 1,
895 mum->body, mum->body_end);
896 }
897
898 static int
899 mbox_body_read (stream_t is, char *buffer, size_t buflen,
900 off_t off, size_t *pnread)
901 {
902 body_t body = stream_get_owner (is);
903 message_t msg = body_get_owner (body);
904 mbox_message_t mum = message_get_owner (msg);
905 return mbox_readstream (mum, buffer, buflen, off, pnread, 0,
906 mum->body, mum->body_end);
907 }
908
909 static int
910 mbox_readstream (mbox_message_t mum, char *buffer, size_t buflen,
911 off_t off, size_t *pnread, int isreadline,
912 off_t start, off_t end)
913 {
914 size_t nread = 0;
915 int status = 0;
916
917 if (buffer == NULL || buflen == 0)
918 {
919 if (pnread)
920 *pnread = nread;
921 return 0;
922 }
923
924 monitor_rdlock (mum->mud->mailbox->monitor);
925 #ifdef WITH_PTHREAD
926 /* read() is cancellation point since we're doing a potentially
927 long operation. Lets make sure we clean the state. */
928 pthread_cleanup_push (mbox_cleanup, (void *)mum->mud->mailbox);
929 #endif
930 {
931 off_t ln = end - (start + off);
932 if (ln > 0)
933 {
934 /* Position the file pointer and the buffer. */
935 nread = ((size_t)ln < buflen) ? (size_t)ln : buflen;
936 if (isreadline)
937 status = stream_readline (mum->mud->mailbox->stream, buffer, buflen,
938 start + off, &nread);
939 else
940 status = stream_read (mum->mud->mailbox->stream, buffer, nread,
941 start + off, &nread);
942 }
943 }
944 monitor_unlock (mum->mud->mailbox->monitor);
945 #ifdef WITH_PTHREAD
946 pthread_cleanup_pop (0);
947 #endif
948
949 if (pnread)
950 *pnread = nread;
951 return status;
952 }
953
954 static int
955 mbox_header_fill (header_t header, char *buffer, size_t len,
956 off_t off, size_t *pnread)
957 {
958 message_t msg = header_get_owner (header);
959 mbox_message_t mum = message_get_owner (msg);
960 size_t j;
961 /* Since we are filling the header there is no need for the cache headers
962 discard them. */
963 for (j = 0; j < HDRSIZE; j++)
964 {
965 if (mum->fhdr[j])
966 {
967 free (mum->fhdr[j]);
968 mum->fhdr[j] = NULL;
969 }
970 }
971 return mbox_readstream (mum, buffer, len, off, pnread, 0,
972 mum->header_from_end, mum->body);
973 }
974
975 static int
976 mbox_header_get_fvalue (header_t header, const char *name, char *buffer,
977 size_t buflen, size_t *pnread)
978 {
979 size_t i, fv_len = 0;
980 message_t msg = header_get_owner (header);
981 mbox_message_t mum = message_get_owner (msg);
982 int err = ENOENT;
983 for (i = 0; i < HDRSIZE; i++)
984 {
985 if (*name == *(fhdr_table[i]) && strcasecmp (fhdr_table[i], name) == 0)
986 {
987 if (mum->fhdr[i])
988 {
989 fv_len = strlen (mum->fhdr[i]);
990 if (buffer && buflen > 0)
991 {
992 /* For the null. */
993 buflen--;
994 fv_len = (fv_len < buflen) ? fv_len : buflen;
995 memcpy (buffer, mum->fhdr[i], fv_len);
996 buffer[fv_len] = '\0';
997 }
998 err = 0;
999 }
1000 else
1001 err = ENOENT;
1002 break;
1003 }
1004 }
1005
1006 if (pnread)
1007 *pnread = fv_len;
1008 return err;
1009 }
1010
1011 static int
1012 mbox_header_size (header_t header, size_t *psize)
1013 {
1014 message_t msg = header_get_owner (header);
1015 mbox_message_t mum = message_get_owner (msg);
1016 if (mum == NULL)
1017 return EINVAL;
1018 if (psize)
1019 *psize = mum->body - mum->header_from_end;
1020 return 0;
1021 }
1022
1023 static int
1024 mbox_header_lines (header_t header, size_t *plines)
1025 {
1026 message_t msg = header_get_owner (header);
1027 mbox_message_t mum = message_get_owner (msg);
1028 if (mum == NULL)
1029 return EINVAL;
1030 if (plines)
1031 *plines = mum->header_lines;
1032 return 0;
1033 }
1034
1035 static int
1036 mbox_body_size (body_t body, size_t *psize)
1037 {
1038 message_t msg = body_get_owner (body);
1039 mbox_message_t mum = message_get_owner (msg);
1040 if (mum == NULL)
1041 return EINVAL;
1042 if (psize)
1043 *psize = mum->body_end - mum->body;
1044 return 0;
1045 }
1046
1047 static int
1048 mbox_stream_size (stream_t stream, off_t *psize)
1049 {
1050 body_t body = stream_get_owner (stream);
1051 return mbox_body_size (body, (size_t*) psize);
1052 }
1053
1054 static int
1055 mbox_body_lines (body_t body, size_t *plines)
1056 {
1057 message_t msg = body_get_owner (body);
1058 mbox_message_t mum = message_get_owner (msg);
1059 if (mum == NULL)
1060 return EINVAL;
1061 if (plines)
1062 *plines = mum->body_lines;
1063 return 0;
1064 }
1065
1066 static int
1067 mbox_envelope_date (envelope_t envelope, char *buf, size_t len,
1068 size_t *pnwrite)
1069 {
1070 message_t msg = envelope_get_owner (envelope);
1071 mbox_message_t mum = message_get_owner (msg);
1072 size_t n = 0;
1073 int status;
1074 char buffer[512];
1075 char *s;
1076
1077 if (mum == NULL)
1078 return EINVAL;
1079
1080 status = stream_readline (mum->mud->mailbox->stream, buffer, sizeof(buffer),
1081 mum->header_from, &n);
1082 if (status != 0)
1083 {
1084 if (pnwrite)
1085 *pnwrite = 0;
1086 return status;
1087 }
1088
1089 /* Format: "From [sender] [date]" */
1090 /* strlen ("From ") == 5 */
1091 if (n > 5 && (s = strchr (buffer + 5, ' ')) != NULL)
1092 {
1093 if (buf && len > 0)
1094 {
1095 len--; /* Leave space for the null. */
1096 strncpy (buf, s + 1, len)[len] = '\0';
1097 len = strlen (buf);
1098 }
1099 else
1100 len = strlen (s + 1);
1101 }
1102 else
1103 len = 0;
1104
1105 if (pnwrite)
1106 *pnwrite = len;
1107 return 0;
1108 }
1109
1110 static int
1111 mbox_envelope_sender (envelope_t envelope, char *buf, size_t len,
1112 size_t *pnwrite)
1113 {
1114 message_t msg = envelope_get_owner (envelope);
1115 mbox_message_t mum = message_get_owner (msg);
1116 size_t n = 0;
1117 int status;
1118 char buffer[512];
1119 char *s;
1120
1121 if (mum == NULL)
1122 return EINVAL;
1123
1124 status = stream_readline (mum->mud->mailbox->stream, buffer, sizeof(buffer),
1125 mum->header_from, &n);
1126 if (status != 0)
1127 {
1128 if (pnwrite)
1129 *pnwrite = 0;
1130 return status;
1131 }
1132
1133 /* Format: "From [sender] [date]" */
1134 /* strlen ("From ") == 5 */
1135 if (n > 5 && (s = strchr (buffer + 5, ' ')) != NULL)
1136 {
1137 /* Put a NULL to isolate the sender string, make a C string. */
1138 *s = '\0';
1139 if (buf && len > 0)
1140 {
1141 len--; /* leave space for the null */
1142 strncpy (buf, buffer + 5, len)[len] = '\0';
1143 len = strlen (buf);
1144 }
1145 else
1146 len = strlen (buffer + 5);
1147 }
1148 else
1149 len = 0;
1150
1151 if (pnwrite)
1152 *pnwrite = len;
1153 return 0;
1154 }
1155
1156 static int
1157 mbox_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
1158 {
1159 int status;
1160 mbox_data_t mud = mailbox->data;
1161 mbox_message_t mum;
1162 message_t msg = NULL;
1163
1164 /* Sanity checks. */
1165 if (pmsg == NULL || mud == NULL)
1166 return EINVAL;
1167
1168 /* If we did not start a scanning yet do it now. */
1169 if (mud->messages_count == 0)
1170 {
1171 status = mbox_scan0 (mailbox, 1, NULL, 0);
1172 if (status != 0)
1173 return status;
1174 }
1175
1176 /* Second sanity: check the message number. */
1177 if (!(mud->messages_count > 0
1178 && msgno > 0
1179 && msgno <= mud->messages_count))
1180 return EINVAL;
1181
1182 mum = mud->umessages[msgno - 1];
1183
1184 /* Check if we already have it. */
1185 if (mum->message)
1186 {
1187 if (pmsg)
1188 *pmsg = mum->message;
1189 return 0;
1190 }
1191
1192 MAILBOX_DEBUG2 (mailbox, MU_DEBUG_TRACE, "mbox_get_message(%s, %d)\n",
1193 mud->name, msgno);
1194
1195 /* Get an empty message struct. */
1196 status = message_create (&msg, mum);
1197 if (status != 0)
1198 return status;
1199
1200 /* Set the header. */
1201 {
1202 header_t header = NULL;
1203 status = header_create (&header, NULL, 0, msg);
1204 if (status != 0)
1205 {
1206 message_destroy (&msg, mum);
1207 return status;
1208 }
1209 header_set_fill (header, mbox_header_fill, msg);
1210 header_set_get_fvalue (header, mbox_header_get_fvalue, msg);
1211 header_set_size (header, mbox_header_size, msg);
1212 header_set_lines (header, mbox_header_lines, msg);
1213 message_set_header (msg, header, mum);
1214 }
1215
1216 /* Set the attribute. */
1217 {
1218 attribute_t attribute;
1219 status = attribute_create (&attribute, msg);
1220 if (status != 0)
1221 {
1222 message_destroy (&msg, mum);
1223 return status;
1224 }
1225 attribute_set_get_flags (attribute, mbox_get_attr_flags, msg);
1226 attribute_set_set_flags (attribute, mbox_set_attr_flags, msg);
1227 attribute_set_unset_flags (attribute, mbox_unset_attr_flags, msg);
1228 message_set_attribute (msg, attribute, mum);
1229 }
1230
1231 /* Prepare the body. */
1232 {
1233 body_t body = NULL;
1234 stream_t stream = NULL;
1235 if ((status = body_create (&body, msg)) != 0
1236 || (status = stream_create (&stream,
1237 mailbox->flags | MU_STREAM_SEEKABLE,
1238 body)) != 0)
1239 {
1240 body_destroy (&body, msg);
1241 stream_destroy (&stream, body);
1242 message_destroy (&msg, mum);
1243 return status;
1244 }
1245 stream_set_read (stream, mbox_body_read, body);
1246 stream_set_readline (stream, mbox_body_readline, body);
1247 stream_set_fd (stream, mbox_get_body_fd, body);
1248 stream_set_size (stream, mbox_stream_size, body);
1249 body_set_stream (body, stream, msg);
1250 body_set_size (body, mbox_body_size, msg);
1251 body_set_lines (body, mbox_body_lines, msg);
1252 message_set_body (msg, body, mum);
1253 }
1254
1255 /* Set the envelope. */
1256 {
1257 envelope_t envelope= NULL;
1258 status = envelope_create (&envelope, msg);
1259 if (status != 0)
1260 {
1261 message_destroy (&msg, mum);
1262 return status;
1263 }
1264 envelope_set_sender (envelope, mbox_envelope_sender, msg);
1265 envelope_set_date (envelope, mbox_envelope_date, msg);
1266 message_set_envelope (msg, envelope, mum);
1267 }
1268
1269 /* Set the UID. */
1270 message_set_uid (msg, mbox_message_uid, mum);
1271
1272 /* Attach the message to the mailbox mbox data. */
1273 mum->message = msg;
1274 message_set_mailbox (msg, mailbox, mum);
1275
1276 *pmsg = msg;
1277 return 0;
1278 }
1279
1280 static int
1281 mbox_append_message (mailbox_t mailbox, message_t msg)
1282 {
1283 int status = 0;
1284 mbox_data_t mud = mailbox->data;
1285 if (msg == NULL || mud == NULL)
1286 return EINVAL;
1287
1288 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mbox_append_message (%s)\n",
1289 mud->name);
1290
1291 switch (mud->state)
1292 {
1293 case MBOX_NO_STATE:
1294 if ((status = locker_lock (mailbox->locker)) != 0)
1295 {
1296 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE,
1297 "mbox_append_message: %s\n",
1298 mu_strerror(status));
1299 return status;
1300 }
1301
1302 default:
1303 {
1304 off_t size;
1305 /* Move to the end of the file, not necesary if _APPEND mode. */
1306 if ((status = stream_size (mailbox->stream, &size)) != 0
1307 || (status = mbox_append_message0 (mailbox, msg, &size, 0, 0)) != 0)
1308 {
1309 if (status != EAGAIN)
1310 locker_unlock (mailbox->locker);
1311 return status;
1312 }
1313 }
1314 }
1315 locker_unlock (mailbox->locker);
1316 return 0;
1317 }
1318
1319 /* FIXME: We need to escape body line that begins with "From ", this
1320 will required to read the body by line instead of by chuncks hurting
1321 perfomance big time when expunging. But should not this be the
1322 responsability of the client ? */
1323 static int
1324 mbox_append_message0 (mailbox_t mailbox, message_t msg, off_t *psize,
1325 int is_expunging, int first)
1326 {
1327 mbox_data_t mud = mailbox->data;
1328 int status = 0;
1329 size_t n = 0;
1330 char nl = '\n';
1331
1332 switch (mud->state)
1333 {
1334 case MBOX_NO_STATE:
1335 /* Allocate memory for the sender/date buffers. */
1336 mud->sender = calloc (128, sizeof (char));
1337 if (mud->sender == NULL)
1338 return ENOMEM;
1339 mud->date = calloc (128, sizeof (char));
1340 if (mud->date == NULL)
1341 {
1342 free (mud->sender);
1343 mud->sender = NULL;
1344 return ENOMEM;
1345 }
1346 mud->off = 0;
1347 mud->state = MBOX_STATE_APPEND_SENDER;
1348
1349 case MBOX_STATE_APPEND_SENDER:
1350 /* Generate the sender for the "From " separator. */
1351 {
1352 char *s;
1353 size_t len = 0;
1354 envelope_t envelope = NULL;
1355 message_get_envelope (msg, &envelope);
1356 status = envelope_sender (envelope, mud->sender, 128, &len);
1357 if (status != 0)
1358 {
1359 if (status != EAGAIN)
1360 {
1361 free (mud->sender);
1362 free (mud->date);
1363 mud->date = mud->sender = NULL;
1364 mud->state = MBOX_NO_STATE;
1365 }
1366 return status;
1367 }
1368
1369 /* Nuke trailing newline. */
1370 s = memchr (mud->sender, nl, len);
1371 if (s)
1372 *s = '\0';
1373 mud->state = MBOX_STATE_APPEND_DATE;
1374 }
1375
1376 case MBOX_STATE_APPEND_DATE:
1377 /* Generate a date for the "From " separator. */
1378 {
1379 char *s;
1380 size_t len = 0;
1381 envelope_t envelope = NULL;
1382 char buffer[1024];
1383 message_get_envelope (msg, &envelope);
1384 status = envelope_date (envelope, mud->date, 128, &len);
1385 if (status != 0)
1386 {
1387 if (status != EAGAIN)
1388 {
1389 free (mud->sender);
1390 free (mud->date);
1391 mud->date = mud->sender = NULL;
1392 mud->state = MBOX_NO_STATE;
1393 }
1394 return status;
1395 }
1396
1397 /* Nuke trailing newline. */
1398 s = memchr (mud->date, nl, len);
1399 if (s)
1400 *s = '\0';
1401
1402 /* Write the separator to the mailbox. */
1403 n = snprintf (buffer, sizeof (buffer), "From %s %s",
1404 mud->sender, mud->date);
1405 stream_write (mailbox->stream, buffer, n, *psize, &n);
1406 *psize += n;
1407
1408 /* Add the newline, the above may be truncated. */
1409 stream_write (mailbox->stream, &nl , 1, *psize, &n);
1410 *psize += n;
1411
1412 free (mud->sender);
1413 free (mud->date);
1414 mud->sender = mud->date = NULL;
1415 /* If we are not expunging get the message in one block via the stream
1416 message instead of the header/body. This is good for POP where
1417 there is no separation between header and body(RETR). */
1418 if (! is_expunging)
1419 {
1420 mud->state = MBOX_STATE_APPEND_MESSAGE;
1421 break;
1422 }
1423 mud->state = MBOX_STATE_APPEND_HEADER;
1424 }
1425
1426 case MBOX_STATE_APPEND_HEADER:
1427 /* Append the Header. */
1428 {
1429 char buffer[1024];
1430 size_t nread = 0;
1431 stream_t is = NULL;
1432 header_t header = NULL;
1433 message_get_header (msg, &header);
1434 header_get_stream (header, &is);
1435 do
1436 {
1437 status = stream_readline (is, buffer, sizeof (buffer), mud->off,
1438 &nread);
1439 if (status != 0)
1440 {
1441 if (status != EAGAIN)
1442 {
1443 mud->state = MBOX_NO_STATE;
1444 mud->off = 0;
1445 }
1446 return status;
1447 }
1448 mud->off += nread;
1449 if (*buffer == '\n')
1450 break;
1451
1452 /* We do not copy the Status since it is rewritten by the
1453 attribute code below. Ditto for X-UID and X-IMAPBase.
1454 FIXME:
1455 - We have a problem here the header may not fit the buffer.
1456 - Should we skip the IMAP "X-Status"? */
1457 if ((strncasecmp (buffer, "Status", 6) == 0)
1458 || (strncasecmp (buffer, "X-IMAPbase", 10) == 0)
1459 /* FIXME: isn't the length of "X-UID" 5, not 4? And
1460 this will match X-UID and X-UIDL, is this intended? */
1461 || (strncasecmp (buffer, "X-UID", 4) == 0
1462 && (buffer[5] == ':' || buffer[5] == ' '
1463 || buffer[5] == '\t')))
1464 continue;
1465
1466 status = stream_write (mailbox->stream, buffer, nread,
1467 *psize, &n);
1468 if (status != 0)
1469 break;
1470 *psize += n;
1471 }
1472 while (nread > 0);
1473 mud->off = 0;
1474
1475 /* Rewrite the X-IMAPbase marker. */
1476 if (first && is_expunging)
1477 {
1478 n = sprintf (buffer, "X-IMAPbase: %lu %u\n",
1479 (unsigned long) mud->uidvalidity,
1480 (unsigned) mud->uidnext);
1481 stream_write (mailbox->stream, buffer, n, *psize, &n);
1482 *psize += n;
1483 }
1484 mud->state = MBOX_STATE_APPEND_ATTRIBUTE;
1485 }
1486
1487 case MBOX_STATE_APPEND_ATTRIBUTE:
1488 /* Put the new attributes. */
1489 {
1490 char abuf[64];
1491 size_t na = 0;
1492 attribute_t attr = NULL;
1493 abuf[0] = '\0';
1494 message_get_attribute (msg, &attr);
1495 attribute_to_string (attr, abuf, sizeof(abuf), &na);
1496
1497 status = stream_write (mailbox->stream, abuf, na, *psize, &n);
1498 if (status != 0)
1499 break;
1500 *psize += n;
1501
1502 mud->state = MBOX_STATE_APPEND_UID;
1503 }
1504
1505 case MBOX_STATE_APPEND_UID:
1506 /* The new X-UID. */
1507 {
1508 char suid[64];
1509 size_t uid = 0;
1510 if (is_expunging)
1511 {
1512 status = message_get_uid (msg, &uid);
1513 if (status == EAGAIN)
1514 return status;
1515 }
1516 else
1517 uid = mud->uidnext++;
1518
1519 if (status == 0 || uid != 0)
1520 {
1521 n = sprintf (suid, "X-UID: %u\n", (unsigned) uid);
1522 /* Put the UID. */
1523 status = stream_write (mailbox->stream, suid, n, *psize, &n);
1524 if (status != 0)
1525 break;
1526 *psize += n;
1527 }
1528
1529 /* New line separator of the Header. */
1530 status = stream_write (mailbox->stream, &nl , 1, *psize, &n);
1531 if (status != 0)
1532 break;
1533 *psize += n;
1534 mud->state = MBOX_STATE_APPEND_BODY;
1535 }
1536
1537 case MBOX_STATE_APPEND_BODY:
1538 /* Append the Body. */
1539 {
1540 char buffer[1024];
1541 size_t nread = 0;
1542 stream_t is = NULL;
1543 body_t body = NULL;
1544 message_get_body (msg, &body);
1545 body_get_stream (body, &is);
1546 do
1547 {
1548 status = stream_read (is, buffer, sizeof (buffer), mud->off,
1549 &nread);
1550 if (status != 0)
1551 {
1552 if (status != EAGAIN)
1553 {
1554 mud->state = MBOX_NO_STATE;
1555 mud->off = 0;
1556 }
1557 return status;
1558 }
1559 mud->off += nread;
1560 status = stream_write (mailbox->stream, buffer, nread, *psize, &n);
1561 if (status != 0)
1562 break;
1563 *psize += n;
1564 }
1565 while (nread > 0);
1566 mud->off = 0;
1567 n = 0;
1568 stream_write (mailbox->stream, &nl, 1, *psize, &n);
1569 *psize += n;
1570 }
1571
1572 default:
1573 break;
1574 }
1575
1576 /* If not expunging we are taking the stream message. */
1577 if (!is_expunging)
1578 {
1579 switch (mud->state)
1580 {
1581 case MBOX_STATE_APPEND_MESSAGE:
1582 {
1583 /* Append the Message. */
1584 char buffer[1024];
1585 size_t nread = 0;
1586 stream_t is = NULL;
1587 message_get_stream (msg, &is);
1588 do
1589 {
1590 status = stream_read (is, buffer, sizeof (buffer), mud->off,
1591 &nread);
1592 if (status != 0)
1593 {
1594 if (status != EAGAIN)
1595 {
1596 mud->state = MBOX_NO_STATE;
1597 mud->off = 0;
1598 }
1599 stream_flush (mailbox->stream);
1600 return status;
1601 }
1602 stream_write (mailbox->stream, buffer, nread, *psize, &n);
1603 mud->off += nread;
1604 *psize += n;
1605 }
1606 while (nread > 0);
1607 n = 0;
1608 stream_write (mailbox->stream, &nl, 1, *psize, &n);
1609 *psize += n;
1610 }
1611
1612 default:
1613 break;
1614 }
1615 } /* is_expunging */
1616 mud->state = MBOX_NO_STATE;
1617 stream_flush (mailbox->stream);
1618 return status;
1619 }
1620
1621 static int
1622 mbox_get_size (mailbox_t mailbox, off_t *psize)
1623 {
1624 off_t size;
1625 int status;
1626
1627 /* Maybe was not open yet ?? */
1628 status = stream_size (mailbox->stream, &size);
1629 if (status != 0)
1630 return status;
1631 if (psize)
1632 *psize = size;
1633 return 0;
1634 }
1635
1636 static int
1637 mbox_messages_count (mailbox_t mailbox, size_t *pcount)
1638 {
1639 mbox_data_t mud = mailbox->data;
1640 if (mud == NULL)
1641 return EINVAL;
1642
1643 if (! mbox_is_updated (mailbox))
1644 return mbox_scan0 (mailbox, mud->messages_count, pcount, 0);
1645
1646 if (pcount)
1647 *pcount = mud->messages_count;
1648
1649 return 0;
1650 }
1651
1652 /* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN
1653 ('O' in the Status header), i.e. a message that is first seen
1654 by the current session (see attributes.h) */
1655 static int
1656 mbox_messages_recent (mailbox_t mailbox, size_t *pcount)
1657 {
1658 mbox_data_t mud = mailbox->data;
1659 mbox_message_t mum;
1660 size_t j, recent;
1661
1662 /* If we did not start a scanning yet do it now. */
1663 if (mud->messages_count == 0)
1664 {
1665 int status = mbox_scan0 (mailbox, 1, NULL, 0);
1666 if (status != 0)
1667 return status;
1668 }
1669 for (recent = j = 0; j < mud->messages_count; j++)
1670 {
1671 mum = mud->umessages[j];
1672 if (mum && MU_ATTRIBUTE_IS_UNSEEN(mum->attr_flags))
1673 recent++;
1674 }
1675 *pcount = recent;
1676 return 0;
1677 }
1678
1679 /* An "unseen" message is the one that has not been read yet */
1680 static int
1681 mbox_message_unseen (mailbox_t mailbox, size_t *pmsgno)
1682 {
1683 mbox_data_t mud = mailbox->data;
1684 mbox_message_t mum;
1685 size_t j, unseen;
1686
1687 /* If we did not start a scanning yet do it now. */
1688 if (mud->messages_count == 0)
1689 {
1690 int status = mbox_scan0 (mailbox, 1, NULL, 0);
1691 if (status != 0)
1692 return status;
1693 }
1694 for (unseen = j = 0; j < mud->messages_count; j++)
1695 {
1696 mum = mud->umessages[j];
1697 if (mum && MU_ATTRIBUTE_IS_UNREAD(mum->attr_flags))
1698 {
1699 unseen = j + 1;
1700 break;
1701 }
1702 }
1703 *pmsgno = unseen;
1704 return 0;
1705 }
1706
1707 static int
1708 mbox_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity)
1709 {
1710 mbox_data_t mud = mailbox->data;
1711 int status = mbox_messages_count (mailbox, NULL);
1712 if (status != 0)
1713 return status;
1714 /* If we did not start a scanning yet do it now. */
1715 if (mud->messages_count == 0)
1716 {
1717 status = mbox_scan0 (mailbox, 1, NULL, 0);
1718 if (status != 0)
1719 return status;
1720 }
1721 if (puidvalidity)
1722 *puidvalidity = mud->uidvalidity;
1723 return 0;
1724 }
1725
1726 static int
1727 mbox_uidnext (mailbox_t mailbox, size_t *puidnext)
1728 {
1729 mbox_data_t mud = mailbox->data;
1730 int status = mbox_messages_count (mailbox, NULL);
1731 if (status != 0)
1732 return status;
1733 /* If we did not start a scanning yet do it now. */
1734 if (mud->messages_count == 0)
1735 {
1736 status = mbox_scan0 (mailbox, 1, NULL, 0);
1737 if (status != 0)
1738 return status;
1739 }
1740 if (puidnext)
1741 *puidnext = mud->uidnext;
1742 return 0;
1743 }
1744
1745 #ifdef WITH_PTHREAD
1746 void
1747 mbox_cleanup (void *arg)
1748 {
1749 mailbox_t mailbox = arg;
1750 monitor_unlock (mailbox->monitor);
1751 locker_unlock (mailbox->locker);
1752 }
1753 #endif
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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 /* Mailbox Parsing. */
19
20 /* Credits to the c-client and its Authors
21 * The notorius c-client VALID() macro, was written by Mark Crispin.
22 */
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #ifdef WITH_PTHREAD
28 # ifdef HAVE_PTHREAD_H
29 # define _XOPEN_SOURCE 500
30 # include <pthread.h>
31 # endif
32 #endif
33
34 #include <stdlib.h>
35 #include <mbox0.h>
36
37 /* Parsing.
38 The approach is to detect the "From " as start of a new message, give the
39 position of the header and scan until "\n" then set header_end, set body
40 position, scan until we it another "From " and set body_end.
41 ************************************
42 This is a classic case of premature optimisation being the root of all
43 Evil(Donald E. Knuth). But I'm under "pressure" ;-) to come with
44 something "faster". I think it's wastefull * to spend time to gain a few
45 seconds on 30Megs mailboxes ... but then again ... in computer time, 60
46 seconds, is eternity. If they use the event notification stuff to get
47 some headers/messages early ... it's like pissing in the wind(sorry don't
48 have the english equivalent). The worst is progress_bar it should be ...
49 &*($^ nuke. For the events, we have to remove the *.LCK file, release the
50 locks, flush the stream save the pointers etc ... hurry and wait...
51 I this point I'm pretty much ranting. */
52
53
54 /* From the C-Client, part of pine */
55 /* You are not expected to understand this macro, but read the next page if
56 * you are not faint of heart.
57 *
58 * Known formats to the VALID macro are:
59 * From user Wed Dec 2 05:53 1992
60 * BSD From user Wed Dec 2 05:53:22 1992
61 * SysV From user Wed Dec 2 05:53 PST 1992
62 * rn From user Wed Dec 2 05:53:22 PST 1992
63 * From user Wed Dec 2 05:53 -0700 1992
64 * From user Wed Dec 2 05:53:22 -0700 1992
65 * From user Wed Dec 2 05:53 1992 PST
66 * From user Wed Dec 2 05:53:22 1992 PST
67 * From user Wed Dec 2 05:53 1992 -0700
68 * Solaris From user Wed Dec 2 05:53:22 1992 -0700
69 *
70 * Plus all of the above with `` remote from xxx'' after it. Thank you very
71 * much, smail and Solaris, for making my life considerably more complicated.
72 */
73 /*
74 * What? You want to understand the VALID macro anyway? Alright, since you
75 * insist. Actually, it isn't really all that difficult, provided that you
76 * take it step by step.
77 *
78 * Line 1 Initializes the return ti value to failure (0);
79 * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
80 * Lines 4-6 Validates that there is an end of line and points x at it.
81 * Lines 7-14 First checks to see if the line is at least 41 characters long
82 .
83 * If so, it scans backwards to find the rightmost space. From
84 * that point, it scans backwards to see if the string matches
85 * `` remote from''. If so, it sets x to point to the space at
86 * the start of the string.
87 * Line 15 Makes sure that there are at least 27 characters in the line.
88 * Lines 16-21 Checks if the date/time ends with the year (there is a space
89 * five characters back). If there is a colon three characters
90 * further back, there is no timezone field, so zn is set to 0
91 * and ti is set in front of the year. Otherwise, there must
92 * either to be a space four characters back for a three-letter
93 * timezone, or a space six characters back followed by a + or -
94 * for a numeric timezone; in either case, zn and ti become the
95 * offset of the space immediately before it.
96 * Lines 22-24 Are the failure case for line 14. If there is a space four
97 * characters back, it is a three-letter timezone; there must be
98 a
99 * space for the year nine characters back. zn is the zone
100 * offset; ti is the offset of the space.
101 * Lines 25-28 Are the failure case for line 20. If there is a space six
102 * characters back, it is a numeric timezone; there must be a
103 * space eleven characters back and a + or - five characters back
104 .
105 * zn is the zone offset; ti is the offset of the space.
106 * Line 29-32 If ti is valid, make sure that the string before ti is of the
107 * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
108 * invalidate ti. There must be a colon three characters back
109 * and a space six or nine characters back (depending upon
110 * whether or not the character six characters back is a colon).
111 * There must be a space three characters further back (in front
112 * of the day), one seven characters back (in front of the month)
113 ,
114 * and one eleven characters back (in front of the day of week).
115 * ti is set to be the offset of the space before the time.
116 *
117 * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
118 * newer pipelined machines it is faster being open-coded than it would be if
119 * subroutines are called.
120 *
121 * Why does it scan backwards from the end of the line, instead of doing the
122 * much easier forward scan? There is no deterministic way to parse the
123 * ``user'' field, because it may contain unquoted spaces! Yes, I tested it t
124 o
125 * see if unquoted spaces were possible. They are, and I've encountered enoug
126 h
127 * evil mail to be totally unwilling to trust that ``it will never happen''.
128 */
129 #define VALID(s,x,ti,zn) { \
130 ti = 0; \
131 if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
132 (s[4] == ' ')) { \
133 for (x = s + 5; *x && *x != '\n'; x++); \
134 if (x) { \
135 if (x - s >= 41) { \
136 for (zn = -1; x[zn] != ' '; zn--); \
137 if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
138 (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
139 (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
140 (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
141 x += zn - 12; \
142 } \
143 if (x - s >= 27) { \
144 if (x[-5] == ' ') { \
145 if (x[-8] == ':') zn = 0,ti = -5; \
146 else if (x[-9] == ' ') ti = zn = -9; \
147 else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
148 ti = zn = -11; \
149 } \
150 else if (x[-4] == ' ') { \
151 if (x[-9] == ' ') zn = -4,ti = -9; \
152 } \
153 else if (x[-6] == ' ') { \
154 if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
155 zn = -6,ti = -11; \
156 } \
157 if (ti && !((x[ti - 3] == ':') && \
158 (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
159 (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
160 (x[ti - 11] == ' '))) ti = 0; \
161 } \
162 } \
163 } \
164 }
165
166 #define ATTRIBUTE_SET(buf,mum,c0,c1,type) \
167 do \
168 { \
169 char *s; \
170 for (s = (buf) + 7; *s; s++) \
171 { \
172 if (*s == c0 || *s == c1) \
173 { \
174 (mum)->attr_flags |= (type); \
175 break; \
176 } \
177 } \
178 } while (0)
179
180 #define ISBCC(buf) (\
181 (buf[0] == 'B' || buf[0] == 'b') \
182 && (buf[1] == 'C' || buf[1] == 'c') \
183 && (buf[2] == 'C' || buf[2] == 'c') \
184 && (buf[3] == ':' || buf[3] == ' ' || buf[3] == '\t'))
185
186 #define ISCC(buf) (\
187 (buf[0] == 'C' || buf[0] == 'c') \
188 && (buf[1] == 'C' || buf[1] == 'c') \
189 && (buf[2] == ':' || buf[2] == ' ' || buf[2] == '\t'))
190
191 #define ISCONTENT_LANGUAGE(buf) (\
192 (buf[0] == 'C' || buf[0] == 'c') \
193 && (buf[1] == 'O' || buf[1] == 'o') \
194 && (buf[2] == 'N' || buf[2] == 'n') \
195 && (buf[3] == 'T' || buf[3] == 't') \
196 && (buf[4] == 'E' || buf[4] == 'e') \
197 && (buf[5] == 'N' || buf[5] == 'n') \
198 && (buf[6] == 'T' || buf[6] == 't') \
199 && (buf[7] == '-') \
200 && (buf[8] == 'L' || buf[8] == 'l') \
201 && (buf[9] == 'A' || buf[9] == 'a') \
202 && (buf[10] == 'N' || buf[10] == 'n') \
203 && (buf[11] == 'G' || buf[11] == 'g') \
204 && (buf[12] == 'U' || buf[12] == 'u') \
205 && (buf[13] == 'A' || buf[13] == 'a') \
206 && (buf[14] == 'G' || buf[14] == 'g') \
207 && (buf[15] == 'E' || buf[15] == 'e') \
208 && (buf[16] == ':' || buf[16] == ' ' || buf[16] == '\t'))
209
210 #define ISCONTENT_TRANSFER_ENCODING(buf) (\
211 (buf[0] == 'C' || buf[0] == 'c') \
212 && (buf[1] == 'O' || buf[1] == 'o') \
213 && (buf[2] == 'N' || buf[2] == 'n') \
214 && (buf[3] == 'T' || buf[3] == 't') \
215 && (buf[4] == 'E' || buf[4] == 'e') \
216 && (buf[5] == 'N' || buf[5] == 'n') \
217 && (buf[6] == 'T' || buf[6] == 't') \
218 && (buf[7] == '-') \
219 && (buf[8] == 'T' || buf[8] == 't') \
220 && (buf[9] == 'R' || buf[9] == 'r') \
221 && (buf[10] == 'A' || buf[10] == 'a') \
222 && (buf[11] == 'N' || buf[11] == 'n') \
223 && (buf[12] == 'S' || buf[12] == 's') \
224 && (buf[13] == 'F' || buf[13] == 'f') \
225 && (buf[14] == 'E' || buf[14] == 'e') \
226 && (buf[15] == 'R' || buf[15] == 'r') \
227 && (buf[16] == '-') \
228 && (buf[17] == 'E' || buf[17] == 'e') \
229 && (buf[18] == 'N' || buf[18] == 'n') \
230 && (buf[19] == 'C' || buf[19] == 'c') \
231 && (buf[20] == 'O' || buf[20] == 'o') \
232 && (buf[21] == 'D' || buf[21] == 'd') \
233 && (buf[22] == 'I' || buf[22] == 'i') \
234 && (buf[23] == 'N' || buf[23] == 'n') \
235 && (buf[24] == 'G' || buf[24] == 'g') \
236 && (buf[25] == ':' || buf[25] == ' ' || buf[25] == '\t'))
237
238 #define ISCONTENT_TYPE(buf) (\
239 (buf[0] == 'C' || buf[0] == 'c') \
240 && (buf[1] == 'O' || buf[1] == 'o') \
241 && (buf[2] == 'N' || buf[2] == 'n') \
242 && (buf[3] == 'T' || buf[3] == 't') \
243 && (buf[4] == 'E' || buf[4] == 'e') \
244 && (buf[5] == 'N' || buf[5] == 'n') \
245 && (buf[6] == 'T' || buf[6] == 't') \
246 && (buf[7] == '-') \
247 && (buf[8] == 'T' || buf[8] == 't') \
248 && (buf[9] == 'Y' || buf[9] == 'y') \
249 && (buf[10] == 'P' || buf[10] == 'p') \
250 && (buf[11] == 'E' || buf[11] == 'e') \
251 && (buf[12] == ':' || buf[12] == ' ' || buf[12] == '\t'))
252
253 #define ISDATE(buf) (\
254 (buf[0] == 'D' || buf[0] == 'd') \
255 && (buf[1] == 'A' || buf[1] == 'a') \
256 && (buf[2] == 'T' || buf[2] == 't') \
257 && (buf[3] == 'E' || buf[3] == 'e') \
258 && (buf[4] == ':' || buf[4] == ' ' || buf[4] == '\t'))
259
260 #define ISFROM(buf) (\
261 (buf[0] == 'F' || buf[0] == 'f') \
262 && (buf[1] == 'R' || buf[1] == 'r') \
263 && (buf[2] == 'O' || buf[2] == 'o') \
264 && (buf[3] == 'M' || buf[3] == 'm') \
265 && (buf[4] == ':' || buf[4] == ' ' || buf[4] == '\t'))
266
267 #define ISIN_REPLY_TO(buf) (\
268 (buf[0] == 'I' || buf[0] == 'i') \
269 && (buf[1] == 'N' || buf[1] == 'n') \
270 && (buf[2] == '-' || buf[2] == '-') \
271 && (buf[3] == 'R' || buf[3] == 'r') \
272 && (buf[4] == 'E' || buf[4] == 'e') \
273 && (buf[5] == 'P' || buf[5] == 'p') \
274 && (buf[6] == 'L' || buf[6] == 'l') \
275 && (buf[7] == 'Y' || buf[7] == 'y') \
276 && (buf[8] == '-') \
277 && (buf[9] == 'T' || buf[9] == 't') \
278 && (buf[10] == 'O' || buf[10] == 'o') \
279 && (buf[11] == ':' || buf[11] == ' ' || buf[11] == '\t'))
280
281 #define ISMESSAGE_ID(buf) (\
282 (buf[0] == 'M' || buf[0] == 'm') \
283 && (buf[1] == 'E' || buf[1] == 'e') \
284 && (buf[2] == 'S' || buf[2] == 's') \
285 && (buf[3] == 'S' || buf[3] == 's') \
286 && (buf[4] == 'A' || buf[4] == 'a') \
287 && (buf[5] == 'G' || buf[5] == 'g') \
288 && (buf[6] == 'E' || buf[6] == 'e') \
289 && (buf[7] == '-') \
290 && (buf[8] == 'I' || buf[8] == 'i') \
291 && (buf[9] == 'D' || buf[9] == 'd') \
292 && (buf[10] == ':' || buf[10] == ' ' || buf[10] == '\t'))
293
294 #define ISREFERENCE(buf) (\
295 (buf[0] == 'R' || buf[0] == 'r') \
296 && (buf[1] == 'E' || buf[1] == 'e') \
297 && (buf[2] == 'F' || buf[2] == 'f') \
298 && (buf[3] == 'E' || buf[3] == 'e') \
299 && (buf[4] == 'R' || buf[4] == 'r') \
300 && (buf[5] == 'E' || buf[5] == 'e') \
301 && (buf[6] == 'n' || buf[6] == 'n') \
302 && (buf[7] == 'C' || buf[7] == 'c') \
303 && (buf[8] == 'E' || buf[8] == 'e') \
304 && (buf[9] == ':' || buf[9] == ' ' || buf[9] == '\t'))
305
306 #define ISREPLY_TO(buf) (\
307 (buf[0] == 'R' || buf[0] == 'r') \
308 && (buf[1] == 'E' || buf[1] == 'e') \
309 && (buf[2] == 'P' || buf[2] == 'p') \
310 && (buf[3] == 'L' || buf[3] == 'l') \
311 && (buf[4] == 'Y' || buf[4] == 'y') \
312 && (buf[5] == '-') \
313 && (buf[6] == 'T' || buf[6] == 't') \
314 && (buf[7] == 'O' || buf[7] == 'o') \
315 && (buf[8] == ':' || buf[8] == ' ' || buf[8] == '\t'))
316
317 #define ISSENDER(buf) (\
318 (buf[0] == 'S' || buf[0] == 's') \
319 && (buf[1] == 'E' || buf[1] == 'e') \
320 && (buf[2] == 'N' || buf[2] == 'n') \
321 && (buf[3] == 'D' || buf[3] == 'd') \
322 && (buf[4] == 'E' || buf[4] == 'e') \
323 && (buf[5] == 'R' || buf[5] == 'r') \
324 && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t'))
325
326 #define ISSTATUS(buf) (\
327 (buf[0] == 'S' || buf[0] == 's') \
328 && (buf[1] == 'T' || buf[1] == 't') \
329 && (buf[2] == 'A' || buf[2] == 'a') \
330 && (buf[3] == 'T' || buf[3] == 't') \
331 && (buf[4] == 'U' || buf[4] == 'u') \
332 && (buf[5] == 'S' || buf[5] == 's') \
333 && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t'))
334
335 #define ISSUBJECT(buf) (\
336 (buf[0] == 'S' || buf[0] == 's') \
337 && (buf[1] == 'U' || buf[1] == 'u') \
338 && (buf[2] == 'B' || buf[2] == 'b') \
339 && (buf[3] == 'J' || buf[3] == 'j') \
340 && (buf[4] == 'E' || buf[4] == 'e') \
341 && (buf[5] == 'C' || buf[5] == 'c') \
342 && (buf[6] == 'T' || buf[6] == 't') \
343 && (buf[7] == ':' || buf[7] == ' ' || buf[7] == '\t'))
344
345 #define ISTO(buf) (\
346 (buf[0] == 'T' || buf[0] == 't') \
347 && (buf[1] == 'O' || buf[1] == 'o') \
348 && (buf[2] == ':' || buf[2] == ' ' || buf[2] == '\t'))
349
350 #define ISX_IMAPBASE(buf) (\
351 (buf[0] == 'X' || buf[0] == 'x') \
352 && (buf[1] == '-') \
353 && (buf[2] == 'I' || buf[2] == 'i') \
354 && (buf[3] == 'M' || buf[3] == 'm') \
355 && (buf[4] == 'A' || buf[4] == 'a') \
356 && (buf[5] == 'P' || buf[5] == 'p') \
357 && (buf[6] == 'B' || buf[6] == 'b') \
358 && (buf[7] == 'A' || buf[7] == 'a') \
359 && (buf[8] == 'S' || buf[8] == 's') \
360 && (buf[9] == 'E' || buf[9] == 'e') \
361 && (buf[10] == ':' || buf[10] == ' ' || buf[10] == '\t'))
362
363 #define ISX_UIDL(buf) (\
364 (buf[0] == 'X' || buf[0] == 'x') \
365 && (buf[1] == '-') \
366 && (buf[2] == 'U' || buf[2] == 'u') \
367 && (buf[3] == 'I' || buf[3] == 'i') \
368 && (buf[4] == 'D' || buf[4] == 'd') \
369 && (buf[5] == 'L' || buf[5] == 'l') \
370 && (buf[6] == ':' || buf[6] == ' ' || buf[6] == '\t'))
371
372 #define ISX_UID(buf) (\
373 (buf[0] == 'X' || buf[0] == 'x') \
374 && (buf[1] == '-') \
375 && (buf[2] == 'U' || buf[2] == 'u') \
376 && (buf[3] == 'I' || buf[3] == 'i') \
377 && (buf[4] == 'D' || buf[4] == 'd') \
378 && (buf[5] == ':' || buf[5] == ' ' || buf[5] == '\t'))
379
380 /* Skip prepend spaces. */
381 #define SKIPSPACE(p) while (*p == ' ') p++
382
383 #define ATOI(a,i) \
384 do {\
385 SKIPSPACE(a); \
386 for (i = 0; *a >= '0' && *a <= '9'; a++) \
387 i = 10 * i + (*a - '0'); \
388 } while (0)
389
390 /* Save/concatenate the field-value in the fast header(fhd) field. */
391 #define FAST_HEADER(field,buf,n) \
392 do { \
393 int i = 0; \
394 char *s = field; \
395 char *p = buf; \
396 if (s) \
397 while (*s++) i++; \
398 else \
399 p = memchr (buf, ':', n); \
400 if (p) \
401 { \
402 int l; \
403 char *tmp; \
404 buf[n - 1] = '\0'; \
405 p++; \
406 if (!field) \
407 SKIPSPACE(p); \
408 l = n - (p - buf); \
409 tmp = realloc (field, (l + i + 1) * sizeof (char)); \
410 if (tmp) \
411 { \
412 field = tmp; \
413 memcpy (field + i, p, l); \
414 } \
415 } \
416 } while (0)
417
418 #define FAST_H_BCC(mum,save_field,buf,n) \
419 FAST_HEADER(mum->fhdr[H_BCC],buf,n); \
420 save_field = &(mum->fhdr[H_BCC])
421
422 #define FAST_H_CC(mum,save_field,buf,n) \
423 FAST_HEADER(mum->fhdr[H_CC],buf,n); \
424 save_field = &(mum->fhdr[H_CC])
425
426 #define FAST_H_CONTENT_LANGUAGE(mum,save_field,buf,n) \
427 FAST_HEADER(mum->fhdr[H_CONTENT_LANGUAGE],buf,n); \
428 save_field = &(mum->fhdr[H_CONTENT_LANGUAGE])
429
430 #define FAST_H_CONTENT_TRANSFER_ENCODING(mum,save_field,buf,n) \
431 FAST_HEADER(mum->fhdr[H_CONTENT_TRANSFER_ENCODING],buf,n); \
432 save_field = &(mum->fhdr[H_CONTENT_TRANSFER_ENCODING])
433
434 #define FAST_H_CONTENT_TYPE(mum,save_field,buf,n) \
435 FAST_HEADER(mum->fhdr[H_CONTENT_TYPE],buf,n); \
436 save_field = &(mum->fhdr[H_CONTENT_TYPE])
437
438 #define FAST_H_DATE(mum,save_field,buf,n) \
439 FAST_HEADER(mum->fhdr[H_DATE],buf,n); \
440 save_field = &(mum->fhdr[H_DATE])
441
442 #define FAST_H_FROM(mum,save_field,buf,n) \
443 FAST_HEADER(mum->fhdr[H_FROM],buf,n); \
444 save_field = &(mum->fhdr[H_FROM])
445
446 #define FAST_H_IN_REPLY_TO(mum,save_field,buf,n) \
447 FAST_HEADER(mum->fhdr[H_IN_REPLY_TO],buf,n); \
448 save_field = &(mum->fhdr[H_IN_REPLY_TO])
449
450 #define FAST_H_MESSAGE_ID(mum,save_field,buf,n) \
451 FAST_HEADER(mum->fhdr[H_MESSAGE_ID],buf,n); \
452 save_field = &(mum->fhdr[H_MESSAGE_ID])
453
454 #define FAST_H_REFERENCE(mum,save_field,buf,n) \
455 FAST_HEADER(mum->fhdr[H_REFERENCE],buf,n); \
456 save_field = &(mum->fhdr[H_REFERENCE])
457
458 #define FAST_H_REPLY_TO(mum,save_field,buf,n) \
459 FAST_HEADER(mum->fhdr[H_REPLY_TO],buf,n); \
460 save_field = &(mum->fhdr[H_REPLY_TO])
461
462 #define FAST_H_SENDER(mum,save_field,buf,n) \
463 FAST_HEADER(mum->fhdr[H_SENDER],buf,n); \
464 save_field = &(mum->fhdr[H_SENDER])
465
466 #define FAST_H_SUBJECT(mum,save_field,buf,n) \
467 FAST_HEADER(mum->fhdr[H_SUBJECT],buf,n); \
468 save_field = &(mum->fhdr[H_SUBJECT])
469
470 #define FAST_H_TO(mum,save_field,buf,n) \
471 FAST_HEADER(mum->fhdr[H_TO],buf,n); \
472 save_field = &(mum->fhdr[H_TO])
473
474 #define FAST_H_X_UIDL(mum,save_field,buf,n) \
475 FAST_HEADER(mum->fhdr[H_X_UIDL],buf,n); \
476 save_field = &(mum->fhdr[H_X_UIDL])
477
478 /* Notifications ADD_MESG. */
479 #define DISPATCH_ADD_MSG(mbox,mud) \
480 do \
481 { \
482 int bailing = 0; \
483 monitor_unlock (mbox->monitor); \
484 if (mbox->observable) \
485 bailing = observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD); \
486 if (bailing != 0) \
487 { \
488 if (pcount) \
489 *pcount = (mud)->messages_count; \
490 locker_unlock (mbox->locker); \
491 return EINTR; \
492 } \
493 monitor_wrlock (mbox->monitor); \
494 } while (0);
495
496 /* Notification MBX_PROGRESS
497 We do not want to fire up the progress notification every line, it will be
498 too expensive, so we do it arbitrarely every 10 000 Lines.
499 FIXME: maybe this should be configurable. */
500 /* This is more tricky we can not leave the mum struct incomplete. So we
501 only tell them about the complete messages. */
502 #define DISPATCH_PROGRESS(mbox,mud) \
503 do \
504 { \
505 { \
506 int bailing = 0; \
507 monitor_unlock (mbox->monitor); \
508 mud->messages_count--; \
509 if (mbox->observable) \
510 bailing = observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS); \
511 if (bailing != 0) \
512 { \
513 if (pcount) \
514 *pcount = (mud)->messages_count; \
515 locker_unlock (mbox->locker); \
516 return EINTR; \
517 } \
518 mud->messages_count++; \
519 monitor_wrlock (mbox->monitor); \
520 } \
521 } while (0)
522
523 /* Allocate slots for the new messages. */
524 /* size_t num = 2 * ((mud)->messages_count) + 10; */
525 #define ALLOCATE_MSGS(mbox,mud) \
526 do \
527 { \
528 if ((mud)->messages_count >= (mud)->umessages_count) \
529 { \
530 mbox_message_t *m; \
531 size_t num = ((mud)->umessages_count) + 1; \
532 m = realloc ((mud)->umessages, num * sizeof (*m)); \
533 if (m == NULL) \
534 { \
535 locker_unlock (mbox->locker); \
536 monitor_unlock (mbox->monitor); \
537 return ENOMEM; \
538 } \
539 (mud)->umessages = m; \
540 (mud)->umessages[num - 1] = calloc (1, sizeof (*(mum))); \
541 if ((mud)->umessages[num - 1] == NULL) \
542 { \
543 locker_unlock (mbox->locker); \
544 monitor_unlock (mbox->monitor); \
545 return ENOMEM; \
546 } \
547 (mud)->umessages_count = num; \
548 } \
549 } while (0)
550
551 int
552 mbox_scan0 (mailbox_t mailbox, size_t msgno, size_t *pcount, int do_notif)
553 {
554 #define MSGLINELEN 1024
555 char buf[MSGLINELEN];
556 int inheader;
557 int inbody;
558 off_t total = 0;
559 mbox_data_t mud = mailbox->data;
560 mbox_message_t mum = NULL;
561 int status = 0;
562 size_t lines;
563 int newline;
564 size_t n = 0;
565 stream_t stream;
566 char **sfield = NULL;
567 size_t min_uid = 0;
568 int zn, isfrom = 0;
569 char *temp;
570
571 /* Sanity. */
572 if (mud == NULL)
573 return EINVAL;
574
575 /* Grab the lock. */
576 monitor_wrlock (mailbox->monitor);
577
578 #ifdef WITH_PTHREAD
579 /* read() is cancellation point since we're doing a potentially
580 long operation. Lets make sure we clean the state. */
581 pthread_cleanup_push (mbox_cleanup, (void *)mailbox);
582 #endif
583
584 /* Save the timestamp and size. */
585 status = stream_size (mailbox->stream, &(mud->size));
586 if (status != 0)
587 {
588 monitor_unlock (mailbox->monitor);
589 return status;
590 }
591
592 if((status = locker_lock (mailbox->locker)))
593 {
594 monitor_unlock (mailbox->monitor);
595 return status;
596 }
597
598 /* Seek to the starting point. */
599 if (mud->umessages && msgno > 0 && mud->messages_count > 0
600 && msgno <= mud->messages_count)
601 {
602 mum = mud->umessages[msgno - 1];
603 if (mum)
604 total = mum->header_from;
605 mud->messages_count = msgno - 1;
606 }
607 else
608 mud->messages_count = 0;
609
610 #if 0
611 {
612 size_t j, k;
613 for (j = 0; j < mud->umessages_count; j++)
614 {
615 mum = mud->umessages[j];
616 for (k = 0; k < HDRSIZE; k++)
617 if (mum->fhdr[k])
618 free (mum->fhdr[k]);
619 }
620 }
621 #endif
622 newline = 1;
623 errno = lines = inheader = inbody = 0;
624
625 stream = mailbox->stream;
626 while ((status = stream_readline (mailbox->stream, buf, sizeof (buf),
627 total, &n)) == 0 && n != 0)
628 {
629 int nl;
630 total += n;
631
632 nl = (*buf == '\n') ? 1 : 0;
633 VALID(buf, temp, isfrom, zn);
634 isfrom = (isfrom) ? 1 : 0;
635
636 /* Which part of the message are we in ? */
637 inheader = isfrom | ((!nl) & inheader);
638 inbody = (!isfrom) & (!inheader);
639
640 if (buf[n - 1] == '\n')
641 lines++;
642
643 if (inheader)
644 {
645 /* New message. */
646 if (isfrom)
647 {
648 size_t j;
649 /* Signal the end of the body. */
650 if (mum && !mum->body_end)
651 {
652 mum->body_end = total - n - newline;
653 mum->body_lines = --lines - newline;
654
655 if (mum->uid <= min_uid)
656 {
657 mum->uid = ++min_uid;
658 /* Note that modification for when expunging. */
659 mum->attr_flags |= MU_ATTRIBUTE_MODIFIED;
660 }
661 else
662 min_uid = mum->uid;
663
664 if (do_notif)
665 DISPATCH_ADD_MSG(mailbox, mud);
666
667 }
668 /* Allocate_msgs will initialize mum. */
669 ALLOCATE_MSGS(mailbox, mud);
670 mud->messages_count++;
671 mum = mud->umessages[mud->messages_count - 1];
672 mum->mud = mud;
673 mum->header_from = total - n;
674 mum->header_from_end = total;
675 mum->body_end = mum->body = 0;
676 mum->attr_flags = 0;
677 lines = 0;
678 sfield = NULL;
679 for (j = 0; j < HDRSIZE; j++)
680 if (mum->fhdr[j])
681 {
682 free (mum->fhdr[j]);
683 mum->fhdr[j] = NULL;
684 }
685 }
686 else if (ISSTATUS(buf))
687 {
688 ATTRIBUTE_SET(buf, mum, 'r', 'R', MU_ATTRIBUTE_READ);
689 ATTRIBUTE_SET(buf, mum, 'o', 'O', MU_ATTRIBUTE_SEEN);
690 ATTRIBUTE_SET(buf, mum, 'a', 'A', MU_ATTRIBUTE_ANSWERED);
691 ATTRIBUTE_SET(buf, mum, 'd', 'D', MU_ATTRIBUTE_DELETED);
692 sfield = NULL;
693 }
694 else if (ISBCC(buf))
695 {
696 FAST_H_BCC(mum, sfield, buf, n);
697 }
698 else if (ISCC(buf))
699 {
700 FAST_H_CC(mum, sfield, buf, n);
701 }
702 else if (ISCONTENT_LANGUAGE(buf))
703 {
704 FAST_H_CONTENT_LANGUAGE(mum, sfield, buf, n);
705 }
706 else if (ISCONTENT_TRANSFER_ENCODING(buf))
707 {
708 FAST_H_CONTENT_TRANSFER_ENCODING(mum, sfield, buf, n);
709 }
710 else if (ISCONTENT_TYPE(buf))
711 {
712 FAST_H_CONTENT_TYPE(mum, sfield, buf, n);
713 }
714 else if (ISDATE(buf))
715 {
716 FAST_H_DATE(mum, sfield, buf, n);
717 }
718 else if (ISFROM(buf))
719 {
720 FAST_H_FROM(mum, sfield, buf, n);
721 }
722 else if (ISIN_REPLY_TO(buf))
723 {
724 FAST_H_IN_REPLY_TO(mum, sfield, buf, n);
725 }
726 else if (ISMESSAGE_ID(buf))
727 {
728 FAST_H_MESSAGE_ID(mum, sfield, buf, n);
729 }
730 else if (ISREFERENCE(buf))
731 {
732 FAST_H_REFERENCE(mum, sfield, buf, n);
733 }
734 else if (ISREPLY_TO(buf))
735 {
736 FAST_H_REPLY_TO(mum, sfield, buf, n);
737 }
738 else if (ISSENDER(buf))
739 {
740 FAST_H_SENDER (mum, sfield, buf, n);
741 }
742 else if (ISSUBJECT(buf))
743 {
744 FAST_H_SUBJECT (mum, sfield, buf, n);
745 }
746 else if (ISTO(buf))
747 {
748 FAST_H_TO (mum, sfield, buf, n);
749 }
750 else if (ISX_UIDL(buf))
751 {
752 FAST_H_X_UIDL (mum, sfield, buf, n);
753 }
754 else if (ISX_IMAPBASE(buf))
755 {
756 char *s = memchr (buf, ':', n);
757 if (s)
758 {
759 s++;
760 ATOI(s, mud->uidvalidity);
761 ATOI(s, mud->uidnext);
762 }
763 }
764 else if (ISX_UID(buf))
765 {
766 char *s = memchr (buf, ':', n);
767 if (s)
768 {
769 s++;
770 ATOI(s, mum->uid);
771 }
772 }
773 else if (sfield && (buf[0] == ' ' || buf[0] == '\t'))
774 {
775 char *save = *sfield;
776 FAST_HEADER (save, buf, n);
777 *sfield = save;
778 }
779 else
780 {
781 sfield = NULL;
782 }
783 }
784
785 /* Body. */
786 if (inbody)
787 {
788 /* Set the body position. */
789 if (mum && !mum->body)
790 {
791 mum->body = total - n + nl;
792 mum->header_lines = lines;
793 lines = 0;
794 }
795 }
796
797 newline = nl;
798
799 /* Every 100 mesgs update the lock, it should be every minute. */
800 if ((mud->messages_count % 100) == 0)
801 locker_touchlock (mailbox->locker);
802
803 /* Ping them every 1000 lines. Should be tunable. */
804 if (do_notif)
805 if (((lines +1) % 1000) == 0)
806 DISPATCH_PROGRESS(mailbox, mud);
807
808 } /* while */
809
810 if (mum)
811 {
812 mum->body_end = total - newline;
813 mum->body_lines = lines - newline;
814
815 if (mum->uid <= min_uid)
816 {
817 mum->uid = ++min_uid;
818 /* Note that modification for when expunging. */
819 mum->attr_flags |= MU_ATTRIBUTE_MODIFIED;
820 }
821 else
822 min_uid = mum->uid;
823
824 if (do_notif)
825 DISPATCH_ADD_MSG(mailbox, mud);
826 }
827 if (pcount)
828 *pcount = mud->messages_count;
829 locker_unlock (mailbox->locker);
830 monitor_unlock (mailbox->monitor);
831
832 /* Reset the uidvalidity. */
833 if (mud->messages_count > 0)
834 {
835 mum = mud->umessages[0];
836 if (mud->uidvalidity == 0)
837 {
838 mud->uidvalidity = (unsigned long)time (NULL);
839 mud->uidnext = mud->messages_count + 1;
840 /* Tell that we have been modified for expunging. */
841 mum->attr_flags |= MU_ATTRIBUTE_MODIFIED;
842 }
843 }
844
845 if (mud->messages_count > 0 && min_uid >= mud->uidnext)
846 {
847 mum = mud->umessages[0];
848 mud->uidnext = min_uid + 1;
849 mum->attr_flags |= MU_ATTRIBUTE_MODIFIED;
850 }
851
852 #ifdef WITH_PTHREAD
853 pthread_cleanup_pop (0);
854 #endif
855 return status;
856 }
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2002, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003 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