Commit b6bb9ca3 b6bb9ca37cb0fcf8e8a5131a49a21c5a41dc2e62 by Sergey Poznyakoff

Moved from ../

1 parent 11bc148b
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 Sergey Poznyakoff */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #ifdef ENABLE_MH
25
26 #include <sys/types.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <time.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <dirent.h>
36
37 #ifdef WITH_PTHREAD
38 # ifdef HAVE_PTHREAD_H
39 # define _XOPEN_SOURCE 500
40 # include <pthread.h>
41 # endif
42 #endif
43
44 #include <string.h>
45 #ifdef HAVE_STRINGS_H
46 # include <strings.h>
47 #endif
48
49 #include <mailutils/attribute.h>
50 #include <mailutils/body.h>
51 #include <mailutils/debug.h>
52 #include <mailutils/envelope.h>
53 #include <mailutils/error.h>
54 #include <mailutils/header.h>
55 #include <mailutils/locker.h>
56 #include <mailutils/message.h>
57 #include <mailutils/mutil.h>
58 #include <mailutils/property.h>
59 #include <mailutils/stream.h>
60 #include <mailutils/url.h>
61 #include <mailutils/observer.h>
62 #include <mailbox0.h>
63 #include <registrar0.h>
64
65 #define MAX_OPEN_STREAMS 16
66
67 /* Notifications ADD_MESG. */
68 #define DISPATCH_ADD_MSG(mbox,mhd) \
69 do \
70 { \
71 int bailing = 0; \
72 monitor_unlock (mbox->monitor); \
73 if (mbox->observable) \
74 bailing = observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD); \
75 if (bailing != 0) \
76 { \
77 if (pcount) \
78 *pcount = (mhd)->msg_count; \
79 locker_unlock (mbox->locker); \
80 return EINTR; \
81 } \
82 monitor_wrlock (mbox->monitor); \
83 } while (0);
84
85 /* Note: In this particular implementation the message sequence number
86 serves also as its UID. This allows to avoid many problems related
87 to keeping the uids in the headers of the messages. */
88
89 struct _mh_data;
90 struct _mh_message
91 {
92 struct _mh_message *next;
93 struct _mh_message *prev;
94
95 stream_t stream; /* Associated file stream */
96 off_t body_start; /* Offset of body start in the message file */
97 off_t body_end; /* Offset of body end (size of file, effectively)*/
98
99 size_t seq_number; /* message sequence number */
100
101 int attr_flags; /* Attribute flags */
102 int deleted; /* Was the message originally deleted */
103
104 time_t mtime; /* Time of last modification */
105 size_t header_lines; /* Number of lines in the header part */
106 size_t body_lines; /* Number of lines in the body */
107
108 message_t message; /* Corresponding message_t */
109 struct _mh_data *mhd; /* Back pointer. */
110 };
111
112 struct _mh_data
113 {
114 /* List of messages: */
115 size_t msg_count; /* number of messages in the list */
116 struct _mh_message *msg_head; /* First */
117 struct _mh_message *msg_tail; /* Last */
118
119 unsigned long uidvalidity;
120
121 char *name; /* Directory name */
122
123 /* Pool of open message streams */
124 struct _mh_message *msg_pool[MAX_OPEN_STREAMS];
125 int pool_first; /* Index to the first used entry in msg_pool */
126 int pool_last; /* Index to the first free entry in msg_pool */
127
128 time_t mtime; /* Time of last modification */
129
130 mailbox_t mailbox; /* Back pointer. */
131 };
132
133 static void mh_destroy __P((mailbox_t mailbox));
134 static int mh_open __P ((mailbox_t, int));
135 static int mh_close __P ((mailbox_t));
136 static int mh_get_message __P ((mailbox_t, size_t, message_t *));
137 static int mh_append_message __P ((mailbox_t, message_t));
138 static int mh_messages_count __P ((mailbox_t, size_t *));
139 static int mh_messages_recent __P ((mailbox_t, size_t *));
140 static int mh_message_unseen __P ((mailbox_t, size_t *));
141 static int mh_expunge __P ((mailbox_t));
142 static int mh_save_attributes __P ((mailbox_t));
143 static int mh_uidvalidity __P ((mailbox_t, unsigned long *));
144 static int mh_uidnext __P ((mailbox_t, size_t *));
145 static int mh_scan __P ((mailbox_t, size_t, size_t *));
146 static int mh_scan0 __P ((mailbox_t mailbox, size_t msgno, size_t *pcount,
147 int do_notify));
148 static int mh_is_updated __P ((mailbox_t));
149 static int mh_get_size __P ((mailbox_t, off_t *));
150
151 static int mh_body_read __P ((stream_t, char *, size_t, off_t, size_t *));
152 static int mh_body_readline __P ((stream_t, char *, size_t, off_t, size_t *));
153 static int mh_stream_size __P ((stream_t stream, off_t *psize));
154
155 static int mh_body_size __P ((body_t body, size_t *psize));
156 static int mh_body_lines __P ((body_t body, size_t *plines));
157
158 static int mh_message_uid __P ((message_t msg, size_t *puid));
159
160 static int mh_message_stream_open __P((struct _mh_message *mhm));
161 static void mh_message_stream_close __P((struct _mh_message *mhm));
162
163 static int mh_header_fill __P((header_t header, char *buffer, size_t len,
164 off_t off, size_t *pnread));
165 static int mh_header_size __P((header_t header, size_t *psize));
166 static int mh_header_lines __P((header_t header, size_t *plines));
167
168 static int mh_get_attr_flags __P((attribute_t attr, int *pflags));
169 static int mh_set_attr_flags __P((attribute_t attr, int flags));
170 static int mh_unset_attr_flags __P((attribute_t attr, int flags));
171
172 static void _mh_message_insert __P((struct _mh_data *mhd,
173 struct _mh_message *msg));
174 static void _mh_message_delete __P((struct _mh_data *mhd,
175 struct _mh_message *msg));
176 static int mh_pool_open __P((struct _mh_message *mhm));
177
178 static int mh_envelope_date __P((envelope_t envelope, char *buf, size_t len,
179 size_t *psize));
180 static int mh_envelope_sender __P((envelope_t envelope, char *buf, size_t len,
181 size_t *psize));
182
183 /* Should be in an other header file. */
184 extern int mh_message_number __P ((message_t msg, size_t *pnum));
185
186 /* Return filename for the message.
187 NOTE: Allocates memory. */
188 static char *
189 _mh_message_name (struct _mh_message *mhm, int deleted)
190 {
191 char *filename;
192 size_t len = strlen (mhm->mhd->name) + 32;
193 filename = malloc (len);
194 if (deleted)
195 snprintf (filename, len, "%s/,%lu", mhm->mhd->name,
196 (unsigned long) mhm->seq_number);
197 else
198 snprintf (filename, len, "%s/%lu", mhm->mhd->name,
199 (unsigned long) mhm->seq_number);
200 return filename;
201 }
202
203 int
204 _mailbox_mh_init (mailbox_t mailbox)
205 {
206 struct _mh_data *mhd;
207 size_t name_len;
208
209 if (mailbox == NULL)
210 return EINVAL;
211
212 mhd = mailbox->data = calloc (1, sizeof (*mhd));
213 if (mailbox->data == NULL)
214 return ENOMEM;
215
216 /* Back pointer. */
217 mhd->mailbox = mailbox;
218
219 url_get_path (mailbox->url, NULL, 0, &name_len);
220 mhd->name = calloc (name_len + 1, sizeof (char));
221 if (mhd->name == NULL)
222 {
223 free (mhd);
224 mailbox->data = NULL;
225 return ENOMEM;
226 }
227 url_get_path (mailbox->url, mhd->name, name_len + 1, NULL);
228
229 /* Overloading the defaults. */
230 mailbox->_destroy = mh_destroy;
231
232 mailbox->_open = mh_open;
233 mailbox->_close = mh_close;
234
235 /* Overloading of the entire mailbox object methods. */
236 mailbox->_get_message = mh_get_message;
237 mailbox->_append_message = mh_append_message;
238 mailbox->_messages_count = mh_messages_count;
239 mailbox->_messages_recent = mh_messages_recent;
240 mailbox->_message_unseen = mh_message_unseen;
241 mailbox->_expunge = mh_expunge;
242 mailbox->_save_attributes = mh_save_attributes;
243 mailbox->_uidvalidity = mh_uidvalidity;
244 mailbox->_uidnext = mh_uidnext;
245
246 mailbox->_scan = mh_scan;
247 mailbox->_is_updated = mh_is_updated;
248
249 mailbox->_get_size = mh_get_size;
250
251 /* Set our properties. */
252 {
253 property_t property = NULL;
254 mailbox_get_property (mailbox, &property);
255 property_set_value (property, "TYPE", "MH", 1);
256 }
257
258 MAILBOX_DEBUG1 (mailbox, MU_DEBUG_TRACE, "mh_init(%s)\n", mhd->name);
259 return 0;
260 }
261
262 static void
263 mh_destroy (mailbox_t mailbox)
264 {
265 struct _mh_data *mhd = mailbox->data;
266 struct _mh_message *msg, *next;
267
268 if (!mhd)
269 return;
270
271 monitor_wrlock (mailbox->monitor);
272 msg = mhd->msg_head;
273 while (msg)
274 {
275 next = msg->next;
276 message_destroy (&msg->message, msg);
277 free (msg);
278 msg = next;
279 }
280
281 if (mhd->name)
282 free (mhd->name);
283
284 free (mhd);
285 mailbox->data = NULL;
286 monitor_unlock (mailbox->monitor);
287 }
288
289 static int
290 mh_open (mailbox_t mailbox, int flags)
291 {
292 struct _mh_data *mhd = mailbox->data;
293 int status = 0;
294 struct stat st;
295
296 mailbox->flags = flags;
297 if (stat (mhd->name, &st) < 0)
298 return errno;
299
300 if (!S_ISDIR (st.st_mode))
301 return EINVAL;
302
303 mhd->mtime = st.st_mtime;
304
305 /* FIXME: is this the right kind of locking for mh folders? */
306 if (mailbox->locker == NULL)
307 status = locker_create (&mailbox->locker, mhd->name, 0);
308 return 0;
309 }
310
311 static int
312 mh_close (mailbox_t mailbox)
313 {
314 if (!mailbox)
315 return EINVAL;
316 return locker_unlock (mailbox->locker);
317 }
318
319 static struct _mh_message *
320 _mh_get_message (struct _mh_data *mhd, size_t msgno)
321 {
322 size_t n;
323 struct _mh_message *msg;
324
325 for (n = 1, msg = mhd->msg_head; msg && n < msgno; n++, msg = msg->next)
326 ;
327
328 return msg;
329 }
330
331 /* Find the message with the given sequence number */
332 static struct _mh_message *
333 _mh_get_message_seq (struct _mh_data *mhd, size_t seq)
334 {
335 struct _mh_message *msg;
336
337 for (msg = mhd->msg_head; msg && msg->seq_number < seq; msg = msg->next)
338 ;
339
340 if (msg)
341 return msg->seq_number == seq ? msg : NULL;
342 return NULL;
343 }
344
345 static int
346 _mh_attach_message (mailbox_t mailbox, struct _mh_message *mhm,
347 message_t *pmsg)
348 {
349 int status;
350 message_t msg;
351
352 /* Check if we already have it. */
353 if (mhm->message)
354 {
355 if (pmsg)
356 *pmsg = mhm->message;
357 return 0;
358 }
359
360 /* Get an empty message struct. */
361 status = message_create (&msg, mhm);
362 if (status != 0)
363 return status;
364
365 /* Set the header. */
366 {
367 header_t header = NULL;
368 status = header_create (&header, NULL, 0, msg);
369 if (status != 0)
370 {
371 message_destroy (&msg, mhm);
372 return status;
373 }
374 header_set_fill (header, mh_header_fill, msg);
375 header_set_size (header, mh_header_size, msg);
376 header_set_lines (header, mh_header_lines, msg);
377 /*FIXME:
378 header_set_get_fvalue (header, mh_header_get_fvalue, msg);
379 */
380 message_set_header (msg, header, mhm);
381 }
382
383 /* Set the attribute. */
384 {
385 attribute_t attribute;
386 status = attribute_create (&attribute, msg);
387 if (status != 0)
388 {
389 message_destroy (&msg, mhm);
390 return status;
391 }
392 attribute_set_get_flags (attribute, mh_get_attr_flags, msg);
393 attribute_set_set_flags (attribute, mh_set_attr_flags, msg);
394 attribute_set_unset_flags (attribute, mh_unset_attr_flags, msg);
395 message_set_attribute (msg, attribute, mhm);
396 }
397
398 /* Prepare the body. */
399 {
400 body_t body = NULL;
401 stream_t stream = NULL;
402 if ((status = body_create (&body, msg)) != 0
403 || (status = stream_create (&stream,
404 mailbox->flags | MU_STREAM_SEEKABLE,
405 body)) != 0)
406 {
407 body_destroy (&body, msg);
408 stream_destroy (&stream, body);
409 message_destroy (&msg, mhm);
410 return status;
411 }
412 stream_set_read (stream, mh_body_read, body);
413 stream_set_readline (stream, mh_body_readline, body);
414 stream_set_size (stream, mh_stream_size, body);
415 body_set_stream (body, stream, msg);
416 body_set_size (body, mh_body_size, msg);
417 body_set_lines (body, mh_body_lines, msg);
418 message_set_body (msg, body, mhm);
419 }
420
421 /* Set the envelope. */
422 {
423 envelope_t envelope = NULL;
424 status = envelope_create (&envelope, msg);
425 if (status != 0)
426 {
427 message_destroy (&msg, mhm);
428 return status;
429 }
430 envelope_set_sender (envelope, mh_envelope_sender, msg);
431 envelope_set_date (envelope, mh_envelope_date, msg);
432 message_set_envelope (msg, envelope, mhm);
433 }
434
435 /* Set the UID. */
436 message_set_uid (msg, mh_message_uid, mhm);
437
438 /* Attach the message to the mailbox mbox data. */
439 mhm->message = msg;
440 message_set_mailbox (msg, mailbox, mhm);
441
442 if (pmsg)
443 *pmsg = msg;
444
445 return 0;
446 }
447
448 static int
449 mh_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
450 {
451 int status;
452 struct _mh_data *mhd = mailbox->data;
453 struct _mh_message *mhm;
454
455 /* Sanity checks. */
456 if (pmsg == NULL || mhd == NULL)
457 return EINVAL;
458
459 /* If we did not start a scanning yet do it now. */
460 if (mhd->msg_count == 0)
461 {
462 status = mh_scan0 (mailbox, 1, NULL, 0);
463 if (status != 0)
464 return status;
465 }
466
467 if ((mhm = _mh_get_message (mhd, msgno)) == NULL)
468 return EINVAL;
469 return _mh_attach_message (mailbox, mhm, pmsg);
470 }
471
472 static size_t
473 _mh_next_seq (struct _mh_data *mhd)
474 {
475 return (mhd->msg_tail ? mhd->msg_tail->seq_number : 0) + 1;
476 }
477
478 static FILE *
479 _mh_tempfile(struct _mh_data *mhd, char **namep)
480 {
481 int fd = mu_tempfile (mhd->name, namep);
482 if (fd == -1)
483 return NULL;
484 return fdopen (fd, "w");
485 }
486
487 static int
488 _mh_delim (char *str)
489 {
490 if (str[0] == '-')
491 {
492 for (; *str == '-'; str++)
493 ;
494 for (; *str == ' ' || *str == '\t'; str++)
495 ;
496 }
497 return str[0] == '\n';
498 }
499
500 static int
501 _mh_message_save (struct _mh_data *mhd, struct _mh_message *mhm, int expunge)
502 {
503 stream_t stream = NULL;
504 char *name = NULL, *buf = NULL, *msg_name;
505 size_t n, off = 0;
506 size_t bsize;
507 size_t nlines, nbytes;
508 size_t new_body_start, new_header_lines;
509 FILE *fp;
510 message_t msg = mhm->message;
511 header_t hdr;
512 int status;
513 attribute_t attr;
514 body_t body;
515 char buffer[512];
516 envelope_t env = NULL;
517
518 fp = _mh_tempfile (mhm->mhd, &name);
519 if (!fp)
520 return errno;
521
522 message_size (msg, &bsize);
523
524 /* Try to allocate large buffer */
525 for (; bsize > 1; bsize /= 2)
526 if ((buf = malloc (bsize)))
527 break;
528
529 if (!bsize)
530 return ENOMEM;
531
532 /* Copy flags */
533 message_get_header (msg, &hdr);
534 header_get_stream (hdr, &stream);
535 off = 0;
536 nlines = nbytes = 0;
537 while ((status = stream_readline (stream, buf, bsize, off, &n)) == 0
538 && n != 0)
539 {
540 if (_mh_delim(buf))
541 break;
542
543 if (!(strncasecmp (buf, "status:", 7) == 0
544 || strncasecmp (buf, "x-imapbase:", 11) == 0
545 || strncasecmp (buf, "x-uid:", 6) == 0
546 || strncasecmp (buf, MU_HEADER_ENV_DATE ":", sizeof (MU_HEADER_ENV_DATE)) == 0
547 || strncasecmp (buf, MU_HEADER_ENV_SENDER ":", sizeof (MU_HEADER_ENV_SENDER)) == 0))
548 {
549 nlines++;
550 nbytes += fprintf (fp, "%s", buf);
551 }
552
553 off += n;
554 }
555
556 /* Add imapbase */
557 if (!mhd->msg_head || (mhd->msg_head == mhm)) /*FIXME*/
558 {
559 nbytes += fprintf (fp, "X-IMAPbase: %lu %u\n",
560 (unsigned long) mhd->uidvalidity,
561 (unsigned) _mh_next_seq(mhd));
562 nlines++;
563 }
564
565 message_get_envelope (msg, &env);
566 if (envelope_date (env, buffer, sizeof buffer, &n) == 0 && n > 0)
567 {
568 /* NOTE: buffer is terminated with \n */
569 char *p = buffer;
570 while (isspace (*p))
571 p++;
572 nbytes += fprintf (fp, "%s: %s", MU_HEADER_ENV_DATE, p);
573
574 if (*p && p[strlen (p) - 1] != '\n')
575 nbytes += fprintf (fp, "\n");
576
577 nlines++;
578 }
579
580 if (envelope_sender (env, buffer, sizeof buffer, &n) == 0 && n > 0)
581 {
582 fprintf (fp, "%s: %s\n", MU_HEADER_ENV_SENDER, buffer);
583 nlines++;
584 }
585
586 /* Add status */
587 message_get_attribute (msg, &attr);
588 attribute_to_string (attr, buf, bsize, &n);
589 if (n)
590 {
591 nbytes += fprintf (fp, "%s", buf);
592 nlines++;
593 }
594 nbytes += fprintf (fp, "\n");
595 nlines++;
596
597 new_header_lines = nlines;
598 new_body_start = nbytes;
599
600 /* Copy message body */
601
602 message_get_body (msg, &body);
603 body_get_stream (body, &stream);
604 off = 0;
605 nlines = 0;
606 while (stream_read (stream, buf, bsize, off, &n) == 0 && n != 0)
607 {
608 char *p;
609 for (p = buf; p < buf + n; p++)
610 if (*p == '\n')
611 nlines++;
612 fwrite (buf, 1, n, fp);
613 off += n;
614 nbytes += n;
615 }
616
617 mhm->header_lines = new_header_lines;
618 mhm->body_start = new_body_start;
619 mhm->body_lines = nlines;
620 mhm->body_end = nbytes;
621
622 free (buf);
623 fclose (fp);
624
625 msg_name = _mh_message_name (mhm, mhm->deleted);
626 rename (name, msg_name);
627 free (name);
628 free (msg_name);
629
630 return 0;
631 }
632
633 static int
634 mh_append_message (mailbox_t mailbox, message_t msg)
635 {
636 int status;
637 struct _mh_data *mhd = mailbox->data;
638 struct _mh_message *mhm;
639
640 if (!mailbox || !msg)
641 return EINVAL;
642
643 mhm = calloc (1, sizeof(*mhm));
644 if (!mhm)
645 return ENOMEM;
646
647 /* If we did not start a scanning yet do it now. */
648 if (mhd->msg_count == 0)
649 {
650 status = mh_scan0 (mailbox, 1, NULL, 0);
651 if (status != 0)
652 return status;
653 }
654
655 mhm->mhd = mhd;
656 mhm->seq_number = _mh_next_seq (mhd);
657 mhm->message = msg;
658 status = _mh_message_save (mhd, mhm, 0);
659 mhm->message = NULL;
660 /* Insert and re-scan the message */
661 _mh_message_insert (mhd, mhm);
662 return status;
663 }
664
665 static int
666 mh_messages_count (mailbox_t mailbox, size_t *pcount)
667 {
668 struct _mh_data *mhd = mailbox->data;
669
670 if (mhd == NULL)
671 return EINVAL;
672
673 if (!mh_is_updated (mailbox))
674 return mh_scan0 (mailbox, mhd->msg_count, pcount, 0);
675
676 if (pcount)
677 *pcount = mhd->msg_count;
678
679 return 0;
680 }
681
682 /* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN
683 ('O' in the Status header), i.e. a message that is first seen
684 by the current session (see attributes.h) */
685 static int
686 mh_messages_recent (mailbox_t mailbox, size_t *pcount)
687 {
688 struct _mh_data *mhd = mailbox->data;
689 struct _mh_message *mhm;
690 size_t count;
691
692 /* If we did not start a scanning yet do it now. */
693 if (mhd->msg_count == 0)
694 {
695 int status = mh_scan0 (mailbox, 1, NULL, 0);
696 if (status != 0)
697 return status;
698 }
699 count = 0;
700 for (mhm = mhd->msg_head; mhm; mhm = mhm->next)
701 {
702 if (MU_ATTRIBUTE_IS_UNSEEN(mhm->attr_flags))
703 count++;
704 }
705 *pcount = count;
706 return 0;
707 }
708
709 /* An "unseen" message is the one that has not been read yet */
710 static int
711 mh_message_unseen (mailbox_t mailbox, size_t *pmsgno)
712 {
713 struct _mh_data *mhd = mailbox->data;
714 struct _mh_message *mhm;
715 size_t i, unseen;
716
717 /* If we did not start a scanning yet do it now. */
718 if (mhd->msg_count == 0)
719 {
720 int status = mh_scan0 (mailbox, 1, NULL, 0);
721 if (status != 0)
722 return status;
723 }
724 for (unseen = i = 1, mhm = mhd->msg_head; mhm; i++, mhm = mhm->next)
725 {
726 if (MU_ATTRIBUTE_IS_UNREAD(mhm->attr_flags))
727 {
728 unseen = i;
729 break;
730 }
731 }
732 *pmsgno = unseen;
733 return 0;
734 }
735
736 static int
737 mh_expunge (mailbox_t mailbox)
738 {
739 struct _mh_data *mhd = mailbox->data;
740 struct _mh_message *mhm;
741
742 if (mhd == NULL)
743 return EINVAL;
744
745 if (mhd->msg_count == 0)
746 return 0;
747
748 /* Find the first dirty(modified) message. */
749 for (mhm = mhd->msg_head; mhm; mhm = mhm->next)
750 {
751 if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED) ||
752 (mhm->attr_flags & MU_ATTRIBUTE_DELETED) ||
753 (mhm->message && message_is_modified (mhm->message)))
754 break;
755 }
756
757 if (!mhm)
758 return 0; /* Nothing changed, just return. */
759
760 while (mhm)
761 {
762 struct _mh_message *next = mhm->next;
763
764 if (mhm->attr_flags & MU_ATTRIBUTE_DELETED)
765 {
766 if (!mhm->deleted)
767 {
768 char *old_name, *new_name;
769 /* Rename original message */
770 old_name = _mh_message_name (mhm, 0);
771 new_name = _mh_message_name (mhm, 1);
772 rename (old_name, new_name);
773 free (old_name);
774 free (new_name);
775 }
776 _mh_message_delete (mhd, mhm);
777 }
778 else if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED)
779 || (mhm->message && message_is_modified (mhm->message)))
780 {
781 _mh_attach_message (mailbox, mhm, NULL);
782 mhm->deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED;
783 _mh_message_save (mhd, mhm, 1);
784 }
785 mhm = next;
786 }
787
788 return 0;
789 }
790
791 static int
792 mh_save_attributes (mailbox_t mailbox)
793 {
794 struct _mh_data *mhd = mailbox->data;
795 struct _mh_message *mhm;
796
797 if (mhd == NULL)
798 return EINVAL;
799
800 if (mhd->msg_count == 0)
801 return 0;
802
803 /* Find the first dirty(modified) message. */
804 for (mhm = mhd->msg_head; mhm; mhm = mhm->next)
805 {
806 if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED)
807 || (mhm->message && message_is_modified (mhm->message)))
808 break;
809 }
810
811 if (!mhm)
812 return 0; /* Nothing changed, just return. */
813
814 while (mhm)
815 {
816 struct _mh_message *next = mhm->next;
817
818 if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED)
819 || (mhm->message && message_is_modified (mhm->message)))
820 {
821 _mh_attach_message (mailbox, mhm, NULL);
822 mhm->deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED;
823 _mh_message_save (mhd, mhm, 0);
824 }
825 mhm = next;
826 }
827
828 return 0;
829 }
830
831 static int
832 mh_uidvalidity (mailbox_t mailbox, unsigned long *puidvalidity)
833 {
834 struct _mh_data *mhd = mailbox->data;
835 int status = mh_messages_count (mailbox, NULL);
836 if (status != 0)
837 return status;
838 /* If we did not start a scanning yet do it now. */
839 if (mhd->msg_count == 0)
840 {
841 status = mh_scan0 (mailbox, 1, NULL, 0);
842 if (status != 0)
843 return status;
844 }
845 if (puidvalidity)
846 *puidvalidity = mhd->uidvalidity;
847 return 0;
848 }
849
850 static int
851 mh_uidnext (mailbox_t mailbox, size_t *puidnext)
852 {
853 struct _mh_data *mhd = mailbox->data;
854 int status = mh_messages_count (mailbox, NULL);
855 if (status != 0)
856 return status;
857 /* If we did not start a scanning yet do it now. */
858 if (mhd->msg_count == 0)
859 {
860 status = mh_scan0 (mailbox, 1, NULL, 0);
861 if (status != 0)
862 return status;
863 }
864 if (puidnext)
865 *puidnext = _mh_next_seq(mhd);
866 return 0;
867 }
868
869 /* FIXME: effectively the same as mbox_cleanup */
870 static void
871 mh_cleanup (void *arg)
872 {
873 mailbox_t mailbox = arg;
874 monitor_unlock (mailbox->monitor);
875 locker_unlock (mailbox->locker);
876 }
877
878 /* Insert message msg into the message list on the appropriate position */
879 static void
880 _mh_message_insert (struct _mh_data *mhd, struct _mh_message *msg)
881 {
882 struct _mh_message *p;
883 struct _mh_message *prev;
884 size_t n = msg->seq_number;
885
886 for (p = mhd->msg_head; p && p->seq_number < n; p = p->next)
887 ;
888
889 if (!p)
890 {
891 msg->next = NULL;
892 msg->prev = mhd->msg_tail;
893 mhd->msg_tail = msg;
894 if (!mhd->msg_head)
895 mhd->msg_head = msg;
896 }
897 else
898 {
899 msg->next = p;
900 msg->prev = p->prev;
901 p->prev = msg;
902 }
903 if ((prev = msg->prev) != NULL)
904 prev->next = msg;
905 else
906 mhd->msg_head = msg;
907 msg->mhd = mhd;
908 mhd->msg_count++;
909 }
910
911 static void
912 _mh_message_delete (struct _mh_data *mhd, struct _mh_message *msg)
913 {
914 struct _mh_message *p;
915
916 if ((p = msg->next) != NULL)
917 p->prev = msg->prev;
918 else
919 mhd->msg_tail = msg->prev;
920
921 if ((p = msg->prev) != NULL)
922 p->next = msg->next;
923 else
924 mhd->msg_head = msg->next;
925
926 message_destroy (&msg->message, msg);
927 free (msg);
928 mhd->msg_count--;
929 }
930
931 /* Scan given message and fill mh_message_t fields.
932 NOTE: the function assumes mhm->stream != NULL. */
933 static int
934 mh_scan_message (struct _mh_message *mhm)
935 {
936 stream_t stream = mhm->stream;
937 char buf[1024];
938 off_t off = 0;
939 size_t n;
940 int status;
941 int in_header = 1;
942 size_t hlines = 0;
943 size_t blines = 0;
944 size_t body_start = 0;
945
946 /* Check if the message was modified after the last scan */
947 if (mhm->mtime)
948 {
949 struct stat st;
950 char *msg_name = _mh_message_name (mhm, mhm->deleted);
951
952 if (stat (msg_name, &st) == 0 && st.st_mtime == mhm->mtime)
953 {
954 /* Nothing to do */
955 free (msg_name);
956 return 0;
957 }
958 free (msg_name);
959 }
960
961 while ((status = stream_readline (stream, buf, sizeof (buf), off, &n) == 0)
962 && n != 0)
963 {
964 if (in_header)
965 {
966 if (buf[0] == '\n')
967 {
968 in_header = 0;
969 body_start = off+1;
970 }
971 if (buf[n - 1] == '\n')
972 hlines++;
973
974 /* Process particular attributes */
975 if (strncasecmp (buf, "status:", 7) == 0)
976 {
977 int deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED;
978 string_to_flags (buf, &mhm->attr_flags);
979 mhm->attr_flags |= deleted;
980 }
981 else if (strncasecmp (buf, "x-imapbase:", 11) == 0)
982 {
983 char *p;
984 mhm->mhd->uidvalidity = strtoul (buf + 11, &p, 10);
985 /* second number is next uid. Ignored */
986 }
987 }
988 else
989 {
990 if (buf[n - 1] == '\n')
991 blines++;
992 }
993 off += n;
994 }
995
996 if (!body_start)
997 body_start = off;
998 mhm->header_lines = hlines;
999 mhm->body_lines = blines;
1000 mhm->body_start = body_start;
1001 mhm->body_end = off;
1002 return 0;
1003 }
1004
1005 /* Scan the mailbox */
1006 static int
1007 mh_scan0 (mailbox_t mailbox, size_t msgno, size_t *pcount, int do_notify)
1008 {
1009 struct _mh_data *mhd = mailbox->data;
1010 struct _mh_message *msg;
1011 DIR *dir;
1012 struct dirent *entry;
1013 int status = 0;
1014 struct stat st;
1015 (void)msgno;
1016
1017 if (mhd == NULL)
1018 return EINVAL;
1019
1020 dir = opendir (mhd->name);
1021 if (!dir)
1022 return errno;
1023
1024 monitor_wrlock (mailbox->monitor);
1025
1026 #ifdef WITH_PTHREAD
1027 pthread_cleanup_push (mh_cleanup, (void *)mailbox);
1028 #endif
1029
1030 locker_lock (mailbox->locker);
1031
1032 /* Do actual work. */
1033
1034 while ((entry = readdir (dir)))
1035 {
1036 char *namep;
1037 int attr_flags;
1038 size_t num;
1039
1040 attr_flags = 0;
1041 switch (entry->d_name[0])
1042 {
1043 case '.':
1044 /* FIXME: .mh_sequences */
1045 continue;
1046 case ',':
1047 continue;
1048 #if 0
1049 attr_flags |= MU_ATTRIBUTE_DELETED;
1050 namep = entry->d_name+1;
1051 break;
1052 #endif
1053 case '0':case '1':case '2':case '3':case '4':
1054 case '5':case '6':case '7':case '8':case '9':
1055 namep = entry->d_name;
1056 break;
1057 default:
1058 /*FIXME: Invalid entry. Report? */
1059 continue;
1060 }
1061
1062 num = strtoul (namep, &namep, 10);
1063 if (namep[0])
1064 continue;
1065
1066 msg = _mh_get_message_seq (mhd, num);
1067 if (!msg)
1068 {
1069 msg = calloc (1, sizeof(*msg));
1070
1071 msg->seq_number = num;
1072 msg->attr_flags = attr_flags;
1073 msg->deleted = attr_flags & MU_ATTRIBUTE_DELETED;
1074
1075 _mh_message_insert (mhd, msg);
1076
1077 }
1078 else
1079 {
1080 msg->attr_flags = attr_flags;
1081 }
1082 }
1083
1084 closedir (dir);
1085
1086 if (do_notify)
1087 for (msg = mhd->msg_head; msg; msg = msg->next)
1088 {
1089 DISPATCH_ADD_MSG(mailbox, mhd);
1090 }
1091
1092 if (stat (mhd->name, &st) == 0)
1093 mhd->mtime = st.st_mtime;
1094
1095 if (pcount)
1096 *pcount = mhd->msg_count;
1097
1098 /* Reset the uidvalidity. */
1099 if (mhd->msg_count > 0)
1100 {
1101 if (mhd->uidvalidity == 0)
1102 {
1103 mhd->uidvalidity = (unsigned long)time (NULL);
1104 /* FIXME mhd->uidnext = mhd->msg_count + 1;*/
1105 /* Tell that we have been modified for expunging. */
1106 if (mhd->msg_head)
1107 {
1108 mh_message_stream_open (mhd->msg_head);
1109 mh_message_stream_close (mhd->msg_head);
1110 mhd->msg_head->attr_flags |= MU_ATTRIBUTE_MODIFIED;
1111 }
1112 }
1113 }
1114
1115 /* Clean up the things */
1116
1117 mh_cleanup (mailbox);
1118 #ifdef WITH_PTHREAD
1119 pthread_cleanup_pop (0);
1120 #endif
1121 return status;
1122 }
1123
1124
1125 static int
1126 mh_scan (mailbox_t mailbox, size_t msgno, size_t *pcount)
1127 {
1128 struct _mh_data *mhd = mailbox->data;
1129
1130 if (! mh_is_updated (mailbox))
1131 return mh_scan0 (mailbox, msgno, pcount, 1);
1132
1133 if (pcount)
1134 *pcount = mhd->msg_count;
1135
1136 return 0;
1137 }
1138
1139 /* Is the internal representation of the mailbox up to date.
1140 Return 1 if so, 0 otherwise. */
1141 static int
1142 mh_is_updated (mailbox_t mailbox)
1143 {
1144 struct stat st;
1145 struct _mh_data *mhd = mailbox->data;
1146
1147 if (!mhd->msg_head)
1148 return 0;
1149
1150 if (stat (mhd->name, &st) < 0)
1151 return 1;
1152
1153 return mhd->mtime == st.st_mtime;
1154 }
1155
1156 static int
1157 mh_get_size (mailbox_t mailbox, off_t *psize)
1158 {
1159 /*FIXME*/
1160 (void)mailbox;
1161 (void)psize;
1162 return ENOSYS;
1163 }
1164
1165 /* Return number of open streams residing in a message pool */
1166 static int
1167 mh_pool_open_count(struct _mh_data *mhd)
1168 {
1169 int cnt = mhd->pool_last - mhd->pool_first;
1170 if (cnt < 0)
1171 cnt += MAX_OPEN_STREAMS;
1172 return cnt;
1173 }
1174
1175 /* Look up a _mh_message in the pool of open messages.
1176 Returns 1 if the message is found in the pool, and 0 otherwise. */
1177 static int
1178 mh_pool_lookup (struct _mh_message *mhm)
1179 {
1180 struct _mh_data *mhd = mhm->mhd;
1181 int i;
1182
1183 for (i = mhd->pool_first; i != mhd->pool_last; )
1184 {
1185 if (mhd->msg_pool[i] == mhm)
1186 return 1;
1187 if (++i == MAX_OPEN_STREAMS)
1188 i = 0;
1189 }
1190 return 0;
1191 }
1192
1193 /* Open a stream associated with the message mhm. If the stream is
1194 already open, do nothing */
1195 static int
1196 mh_pool_open (struct _mh_message *mhm)
1197 {
1198 struct _mh_data *mhd = mhm->mhd;
1199 if (mh_pool_lookup (mhm))
1200 return 0;
1201 if (mh_pool_open_count(mhd) == MAX_OPEN_STREAMS-1)
1202 {
1203 mh_message_stream_close (mhd->msg_pool[mhd->pool_first++]);
1204 mhd->pool_first %= MAX_OPEN_STREAMS;
1205 }
1206 mh_message_stream_open (mhm);
1207 mhd->msg_pool[mhd->pool_last++] = mhm;
1208 mhd->pool_last %= MAX_OPEN_STREAMS;
1209 return 0;
1210 }
1211
1212 /* Attach a stream to a given message structure. The latter is supposed
1213 to be already added to the open message pool. */
1214 int
1215 mh_message_stream_open (struct _mh_message *mhm)
1216 {
1217 struct _mh_data *mhd = mhm->mhd;
1218 char *filename = NULL;
1219 int status;
1220 int flags = MU_STREAM_ALLOW_LINKS;
1221 filename = _mh_message_name (mhm, mhm->deleted);
1222
1223 if (!filename)
1224 return ENOMEM;
1225
1226 /* The message should be at least readable */
1227 if (mhd->mailbox->flags & (MU_STREAM_RDWR|MU_STREAM_WRITE|MU_STREAM_APPEND))
1228 flags |= MU_STREAM_RDWR;
1229 else
1230 flags |= MU_STREAM_READ;
1231 status = file_stream_create (&mhm->stream, filename, flags);
1232
1233 free (filename);
1234
1235 if (status != 0)
1236 return status;
1237
1238 status = stream_open (mhm->stream);
1239
1240 if (status != 0)
1241 stream_destroy (&mhm->stream, NULL);
1242
1243 if (status == 0)
1244 status = mh_scan_message (mhm);
1245
1246 return status;
1247 }
1248
1249 /* Close the stream associated with the given message. */
1250 void
1251 mh_message_stream_close (struct _mh_message *mhm)
1252 {
1253 if (mhm)
1254 {
1255 stream_close (mhm->stream);
1256 mhm->stream = NULL;
1257 }
1258 }
1259
1260 void
1261 mh_check_message (struct _mh_message *mhm)
1262 {
1263 if (mhm->body_end == 0)
1264 mh_pool_open (mhm);
1265 }
1266
1267 /* Reading functions */
1268
1269 static int
1270 mh_readstream (struct _mh_message *mhm, char *buffer, size_t buflen,
1271 off_t off, size_t *pnread, int isreadline,
1272 off_t start, off_t end)
1273 {
1274 size_t nread = 0;
1275 int status = 0;
1276 off_t ln;
1277
1278 if (buffer == NULL || buflen == 0)
1279 {
1280 if (pnread)
1281 *pnread = nread;
1282 return 0;
1283 }
1284
1285 monitor_rdlock (mhm->mhd->mailbox->monitor);
1286 #ifdef WITH_PTHREAD
1287 /* read() is cancellation point since we're doing a potentially
1288 long operation. Lets make sure we clean the state. */
1289 pthread_cleanup_push (mh_cleanup, (void *)mhm->mhd->mailbox);
1290 #endif
1291
1292 ln = end - (start + off);
1293 if (ln > 0)
1294 {
1295 /* Position the file pointer and the buffer. */
1296 nread = ((size_t)ln < buflen) ? (size_t)ln : buflen;
1297 if (isreadline)
1298 status = stream_readline (mhm->stream, buffer, buflen,
1299 start + off, &nread);
1300 else
1301 status = stream_read (mhm->stream, buffer, nread,
1302 start + off, &nread);
1303 }
1304
1305 monitor_unlock (mhm->mhd->mailbox->monitor);
1306 #ifdef WITH_PTHREAD
1307 pthread_cleanup_pop (0);
1308 #endif
1309
1310 if (pnread)
1311 *pnread = nread;
1312 return status;
1313 }
1314
1315 static int
1316 mh_body_read (stream_t is, char *buffer, size_t buflen, off_t off,
1317 size_t *pnread)
1318 {
1319 body_t body = stream_get_owner (is);
1320 message_t msg = body_get_owner (body);
1321 struct _mh_message *mhm = message_get_owner (msg);
1322 mh_pool_open (mhm);
1323 return mh_readstream (mhm, buffer, buflen, off, pnread, 0,
1324 mhm->body_start, mhm->body_end);
1325 }
1326
1327 static int
1328 mh_body_readline (stream_t is, char *buffer, size_t buflen,
1329 off_t off, size_t *pnread)
1330 {
1331 body_t body = stream_get_owner (is);
1332 message_t msg = body_get_owner (body);
1333 struct _mh_message *mhm = message_get_owner (msg);
1334 mh_pool_open (mhm);
1335 return mh_readstream (mhm, buffer, buflen, off, pnread, 1,
1336 mhm->body_start, mhm->body_end);
1337 }
1338
1339 /* Return corresponding sizes */
1340
1341 static int
1342 mh_stream_size (stream_t stream, off_t *psize)
1343 {
1344 body_t body = stream_get_owner (stream);
1345 return mh_body_size (body, (size_t*) psize);
1346 }
1347
1348 static int
1349 mh_body_size (body_t body, size_t *psize)
1350 {
1351 message_t msg = body_get_owner (body);
1352 struct _mh_message *mhm = message_get_owner (msg);
1353 if (mhm == NULL)
1354 return EINVAL;
1355 mh_check_message (mhm);
1356 if (psize)
1357 *psize = mhm->body_end - mhm->body_start;
1358 return 0;
1359 }
1360
1361 static int
1362 mh_body_lines (body_t body, size_t *plines)
1363 {
1364 message_t msg = body_get_owner (body);
1365 struct _mh_message *mhm = message_get_owner (msg);
1366 if (mhm == NULL)
1367 return EINVAL;
1368 mh_check_message (mhm);
1369 if (plines)
1370 *plines = mhm->body_lines;
1371 return 0;
1372 }
1373
1374 static int
1375 mh_message_uid (message_t msg, size_t *puid)
1376 {
1377 struct _mh_message *mhm = message_get_owner (msg);
1378 if (puid)
1379 *puid = mhm->seq_number;
1380 return 0;
1381 }
1382
1383 /* Headers */
1384 static int
1385 mh_header_fill (header_t header, char *buffer, size_t len,
1386 off_t off, size_t *pnread)
1387 {
1388 message_t msg = header_get_owner (header);
1389 struct _mh_message *mhm = message_get_owner (msg);
1390
1391 mh_pool_open (mhm);
1392 return mh_readstream (mhm, buffer, len, off, pnread, 0,
1393 0, mhm->body_start);
1394 }
1395
1396 static int
1397 mh_header_size (header_t header, size_t *psize)
1398 {
1399 message_t msg = header_get_owner (header);
1400 struct _mh_message *mhm = message_get_owner (msg);
1401 if (mhm == NULL)
1402 return EINVAL;
1403 mh_check_message (mhm);
1404 if (psize)
1405 *psize = mhm->body_start;
1406 return 0;
1407 }
1408
1409 static int
1410 mh_header_lines (header_t header, size_t *plines)
1411 {
1412 message_t msg = header_get_owner (header);
1413 struct _mh_message *mhm = message_get_owner (msg);
1414 if (mhm == NULL)
1415 return EINVAL;
1416 mh_check_message (mhm);
1417 if (plines)
1418 *plines = mhm->header_lines;
1419 return 0;
1420 }
1421
1422 /* Attributes */
1423 static int
1424 mh_get_attr_flags (attribute_t attr, int *pflags)
1425 {
1426 message_t msg = attribute_get_owner (attr);
1427 struct _mh_message *mhm = message_get_owner (msg);
1428
1429 if (mhm == NULL)
1430 return EINVAL;
1431 if (pflags)
1432 *pflags = mhm->attr_flags;
1433 return 0;
1434 }
1435
1436 static int
1437 mh_set_attr_flags (attribute_t attr, int flags)
1438 {
1439 message_t msg = attribute_get_owner (attr);
1440 struct _mh_message *mhm = message_get_owner (msg);
1441
1442 if (mhm == NULL)
1443 return EINVAL;
1444 mhm->attr_flags |= flags;
1445 return 0;
1446 }
1447
1448 static int
1449 mh_unset_attr_flags (attribute_t attr, int flags)
1450 {
1451 message_t msg = attribute_get_owner (attr);
1452 struct _mh_message *mhm = message_get_owner (msg);
1453
1454 if (mhm == NULL)
1455 return EINVAL;
1456 mhm->attr_flags &= ~flags;
1457 return 0;
1458 }
1459
1460 /* Envelope */
1461 static int
1462 mh_envelope_date (envelope_t envelope, char *buf, size_t len,
1463 size_t *psize)
1464 {
1465 message_t msg = envelope_get_owner (envelope);
1466 struct _mh_message *mhm = message_get_owner (msg);
1467 header_t hdr = NULL;
1468 char *from;
1469 int status;
1470
1471 if (mhm == NULL)
1472 return EINVAL;
1473
1474 if ((status = message_get_header (msg, &hdr)) != 0)
1475 return status;
1476 if (header_aget_value (hdr, MU_HEADER_ENV_DATE, &from))
1477 return ENOSYS;
1478
1479 /* Format: "sender date" */
1480 if (buf && len > 0)
1481 {
1482 len--; /* Leave space for the null. */
1483 strncpy (buf, from, len);
1484 if (strlen (from) < len)
1485 {
1486 len = strlen (buf);
1487 buf[len++] = '\n';
1488 }
1489 buf[len] = '\0';
1490 }
1491 else
1492 len = 0;
1493
1494 if (psize)
1495 *psize = len;
1496 return 0;
1497 }
1498
1499 static int
1500 mh_envelope_sender (envelope_t envelope, char *buf, size_t len, size_t *psize)
1501 {
1502 message_t msg = envelope_get_owner (envelope);
1503 struct _mh_message *mhm = message_get_owner (msg);
1504 header_t hdr = NULL;
1505 char *from;
1506 int status;
1507
1508 if (mhm == NULL)
1509 return EINVAL;
1510
1511 if ((status = message_get_header (msg, &hdr)) != 0)
1512 return status;
1513 if (header_aget_value (hdr, MU_HEADER_ENV_SENDER, &from))
1514 return ENOSYS;
1515
1516 if (buf && len > 0)
1517 {
1518 int slen = strlen (from);
1519
1520 if (len < slen + 1)
1521 slen = len - 1;
1522 memcpy (buf, from, slen);
1523 buf[slen] = 0;
1524 }
1525 else
1526 len = 0;
1527
1528 if (psize)
1529 *psize = len;
1530 return 0;
1531 }
1532
1533 #endif