1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2019-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 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
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #ifdef WITH_PTHREAD
21 # include <pthread.h>
22 #endif
23 #include <sys/stat.h>
24 #include <signal.h>
25 #include <mailutils/sys/mboxrd.h>
26 #include <mailutils/sys/mailbox.h>
27 #include <mailutils/sys/message.h>
28 #include <mailutils/diag.h>
29 #include <mailutils/errno.h>
30 #include <mailutils/url.h>
31 #include <mailutils/property.h>
32 #include <mailutils/io.h>
33 #include <mailutils/observer.h>
34 #include <mailutils/filter.h>
35 #include <mailutils/stream.h>
36 #include <mailutils/locker.h>
37 #include <mailutils/nls.h>
38 #include <mailutils/header.h>
39 #include <mailutils/attribute.h>
40 #include <mailutils/envelope.h>
41 #include <mailutils/util.h>
42 #include <mailutils/cctype.h>
43 #include <mailutils/sys/folder.h>
44 #include <mailutils/sys/registrar.h>
45
46 static void
mboxrd_destroy(mu_mailbox_t mailbox)47 mboxrd_destroy (mu_mailbox_t mailbox)
48 {
49 size_t i;
50 struct mu_mboxrd_mailbox *dmp = mailbox->data;
51
52 if (!dmp)
53 return;
54
55 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
56 ("%s (%s)", __func__, dmp->name));
57 mu_monitor_wrlock (mailbox->monitor);
58 for (i = 0; i < dmp->mesg_count; i++)
59 {
60 mu_mboxrd_message_free (dmp->mesg[i]);
61 }
62 free (dmp->mesg);
63 free (dmp->name);
64 free (dmp);
65 mailbox->data = NULL;
66 mu_monitor_unlock (mailbox->monitor);
67 }
68
69 static int
mboxrd_mailbox_init_stream(struct mu_mboxrd_mailbox * dmp)70 mboxrd_mailbox_init_stream (struct mu_mboxrd_mailbox *dmp)
71 {
72 int rc;
73 mu_mailbox_t mailbox = dmp->mailbox;
74
75 /*
76 * Initialize stream flags. If append mode is requested, convert it to
77 * read-write, so that mboxrd_flush_unlocked be able to update the
78 * X-IMAPbase header in the first message, if necessary.
79 */
80 dmp->stream_flags = mailbox->flags;
81 if (dmp->stream_flags & MU_STREAM_APPEND)
82 dmp->stream_flags = (dmp->stream_flags & ~MU_STREAM_APPEND) | MU_STREAM_RDWR;
83 else if (dmp->stream_flags & MU_STREAM_WRITE)
84 dmp->stream_flags |= MU_STREAM_READ;
85
86 rc = mu_mapfile_stream_create (&mailbox->stream, dmp->name, dmp->stream_flags);
87 if (rc)
88 {
89 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
90 ("%s:%s (%s): %s",
91 __func__, "mu_mapfile_stream_create", dmp->name,
92 mu_strerror (rc)));
93
94 /* Fallback to regular file stream */
95 rc = mu_file_stream_create (&mailbox->stream, dmp->name, dmp->stream_flags);
96 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
97 ("%s:%s (%s): %s",
98 __func__, "mu_file_stream_create", dmp->name,
99 mu_strerror (rc)));
100
101 if (rc)
102 return rc;
103 }
104
105 mu_stream_set_buffer (mailbox->stream, mu_buffer_full, 0);
106 return 0;
107 }
108
109 static int
mboxrd_open(mu_mailbox_t mailbox,int flags)110 mboxrd_open (mu_mailbox_t mailbox, int flags)
111 {
112 struct mu_mboxrd_mailbox *dmp = mailbox->data;
113 int rc;
114
115 if (!dmp)
116 return EINVAL;
117
118 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
119 ("%s(%s, 0x%x)", __func__, dmp->name, mailbox->flags));
120
121 mailbox->flags = flags;
122
123 rc = mboxrd_mailbox_init_stream (dmp);
124
125 if (rc == 0
126 && mailbox->locker == NULL
127 && (flags & (MU_STREAM_WRITE | MU_STREAM_APPEND | MU_STREAM_CREAT)))
128 {
129 rc = mu_locker_create_ext (&mailbox->locker, dmp->name, NULL);
130 if (rc)
131 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
132 ("%s:%s (%s): %s",
133 __func__, "mu_locker_create_ext", dmp->name,
134 mu_strerror (rc)));
135 }
136
137 return rc;
138 }
139
140 enum
141 {
142 FLUSH_SYNC,
143 FLUSH_EXPUNGE, /* implies SYNC */
144 FLUSH_UIDVALIDITY
145 };
146
147 static int mboxrd_flush (struct mu_mboxrd_mailbox *dmp, int flag);
148
149 static int
mboxrd_close(mu_mailbox_t mailbox)150 mboxrd_close (mu_mailbox_t mailbox)
151 {
152 struct mu_mboxrd_mailbox *dmp = mailbox->data;
153 size_t i;
154
155 if (!dmp)
156 return EINVAL;
157
158 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
159 ("%s (%s)", __func__, dmp->name));
160
161 if (dmp->uidvalidity_changed && (dmp->stream_flags & MU_STREAM_WRITE))
162 mboxrd_flush (dmp, FLUSH_UIDVALIDITY);
163
164 mu_locker_unlock (mailbox->locker);
165 mu_monitor_wrlock (mailbox->monitor);
166 for (i = 0; i < dmp->mesg_count; i++)
167 {
168 mu_mboxrd_message_free (dmp->mesg[i]);
169 }
170 free (dmp->mesg);
171 dmp->mesg = NULL;
172 dmp->mesg_count = dmp->mesg_max = 0;
173 dmp->size = 0;
174 dmp->uidvalidity = 0;
175 dmp->uidnext = 1;
176 mu_monitor_unlock (mailbox->monitor);
177 mu_stream_destroy (&mailbox->stream);
178 return 0;
179 }
180
181 static int
mboxrd_remove(mu_mailbox_t mailbox)182 mboxrd_remove (mu_mailbox_t mailbox)
183 {
184 struct mu_mboxrd_mailbox *dmp = mailbox->data;
185
186 if (!dmp)
187 return EINVAL;
188 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
189 ("%s (%s)", __func__, dmp->name));
190 if (unlink (dmp->name))
191 return errno;
192 return 0;
193 }
194
195 static int
mboxrd_is_updated(mu_mailbox_t mailbox)196 mboxrd_is_updated (mu_mailbox_t mailbox)
197 {
198 struct mu_mboxrd_mailbox *dmp = mailbox->data;
199 mu_off_t size = 0;
200
201 if (!dmp)
202 return 0;
203
204 if (mu_stream_size (mailbox->stream, &size) != 0)
205 return 1;
206 if (size < dmp->size)
207 {
208 mu_observable_notify (mailbox->observable, MU_EVT_MAILBOX_CORRUPT,
209 mailbox);
210 mu_diag_output (MU_DIAG_EMERG, _("mailbox corrupted, shrank in size"));
211 return 0;
212 }
213 return (dmp->size == size);
214 }
215
216 #ifdef WITH_PTHREAD
217 void
mboxrd_cleanup(void * arg)218 mboxrd_cleanup (void *arg)
219 {
220 mu_mailbox_t mailbox = arg;
221 mu_monitor_unlock (mailbox->monitor);
222 mu_locker_unlock (mailbox->locker);
223 }
224 #endif
225
226 static int
mboxrd_alloc_message(struct mu_mboxrd_mailbox * dmp,struct mu_mboxrd_message ** dmsg_ptr)227 mboxrd_alloc_message (struct mu_mboxrd_mailbox *dmp,
228 struct mu_mboxrd_message **dmsg_ptr)
229 {
230 struct mu_mboxrd_message *dmsg;
231
232 if (dmp->mesg_count == dmp->mesg_max)
233 {
234 size_t n = dmp->mesg_max;
235 void *p;
236
237 if (n == 0)
238 n = 64;
239 else
240 {
241 if ((size_t) -1 / 3 * 2 / sizeof (dmp->mesg[0]) <= n)
242 return ENOMEM;
243 n += (n + 1) / 2;
244 }
245 p = realloc (dmp->mesg, n * sizeof (dmp->mesg[0]));
246 if (!p)
247 return ENOMEM;
248 dmp->mesg = p;
249 dmp->mesg_max = n;
250 }
251 dmsg = calloc (1, sizeof (*dmsg));
252 if (!dmsg)
253 return ENOMEM;
254 dmsg->mbox = dmp;
255 dmsg->num = dmp->mesg_count;
256 dmp->mesg[dmp->mesg_count++] = dmsg;
257 *dmsg_ptr = dmsg;
258 return 0;
259 }
260
261 static int
mboxrd_dispatch(mu_mailbox_t mailbox,int evt,void * data)262 mboxrd_dispatch (mu_mailbox_t mailbox, int evt, void *data)
263 {
264 if (!mailbox->observable)
265 return 0;
266
267 mu_monitor_unlock (mailbox->monitor);
268 if (mu_observable_notify (mailbox->observable, evt, data))
269 {
270 if (mailbox->locker)
271 mu_locker_unlock (mailbox->locker);
272 return EINTR;
273 }
274 mu_monitor_wrlock (mailbox->monitor);
275 return 0;
276 }
277
278 /* Notes on the UID subsystem
279
280 1. The values of uidvalidity and uidnext are stored in the
281 X-IMAPbase header in the first message.
282 2. Message UID is stored in the X-UID header in that message.
283 3. To minimize unwanted modifications to the mailbox, the
284 UID subsystem is initialized only in the following cases:
285
286 3a. Upon mailbox scanning, if the first message contains a
287 valid X-IMAPbase header. In this case, the
288 mboxrd_rescan_unlocked function initializes each
289 message's uid value from the X-UID header. The first
290 message that lacks X-UID or with an X-UID that cannot
291 be parsed, gets assigned new UID. The subsequent
292 messages are assigned new UIDs no matter whether they
293 have X-UID headers. In this case, the uidvalidity value
294 is reset to the current timestamp, to indicate that all
295 UIDs might have changed.
296
297 3b. When any of the following functions are called for
298 the first time: mboxrd_uidvalidity, mboxrd_uidnext,
299 mboxrd_message_uid. This means that the caller used
300 mu_mailbox_uidvalidity, mu_mailbox_uidnext, or
301 mu_message_get_uid.
302 In this case, each message is assigned a UID equal to
303 its ordinal number (1-based) in the mailbox.
304 This is done by the mu_mboxrd_mailbox_uid_setup function.
305
306 4. When a message is appended to the mailbox, any existing
307 X-IMAPbase and X-UID headers are removed from it. If the
308 UID subsystem is initialized, the message is assigned a new
309 UID.
310 5. Assigning new UID to a message does not change its attributes.
311 Instead, its uid_modified flag is set.
312 */
313
314 /* Allocate next available UID for the mailbox.
315 The caller must ensure that the UID subsystem is initialized.
316 */
317 static unsigned long
mboxrd_alloc_next_uid(struct mu_mboxrd_mailbox * mbox)318 mboxrd_alloc_next_uid (struct mu_mboxrd_mailbox *mbox)
319 {
320 mbox->uidvalidity_changed = 1;
321 return mbox->uidnext++;
322 }
323
324 static void
mboxrd_message_alloc_uid(struct mu_mboxrd_message * dmsg)325 mboxrd_message_alloc_uid (struct mu_mboxrd_message *dmsg)
326 {
327 dmsg->uid = mboxrd_alloc_next_uid (dmsg->mbox);
328 dmsg->uid_modified = 1;
329 }
330
331 /* Width of the decimal representation of the maximum value of the unsigned
332 * type t. 146/485 is the closest approximation of log10(2):
333 *
334 * log10(2) = .301030
335 * 146/485 = .301031
336 */
337 #define UINT_STRWIDTH(t) ((int)((sizeof(t) * 8 * 146 + 484) / 485))
338
339 /*
340 * The format for the X-IMAPbase header is:
341 *
342 * X-IMAPbase: <V> <N>
343 *
344 * where <V> and <N> are current values of the uidvalidity and uidnext
345 * parameters, correspondingly.
346 *
347 * The header is stored in the first message. To avoid rewriting entire
348 * mailbox when one of the parameters chages, the values of <V> and <N>
349 * are left-padded with spaces to the maximum width of their data types.
350 *
351 * Offset of the header in the mailbox and its length (without the
352 * trailing newline) are stored in x_imapbase_off and x_imapbase_len
353 * members of struct mu_mboxrd_mailbox.
354 *
355 * The X_IMAPBASE_MAX macro returns maximum size of the buffer necessary
356 * for formatting the X-IMAPbase header. In fact, it is 2 bytes wider
357 * than necessary (due to the two '0' in the sample string below).
358 */
359 #define X_IMAPBASE_MAX(d) \
360 (sizeof (MU_HEADER_X_IMAPBASE ": 0 0") + \
361 UINT_STRWIDTH ((d)->uidvalidity) + \
362 UINT_STRWIDTH ((d)->uidnext))
363
364 /*
365 * A modified version of Marc Crispin VALID macro.
366 *
367 * This function handles all existing flavors of the From_ line, most
368 * of which are antiquated and fallen out of use:
369 *
370 * From user Wed Dec 2 05:53 1992
371 * BSD From user Wed Dec 2 05:53:22 1992
372 * SysV From user Wed Dec 2 05:53 PST 1992
373 * rn From user Wed Dec 2 05:53:22 PST 1992
374 * From user Wed Dec 2 05:53 -0700 1992
375 * From user Wed Dec 2 05:53:22 -0700 1992
376 * From user Wed Dec 2 05:53 1992 PST
377 * From user Wed Dec 2 05:53:22 1992 PST
378 * From user Wed Dec 2 05:53 1992 -0700
379 * Solaris From user Wed Dec 2 05:53:22 1992 -0700
380 *
381 * (plus all of them followed by " remote from xxx"). Moreover, the
382 * user part is allowed to have whitespaces in it (although mailutils
383 * email address functions won't tolerate it).
384 *
385 * Input: S - line read from the mailbox (with \n terminator).
386 * Output: ZP - points to the space character preceding the time zone
387 * information in S. If there is no TZ, points to the terminating \n.
388 *
389 * Return value: If S is a valid From_ line, a pointer to the time
390 * information in S. Otherwise, NULL.
391 */
392 static inline char *
parse_from_line(char const * s,char ** zp)393 parse_from_line (char const *s, char **zp)
394 {
395 int ti = 0;
396 int zn;
397 // Validate if the string begins with 'From '
398 if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') &&
399 (s[4] == ' '))
400 {
401 char *x = strchr (s, '\n');
402 if (x)
403 {
404 if (x - s >= 41)
405 {
406 static char suf[] = " remote from ";
407 # define suflen (sizeof(suf)-1)
408
409 for (zn = -1; x + zn > s && x[zn] != ' '; (zn)--);
410 if (memcmp (x + zn - suflen + 1, suf, suflen) == 0)
411 x += zn - suflen + 1;
412 }
413 if (x - s >= 27)
414 {
415 if (x[-5] == ' ')
416 {
417 if (x[-8] == ':')
418 {
419 zn = 0;
420 ti = -5;
421 }
422 else if (x[-9] == ' ')
423 ti = zn = -9;
424 else if ((x[-11] == ' ')
425 && ((x[-10] == '+') || (x[-10] == '-')))
426 ti = zn = -11;
427 }
428 else if (x[-4] == ' ')
429 {
430 if (x[-9] == ' ')
431 {
432 zn = -4;
433 ti = -9;
434 }
435 }
436 else if (x[-6] == ' ')
437 {
438 if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-')))
439 {
440 zn = -6;
441 ti = -11;
442 }
443 }
444 if (ti && !((x[ti - 3] == ':') &&
445 (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') &&
446 (x[ti - 3] == ' ') && (x[ti - 7] == ' ') &&
447 (x[ti - 11] == ' ')))
448 ti = 0;
449 }
450 }
451 if (ti)
452 {
453 *zp = x + zn;
454 return (char*) x + ti;
455 }
456 }
457 return NULL;
458 }
459
460 /* Finalize current message */
461 static inline int
scan_message_finalize(struct mu_mboxrd_mailbox * dmp,struct mu_mboxrd_message * dmsg,mu_stream_t stream,size_t n,int * force_init_uids)462 scan_message_finalize (struct mu_mboxrd_mailbox *dmp,
463 struct mu_mboxrd_message *dmsg, mu_stream_t stream,
464 size_t n, int *force_init_uids)
465 {
466 int rc;
467 size_t count;
468
469 rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_end);
470 if (rc)
471 {
472 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
473 ("%s:%s (%s): %s",
474 __func__, "mu_stream_seek", dmp->name,
475 mu_strerror (rc)));
476 return -1;
477 }
478 dmsg->message_end -= n + 1;
479 if (dmsg->uid == 0)
480 *force_init_uids = 1;
481 if (*force_init_uids)
482 mboxrd_message_alloc_uid (dmsg);
483
484 /* Every 100 mesgs update the lock, it should be every minute. */
485 if (dmp->mailbox->locker && (dmp->mesg_count % 100) == 0)
486 mu_locker_touchlock (dmp->mailbox->locker);
487
488 count = dmp->mesg_count;
489 mboxrd_dispatch (dmp->mailbox, MU_EVT_MESSAGE_ADD, &count);
490 return 0;
491 }
492
493 static inline struct mu_mboxrd_message *
scan_message_begin(struct mu_mboxrd_mailbox * dmp,mu_stream_t stream,char * buf,size_t n,char * ti,char * zn)494 scan_message_begin (struct mu_mboxrd_mailbox *dmp, mu_stream_t stream,
495 char *buf, size_t n, char *ti, char *zn)
496 {
497 int rc;
498 struct mu_mboxrd_message *dmsg;
499
500 /* Create new message */
501 rc = mboxrd_alloc_message (dmp, &dmsg);
502 if (rc)
503 {
504 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
505 ("%s:%s (%s): %s",
506 __func__, "mboxrd_alloc_message", dmp->name,
507 mu_strerror (rc)));
508 return NULL;
509 }
510 rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->message_start);
511 if (rc)
512 {
513 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
514 ("%s:%s (%s): %s",
515 __func__, "mu_stream_seek", dmp->name,
516 mu_strerror (rc)));
517 return NULL;
518 }
519 dmsg->message_start -= n;
520 dmsg->from_length = n;
521 dmsg->env_sender_len = ti - buf - 10;
522 while (dmsg->env_sender_len > 6 && buf[dmsg->env_sender_len-1] == ' ')
523 dmsg->env_sender_len--;
524 dmsg->env_sender_len -= 5;
525
526 if (zn[0] != '\n')
527 {
528 /*
529 * Ideally, From_ line should not contain time zone info. If it does,
530 * zn points to the first space before the TZ. The latter can be an
531 * abbreviated time zone or a numeric offset from UTC (see the comment
532 * to parse_from_line above). Parse the date line and convert it to
533 * a normalized form (ctime(3)).
534 */
535 struct tm tm;
536 static char const fmt[] = "%a %b %e %H:%M%[:%S%] %(%Z %Y%|%Y%[ %Z%]%)";
537 struct mu_timezone tz;
538 char *te;
539 time_t t;
540
541 if (mu_scan_datetime (ti - 10, fmt, &tm, &tz, &te) == 0)
542 t = mu_datetime_to_utc (&tm, &tz);
543 else
544 t = time (NULL);
545 gmtime_r (&t, &tm);
546 mu_strftime (dmsg->date, sizeof (dmsg->date), MU_DATETIME_FROM, &tm);
547 }
548 else
549 {
550 /*
551 * No zone information in the timestamp. Copy the timestamp to
552 * the date buffer. The timestamp may or may not contain seconds.
553 * In the latter case, assume '0' seconds.
554 */
555 if (ti[6] == ':')
556 memcpy (dmsg->date, ti - 10, MU_DATETIME_FROM_LENGTH);
557 else
558 {
559 memcpy (dmsg->date, ti - 10, 16);
560 memcpy (dmsg->date + 16, ":00", 3);
561 dmsg->date[19] = ' ';
562 memcpy (dmsg->date + 20, ti + 7, 4);
563 }
564 dmsg->date[24] = 0;
565 }
566
567 return dmsg;
568 }
569
570 /* Scan the mailbox starting from the given offset.
571 *
572 * Notes on the mailbox format:
573 *
574 * 1. A mailbox consists of a series of messages.
575 *
576 * 2. Each message is preceded by a From_ line and followed by a blank line.
577 * A From_ line is a line that begins with the five characters 'F', 'r',
578 * 'o', 'm', and ' ', followed by sender email and delivery date. The
579 * From_ line parser is able to handle various From_ line formats
580 * (differing mainly in date/time format), most of which are encountered
581 * only in ancient mailboxes. Nevertheless, this makes escaping of the
582 * From_ lines less crucial and ensures optimal robustness in handling
583 * different mailbox formats (mboxo, mboxrd and mboxcl (mboxcl2) are
584 * all handled properly.
585 *
586 * 3. The From_ lines and the blank lines bracket messages, fore and aft.
587 * They do not comprise a message divider. This means, in particular,
588 * that both bracketing lines should be included in the message octet
589 * and line counts. However, counting the From_ line goes against
590 * the mailutils approach of logically dividing envelope and the rest
591 * of the message and would create useless differences compared to
592 * another mailbox formats. For this reason, the From_ line is not
593 * reflected in returns from the mu_message_size and mu_message_lines
594 * functions. The terminating blank line, on the contrary, is assumed
595 * to be part of the message body and is counted in body and message
596 * size and line count computations.
597 *
598 * 4. A mailbox that contains zero messages contains no lines.
599 *
600 * 5. The first message in the mailbox is not preceded by a blank line.
601 * The last message in the mailbox is not followed by a From_ line.
602 *
603 * 6. If a non-empty file does not have valid From_ construct in its
604 * first physical line, it will be rejected by the parser.
605 *
606 * 7. A message may contain blank lines. It should not, however, contain
607 * lines beginning with the sequence 'F', 'r', 'o', 'm', ' '.
608 *
609 * 8. When incorporating a message into the mailbox, any line in the message
610 * body that begins with zero or more '>' characters immediately followed
611 * by the sequence 'F', 'r', 'o', 'm', ' ', is escaped by prepending it
612 * with a '>' character. Thus, "From " becomes ">From ", ">From "
613 * becomes ">>From ", and so on. When reading the message body, a
614 * reverse operation is performed.
615 *
616 * 9. Last message in the mailbox is allowed to end with a partial last line
617 * (i.e. the one whose final characters are not two newlines). The message
618 * will be handled as usual. When a new message is incorporated to the
619 * mailbox, the missing newlines will be added to the end of the last
620 * message.
621 */
622 static int
mboxrd_rescan_unlocked(mu_mailbox_t mailbox,mu_off_t offset)623 mboxrd_rescan_unlocked (mu_mailbox_t mailbox, mu_off_t offset)
624 {
625 struct mu_mboxrd_mailbox *dmp = mailbox->data;
626 int rc;
627 mu_stream_t stream;
628 char *buf = NULL;
629 size_t bufsize = 0;
630 size_t n;
631 enum mboxrd_scan_state
632 {
633 mboxrd_scan_init, /* At the beginning of the file */
634 mboxrd_scan_header,/* Scanning message header */
635 mboxrd_scan_body, /* Scanning message body */
636 mboxrd_scan_empty_line /* At the empty line within or at the end of body */
637 } state = mboxrd_scan_init;
638 struct mu_mboxrd_message *dmsg = NULL;
639 char *zn, *ti;
640 int force_init_uids = 0;
641 size_t numlines = 0;
642
643 # define IS_HEADER(h,b,n) \
644 ((n) > sizeof (h) - 1 \
645 && strncasecmp (b, h, sizeof (h) - 1) == 0 \
646 && b[sizeof (h) - 1] == ':')
647
648
649 rc = mu_stream_size (mailbox->stream, &dmp->size);
650 if (rc)
651 return rc;
652 if (offset == dmp->size)
653 return 0;
654 if (!(dmp->stream_flags & MU_STREAM_READ))
655 return 0;
656
657 rc = mu_streamref_create (&stream, mailbox->stream);
658 if (rc)
659 {
660 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
661 ("%s:%s (%s): %s",
662 __func__, "mu_streamref_create", dmp->name,
663 mu_strerror (rc)));
664 return rc;
665 }
666
667 rc = mu_stream_seek (stream, offset, MU_SEEK_SET, NULL);
668 if (rc)
669 {
670 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
671 ("%s:%s (%s): %s",
672 __func__, "mu_stream_seek", dmp->name,
673 mu_strerror (rc)));
674 return rc;
675 }
676
677 while ((rc = mu_stream_getline (stream, &buf, &bufsize, &n)) == 0
678 && n > 0)
679 {
680 switch (state)
681 {
682 case mboxrd_scan_init:
683 if ((ti = parse_from_line (buf, &zn)) == 0)
684 {
685 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
686 ("%s does not start with a valid From_ line",
687 dmp->name));
688 rc = MU_ERR_PARSE;
689 goto err;
690 }
691 if ((dmsg = scan_message_begin (dmp, stream, buf, n, ti, zn)) == NULL)
692 goto err;
693 state = mboxrd_scan_header;
694 break;
695
696 case mboxrd_scan_header:
697 if (n == 1 && buf[0] == '\n')
698 {
699 rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &dmsg->body_start);
700 if (rc)
701 {
702 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
703 ("%s:%s (%s): %s",
704 __func__, "mu_stream_seek", dmp->name,
705 mu_strerror (rc)));
706 goto err;
707 }
708 state = mboxrd_scan_body;
709 }
710 else if (mu_isspace (buf[0]))
711 continue;
712 else if (!dmp->uidvalidity_scanned
713 && IS_HEADER (MU_HEADER_X_IMAPBASE, buf, n))
714 {
715 if (sscanf (buf + sizeof (MU_HEADER_X_IMAPBASE),
716 "%lu %lu",
717 &dmp->uidvalidity, &dmp->uidnext) == 2)
718 {
719 mu_off_t off;
720
721 rc = mu_stream_seek (stream, 0, MU_SEEK_CUR, &off);
722 if (rc)
723 {
724 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
725 ("%s:%s (%s): %s",
726 __func__, "mu_stream_seek", dmp->name,
727 mu_strerror (rc)));
728 goto err;
729 }
730 dmp->x_imapbase_len = n - 1;
731 dmp->x_imapbase_off = off - n;
732 dmp->uidvalidity_scanned = 1;
733 }
734 }
735 else if (!force_init_uids
736 && dmsg->uid == 0
737 && IS_HEADER (MU_HEADER_X_UID, buf, n))
738 {
739 if (!(sscanf (buf + sizeof (MU_HEADER_X_UID), "%lu", &dmsg->uid) == 1
740 && dmsg->uid < dmp->uidnext
741 && (dmsg->num == 0 || dmsg->uid > dmp->mesg[dmsg->num - 1]->uid)))
742 {
743 force_init_uids = 1;
744 }
745 }
746 else if (IS_HEADER (MU_HEADER_STATUS, buf, n))
747 {
748 mu_attribute_string_to_flags (buf + sizeof (MU_HEADER_STATUS),
749 &dmsg->attr_flags);
750 }
751 break;
752
753 case mboxrd_scan_body:
754 if (n == 1 && buf[0] == '\n')
755 {
756 state = mboxrd_scan_empty_line;
757 }
758 break;
759
760 case mboxrd_scan_empty_line:
761 if ((ti = parse_from_line (buf, &zn)) != 0)
762 {
763 if (scan_message_finalize (dmp, dmsg, stream, n, &force_init_uids))
764 goto err;
765 if ((dmsg = scan_message_begin (dmp, stream, buf, n, ti, zn)) == NULL)
766 goto err;
767 state = mboxrd_scan_header;
768 }
769 else if (n == 1 && buf[0] == '\n')
770 state = mboxrd_scan_empty_line;
771 else
772 state = mboxrd_scan_body;
773 }
774 if (++numlines % 1000 == 0)
775 mboxrd_dispatch (mailbox, MU_EVT_MAILBOX_PROGRESS, NULL);
776 }
777
778 if (dmsg)
779 {
780 if (scan_message_finalize (dmp, dmsg, stream, 0, &force_init_uids))
781 goto err;
782 }
783
784 err:
785 if (rc)
786 {
787 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
788 ("%s:%s (%s): %s",
789 __func__, "mu_stream_read", dmp->name,
790 mu_strerror (rc)));
791 }
792 mu_stream_unref (stream);
793 free (buf);
794
795 if (force_init_uids)
796 {
797 dmp->uidvalidity = (unsigned long) time (NULL);
798 dmp->uidvalidity_changed = 1;
799 dmp->uidvalidity_scanned = 1;
800 }
801
802 return rc;
803 }
804
805 /* Scan the mailbox starting from the given offset */
806 static int
mboxrd_rescan(mu_mailbox_t mailbox,mu_off_t offset)807 mboxrd_rescan (mu_mailbox_t mailbox, mu_off_t offset)
808 {
809 struct mu_mboxrd_mailbox *dmp = mailbox->data;
810 int rc;
811
812 if (!dmp)
813 return EINVAL;
814
815 if (!(dmp->stream_flags & MU_STREAM_READ))
816 return 0;
817
818 mu_monitor_wrlock (mailbox->monitor);
819 #ifdef WITH_PTHREAD
820 pthread_cleanup_push (mboxrd_cleanup, (void *)mailbox);
821 #endif
822
823 if (mailbox->locker && (rc = mu_locker_lock (mailbox->locker)))
824 {
825 mu_monitor_unlock (mailbox->monitor);
826 return rc;
827 }
828
829 rc = mboxrd_rescan_unlocked (mailbox, offset);
830
831 if (mailbox->locker)
832 mu_locker_unlock (mailbox->locker);
833 mu_monitor_unlock (mailbox->monitor);
834
835 #ifdef WITH_PTHREAD
836 pthread_cleanup_pop (0);
837 #endif
838
839 return rc;
840 }
841
842 static int
mboxrd_refresh(mu_mailbox_t mailbox)843 mboxrd_refresh (mu_mailbox_t mailbox)
844 {
845 struct mu_mboxrd_mailbox *dmp = mailbox->data;
846
847 if (mboxrd_is_updated (mailbox))
848 return 0;
849 return mboxrd_rescan (mailbox,
850 dmp->mesg_count == 0
851 ? 0
852 : dmp->mesg[dmp->mesg_count - 1]->message_end + 1);
853 }
854
855 static int
mboxrd_scan(mu_mailbox_t mailbox,size_t i,size_t * pcount)856 mboxrd_scan (mu_mailbox_t mailbox, size_t i, size_t *pcount)
857 {
858 struct mu_mboxrd_mailbox *dmp = mailbox->data;
859
860 if (!dmp)
861 return EINVAL;
862
863 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
864 ("%s (%s)", __func__, dmp->name));
865
866 if (i == 0 || (dmp->mesg_count && i > dmp->mesg_count))
867 return EINVAL;
868
869 if (!mboxrd_is_updated (mailbox))
870 {
871 int rc;
872
873 while (i < dmp->mesg_count)
874 mu_mboxrd_message_free (dmp->mesg[dmp->mesg_count--]);
875
876 rc = mboxrd_refresh (mailbox);
877 if (rc)
878 return rc;
879 }
880 else if (mailbox->observable)
881 {
882 for (; i <= dmp->mesg_count; i++)
883 {
884 size_t tmp = i;
885 if (mu_observable_notify (mailbox->observable, MU_EVT_MESSAGE_ADD,
886 &tmp) != 0)
887 break;
888 /* FIXME: Hardcoded value! Must be configurable */
889 if (((i + 1) % 50) == 0)
890 mu_observable_notify (mailbox->observable, MU_EVT_MAILBOX_PROGRESS,
891 NULL);
892 }
893 }
894 if (pcount)
895 *pcount = dmp->mesg_count;
896 return 0;
897 }
898
899 static int
mboxrd_messages_recent(mu_mailbox_t mailbox,size_t * pcount)900 mboxrd_messages_recent (mu_mailbox_t mailbox, size_t *pcount)
901 {
902 size_t i;
903 size_t count = 0;
904 struct mu_mboxrd_mailbox *dmp = mailbox->data;
905
906 int rc = mboxrd_refresh (mailbox);
907 if (rc)
908 return rc;
909
910 for (i = 0; i < dmp->mesg_count; i++)
911 {
912 if (MU_ATTRIBUTE_IS_UNSEEN (dmp->mesg[i]->attr_flags))
913 ++count;
914 }
915
916 *pcount = count;
917
918 return 0;
919 }
920
921 static int
mboxrd_message_unseen(mu_mailbox_t mailbox,size_t * pmsgno)922 mboxrd_message_unseen (mu_mailbox_t mailbox, size_t *pmsgno)
923 {
924 size_t i;
925 struct mu_mboxrd_mailbox *dmp = mailbox->data;
926
927 int rc = mboxrd_refresh (mailbox);
928 if (rc)
929 return rc;
930
931 for (i = 0; i < dmp->mesg_count; i++)
932 {
933 if (MU_ATTRIBUTE_IS_UNREAD (dmp->mesg[i]->attr_flags))
934 {
935 *pmsgno = i + 1;
936 return 0;
937 }
938 }
939
940 *pmsgno = 0;
941 return 0;
942 }
943
944 /* Initialize the mailbox UID subsystem. See the Notes above. */
945 int
mu_mboxrd_mailbox_uid_setup(struct mu_mboxrd_mailbox * dmp)946 mu_mboxrd_mailbox_uid_setup (struct mu_mboxrd_mailbox *dmp)
947 {
948 if (!dmp->uidvalidity_scanned)
949 {
950 size_t i;
951 int rc = mboxrd_refresh (dmp->mailbox);
952 if (rc || dmp->uidvalidity_scanned)
953 return rc;
954
955 dmp->uidvalidity = (unsigned long)time (NULL);
956 dmp->uidnext = 1;
957 dmp->uidvalidity_scanned = 1;
958 dmp->uidvalidity_changed = 1;
959
960 for (i = 0; i < dmp->mesg_count; i++)
961 mboxrd_message_alloc_uid (dmp->mesg[i]);
962 }
963 return 0;
964 }
965
966 static int
mboxrd_get_uidvalidity(mu_mailbox_t mailbox,unsigned long * puidvalidity)967 mboxrd_get_uidvalidity (mu_mailbox_t mailbox, unsigned long *puidvalidity)
968 {
969 struct mu_mboxrd_mailbox *dmp = mailbox->data;
970 int rc = mu_mboxrd_mailbox_uid_setup (dmp);
971 if (rc == 0)
972 *puidvalidity = dmp->uidvalidity;
973 return rc;
974 }
975
976 static int
mboxrd_set_uidvalidity(mu_mailbox_t mailbox,unsigned long uidvalidity)977 mboxrd_set_uidvalidity (mu_mailbox_t mailbox, unsigned long uidvalidity)
978 {
979 struct mu_mboxrd_mailbox *dmp = mailbox->data;
980 int rc = mu_mboxrd_mailbox_uid_setup (dmp);
981 if (rc == 0)
982 dmp->uidvalidity = uidvalidity;
983 return rc;
984 }
985
986 static int
mboxrd_uidnext(mu_mailbox_t mailbox,size_t * puidnext)987 mboxrd_uidnext (mu_mailbox_t mailbox, size_t *puidnext)
988 {
989 struct mu_mboxrd_mailbox *dmp = mailbox->data;
990 int rc = mu_mboxrd_mailbox_uid_setup (dmp);
991 if (rc == 0)
992 *puidnext = dmp->uidnext;
993 return rc;
994 }
995
996 static int
mboxrd_get_message(mu_mailbox_t mailbox,size_t msgno,mu_message_t * pmsg)997 mboxrd_get_message (mu_mailbox_t mailbox, size_t msgno, mu_message_t *pmsg)
998 {
999 struct mu_mboxrd_mailbox *dmp = mailbox->data;
1000 int rc;
1001
1002 if (!dmp || msgno < 1)
1003 return EINVAL;
1004 if (pmsg == NULL)
1005 return MU_ERR_OUT_PTR_NULL;
1006
1007 if (dmp->mesg_count == 0)
1008 {
1009 rc = mboxrd_scan (mailbox, 1, NULL);
1010 if (rc)
1011 return rc;
1012 }
1013
1014 if (msgno > dmp->mesg_count)
1015 return MU_ERR_NOENT;
1016
1017 return mu_mboxrd_message_get (dmp->mesg[msgno-1], pmsg);
1018 }
1019
1020 static int
qid2off(mu_message_qid_t qid,mu_off_t * pret)1021 qid2off (mu_message_qid_t qid, mu_off_t *pret)
1022 {
1023 mu_off_t ret = 0;
1024 for (;*qid; qid++)
1025 {
1026 if (!('0' <= *qid && *qid <= '9'))
1027 return 1;
1028 ret = ret * 10 + *qid - '0';
1029 }
1030 *pret = ret;
1031 return 0;
1032 }
1033
1034 static int
mboxrd_quick_get_message(mu_mailbox_t mailbox,mu_message_qid_t qid,mu_message_t * pmsg)1035 mboxrd_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid,
1036 mu_message_t *pmsg)
1037 {
1038 int rc;
1039 struct mu_mboxrd_mailbox *dmp = mailbox->data;
1040 struct mu_mboxrd_message *dmsg;
1041 mu_off_t offset;
1042
1043 if (mailbox == NULL || qid2off (qid, &offset)
1044 || !(mailbox->flags & MU_STREAM_QACCESS))
1045 return EINVAL;
1046
1047 if (dmp->mesg_count == 0)
1048 {
1049 rc = mboxrd_rescan (mailbox, offset);
1050 if (rc)
1051 return rc;
1052 if (dmp->mesg_count == 0)
1053 return MU_ERR_NOENT;
1054 }
1055
1056 dmsg = dmp->mesg[0];
1057 if (dmsg->message_start != offset)
1058 return MU_ERR_EXISTS;
1059 if (dmsg->message)
1060 {
1061 if (pmsg)
1062 *pmsg = dmsg->message;
1063 return 0;
1064 }
1065 return mu_mboxrd_message_get (dmsg, pmsg);
1066 }
1067
1068 static int
mailbox_append_message(mu_mailbox_t mailbox,mu_message_t msg)1069 mailbox_append_message (mu_mailbox_t mailbox, mu_message_t msg)
1070 {
1071 int rc;
1072 mu_off_t size;
1073 mu_stream_t istr, flt;
1074 static char *exclude_headers[] = {
1075 MU_HEADER_X_IMAPBASE,
1076 MU_HEADER_X_UID,
1077 NULL
1078 };
1079 struct mu_mboxrd_mailbox *dmp = mailbox->data;
1080
1081 if (dmp->mesg_count)
1082 {
1083 char nl[2];
1084 static char pad[] = { '\n', '\n' };
1085 int n;
1086
1087 size = dmp->mesg[dmp->mesg_count-1]->message_end - 1;
1088 rc = mu_stream_seek (mailbox->stream, size, MU_SEEK_SET, NULL);
1089 if (rc)
1090 return rc;
1091 rc = mu_stream_read (mailbox->stream, nl, 2, NULL);
1092 if (rc)
1093 return rc;
1094
1095 if (nl[1] != '\n')
1096 n = 2;
1097 else if (nl[0] != '\n')
1098 n = 1;
1099 else
1100 n = 0;
1101
1102 if (n)
1103 {
1104 mu_stream_write (mailbox->stream, pad, n, NULL);
1105 }
1106 size += n + 2;
1107 }
1108 else
1109 {
1110 size = 0;
1111 rc = mu_stream_seek (mailbox->stream, size, MU_SEEK_SET, NULL);
1112 }
1113
1114 if (rc)
1115 return rc;
1116
1117 rc = mu_message_get_streamref (msg, &istr);
1118 if (rc)
1119 return rc;
1120
1121 do
1122 {
1123 mu_envelope_t env;
1124 char *date = NULL;
1125 char *sender = NULL;
1126
1127 rc = mu_message_get_envelope (msg, &env);
1128 if (rc)
1129 break;
1130
1131 rc = mu_envelope_aget_sender (env, &sender);
1132 if (rc == 0)
1133 {
1134 rc = mu_envelope_aget_date (env, &date);
1135 if (rc)
1136 {
1137 rc = mu_message_reconstruct_envelope (msg, &env);
1138 if (rc == 0)
1139 {
1140 rc = mu_envelope_aget_sender (env, &sender);
1141 if (rc == 0)
1142 {
1143 rc = mu_envelope_aget_date (env, &date);
1144 if (rc == MU_ERR_NOENT)
1145 {
1146 date = strdup ("Thu Jan 1 00:00:00 1970");
1147 if (date)
1148 rc = 0;
1149 else
1150 rc = ENOMEM;
1151 }
1152 }
1153
1154 mu_envelope_destroy (&env, msg);
1155 }
1156 }
1157
1158 if (rc)
1159 {
1160 free (sender);
1161 break;
1162 }
1163
1164 rc = mu_stream_printf (mailbox->stream, "From %s %s\n",
1165 sender, date);
1166 free (sender);
1167 free (date);
1168 }
1169
1170 if (rc)
1171 break;
1172
1173 rc = mu_stream_header_copy (mailbox->stream, istr, exclude_headers);
1174 if (rc)
1175 break;
1176
1177 /* Write UID-related data */
1178 if (dmp->uidvalidity_scanned)
1179 {
1180 if (dmp->mesg_count == 0)
1181 mu_stream_printf (mailbox->stream, "%s: %*lu %*lu\n",
1182 MU_HEADER_X_IMAPBASE,
1183 UINT_STRWIDTH (dmp->uidvalidity),
1184 dmp->uidvalidity,
1185 UINT_STRWIDTH (dmp->uidnext),
1186 dmp->uidnext);
1187 mu_stream_printf (mailbox->stream, "%s: %lu\n",
1188 MU_HEADER_X_UID,
1189 mboxrd_alloc_next_uid (dmp));
1190 }
1191
1192 rc = mu_stream_write (mailbox->stream, "\n", 1, NULL);
1193 if (rc)
1194 break;
1195
1196 rc = mu_filter_create (&flt, istr, "FROMRD",
1197 MU_FILTER_ENCODE, MU_STREAM_READ);
1198 mu_stream_destroy (&istr);
1199 rc = mu_stream_copy_nl (mailbox->stream, flt, 0, NULL);
1200 mu_stream_unref (flt);
1201 }
1202 while (0);
1203
1204 if (rc)
1205 {
1206 int rc1;
1207 mu_stream_destroy (&istr);
1208 rc1 = mu_stream_truncate (mailbox->stream, size);
1209 if (rc1)
1210 mu_error (_("cannot truncate stream after failed append: %s"),
1211 mu_stream_strerror (mailbox->stream, rc1));
1212 return rc;
1213 }
1214
1215 /* Rescan the message */
1216 rc = mboxrd_rescan_unlocked (mailbox, size);
1217 if (rc)
1218 return rc;
1219
1220 if (mailbox->observable)
1221 {
1222 char *buf = NULL;
1223 mu_asprintf (&buf, "%lu", (unsigned long) size);
1224 mu_observable_notify (mailbox->observable,
1225 MU_EVT_MAILBOX_MESSAGE_APPEND, buf);
1226 free (buf);
1227 }
1228
1229 return 0;
1230 }
1231
1232 static int
mboxrd_append_message(mu_mailbox_t mailbox,mu_message_t msg)1233 mboxrd_append_message (mu_mailbox_t mailbox, mu_message_t msg)
1234 {
1235 struct mu_mboxrd_mailbox *dmp = mailbox->data;
1236 int rc;
1237
1238 rc = mboxrd_refresh (mailbox);
1239 if (rc)
1240 return rc;
1241
1242 mu_monitor_wrlock (mailbox->monitor);
1243 if (mailbox->locker && (rc = mu_locker_lock (mailbox->locker)) != 0)
1244 {
1245 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1246 ("%s(%s):%s: %s",
1247 __func__, dmp->name, "mu_locker_lock",
1248 mu_strerror (rc)));
1249 }
1250 else
1251 {
1252 rc = mailbox_append_message (mailbox, msg);
1253
1254 if (mailbox->locker)
1255 mu_locker_unlock (mailbox->locker);
1256 }
1257 mu_monitor_unlock (mailbox->monitor);
1258 return rc;
1259 }
1260
1261 static int
mboxrd_messages_count(mu_mailbox_t mailbox,size_t * pcount)1262 mboxrd_messages_count (mu_mailbox_t mailbox, size_t *pcount)
1263 {
1264 struct mu_mboxrd_mailbox *dmp = mailbox->data;
1265 int rc;
1266
1267 if (!dmp)
1268 return EINVAL;
1269
1270 rc = mboxrd_refresh (mailbox);
1271 if (rc)
1272 return rc;
1273
1274 if (pcount)
1275 *pcount = dmp->mesg_count;
1276
1277 return 0;
1278 }
1279
1280 static int
mboxrd_get_size(mu_mailbox_t mailbox,mu_off_t * psize)1281 mboxrd_get_size (mu_mailbox_t mailbox, mu_off_t *psize)
1282 {
1283 mu_off_t size;
1284 int rc;
1285
1286 rc = mu_stream_size (mailbox->stream, &size);
1287 if (rc != 0)
1288 return rc;
1289 if (psize)
1290 *psize = size;
1291 return 0;
1292 }
1293
1294 static int
mboxrd_stat(mu_mailbox_t mailbox,struct stat * st)1295 mboxrd_stat (mu_mailbox_t mailbox, struct stat *st)
1296 {
1297 int rc;
1298 mu_transport_t trans[2];
1299
1300 rc = mu_stream_ioctl (mailbox->stream, MU_IOCTL_TRANSPORT,
1301 MU_IOCTL_OP_GET, trans);
1302 if (rc == 0)
1303 {
1304 if (fstat ((int) (intptr_t) trans[0], st))
1305 rc = errno;
1306 }
1307 return rc;
1308 }
1309
1310 static int
mboxrd_set_priv(struct mu_mboxrd_mailbox * dmp,struct stat * st)1311 mboxrd_set_priv (struct mu_mboxrd_mailbox *dmp, struct stat *st)
1312 {
1313 int rc;
1314 mu_transport_t trans[2];
1315
1316 rc = mu_stream_ioctl (dmp->mailbox->stream, MU_IOCTL_TRANSPORT,
1317 MU_IOCTL_OP_GET, trans);
1318 if (rc == 0)
1319 {
1320 int fd = (intptr_t) trans[0];
1321 if (fchmod (fd, st->st_mode))
1322 {
1323 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1324 ("%s:%s: chmod failed: %s",
1325 __func__, dmp->name, strerror (errno)));
1326 rc = errno;
1327 }
1328 else if (fchown (fd, st->st_uid, st->st_gid))
1329 {
1330 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1331 ("%s:%s: chown failed: %s",
1332 __func__, dmp->name, strerror (errno)));
1333 rc = errno;
1334 }
1335 }
1336 return rc;
1337 }
1338
1339 static int
mboxrd_get_atime(mu_mailbox_t mailbox,time_t * return_time)1340 mboxrd_get_atime (mu_mailbox_t mailbox, time_t *return_time)
1341 {
1342 struct mu_mboxrd_mailbox *dmp = mailbox->data;
1343 struct stat st;
1344 int rc;
1345
1346 if (dmp == NULL)
1347 return EINVAL;
1348 if ((rc = mboxrd_stat (mailbox, &st)) == 0)
1349 *return_time = st.st_atime;
1350 return rc;
1351 }
1352
1353 struct mu_mboxrd_flush_tracker
1354 {
1355 struct mu_mboxrd_mailbox *dmp;
1356 size_t *ref;
1357 size_t mesg_count;
1358 };
1359
1360 static int
tracker_init(struct mu_mboxrd_flush_tracker * trk,struct mu_mboxrd_mailbox * dmp)1361 tracker_init (struct mu_mboxrd_flush_tracker *trk,
1362 struct mu_mboxrd_mailbox *dmp)
1363 {
1364 trk->ref = calloc (dmp->mesg_count, sizeof (trk->ref[0]));
1365 if (!trk->ref)
1366 return ENOMEM;
1367 trk->dmp = dmp;
1368 trk->mesg_count = 0;
1369 return 0;
1370 }
1371
1372 static void
tracker_free(struct mu_mboxrd_flush_tracker * trk)1373 tracker_free (struct mu_mboxrd_flush_tracker *trk)
1374 {
1375 free (trk->ref);
1376 }
1377
1378 static struct mu_mboxrd_message *
tracker_next_ref(struct mu_mboxrd_flush_tracker * trk,size_t orig_num)1379 tracker_next_ref (struct mu_mboxrd_flush_tracker *trk, size_t orig_num)
1380 {
1381 trk->ref[trk->mesg_count++] = orig_num;
1382 return trk->dmp->mesg[orig_num];
1383 }
1384
1385 static void
mboxrd_tracker_sync(struct mu_mboxrd_flush_tracker * trk)1386 mboxrd_tracker_sync (struct mu_mboxrd_flush_tracker *trk)
1387 {
1388 struct mu_mboxrd_mailbox *dmp = trk->dmp;
1389 size_t i;
1390
1391 if (trk->mesg_count == 0)
1392 {
1393 for (i = 0; i < dmp->mesg_count; i++)
1394 mu_mboxrd_message_free (dmp->mesg[i]);
1395 dmp->size = 0;
1396 dmp->uidvalidity_scanned = 0;
1397 }
1398 else
1399 {
1400 /* Mark */
1401 for (i = 0; i < trk->mesg_count; i++)
1402 dmp->mesg[trk->ref[i]]->mark = 1;
1403 /* Sweep */
1404 for (i = 0; i < dmp->mesg_count; i++)
1405 if (!dmp->mesg[i]->mark)
1406 mu_mboxrd_message_free (dmp->mesg[i]);
1407 /* Reorder */
1408 for (i = 0; i < trk->mesg_count; i++)
1409 {
1410 dmp->mesg[i] = dmp->mesg[trk->ref[i]];
1411 dmp->mesg[i]->mark = 0;
1412 }
1413 dmp->mesg_count = trk->mesg_count;
1414 dmp->size = dmp->mesg[dmp->mesg_count - 1]->message_end + 1;
1415 }
1416 dmp->mesg_count = trk->mesg_count;
1417 /* FIXME: Check uidvalidity values?? */
1418 }
1419
1420 /* Write to the output stream DEST messages in the range [from,to).
1421 Update TRK accordingly.
1422 */
1423 static int
mboxrd_mailbox_copy_unchanged(struct mu_mboxrd_flush_tracker * trk,size_t from,size_t to,mu_stream_t dest)1424 mboxrd_mailbox_copy_unchanged (struct mu_mboxrd_flush_tracker *trk,
1425 size_t from, size_t to,
1426 mu_stream_t dest)
1427 {
1428 if (to > from)
1429 {
1430 struct mu_mboxrd_mailbox *dmp = trk->dmp;
1431 mu_off_t start;
1432 mu_off_t stop;
1433 size_t i;
1434 mu_off_t off;
1435 int rc;
1436
1437 start = dmp->mesg[from]->message_start;
1438 if (to == dmp->mesg_count)
1439 stop = dmp->mesg[to-1]->message_end + 1;
1440 else
1441 stop = dmp->mesg[to]->message_start;
1442
1443 rc = mu_stream_seek (dest, 0, MU_SEEK_CUR, &off);
1444 if (rc)
1445 return rc;
1446 off -= start;
1447 /* Fixup offsets */
1448 for (i = from; i < to; i++)
1449 {
1450 struct mu_mboxrd_message *ref = tracker_next_ref (trk, i);
1451 ref->message_start += off;
1452 ref->body_start += off;
1453 ref->message_end += off;
1454 }
1455
1456 /* Copy data */
1457 rc = mu_stream_seek (dmp->mailbox->stream, start, MU_SEEK_SET, NULL);
1458 if (rc)
1459 return rc;
1460 return mu_stream_copy (dest, dmp->mailbox->stream, stop - start, NULL);
1461 }
1462 return 0;
1463 }
1464
1465 /* Flush the mailbox described by the tracker TRK to the stream TEMPSTR.
1466 First modified message is I (0-based). EXPUNGE is 1 if the
1467 MU_ATTRIBUTE_DELETED attribute is to be honored.
1468 */
1469 static int
mboxrd_flush_temp(struct mu_mboxrd_flush_tracker * trk,size_t i,mu_stream_t tempstr,int expunge)1470 mboxrd_flush_temp (struct mu_mboxrd_flush_tracker *trk,
1471 size_t i,
1472 mu_stream_t tempstr, int expunge)
1473 {
1474 struct mu_mboxrd_mailbox *dmp = trk->dmp;
1475 size_t start = 0;
1476 size_t save_imapbase = 0;
1477 size_t expcount = 0;
1478 int rc;
1479
1480 rc = mu_stream_seek (trk->dmp->mailbox->stream, 0, MU_SEEK_SET, NULL);
1481 if (rc)
1482 return rc;
1483 while (i < dmp->mesg_count)
1484 {
1485 struct mu_mboxrd_message *dmsg = dmp->mesg[i];
1486
1487 if (expunge && (dmsg->attr_flags & MU_ATTRIBUTE_DELETED))
1488 {
1489 size_t expevt[2] = { i + 1, expcount };
1490
1491 rc = mboxrd_mailbox_copy_unchanged (trk, start, i, tempstr);
1492 if (rc)
1493 return rc;
1494 mu_observable_notify (dmp->mailbox->observable,
1495 MU_EVT_MAILBOX_MESSAGE_EXPUNGE,
1496 expevt);
1497 expcount++;
1498 mu_message_destroy (&dmsg->message, dmsg);
1499
1500 /* Make sure uidvalidity and next uid are preserved even if
1501 the first message (where they are saved) is deleted */
1502 if (i == save_imapbase)
1503 {
1504 save_imapbase = i + 1;
1505 if (save_imapbase < dmp->mesg_count)
1506 dmp->mesg[save_imapbase]->attr_flags |= MU_ATTRIBUTE_MODIFIED;
1507 }
1508 i++;
1509 start = i;
1510 continue;
1511 }
1512
1513 if (dmsg->uid_modified
1514 || (dmsg->attr_flags & MU_ATTRIBUTE_MODIFIED)
1515 || mu_message_is_modified (dmsg->message))
1516 {
1517 char *x_imapbase = NULL;
1518
1519 rc = mboxrd_mailbox_copy_unchanged (trk, start, i, tempstr);
1520 if (rc)
1521 return rc;
1522 if (save_imapbase == i)
1523 {
1524 mu_asprintf (&x_imapbase, "%*lu %*lu",
1525 UINT_STRWIDTH (dmp->uidvalidity),
1526 dmp->uidvalidity,
1527 UINT_STRWIDTH (dmp->uidnext),
1528 dmp->uidnext);
1529 }
1530 rc = mu_mboxrd_message_reconstruct (tempstr, dmsg,
1531 tracker_next_ref (trk, i),
1532 x_imapbase);
1533 free (x_imapbase);
1534 if (rc)
1535 return rc;
1536 i++;
1537 start = i;
1538 continue;
1539 }
1540
1541 i++;
1542 }
1543 rc = mboxrd_mailbox_copy_unchanged (trk, start, i, tempstr);
1544 if (rc)
1545 return rc;
1546 if (trk->mesg_count)
1547 {
1548 rc = mu_stream_truncate (tempstr,
1549 trk->dmp->mesg[trk->ref[trk->mesg_count - 1]]->message_end + 1);
1550 }
1551
1552 return mu_stream_flush (tempstr);
1553 }
1554
1555 /*
1556 * Copy the temporary mailbox stream TEMPSTR to the mailbox referred to by
1557 * the tracker TRK.
1558 */
1559 static inline int
mboxrd_copyback(struct mu_mboxrd_flush_tracker * trk,mu_stream_t tempstr)1560 mboxrd_copyback (struct mu_mboxrd_flush_tracker *trk, mu_stream_t tempstr)
1561 {
1562 int rc;
1563 mu_stream_t mbx_stream = trk->dmp->mailbox->stream;
1564 mu_off_t size;
1565
1566 rc = mu_stream_seek (tempstr, 0, MU_SEEK_SET, NULL);
1567 if (rc)
1568 {
1569 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1570 ("%s: can't rewind temporary file: %s",
1571 __func__, mu_strerror (rc)));
1572 return rc;
1573 }
1574
1575 rc = mu_stream_seek (mbx_stream, 0, MU_SEEK_SET, NULL);
1576 if (rc)
1577 {
1578 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1579 ("%s: can't rewind mailbox %s: %s",
1580 __func__, trk->dmp->name, mu_strerror (rc)));
1581 return rc;
1582 }
1583
1584 rc = mu_stream_copy (mbx_stream, tempstr, 0, &size);
1585 if (rc)
1586 {
1587 mu_error (_("copying back to mailbox %s failed: %s"),
1588 trk->dmp->name, mu_strerror (rc));
1589 return rc;
1590 }
1591 rc = mu_stream_truncate (mbx_stream, size);
1592 if (rc)
1593 {
1594 mu_error (_("cannot truncate mailbox stream: %s"),
1595 mu_stream_strerror (mbx_stream, rc));
1596 return rc;
1597 }
1598
1599 mboxrd_tracker_sync (trk);
1600 return 0;
1601 }
1602
1603 /* Flush the mailbox described by the tracker TRK to the stream TEMPSTR.
1604 EXPUNGE is 1 if the MU_ATTRIBUTE_DELETED attribute is to be honored.
1605 Assumes that simultaneous access to the mailbox has been blocked.
1606 */
1607 static int
mboxrd_flush_unlocked(struct mu_mboxrd_flush_tracker * trk,int mode)1608 mboxrd_flush_unlocked (struct mu_mboxrd_flush_tracker *trk, int mode)
1609 {
1610 struct mu_mboxrd_mailbox *dmp = trk->dmp;
1611 int rc;
1612 size_t dirty;
1613 mu_stream_t tempstr;
1614 struct mu_tempfile_hints hints;
1615 int tempfd;
1616 char *tempname;
1617 char *p;
1618
1619 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
1620 ("%s (%s)", __func__, dmp->name));
1621 if (dmp->mesg_count == 0)
1622 return 0;
1623 if (mode == FLUSH_UIDVALIDITY && !dmp->uidvalidity_changed)
1624 return 0;
1625
1626 rc = mboxrd_refresh (dmp->mailbox);
1627 if (rc)
1628 return rc;
1629
1630 if (dmp->uidvalidity_changed)
1631 {
1632 size_t i;
1633 char buf[X_IMAPBASE_MAX (dmp)];
1634 int n;
1635 mu_stream_t stream = dmp->mailbox->stream;
1636
1637 /*
1638 * Format the X-IMAPbase header and check if it will fit in place
1639 * of the existing one (if any). If so, write it at once and return.
1640 */
1641 n = snprintf (buf, sizeof (buf), "%s: %*lu %*lu",
1642 MU_HEADER_X_IMAPBASE,
1643 UINT_STRWIDTH (dmp->uidvalidity),
1644 dmp->uidvalidity,
1645 UINT_STRWIDTH (dmp->uidnext),
1646 dmp->uidnext);
1647
1648 if (dmp->x_imapbase_len && dmp->x_imapbase_len >= n)
1649 {
1650 rc = mu_stream_seek (stream, dmp->x_imapbase_off, MU_SEEK_SET, NULL);
1651 if (rc)
1652 {
1653 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1654 ("%s:%s (%s): %s",
1655 __func__, "mu_stream_seek", dmp->name,
1656 mu_strerror (rc)));
1657 return rc;
1658 }
1659 rc = mu_stream_printf (stream, "%-*s",
1660 (int) dmp->x_imapbase_len,
1661 buf);
1662 if (rc)
1663 {
1664 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1665 ("%s:%s (%s): %s",
1666 __func__, "mu_stream_printf", dmp->name,
1667 mu_strerror (rc)));
1668 return rc;
1669 }
1670 }
1671 else
1672 {
1673 /*
1674 * There is no X-IMAPbase header yet or it is not wide enough to
1675 * accept the current value. Fall back to reformatting entire
1676 * mailbox. Clear any other changes that might have been done
1677 * to its messages.
1678 */
1679 dmp->mesg[0]->uid_modified = 1;
1680
1681 if (mode == FLUSH_UIDVALIDITY)
1682 {
1683 for (i = 1; i < dmp->mesg_count; i++)
1684 {
1685 struct mu_mboxrd_message *dmsg = dmp->mesg[i];
1686 dmsg->attr_flags &= ~(MU_ATTRIBUTE_MODIFIED|MU_ATTRIBUTE_DELETED);
1687 }
1688 }
1689 }
1690 }
1691
1692 for (dirty = 0; dirty < dmp->mesg_count; dirty++)
1693 {
1694 struct mu_mboxrd_message *dmsg = dmp->mesg[dirty];
1695 if (dmsg->uid_modified)
1696 break;
1697 if ((dmsg->attr_flags & MU_ATTRIBUTE_MODIFIED)
1698 || (dmsg->attr_flags & MU_ATTRIBUTE_DELETED)
1699 || (dmsg->message && mu_message_is_modified (dmsg->message)))
1700 break;
1701 }
1702
1703 rc = 0;
1704 if (dirty < dmp->mesg_count)
1705 {
1706 p = strrchr (dmp->name, '/');
1707 if (p)
1708 {
1709 size_t l = p - dmp->name;
1710 hints.tmpdir = malloc (l + 1);
1711 if (!hints.tmpdir)
1712 return ENOMEM;
1713 memcpy (hints.tmpdir, dmp->name, l);
1714 hints.tmpdir[l] = 0;
1715 }
1716 else
1717 {
1718 hints.tmpdir = mu_getcwd ();
1719 if (!hints.tmpdir)
1720 return ENOMEM;
1721 }
1722 rc = mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &tempfd, &tempname);
1723 if (rc == 0)
1724 {
1725 rc = mu_fd_stream_create (&tempstr, tempname, tempfd,
1726 MU_STREAM_RDWR|MU_STREAM_SEEK);
1727 }
1728 else if (rc == EACCES)
1729 {
1730 /*
1731 * Mail spool directory is not writable for the user. Fall
1732 * back to using temporary stream located elsewhere. When
1733 * ready, it will be copied back to the mailbox.
1734 *
1735 * Reset the tempname to NULL to instruct the code below
1736 * which approach to take.
1737 */
1738 tempname = NULL;
1739
1740 rc = mu_temp_file_stream_create (&tempstr, NULL, 0);
1741 }
1742
1743 if (rc)
1744 {
1745 free (hints.tmpdir);
1746 close (tempfd);
1747 free (tempname);
1748 return rc;
1749 }
1750
1751 rc = mboxrd_flush_temp (trk, dirty, tempstr, mode == FLUSH_EXPUNGE);
1752 if (rc == 0)
1753 {
1754 if (tempname)
1755 {
1756 /* Mail spool is writable. Rename the temporary copy back
1757 to mailbox */
1758 char *backup;
1759 struct stat st;
1760
1761 if ((rc = mboxrd_stat (dmp->mailbox, &st)) != 0)
1762 {
1763 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1764 ("%s:%s: stat failed: %s",
1765 __func__, dmp->name, strerror (errno)));
1766 }
1767 else
1768 {
1769 mu_stream_flush (tempstr);
1770 backup = mu_tempname (hints.tmpdir);
1771 if (rename (dmp->name, backup))
1772 {
1773 rc = errno;
1774 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1775 ("%s:%s: failed to rename to backup file %s: %s",
1776 __func__, dmp->name, tempname,
1777 mu_strerror (rc)));
1778 unlink (backup);
1779 }
1780 else
1781 {
1782 rc = rename (tempname, dmp->name);
1783 if (rc == 0)
1784 {
1785 /* Success. Synchronize internal data with the
1786 counter. */
1787 mboxrd_tracker_sync (trk);
1788 mu_stream_destroy (&dmp->mailbox->stream);
1789 rc = mboxrd_mailbox_init_stream (dmp);
1790 if (rc == 0)
1791 mboxrd_set_priv (dmp, &st);
1792 }
1793 else
1794 {
1795 int rc1;
1796 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1797 ("%s: failed to rename temporary file %s %s: %s",
1798 __func__, tempname, dmp->name,
1799 mu_strerror (rc)));
1800 rc1 = rename (backup, dmp->name);
1801 if (rc1)
1802 {
1803 mu_error (_("failed to restore %s from backup %s: %s"),
1804 dmp->name, backup, mu_strerror (rc1));
1805 mu_error (_("backup left in %s"), backup);
1806 free (backup);
1807 backup = NULL;
1808 }
1809 }
1810 }
1811
1812 if (backup)
1813 {
1814 unlink (backup);
1815 free (backup);
1816 }
1817 unlink (tempname);
1818 }
1819 }
1820 else
1821 {
1822 /* Mail spool not writable. Copy the tempstr back to mailbox. */
1823 rc = mboxrd_copyback (trk, tempstr);
1824 }
1825 }
1826 free (tempname);
1827 free (hints.tmpdir);
1828 mu_stream_unref (tempstr);
1829 }
1830
1831 dmp->uidvalidity_changed = 0;
1832
1833 return rc;
1834 }
1835
1836 /* Flush the changes in the mailbox DMP to disk storage.
1837 EXPUNGE is 1 if the MU_ATTRIBUTE_DELETED attribute is to be honored.
1838 Block simultaneous access for the duration of the process.
1839
1840 This is done by creating a temporary mailbox on the same device as
1841 DMP and by transferring all messages (whether changed or not) to
1842 it. If the process succeeds, old mailbox is removed and the temporary
1843 one is renamed to it. In case of failure, the temporary is removed and
1844 the original mailbox remains unchanged.
1845 */
1846 static int
mboxrd_flush(struct mu_mboxrd_mailbox * dmp,int mode)1847 mboxrd_flush (struct mu_mboxrd_mailbox *dmp, int mode)
1848 {
1849 int rc;
1850 sigset_t signalset;
1851 #ifdef WITH_PTHREAD
1852 int state;
1853 #endif
1854 struct mu_mboxrd_flush_tracker trk;
1855
1856 /* Lock the mailbox */
1857 if (dmp->mailbox->locker
1858 && (rc = mu_locker_lock (dmp->mailbox->locker)) != 0)
1859 return rc;
1860
1861 #ifdef WITH_PTHREAD
1862 pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
1863 #endif
1864 sigemptyset (&signalset);
1865 sigaddset (&signalset, SIGTERM);
1866 sigaddset (&signalset, SIGHUP);
1867 sigaddset (&signalset, SIGTSTP);
1868 sigaddset (&signalset, SIGINT);
1869 sigaddset (&signalset, SIGWINCH);
1870 sigprocmask (SIG_BLOCK, &signalset, 0);
1871
1872 rc = tracker_init (&trk, dmp);
1873 if (rc == 0)
1874 {
1875 rc = mboxrd_flush_unlocked (&trk, mode);
1876 tracker_free (&trk);
1877 }
1878
1879 #ifdef WITH_PTHREAD
1880 pthread_setcancelstate (state, &state);
1881 #endif
1882 sigprocmask (SIG_UNBLOCK, &signalset, 0);
1883
1884 if (dmp->mailbox->locker)
1885 mu_locker_unlock (dmp->mailbox->locker);
1886 return rc;
1887 }
1888
1889 static int
mboxrd_expunge(mu_mailbox_t mailbox)1890 mboxrd_expunge (mu_mailbox_t mailbox)
1891 {
1892 struct mu_mboxrd_mailbox *dmp = mailbox->data;
1893 return mboxrd_flush (dmp, FLUSH_EXPUNGE);
1894 }
1895
1896 static int
mboxrd_sync(mu_mailbox_t mailbox)1897 mboxrd_sync (mu_mailbox_t mailbox)
1898 {
1899 struct mu_mboxrd_mailbox *dmp = mailbox->data;
1900 return mboxrd_flush (dmp, FLUSH_SYNC);
1901 }
1902
1903 int
mu_mboxrd_mailbox_init(mu_mailbox_t mailbox)1904 mu_mboxrd_mailbox_init (mu_mailbox_t mailbox)
1905 {
1906 int status;
1907 struct mu_mboxrd_mailbox *dmp;
1908 mu_property_t property = NULL;
1909
1910 if (mailbox == NULL)
1911 return EINVAL;
1912
1913 /* Allocate specific mbox data. */
1914 dmp = calloc (1, sizeof (*dmp));
1915 if (dmp == NULL)
1916 return ENOMEM;
1917
1918 /* Back pointer. */
1919 dmp->mailbox = mailbox;
1920 dmp->uidnext = 1;
1921
1922 status = mu_url_aget_path (mailbox->url, &dmp->name);
1923 if (status)
1924 {
1925 free (dmp);
1926 return status;
1927 }
1928
1929 mailbox->data = dmp;
1930
1931 /* Overloading the defaults. */
1932 mailbox->_destroy = mboxrd_destroy;
1933 mailbox->_open = mboxrd_open;
1934 mailbox->_close = mboxrd_close;
1935 mailbox->_remove = mboxrd_remove;
1936 mailbox->_scan = mboxrd_scan;
1937 mailbox->_is_updated = mboxrd_is_updated;
1938
1939 mailbox->_get_message = mboxrd_get_message;
1940 mailbox->_quick_get_message = mboxrd_quick_get_message;
1941 mailbox->_messages_count = mboxrd_messages_count;
1942 mailbox->_messages_recent = mboxrd_messages_recent;
1943 mailbox->_message_unseen = mboxrd_message_unseen;
1944
1945 mailbox->_append_message = mboxrd_append_message;
1946
1947 mailbox->_expunge = mboxrd_expunge;
1948 mailbox->_sync = mboxrd_sync;
1949
1950 mailbox->_get_uidvalidity = mboxrd_get_uidvalidity;
1951 mailbox->_set_uidvalidity = mboxrd_set_uidvalidity;
1952 mailbox->_uidnext = mboxrd_uidnext;
1953 mailbox->_get_size = mboxrd_get_size;
1954 mailbox->_get_atime = mboxrd_get_atime;
1955
1956 mu_mailbox_get_property (mailbox, &property);
1957 mu_property_set_value (property, "TYPE", "MBOX", 1);
1958
1959 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
1960 ("%s (%s)", __func__, dmp->name));
1961 return 0;
1962 }
1963
1964 /* Folder support */
1965
1966 /* Return MU_FOLDER_ATTRIBUTE_FILE if NAME looks like a mboxrd
1967 mailbox.
1968
1969 If MU_AUTODETECT_ACCURACY is 0 (i.e. autodetection is disabled),
1970 always returns MU_FOLDER_ATTRIBUTE_FILE.
1971
1972 Otherwise, the function analyzes first 128 bytes from file. If they
1973 look like a message header start, i.e. match "^[A-Za-z_][A-Za-z0-9_-]*:",
1974 then the file is considered a mboxrd mailbox.
1975
1976 Additionally, if MU_AUTODETECT_ACCURACY is greater than 1, the last
1977 3 characters of the file are considered. For valid mboxrd they must
1978 be "\n.\n".
1979 */
1980 static int
mboxrd_detect(char const * name)1981 mboxrd_detect (char const *name)
1982 {
1983 int res = 0;
1984
1985 if (mu_autodetect_accuracy () == 0)
1986 res = MU_FOLDER_ATTRIBUTE_FILE;
1987 else
1988 {
1989 int rc;
1990 mu_stream_t str = NULL;
1991
1992 rc = mu_file_stream_create (&str, name, MU_STREAM_READ);
1993 if (rc == 0)
1994 {
1995 char *buf = NULL;
1996 size_t size = 0;
1997 size_t n;
1998
1999 rc = mu_stream_getline (str, &buf, &size, &n);
2000 if (rc == 0)
2001 {
2002 char *zn;
2003 if (parse_from_line (buf, &zn))
2004 {
2005 res = MU_FOLDER_ATTRIBUTE_FILE;
2006 }
2007 }
2008 free (buf);
2009 mu_stream_destroy (&str);
2010 }
2011 }
2012 return res;
2013 }
2014
2015 static int
mboxrd_is_scheme(mu_record_t record,mu_url_t url,int flags)2016 mboxrd_is_scheme (mu_record_t record, mu_url_t url, int flags)
2017 {
2018 int rc = 0;
2019 int scheme_matched = mu_url_is_scheme (url, record->scheme);
2020 if (scheme_matched || mu_scheme_autodetect_p (url))
2021 {
2022 struct stat st;
2023 const char *path;
2024
2025 mu_url_sget_path (url, &path);
2026 if (stat (path, &st) < 0)
2027 {
2028 if (errno == ENOENT)
2029 {
2030 if (scheme_matched)
2031 return MU_FOLDER_ATTRIBUTE_FILE & flags;
2032 }
2033 return 0;
2034 }
2035
2036 if (S_ISREG (st.st_mode) || S_ISCHR (st.st_mode))
2037 {
2038 if (st.st_size == 0)
2039 {
2040 rc |= MU_FOLDER_ATTRIBUTE_FILE;
2041 }
2042 else if (flags & MU_FOLDER_ATTRIBUTE_FILE)
2043 {
2044 rc |= mboxrd_detect (path);
2045 }
2046 }
2047
2048 if ((flags & MU_FOLDER_ATTRIBUTE_DIRECTORY)
2049 && S_ISDIR (st.st_mode))
2050 rc |= MU_FOLDER_ATTRIBUTE_DIRECTORY;
2051 }
2052 return rc;
2053 }
2054
2055 static struct _mu_record _mboxrd_record =
2056 {
2057 MU_MBOX_PRIO,
2058 "mbox",
2059 MU_RECORD_LOCAL,
2060 MU_URL_SCHEME | MU_URL_PATH | MU_URL_PARAM,
2061 MU_URL_PATH,
2062 mu_url_expand_path, /* URL init. */
2063 mu_mboxrd_mailbox_init, /* Mailbox init. */
2064 NULL, /* Mailer init. */
2065 _mu_fsfolder_init, /* Folder init. */
2066 NULL, /* No need for back pointer. */
2067 mboxrd_is_scheme, /* _is_scheme method. */
2068 NULL, /* _get_url method. */
2069 NULL, /* _get_mailbox method. */
2070 NULL, /* _get_mailer method. */
2071 NULL /* _get_folder method. */
2072 };
2073 mu_record_t mu_mbox_record = &_mboxrd_record;
2074