1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-2021 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 3 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
15    Public License along with this library.  If not, see
16    <http://www.gnu.org/licenses/>. */
17 
18 /* Mailutils Abstract Mail Directory Layer
19    First draft by Sergey Poznyakoff.
20    Thanks Tang Yong Ping <yongping.tang@radixs.com> for initial
21    patch (although not used here).
22 
23    This module provides basic support for "MH" and "Maildir" formats. */
24 
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28 
29 #include <sys/types.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <time.h>
33 #include <sys/stat.h>
34 #include <sys/time.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <dirent.h>
39 
40 #ifdef WITH_PTHREAD
41 # ifdef HAVE_PTHREAD_H
42 #  ifndef _XOPEN_SOURCE
43 #   define _XOPEN_SOURCE  500
44 #  endif
45 #  include <pthread.h>
46 # endif
47 #endif
48 
49 #include <string.h>
50 #ifdef HAVE_STRINGS_H
51 # include <strings.h>
52 #endif
53 
54 #include <mailutils/cctype.h>
55 #include <mailutils/cstr.h>
56 #include <mailutils/attribute.h>
57 #include <mailutils/body.h>
58 #include <mailutils/debug.h>
59 #include <mailutils/envelope.h>
60 #include <mailutils/error.h>
61 #include <mailutils/errno.h>
62 #include <mailutils/header.h>
63 #include <mailutils/locker.h>
64 #include <mailutils/message.h>
65 #include <mailutils/util.h>
66 #include <mailutils/datetime.h>
67 #include <mailutils/property.h>
68 #include <mailutils/stream.h>
69 #include <mailutils/url.h>
70 #include <mailutils/observer.h>
71 #include <mailutils/sys/stream.h>
72 #include <mailutils/sys/mailbox.h>
73 #include <mailutils/sys/message.h>
74 #include <mailutils/sys/registrar.h>
75 #include <mailutils/sys/url.h>
76 #include <mailutils/sys/amd.h>
77 
78 static void amd_destroy (mu_mailbox_t mailbox);
79 static int amd_open (mu_mailbox_t, int);
80 static int amd_close (mu_mailbox_t);
81 static int amd_get_message (mu_mailbox_t, size_t, mu_message_t *);
82 static int amd_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid,
83 				  mu_message_t *pmsg);
84 static int amd_append_message (mu_mailbox_t, mu_message_t);
85 static int amd_messages_count (mu_mailbox_t, size_t *);
86 static int amd_messages_recent (mu_mailbox_t, size_t *);
87 static int amd_message_unseen (mu_mailbox_t, size_t *);
88 static int amd_expunge (mu_mailbox_t);
89 static int amd_sync (mu_mailbox_t);
90 static int amd_uidnext (mu_mailbox_t mailbox, size_t *puidnext);
91 static int amd_get_uidvalidity (mu_mailbox_t, unsigned long *);
92 static int amd_set_uidvalidity (mu_mailbox_t, unsigned long);
93 static int amd_scan (mu_mailbox_t, size_t, size_t *);
94 static int amd_is_updated (mu_mailbox_t);
95 static int amd_get_size (mu_mailbox_t, mu_off_t *);
96 
97 static int amd_body_size (mu_body_t body, size_t *psize);
98 static int amd_body_lines (mu_body_t body, size_t *plines);
99 
100 static int amd_header_fill (void *data, char **pbuf, size_t *plen);
101 
102 static int amd_get_attr_flags (mu_attribute_t attr, int *pflags);
103 static int amd_set_attr_flags (mu_attribute_t attr, int flags);
104 static int amd_unset_attr_flags (mu_attribute_t attr, int flags);
105 
106 static int amd_pool_open (struct _amd_message *mhm);
107 static int amd_pool_open_count (struct _amd_data *amd);
108 static void amd_pool_flush (struct _amd_data *amd);
109 static struct _amd_message **amd_pool_lookup (struct _amd_message *mhm);
110 
111 static int amd_remove_mbox (mu_mailbox_t mailbox);
112 
113 
114 static int amd_body_stream_read (mu_stream_t str, char *buffer,
115 				 size_t buflen,
116 				 size_t *pnread);
117 static int amd_body_stream_size (mu_stream_t str, mu_off_t *psize);
118 static int amd_body_stream_seek (mu_stream_t str, mu_off_t off,
119 				 mu_off_t *presult);
120 
121 struct _amd_body_stream
122 {
123   struct _mu_stream stream;
124   mu_body_t body;
125   mu_off_t off;
126 };
127 
128 /* AMD Properties */
129 int
_amd_prop_fetch_off(struct _amd_data * amd,const char * name,mu_off_t * pval)130 _amd_prop_fetch_off (struct _amd_data *amd, const char *name, mu_off_t *pval)
131 {
132   const char *p;
133   mu_off_t n = 0;
134 
135   if (!amd->prop || mu_property_sget_value (amd->prop, name, &p))
136     return MU_ERR_NOENT;
137   if (!pval)
138     return 0;
139   for (; *p; p++)
140     {
141       if (!mu_isdigit (*p))
142 	return EINVAL;
143       n = n * 10 + *p - '0';
144     }
145   *pval = n;
146   return 0;
147 }
148 
149 int
_amd_prop_fetch_size(struct _amd_data * amd,const char * name,size_t * pval)150 _amd_prop_fetch_size (struct _amd_data *amd, const char *name, size_t *pval)
151 {
152   mu_off_t n;
153   int rc = _amd_prop_fetch_off (amd, name, &n);
154   if (rc == 0)
155     {
156       size_t s = n;
157       if (s != n)
158 	return ERANGE;
159       if (pval)
160 	*pval = s;
161     }
162   return rc;
163 }
164 
165 int
_amd_prop_fetch_ulong(struct _amd_data * amd,const char * name,unsigned long * pval)166 _amd_prop_fetch_ulong (struct _amd_data *amd, const char *name,
167 		       unsigned long *pval)
168 {
169   mu_off_t n;
170   int rc = _amd_prop_fetch_off (amd, name, &n);
171   if (rc == 0)
172     {
173       unsigned long s = n;
174       if (s != n)
175 	return ERANGE;
176       if (pval)
177 	*pval = s;
178     }
179   return rc;
180 }
181 
182 int
_amd_prop_store_off(struct _amd_data * amd,const char * name,mu_off_t val)183 _amd_prop_store_off (struct _amd_data *amd, const char *name, mu_off_t val)
184 {
185   char nbuf[128];
186   char *p;
187   int sign = 0;
188 
189   p = nbuf + sizeof nbuf;
190   *--p = 0;
191   if (val < 0)
192     {
193       sign = 1;
194       val = - val;
195     }
196   do
197     {
198       unsigned d = val % 10;
199       if (p == nbuf)
200 	return ERANGE;
201       *--p = d + '0';
202       val /= 10;
203     }
204   while (val);
205   if (sign)
206     {
207       if (p == nbuf)
208 	return ERANGE;
209       *--p = '-';
210     }
211   return mu_property_set_value (amd->prop, name, p, 1);
212 }
213 
214 static int
_amd_prop_create(struct _amd_data * amd)215 _amd_prop_create (struct _amd_data *amd)
216 {
217   int rc;
218   struct mu_mh_prop *mhprop;
219   mhprop = calloc (1, sizeof (mhprop[0]));
220   if (!mhprop)
221     return ENOMEM;
222   mhprop->filename = mu_make_file_name (amd->name, _MU_AMD_PROP_FILE_NAME);
223   if (!mhprop->filename)
224     {
225       free (mhprop);
226       return errno;
227     }
228 
229   if (access (mhprop->filename, F_OK) == 0)
230     {
231       amd->flags |= MU_AMD_F_PROP;
232     }
233 
234   rc = mu_property_create_init (&amd->prop, mu_mh_property_init, mhprop);
235   if (rc)
236     {
237       mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
238 		("mu_property_create_init: %s",
239 		 mu_strerror (rc)));
240       free (mhprop->filename);
241       free (mhprop);
242     }
243 
244   return rc;
245 }
246 
247 /* Operations on message array */
248 
249 /* Perform binary search for message MSG on a segment of message array
250    of AMD between the indexes FIRST and LAST inclusively.
251    If found, return 0 and store index of the located entry in the
252    variable PRET. Otherwise, return 1 and place into PRET index of
253    the nearest array element that is less than MSG (in the sense of
254    amd->msg_cmp)
255    Indexes are zero-based. */
256 
257 static int
amd_msg_bsearch(struct _amd_data * amd,mu_off_t first,mu_off_t last,struct _amd_message * msg,mu_off_t * pret)258 amd_msg_bsearch (struct _amd_data *amd, mu_off_t first, mu_off_t last,
259 		 struct _amd_message *msg,
260 		 mu_off_t *pret)
261 {
262   mu_off_t mid;
263   int rc;
264 
265   while (first <= last)
266     {
267       mid = (first + last) / 2;
268       rc = amd->msg_cmp (amd->msg_array[mid], msg);
269       if (rc > 0)
270 	last = mid - 1;
271       else
272 	{
273 	  *pret = mid;
274 	  if (rc < 0)
275 	    first = mid + 1;
276 	  else
277 	    return 0;
278 	}
279     }
280   return 1;
281 }
282 
283 /* Search for message MSG in the message array of AMD.
284    If found, return 0 and store index of the located entry in the
285    variable PRET. Otherwise, return 1 and store in PRET the index of
286    the array element that is less than MSG (in the sense of
287    amd->msg_cmp)
288    Index returned in PRET is 1-based, so *PRET == 0 means that MSG
289    is less than the very first element of the message array.
290 
291    In other words, when amd_msg_lookup() returns 1, the value in *PRET
292    can be regarded as a 0-based index of the array slot where MSG can
293    be inserted */
294 
295 int
amd_msg_lookup(struct _amd_data * amd,struct _amd_message * msg,size_t * pret)296 amd_msg_lookup (struct _amd_data *amd, struct _amd_message *msg,
297 		 size_t *pret)
298 {
299   int rc;
300   mu_off_t i;
301 
302   if (amd->msg_count == 0)
303     {
304       *pret = 0;
305       return 1;
306     }
307 
308   rc = amd->msg_cmp (msg, amd->msg_array[0]);
309   if (rc < 0)
310     {
311       *pret = 0;
312       return 1;
313     }
314   else if (rc == 0)
315     {
316       *pret = 1;
317       return 0;
318     }
319 
320   rc = amd->msg_cmp (msg, amd->msg_array[amd->msg_count - 1]);
321   if (rc > 0)
322     {
323       *pret = amd->msg_count;
324       return 1;
325     }
326   else if (rc == 0)
327     {
328       *pret = amd->msg_count;
329       return 0;
330     }
331 
332   rc = amd_msg_bsearch (amd, 0, amd->msg_count - 1, msg, &i);
333   *pret = i + 1;
334   return rc;
335 }
336 
337 #define AMD_MSG_INC 64
338 
339 /* Prepare the message array for insertion of a new message
340    at position INDEX (zero based), by moving its contents
341    one slot to the right. If necessary, expand the array by
342    AMD_MSG_INC */
343 int
amd_array_expand(struct _amd_data * amd,size_t index)344 amd_array_expand (struct _amd_data *amd, size_t index)
345 {
346   if (amd->msg_count == amd->msg_max)
347     {
348       struct _amd_message **p;
349 
350       amd->msg_max += AMD_MSG_INC; /* FIXME: configurable? */
351       p = realloc (amd->msg_array, amd->msg_max * sizeof (amd->msg_array[0]));
352       if (!p)
353 	{
354 	  amd->msg_max -= AMD_MSG_INC;
355 	  return ENOMEM;
356 	}
357       amd->msg_array = p;
358     }
359   if (amd->msg_count > index)
360     memmove (&amd->msg_array[index+1], &amd->msg_array[index],
361 	     (amd->msg_count-index) * sizeof (amd->msg_array[0]));
362   amd->msg_count++;
363   return 0;
364 }
365 
366 /* Shrink the message array by removing the element at INDEX-COUNT and
367    shifting left by COUNT positions all the elements to the right of
368    it. */
369 int
amd_array_shrink(struct _amd_data * amd,size_t index,size_t count)370 amd_array_shrink (struct _amd_data *amd, size_t index, size_t count)
371 {
372   if (amd->msg_count-index-1 && index < amd->msg_count)
373     memmove (&amd->msg_array[index-count+1], &amd->msg_array[index + 1],
374 	     (amd->msg_count-index-1) * sizeof (amd->msg_array[0]));
375   amd->msg_count -= count;
376   return 0;
377 }
378 
379 
380 int
amd_init_mailbox(mu_mailbox_t mailbox,size_t amd_size,struct _amd_data ** pamd)381 amd_init_mailbox (mu_mailbox_t mailbox, size_t amd_size,
382 		  struct _amd_data **pamd)
383 {
384   int status;
385   struct _amd_data *amd;
386 
387   if (mailbox == NULL)
388     return EINVAL;
389   if (amd_size < sizeof (*amd))
390     return EINVAL;
391 
392   amd = mailbox->data = calloc (1, amd_size);
393   if (amd == NULL)
394     return ENOMEM;
395 
396   /* Back pointer.  */
397   amd->mailbox = mailbox;
398 
399   status = mu_url_aget_path (mailbox->url, &amd->name);
400   if (status)
401     {
402       free (amd);
403       mailbox->data = NULL;
404       return status;
405     }
406 
407   /* Overloading the defaults.  */
408   mailbox->_destroy = amd_destroy;
409 
410   mailbox->_open = amd_open;
411   mailbox->_close = amd_close;
412 
413   /* Overloading of the entire mailbox object methods.  */
414   mailbox->_get_message = amd_get_message;
415   mailbox->_quick_get_message = amd_quick_get_message;
416   mailbox->_append_message = amd_append_message;
417   mailbox->_messages_count = amd_messages_count;
418   mailbox->_messages_recent = amd_messages_recent;
419   mailbox->_message_unseen = amd_message_unseen;
420   mailbox->_expunge = amd_expunge;
421   mailbox->_sync = amd_sync;
422   mailbox->_get_uidvalidity = amd_get_uidvalidity;
423   mailbox->_set_uidvalidity = amd_set_uidvalidity;
424   mailbox->_uidnext = amd_uidnext;
425 
426   mailbox->_scan = amd_scan;
427   mailbox->_is_updated = amd_is_updated;
428 
429   mailbox->_get_size = amd_get_size;
430   mailbox->_remove = amd_remove_mbox;
431 
432   mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1, ("amd_init(%s)", amd->name));
433   *pamd = amd;
434   return 0;
435 }
436 
437 static void
amd_destroy(mu_mailbox_t mailbox)438 amd_destroy (mu_mailbox_t mailbox)
439 {
440   struct _amd_data *amd = mailbox->data;
441   size_t i;
442 
443   if (!amd)
444     return;
445 
446   amd_pool_flush (amd);
447   mu_monitor_wrlock (mailbox->monitor);
448   for (i = 0; i < amd->msg_count; i++)
449     {
450       mu_message_destroy (&amd->msg_array[i]->message, amd->msg_array[i]);
451       if (amd->msg_free)
452 	amd->msg_free (amd->msg_array[i]);
453       free (amd->msg_array[i]);
454     }
455   free (amd->msg_array);
456 
457   mu_property_destroy (&amd->prop);
458 
459   if (amd->name)
460     free (amd->name);
461 
462   free (amd);
463   mailbox->data = NULL;
464   mu_monitor_unlock (mailbox->monitor);
465 }
466 
467 static int
amd_open(mu_mailbox_t mailbox,int flags)468 amd_open (mu_mailbox_t mailbox, int flags)
469 {
470   struct _amd_data *amd = mailbox->data;
471   struct stat st;
472 
473   mailbox->flags = flags;
474   if (stat (amd->name, &st) < 0)
475     {
476       if ((flags & MU_STREAM_CREAT) && errno == ENOENT)
477 	{
478 	  int rc;
479 	  int perms = mu_stream_flags_to_mode (flags, 1);
480 	  if (mkdir (amd->name, S_IRUSR|S_IWUSR|S_IXUSR|perms))
481 	    return errno;
482 	  if (stat (amd->name, &st) < 0)
483 	    return errno;
484 	  if (amd->create && (rc = amd->create (amd, flags)))
485 	    return rc;
486 	}
487       else
488 	return errno;
489     }
490 
491   if (!S_ISDIR (st.st_mode))
492     return EINVAL;
493 
494   if (access (amd->name,
495 	      (flags & (MU_STREAM_WRITE|MU_STREAM_APPEND)) ?
496 	       W_OK : R_OK | X_OK))
497     return errno;
498 
499   /* Create/read properties.  It is not an error if this fails. */
500   _amd_prop_create (amd);
501 
502   if (mailbox->locker == NULL)
503     mu_locker_create_ext (&mailbox->locker, "/dev/null", NULL);
504 
505   return 0;
506 }
507 
508 static int
amd_close(mu_mailbox_t mailbox)509 amd_close (mu_mailbox_t mailbox)
510 {
511   struct _amd_data *amd;
512   int i;
513 
514   if (!mailbox)
515     return EINVAL;
516 
517   amd = mailbox->data;
518 
519   /* Destroy all cached data */
520   amd_pool_flush (amd);
521   mu_monitor_wrlock (mailbox->monitor);
522   for (i = 0; i < amd->msg_count; i++)
523     {
524       mu_message_destroy (&amd->msg_array[i]->message, amd->msg_array[i]);
525       if (amd->msg_free)
526 	amd->msg_free (amd->msg_array[i]);
527       free (amd->msg_array[i]);
528     }
529   free (amd->msg_array);
530   amd->msg_array = NULL;
531 
532   mu_property_save (amd->prop);
533 
534   amd->msg_count = 0; /* number of messages in the list */
535   amd->msg_max = 0;   /* maximum message buffer capacity */
536 
537   mu_monitor_unlock (mailbox->monitor);
538 
539   return 0;
540 }
541 
542 static int
amd_message_qid(mu_message_t msg,mu_message_qid_t * pqid)543 amd_message_qid (mu_message_t msg, mu_message_qid_t *pqid)
544 {
545   struct _amd_message *mhm = mu_message_get_owner (msg);
546 
547   return mhm->amd->cur_msg_file_name (mhm, 0, pqid);
548 }
549 
550 static void
amd_message_detach(mu_message_t msg)551 amd_message_detach (mu_message_t msg)
552 {
553   struct _amd_message *mhm = mu_message_get_owner (msg);
554   mhm->message = NULL;
555 }
556 
557 struct _amd_message *
_amd_get_message(struct _amd_data * amd,size_t msgno)558 _amd_get_message (struct _amd_data *amd, size_t msgno)
559 {
560   msgno--;
561   if (msgno >= amd->msg_count)
562     return NULL;
563   return amd->msg_array[msgno];
564 }
565 
566 static int
_amd_attach_message(mu_mailbox_t mailbox,struct _amd_message * mhm,mu_message_t * pmsg)567 _amd_attach_message (mu_mailbox_t mailbox, struct _amd_message *mhm,
568 		     mu_message_t *pmsg)
569 {
570   int status;
571   mu_message_t msg;
572 
573   /* Check if we already have it.  */
574   if (mhm->message)
575     {
576       if (pmsg)
577 	*pmsg = mhm->message;
578       return 0;
579     }
580 
581   /* Get an empty message struct.  */
582   status = mu_message_create (&msg, mhm);
583   if (status != 0)
584     return status;
585 
586   msg->_detach = amd_message_detach;
587 
588   /* Set the header.  */
589   {
590     mu_header_t header = NULL;
591     status = mu_header_create (&header, NULL, 0);
592     if (status != 0)
593       {
594 	mu_message_destroy (&msg, mhm);
595 	return status;
596       }
597     mu_header_set_fill (header, amd_header_fill, msg);
598     /*FIXME:
599     mu_header_set_get_fvalue (header, amd_header_get_fvalue, msg);
600     */
601     mu_message_set_header (msg, header, mhm);
602   }
603 
604   /* Set the attribute.  */
605   {
606     mu_attribute_t attribute;
607     status = mu_attribute_create (&attribute, msg);
608     if (status != 0)
609       {
610 	mu_message_destroy (&msg, mhm);
611 	return status;
612       }
613     mu_attribute_set_get_flags (attribute, amd_get_attr_flags, msg);
614     mu_attribute_set_set_flags (attribute, amd_set_attr_flags, msg);
615     mu_attribute_set_unset_flags (attribute, amd_unset_attr_flags, msg);
616     mu_message_set_attribute (msg, attribute, mhm);
617   }
618 
619   /* Prepare the body.  */
620   {
621     mu_body_t body = NULL;
622     struct _amd_body_stream *str;
623 
624     if ((status = mu_body_create (&body, msg)) != 0)
625       return status;
626 
627     str = (struct _amd_body_stream *)
628               _mu_stream_create (sizeof (*str),
629 				 mailbox->flags | MU_STREAM_SEEK |
630 				 _MU_STR_OPEN);
631     if (!str)
632       {
633 	mu_body_destroy (&body, msg);
634 	mu_message_destroy (&msg, mhm);
635 	return ENOMEM;
636       }
637     str->stream.read = amd_body_stream_read;
638     str->stream.size = amd_body_stream_size;
639     str->stream.seek = amd_body_stream_seek;
640     mu_body_set_stream (body, (mu_stream_t) str, msg);
641     mu_body_clear_modified (body);
642     mu_body_set_size (body, amd_body_size, msg);
643     mu_body_set_lines (body, amd_body_lines, msg);
644     mu_message_set_body (msg, body, mhm);
645     str->body = body;
646   }
647 
648   /* Set the envelope.  */
649   {
650     mu_envelope_t envelope = NULL;
651     status = mu_message_reconstruct_envelope (msg, &envelope);
652     if (status != 0)
653       {
654 	mu_message_destroy (&msg, mhm);
655 	return status;
656       }
657     mu_message_set_envelope (msg, envelope, mhm);
658   }
659 
660   /* Set the UID.  */
661   if (mhm->amd->message_uid)
662     mu_message_set_uid (msg, mhm->amd->message_uid, mhm);
663   mu_message_set_qid (msg, amd_message_qid, mhm);
664 
665   /* Attach the message to the mailbox mbox data.  */
666   mhm->message = msg;
667   mu_message_set_mailbox (msg, mailbox, mhm);
668 
669   /* Some of mu_message_set_ functions above mark message as modified.
670      Undo it now.
671 
672      FIXME: Marking message as modified is not always appropriate. Find
673      a better way. */
674 
675   mu_message_clear_modified (msg);
676 
677   if (pmsg)
678     *pmsg = msg;
679 
680   return 0;
681 }
682 
683 static int
_amd_scan0(struct _amd_data * amd,size_t msgno,size_t * pcount,int do_notify)684 _amd_scan0 (struct _amd_data *amd, size_t msgno, size_t *pcount,
685 	    int do_notify)
686 {
687   unsigned long uidval;
688   int status = amd->scan0 (amd->mailbox, msgno, pcount, do_notify);
689   if (status != 0)
690     return status;
691   /* Reset the uidvalidity.  */
692   if (amd->msg_count == 0 ||
693       _amd_prop_fetch_ulong (amd, _MU_AMD_PROP_UIDVALIDITY, &uidval) ||
694       !uidval)
695     {
696       uidval = (unsigned long) amd->mtime;
697       _amd_prop_store_off (amd, _MU_AMD_PROP_UIDVALIDITY, uidval);
698     }
699   return 0;
700 }
701 
702 static int
amd_get_message(mu_mailbox_t mailbox,size_t msgno,mu_message_t * pmsg)703 amd_get_message (mu_mailbox_t mailbox, size_t msgno, mu_message_t *pmsg)
704 {
705   int status;
706   struct _amd_data *amd = mailbox->data;
707   struct _amd_message *mhm;
708 
709   /* Sanity checks.  */
710   if (pmsg == NULL)
711     return MU_ERR_OUT_PTR_NULL;
712   if (amd == NULL || msgno < 1)
713     return EINVAL;
714 
715   /* If we did not start a scanning yet do it now.  */
716   if (amd->msg_count == 0)
717     {
718       status = _amd_scan0 (amd, 1, NULL, 0);
719       if (status != 0)
720 	return status;
721     }
722 
723   if ((mhm = _amd_get_message (amd, msgno)) == NULL)
724     return MU_ERR_NOENT;
725   return _amd_attach_message (mailbox, mhm, pmsg);
726 }
727 
728 static int
amd_quick_get_message(mu_mailbox_t mailbox,mu_message_qid_t qid,mu_message_t * pmsg)729 amd_quick_get_message (mu_mailbox_t mailbox, mu_message_qid_t qid,
730 		       mu_message_t *pmsg)
731 {
732   int status;
733   struct _amd_data *amd = mailbox->data;
734   if (amd->msg_count)
735     {
736       mu_message_qid_t vqid;
737       mu_message_t msg = amd->msg_array[0]->message;
738       status = mu_message_get_qid (msg, &vqid);
739       if (status)
740 	return status;
741       status = strcmp (qid, vqid);
742       free (vqid);
743       if (status)
744 	return MU_ERR_EXISTS;
745       *pmsg = msg;
746     }
747   else if (amd->qfetch)
748     {
749       status = amd->qfetch (amd, qid);
750       if (status)
751 	return status;
752       return _amd_attach_message (mailbox, amd->msg_array[0], pmsg);
753     }
754 
755   return ENOSYS;
756 }
757 
758 static int
_amd_tempfile(struct _amd_data * amd,FILE ** pfile,char ** namep)759 _amd_tempfile (struct _amd_data *amd, FILE **pfile, char **namep)
760 {
761   struct mu_tempfile_hints hints;
762   int fd, rc;
763 
764   hints.tmpdir = amd->name;
765   rc = mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &fd, namep);
766   if (rc == 0)
767     if ((*pfile = fdopen (fd, "w")) == NULL)
768       rc = errno;
769   return rc;
770 }
771 
772 static int
_amd_delim(char * str)773 _amd_delim (char *str)
774 {
775   if (str[0] == '-')
776     {
777       for (; *str == '-'; str++)
778 	;
779       for (; *str == ' ' || *str == '\t'; str++)
780 	;
781     }
782   return str[0] == '\n';
783 }
784 
785 static int
_amd_message_save(struct _amd_data * amd,struct _amd_message * mhm,int expunge)786 _amd_message_save (struct _amd_data *amd, struct _amd_message *mhm,
787 		   int expunge)
788 {
789   mu_stream_t stream = NULL;
790   char *name = NULL, *buf = NULL, *msg_name, *old_name;
791   size_t n;
792   size_t bsize;
793   size_t nlines, nbytes;
794   size_t new_body_start, new_header_lines;
795   FILE *fp;
796   mu_message_t msg = mhm->message;
797   mu_header_t hdr;
798   int status;
799   mu_attribute_t attr;
800   mu_body_t body;
801   const char *sbuf;
802   mu_envelope_t env = NULL;
803 
804   status = mu_message_size (msg, &bsize);
805   if (status)
806     return status;
807 
808   status = amd->new_msg_file_name (mhm, mhm->attr_flags, expunge, &msg_name);
809   if (status)
810     return status;
811   if (!msg_name)
812     {
813       /* Unlink the original file */
814       char *old_name;
815       status = amd->cur_msg_file_name (mhm, 1, &old_name);
816       free (msg_name);
817       if (status == 0 && unlink (old_name))
818 	status = errno;
819       free (old_name);
820       return status;
821     }
822 
823   status = _amd_tempfile (mhm->amd, &fp, &name);
824   if (status)
825     {
826       free (msg_name);
827       return status;
828     }
829 
830   /* Try to allocate large buffer */
831   for (; bsize > 1; bsize /= 2)
832     if ((buf = malloc (bsize)))
833       break;
834 
835   if (!bsize)
836     {
837       unlink (name);
838       free (name);
839       free (msg_name);
840       return ENOMEM;
841     }
842 
843   /* Copy flags */
844   mu_message_get_header (msg, &hdr);
845   mu_header_get_streamref (hdr, &stream);
846   status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
847   if (status)
848     {
849       /* FIXME: Provide a common exit point for all error
850 	 cases */
851       unlink (name);
852       free (name);
853       free (msg_name);
854       mu_stream_destroy (&stream);
855       return status;
856     }
857 
858   nlines = nbytes = 0;
859   while ((status = mu_stream_readline (stream, buf, bsize, &n)) == 0
860 	 && n != 0)
861     {
862       if (_amd_delim (buf))
863 	break;
864 
865       if (!(mu_c_strncasecmp (buf, "status:", 7) == 0
866 	    || mu_c_strncasecmp (buf,
867                 MU_HEADER_ENV_DATE ":", sizeof (MU_HEADER_ENV_DATE)) == 0
868 	    || mu_c_strncasecmp (buf,
869                 MU_HEADER_ENV_SENDER ":", sizeof (MU_HEADER_ENV_SENDER)) == 0))
870 	{
871 	  nlines++;
872 	  nbytes += fprintf (fp, "%s", buf);
873 	}
874     }
875   mu_stream_destroy (&stream);
876 
877   mu_message_get_envelope (msg, &env);
878   if (mu_envelope_sget_date (env, &sbuf) == 0)
879     {
880       /* NOTE: buffer might be terminated with \n */
881       while (*sbuf && mu_isspace (*sbuf))
882 	sbuf++;
883       nbytes += fprintf (fp, "%s: %s", MU_HEADER_ENV_DATE, sbuf);
884 
885       if (*sbuf && sbuf[strlen (sbuf) - 1] != '\n')
886 	nbytes += fprintf (fp, "\n");
887 
888       nlines++;
889     }
890 
891   if (mu_envelope_sget_sender (env, &sbuf) == 0)
892     {
893       fprintf (fp, "%s: %s\n", MU_HEADER_ENV_SENDER, sbuf);
894       nlines++;
895     }
896 
897   if (!(amd->capabilities & MU_AMD_STATUS))
898     {
899       /* Add status */
900       char statbuf[MU_STATUS_BUF_SIZE];
901 
902       mu_message_get_attribute (msg, &attr);
903       mu_attribute_to_string (attr, statbuf, sizeof (statbuf), &n);
904       if (n)
905 	{
906 	  nbytes += fprintf (fp, "Status: %s\n", statbuf);
907 	  nlines++;
908 	}
909     }
910 
911   nbytes += fprintf (fp, "\n");
912   nlines++;
913 
914   new_header_lines = nlines;
915   new_body_start = nbytes;
916 
917   /* Copy message body */
918 
919   mu_message_get_body (msg, &body);
920   mu_body_get_streamref (body, &stream);
921   status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
922   if (status)
923     {
924       unlink (name);
925       free (name);
926       free (msg_name);
927       mu_stream_destroy (&stream);
928       return status;
929     }
930 
931   nlines = 0;
932   while (mu_stream_read (stream, buf, bsize, &n) == 0 && n != 0)
933     {
934       char *p;
935       for (p = buf; p < buf + n; p++)
936 	if (*p == '\n')
937 	  nlines++;
938       fwrite (buf, 1, n, fp);
939       nbytes += n;
940     }
941   mu_stream_destroy (&stream);
942 
943   mhm->header_lines = new_header_lines;
944   mhm->body_start = new_body_start;
945   mhm->body_lines = nlines;
946   mhm->body_end = nbytes;
947 
948   free (buf);
949   fclose (fp);
950 
951   status = amd->cur_msg_file_name (mhm, 1, &old_name);
952   if (status == 0)
953     {
954       if (rename (name, msg_name))
955 	{
956 	  if (errno == ENOENT)
957 	    mu_observable_notify (amd->mailbox->observable,
958 				  MU_EVT_MAILBOX_CORRUPT,
959 				  amd->mailbox);
960 	  else
961 	    {
962 	      status = errno;
963 	      mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
964 			("renaming %s to %s failed: %s",
965 			 name, msg_name, mu_strerror (status)));
966 	    }
967 	}
968       else
969 	{
970 	  mode_t perms;
971 
972 	  perms = mu_stream_flags_to_mode (amd->mailbox->flags, 0);
973 	  if (perms != 0)
974 	    {
975 	      /* It is documented that the mailbox permissions are
976 		 affected by the current umask, so take it into account
977 		 here.
978 		 FIXME: I'm still not sure we should honor umask, though.
979 		 --gray
980 	      */
981 	      mode_t mask = umask (0);
982 	      chmod (msg_name, (0600 | perms) & ~mask);
983 	      umask (mask);
984 	    }
985 	  if (strcmp (old_name, msg_name))
986 	    /* Unlink original message */
987 	    unlink (old_name);
988 	}
989       free (old_name);
990     }
991   free (msg_name);
992   free (name);
993 
994   return status;
995 }
996 
997 static int
amd_append_message(mu_mailbox_t mailbox,mu_message_t msg)998 amd_append_message (mu_mailbox_t mailbox, mu_message_t msg)
999 {
1000   int status;
1001   struct _amd_data *amd = mailbox->data;
1002   struct _amd_message *mhm;
1003 
1004   if (!mailbox || !msg)
1005     return EINVAL;
1006 
1007   mhm = calloc (1, amd->msg_size);
1008   if (!mhm)
1009     return ENOMEM;
1010 
1011   /* If we did not start a scanning yet do it now.  */
1012   if (amd->msg_count == 0)
1013     {
1014       status = _amd_scan0 (amd, 1, NULL, 0);
1015       if (status != 0)
1016 	{
1017 	  free (mhm);
1018 	  return status;
1019 	}
1020     }
1021 
1022   amd->has_new_msg = 1;
1023 
1024   mhm->amd = amd;
1025   if (amd->msg_init_delivery)
1026     {
1027       status = amd->msg_init_delivery (amd, mhm);
1028       if (status)
1029 	{
1030 	  free (mhm);
1031 	  return status;
1032 	}
1033     }
1034 
1035   mhm->message = msg;
1036   status = _amd_message_save (amd, mhm, 0);
1037   if (status)
1038     {
1039       free (mhm);
1040       return status;
1041     }
1042 
1043   mhm->message = NULL;
1044   /* Insert and re-scan the message */
1045   status = _amd_message_insert (amd, mhm);
1046   if (status)
1047     {
1048       free (mhm);
1049       return status;
1050     }
1051 
1052   if (amd->msg_finish_delivery)
1053     status = amd->msg_finish_delivery (amd, mhm, msg);
1054 
1055   if (status == 0 && mailbox->observable)
1056     {
1057       char *qid;
1058       if (amd->cur_msg_file_name (mhm, 0, &qid) == 0)
1059 	{
1060 	  mu_observable_notify (mailbox->observable,
1061 	                        MU_EVT_MAILBOX_MESSAGE_APPEND,
1062 				qid);
1063 	  free (qid);
1064 	}
1065     }
1066 
1067   return status;
1068 }
1069 
1070 static int
amd_messages_count(mu_mailbox_t mailbox,size_t * pcount)1071 amd_messages_count (mu_mailbox_t mailbox, size_t *pcount)
1072 {
1073   struct _amd_data *amd = mailbox->data;
1074 
1075   if (amd == NULL)
1076     return EINVAL;
1077 
1078   if (!amd_is_updated (mailbox))
1079     return _amd_scan0 (amd,  amd->msg_count, pcount, 0);
1080 
1081   if (pcount)
1082     *pcount = amd->msg_count;
1083 
1084   return 0;
1085 }
1086 
1087 /* A "recent" message is the one not marked with MU_ATTRIBUTE_SEEN
1088    ('O' in the Status header), i.e. a message that is first seen
1089    by the current session (see attributes.h) */
1090 static int
amd_messages_recent(mu_mailbox_t mailbox,size_t * pcount)1091 amd_messages_recent (mu_mailbox_t mailbox, size_t *pcount)
1092 {
1093   struct _amd_data *amd = mailbox->data;
1094   size_t count, i;
1095 
1096   /* If we did not start a scanning yet do it now.  */
1097   if (amd->msg_count == 0)
1098     {
1099       int status = _amd_scan0 (amd, 1, NULL, 0);
1100       if (status != 0)
1101 	return status;
1102     }
1103   count = 0;
1104   for (i = 0; i < amd->msg_count; i++)
1105     {
1106       if (MU_ATTRIBUTE_IS_UNSEEN(amd->msg_array[i]->attr_flags))
1107 	count++;
1108     }
1109   *pcount = count;
1110   return 0;
1111 }
1112 
1113 /* An "unseen" message is the one that has not been read yet */
1114 static int
amd_message_unseen(mu_mailbox_t mailbox,size_t * pmsgno)1115 amd_message_unseen (mu_mailbox_t mailbox, size_t *pmsgno)
1116 {
1117   struct _amd_data *amd = mailbox->data;
1118   size_t i;
1119 
1120   /* If we did not start a scanning yet do it now.  */
1121   if (amd->msg_count == 0)
1122     {
1123       int status = _amd_scan0 (amd, 1, NULL, 0);
1124       if (status != 0)
1125 	return status;
1126     }
1127 
1128   for (i = 0; i < amd->msg_count; i++)
1129     {
1130       if (MU_ATTRIBUTE_IS_UNREAD(amd->msg_array[0]->attr_flags))
1131 	{
1132 	  *pmsgno = i + 1;
1133 	  break;
1134 	}
1135     }
1136   return 0;
1137 }
1138 
1139 static int
_compute_mailbox_size_recursive(struct _amd_data * amd,const char * name,mu_off_t * psize)1140 _compute_mailbox_size_recursive (struct _amd_data *amd, const char *name,
1141 				 mu_off_t *psize)
1142 {
1143   DIR *dir;
1144   struct dirent *entry;
1145   char *buf;
1146   size_t bufsize;
1147   size_t dirlen;
1148   size_t flen;
1149   int status = 0;
1150   struct stat sb;
1151 
1152   dir = opendir (name);
1153   if (!dir)
1154     return errno;
1155 
1156   dirlen = strlen (name);
1157   bufsize = dirlen + 32;
1158   buf = malloc (bufsize);
1159   if (!buf)
1160     {
1161       closedir (dir);
1162       return ENOMEM;
1163     }
1164 
1165   strcpy (buf, name);
1166   if (buf[dirlen-1] != '/')
1167     buf[++dirlen - 1] = '/';
1168 
1169   while ((entry = readdir (dir)))
1170     {
1171       switch (entry->d_name[0])
1172 	{
1173 	case '.':
1174 	  break;
1175 
1176 	default:
1177 	  flen = strlen (entry->d_name);
1178 	  if (dirlen + flen + 1 > bufsize)
1179 	    {
1180 	      bufsize = dirlen + flen + 1;
1181 	      buf = realloc (buf, bufsize);
1182 	      if (!buf)
1183 		{
1184 		  status = ENOMEM;
1185 		  break;
1186 		}
1187 	    }
1188 	  strcpy (buf + dirlen, entry->d_name);
1189 	  if (stat (buf, &sb) == 0)
1190 	    {
1191 	      if (S_ISREG (sb.st_mode))
1192 		*psize += sb.st_size;
1193 	      else if (S_ISDIR (sb.st_mode))
1194 		_compute_mailbox_size_recursive (amd, buf, psize);
1195 	    }
1196 	  /* FIXME: else? */
1197 	  break;
1198 	}
1199     }
1200 
1201   free (buf);
1202 
1203   closedir (dir);
1204   return status;
1205 }
1206 
1207 static int
compute_mailbox_size(struct _amd_data * amd,mu_off_t * psize)1208 compute_mailbox_size (struct _amd_data *amd, mu_off_t *psize)
1209 {
1210   mu_off_t size = 0;
1211   int rc = _compute_mailbox_size_recursive (amd, amd->name, &size);
1212   if (rc == 0)
1213     {
1214       rc = _amd_prop_store_off (amd, _MU_AMD_PROP_SIZE, size);
1215       if (rc == 0 && psize)
1216 	*psize = size;
1217     }
1218   return rc;
1219 }
1220 
1221 static int
amd_remove_mbox(mu_mailbox_t mailbox)1222 amd_remove_mbox (mu_mailbox_t mailbox)
1223 {
1224   int rc;
1225   struct _amd_data *amd = mailbox->data;
1226 
1227   if (!amd->remove)
1228     return ENOSYS;
1229   rc = amd->remove (amd);
1230   if (rc == 0)
1231     {
1232       char *name;
1233 
1234       name = mu_make_file_name (amd->name, _MU_AMD_PROP_FILE_NAME);
1235       if (!name)
1236 	return ENOMEM;
1237       if (unlink (name) && errno != ENOENT)
1238 	rc = errno;
1239       free (name);
1240     }
1241 
1242   if (rc == 0)
1243     {
1244       if (rmdir (amd->name) && errno != ENOENT)
1245 	{
1246 	  rc = errno;
1247 	  /* POSIX.1-2001 allows EEXIST to be returned if the directory
1248 	     contained entries other than . and .. */
1249 	  if (rc == EEXIST)
1250 	    rc = ENOTEMPTY;
1251 	}
1252     }
1253 
1254   return rc;
1255 }
1256 
1257 static int
_amd_update_message(struct _amd_data * amd,struct _amd_message * mhm,int expunge,int * upd)1258 _amd_update_message (struct _amd_data *amd, struct _amd_message *mhm,
1259 		     int expunge, int *upd)
1260 {
1261   int flg, rc;
1262 
1263   if (mhm->message)
1264     flg = mu_message_is_modified (mhm->message);
1265   else if (mhm->attr_flags & MU_ATTRIBUTE_MODIFIED)
1266     flg = MU_MSG_ATTRIBUTE_MODIFIED;
1267   else
1268     return 0;
1269 
1270   if (!flg)
1271     return 0;
1272 
1273   if (flg == MU_MSG_ATTRIBUTE_MODIFIED && amd->chattr_msg)
1274     {
1275       rc = amd->chattr_msg (mhm, expunge);
1276       if (rc)
1277 	{
1278 	  mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1279 		    ("_amd_update_message: chattr_msg failed: %s",
1280 		     mu_strerror (rc)));
1281 	  return rc;
1282 	}
1283     }
1284   else
1285     {
1286       rc = _amd_attach_message (amd->mailbox, mhm, NULL);
1287       if (rc)
1288 	{
1289 	  mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1290 		    ("_amd_update_message: _amd_attach_message failed: %s",
1291 		     mu_strerror (rc)));
1292 	  return rc;
1293 	}
1294 
1295       rc = _amd_message_save (amd, mhm, expunge);
1296       if (rc)
1297 	{
1298 	  mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1299 		    ("_amd_update_message: _amd_message_save failed: %s",
1300 		     mu_strerror (rc)));
1301 	  return rc;
1302 	}
1303     }
1304   *upd = 1;
1305   return rc;
1306 }
1307 
1308 static int
amd_expunge(mu_mailbox_t mailbox)1309 amd_expunge (mu_mailbox_t mailbox)
1310 {
1311   struct _amd_data *amd = mailbox->data;
1312   struct _amd_message *mhm;
1313   size_t i;
1314   int updated = amd->has_new_msg;
1315   size_t expcount = 0;
1316   size_t last_expunged = 0;
1317 
1318   if (amd == NULL)
1319     return EINVAL;
1320 
1321   if (amd->msg_count == 0)
1322     return 0;
1323 
1324   for (i = 0; i < amd->msg_count; i++)
1325     {
1326       mhm = amd->msg_array[i];
1327 
1328       if (mhm->attr_flags & MU_ATTRIBUTE_DELETED)
1329 	{
1330 	  int rc;
1331 	  struct _amd_message **pp;
1332 
1333 	  if (amd->delete_msg)
1334 	    {
1335 	      rc = amd->delete_msg (amd, mhm);
1336 	      if (rc)
1337 		return rc;
1338 	    }
1339 	  else
1340 	    {
1341 	      char *old_name;
1342 	      char *new_name;
1343 
1344 	      rc = amd->cur_msg_file_name (mhm, 1, &old_name);
1345 	      if (rc)
1346 		return rc;
1347 	      rc = amd->new_msg_file_name (mhm, mhm->attr_flags, 1,
1348 					   &new_name);
1349 	      if (rc)
1350 		{
1351 		  free (old_name);
1352 		  return rc;
1353 		}
1354 
1355 	      if (new_name)
1356 		{
1357 		  /* FIXME: It may be a good idea to have a capability flag
1358 		     in struct _amd_data indicating that no actual removal
1359 		     is needed (e.g. for traditional MH). It will allow to
1360 		     bypass lots of no-op code here. */
1361 		  if (strcmp (old_name, new_name) &&
1362 		      /* Rename original message */
1363 		      rename (old_name, new_name))
1364 		    {
1365 		      if (errno == ENOENT)
1366 			mu_observable_notify (mailbox->observable,
1367 					      MU_EVT_MAILBOX_CORRUPT,
1368 					      mailbox);
1369 		      else
1370 			{
1371 			  rc = errno;
1372 			  mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1373 				    ("renaming %s to %s failed: %s",
1374 				     old_name, new_name, mu_strerror (rc)));
1375 			}
1376 		    }
1377 		}
1378 	      else
1379 		/* Unlink original file */
1380 		unlink (old_name);
1381 
1382 	      free (old_name);
1383 	      free (new_name);
1384 	    }
1385 
1386 	  pp = amd_pool_lookup (mhm);
1387 	  if (pp)
1388 	    *pp = NULL;
1389 	  mu_message_destroy (&mhm->message, mhm);
1390 	  if (amd->msg_free)
1391 	    amd->msg_free (mhm);
1392 	  free (mhm);
1393 	  amd->msg_array[i] = NULL;
1394 	  last_expunged = i;
1395 	  updated = 1;
1396 
1397 	  {
1398 	    size_t expevt[2] = { i + 1, expcount };
1399 	    mu_observable_notify (mailbox->observable,
1400 				  MU_EVT_MAILBOX_MESSAGE_EXPUNGE,
1401 				  expevt);
1402 	    ++expcount;
1403 	  }
1404 	}
1405       else
1406 	{
1407 	  _amd_update_message (amd, mhm, 1, &updated);/*FIXME: Error checking*/
1408 	}
1409     }
1410 
1411   if (expcount)
1412     {
1413       int reset_uidvalidity;
1414 
1415       last_expunged++;
1416 
1417       /* See the description of MU_AMD_VOLATILE_UIDNEXT in amd.h for
1418 	 details.
1419       */
1420       reset_uidvalidity = (amd->capabilities & MU_AMD_VOLATILE_UIDNEXT)
1421 	                   && last_expunged == amd->msg_count;
1422 
1423       do
1424 	{
1425 	  size_t j;
1426 
1427 	  for (j = 1; j < last_expunged && !amd->msg_array[last_expunged-j-1];
1428 	       j++)
1429 	    ;
1430 	  amd_array_shrink (amd, last_expunged - 1, j);
1431 	  for (last_expunged -= j;
1432 	       last_expunged > 0 && amd->msg_array[last_expunged - 1];
1433 	       last_expunged--)
1434 	    ;
1435 	}
1436       while (last_expunged);
1437 
1438       if (reset_uidvalidity)
1439 	{
1440       	  /*
1441 	   * The following is equivalent to
1442 	   * mu_mailbox_uidvalidity_reset (amd->mailbox);
1443 	   */
1444 
1445 	  struct timeval tv;
1446 	  gettimeofday (&tv, NULL);
1447 	  amd_set_uidvalidity (amd->mailbox, tv.tv_sec);
1448 	}
1449     }
1450 
1451   if (updated && !amd->mailbox_size)
1452     {
1453       compute_mailbox_size (amd, NULL);
1454     }
1455   return 0;
1456 }
1457 
1458 static int
amd_sync(mu_mailbox_t mailbox)1459 amd_sync (mu_mailbox_t mailbox)
1460 {
1461   struct _amd_data *amd = mailbox->data;
1462   struct _amd_message *mhm;
1463   size_t i;
1464   int updated = amd->has_new_msg;
1465 
1466   if (amd == NULL)
1467     return EINVAL;
1468 
1469   if (amd->msg_count == 0)
1470     return 0;
1471 
1472   /* Find the first dirty(modified) message.  */
1473   for (i = 0; i < amd->msg_count; i++)
1474     {
1475       mhm = amd->msg_array[i];
1476       if ((mhm->attr_flags & MU_ATTRIBUTE_MODIFIED)
1477 	  || (mhm->message && mu_message_is_modified (mhm->message)))
1478 	break;
1479     }
1480 
1481   for ( ; i < amd->msg_count; i++)
1482     {
1483       mhm = amd->msg_array[i];
1484       _amd_update_message (amd, mhm, 0, &updated);
1485     }
1486 
1487   if (updated && !amd->mailbox_size)
1488     {
1489       compute_mailbox_size (amd, NULL);
1490     }
1491 
1492   return 0;
1493 }
1494 
1495 static inline int
amd_initial_scan(struct _amd_data * amd)1496 amd_initial_scan (struct _amd_data *amd)
1497 {
1498   if (!(amd->flags & MU_AMD_F_INIT_SCAN))
1499     {
1500       int status = _amd_scan0 (amd, 1, NULL, 0);
1501       if (status != 0)
1502         return status;
1503       amd->flags |= MU_AMD_F_INIT_SCAN;
1504     }
1505   return 0;
1506 }
1507 
1508 static int
amd_get_uidvalidity(mu_mailbox_t mailbox,unsigned long * pval)1509 amd_get_uidvalidity (mu_mailbox_t mailbox, unsigned long *pval)
1510 {
1511   struct _amd_data *amd = mailbox->data;
1512   int status = amd_initial_scan (amd);
1513   if (status != 0)
1514     return status;
1515   return _amd_prop_fetch_ulong (amd, _MU_AMD_PROP_UIDVALIDITY, pval);
1516 }
1517 
1518 static int
amd_set_uidvalidity(mu_mailbox_t mailbox,unsigned long uidvalidity)1519 amd_set_uidvalidity (mu_mailbox_t mailbox, unsigned long uidvalidity)
1520 {
1521   struct _amd_data *amd = mailbox->data;
1522   size_t uidnext;
1523   int status = amd_initial_scan (amd);
1524   if (status != 0)
1525     return status;
1526 
1527   if (amd->msg_count == 0)
1528     uidnext = 1;
1529   else
1530     {
1531       mu_message_t msg;
1532 
1533       if ((status = amd_get_message (mailbox, amd->msg_count - 1, &msg)) != 0 ||
1534 	  (status = mu_message_get_uid (msg, &uidnext)) != 0)
1535 	return status;
1536       uidnext++;
1537     }
1538   status = _amd_prop_store_off (amd, _MU_AMD_PROP_UIDNEXT, uidnext);
1539   if (status == 0)
1540     status = _amd_prop_store_off (amd, _MU_AMD_PROP_UIDVALIDITY, uidvalidity);
1541   return status;
1542 }
1543 
1544 static int
amd_uidnext(mu_mailbox_t mailbox,size_t * puidnext)1545 amd_uidnext (mu_mailbox_t mailbox, size_t *puidnext)
1546 {
1547   struct _amd_data *amd = mailbox->data;
1548   int status = amd_initial_scan (amd);
1549   if (status != 0)
1550     return status;
1551   return _amd_prop_fetch_size (amd, _MU_AMD_PROP_UIDNEXT, puidnext);
1552 }
1553 
1554 /* FIXME: effectively the same as mbox_cleanup */
1555 void
amd_cleanup(void * arg)1556 amd_cleanup (void *arg)
1557 {
1558   mu_mailbox_t mailbox = arg;
1559   mu_monitor_unlock (mailbox->monitor);
1560 }
1561 
1562 int
_amd_message_lookup_or_insert(struct _amd_data * amd,struct _amd_message * key,size_t * pindex)1563 _amd_message_lookup_or_insert (struct _amd_data *amd,
1564 			       struct _amd_message *key,
1565 			       size_t *pindex)
1566 {
1567   int result = 0;
1568   size_t index;
1569   if (amd_msg_lookup (amd, key, &index))
1570     {
1571       /* Not found. Index points to the array cell where msg would
1572 	 be placed */
1573       result = amd_array_expand (amd, index);
1574       if (result)
1575 	return result;
1576       else
1577 	result = MU_ERR_NOENT;
1578     }
1579   else
1580     result = 0;
1581   *pindex = index;
1582   return result;
1583 }
1584 
1585 /* Insert message msg into the message list on the appropriate position */
1586 int
_amd_message_insert(struct _amd_data * amd,struct _amd_message * msg)1587 _amd_message_insert (struct _amd_data *amd, struct _amd_message *msg)
1588 {
1589   size_t index;
1590   int rc = _amd_message_lookup_or_insert (amd, msg, &index);
1591 
1592   if (rc == MU_ERR_NOENT)
1593     {
1594       amd->msg_array[index] = msg;
1595       msg->amd = amd;
1596     }
1597   else if (rc == 0)
1598     {
1599       /*FIXME: Found? Shouldn't happen */
1600       return EEXIST;
1601     }
1602   else
1603     return rc;
1604   return 0;
1605 }
1606 
1607 /* Append message to the end of the array, expanding it if necessary */
1608 int
_amd_message_append(struct _amd_data * amd,struct _amd_message * msg)1609 _amd_message_append (struct _amd_data *amd, struct _amd_message *msg)
1610 {
1611   size_t index = amd->msg_count;
1612   int rc = amd_array_expand (amd, index);
1613   if (rc)
1614     return rc;
1615   amd->msg_array[index] = msg;
1616   msg->amd = amd;
1617   return 0;
1618 }
1619 
1620 static int
msg_array_comp(const void * a,const void * b)1621 msg_array_comp (const void *a, const void *b)
1622 {
1623   struct _amd_message **ma = (struct _amd_message **) a;
1624   struct _amd_message **mb = (struct _amd_message **) b;
1625   struct _amd_data *amd = (*ma)->amd;
1626   return amd->msg_cmp (*ma, *mb);
1627 }
1628 
1629 void
amd_sort(struct _amd_data * amd)1630 amd_sort (struct _amd_data *amd)
1631 {
1632   if (amd->msg_count)
1633     qsort (amd->msg_array, amd->msg_count, sizeof (amd->msg_array[0]),
1634 	   msg_array_comp);
1635 }
1636 
1637 /* Scan given message and fill amd_message_t fields.
1638    NOTE: the function assumes mhm->stream != NULL. */
1639 static int
amd_scan_message(struct _amd_message * mhm)1640 amd_scan_message (struct _amd_message *mhm)
1641 {
1642   mu_stream_t stream = mhm->stream;
1643   char buf[1024];
1644   size_t off;
1645   size_t n;
1646   int status;
1647   int in_header = 1;
1648   size_t hlines = 0;
1649   size_t blines = 0;
1650   size_t body_start = 0;
1651   struct stat st;
1652   char *msg_name;
1653   struct _amd_data *amd = mhm->amd;
1654   int amd_capa = amd->capabilities;
1655 
1656   /* Check if the message was modified after the last scan */
1657   status = mhm->amd->cur_msg_file_name (mhm, 1, &msg_name);
1658   if (status)
1659     {
1660       mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1661 		("amd_scan_message: cur_msg_file_name=%s",
1662 		 mu_strerror (status)));
1663       return status;
1664     }
1665 
1666   if (stat (msg_name, &st) == 0 && st.st_mtime == mhm->mtime)
1667     {
1668       /* Nothing to do */
1669       free (msg_name);
1670       return 0;
1671     }
1672 
1673   off = 0;
1674   status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
1675   if (status)
1676     mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1677 	      ("amd_scan_message(%s): mu_stream_seek=%s",
1678 	       msg_name, mu_strerror (status)));
1679   else
1680     {
1681       while ((status = mu_stream_readline (stream, buf, sizeof (buf), &n)) == 0
1682 	     && n != 0)
1683 	{
1684 	  if (in_header)
1685 	    {
1686 	      if (buf[0] == '\n')
1687 		{
1688 		  in_header = 0;
1689 		  body_start = off + 1;
1690 		}
1691 	      if (buf[n - 1] == '\n')
1692 		hlines++;
1693 
1694 	      /* Process particular attributes */
1695 	      if (!(amd_capa & MU_AMD_STATUS) &&
1696 		  mu_c_strncasecmp (buf, "status:", 7) == 0)
1697 		{
1698 		  int deleted = mhm->attr_flags & MU_ATTRIBUTE_DELETED;
1699 		  mu_attribute_string_to_flags (buf, &mhm->attr_flags);
1700 		  mhm->attr_flags |= deleted;
1701 		}
1702 	    }
1703 	  else
1704 	    {
1705 	      if (buf[n - 1] == '\n')
1706 		blines++;
1707 	    }
1708 	  off += n;
1709 	}
1710       if (status)
1711 	mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1712 		  ("amd_scan_message(%s): %s",
1713 		   msg_name, mu_strerror (status)));
1714     }
1715 
1716   free (msg_name);
1717 
1718   if (status == 0)
1719     {
1720       mhm->mtime = st.st_mtime;
1721       if (!body_start)
1722 	body_start = off;
1723       mhm->header_lines = hlines;
1724       mhm->body_lines = blines;
1725       mhm->body_start = body_start;
1726       mhm->body_end = off;
1727     }
1728   return status;
1729 }
1730 
1731 static int
amd_scan(mu_mailbox_t mailbox,size_t msgno,size_t * pcount)1732 amd_scan (mu_mailbox_t mailbox, size_t msgno, size_t *pcount)
1733 {
1734   struct _amd_data *amd = mailbox->data;
1735 
1736   if (! amd_is_updated (mailbox))
1737     return _amd_scan0 (amd, msgno, pcount, 1);
1738 
1739   if (pcount)
1740     *pcount = amd->msg_count;
1741 
1742   return 0;
1743 }
1744 
1745 /* Is the internal representation of the mailbox up to date.
1746    Return 1 if so, 0 otherwise. */
1747 static int
amd_is_updated(mu_mailbox_t mailbox)1748 amd_is_updated (mu_mailbox_t mailbox)
1749 {
1750   struct stat st;
1751   struct _amd_data *amd = mailbox->data;
1752 
1753   if (stat (amd->name, &st) < 0)
1754     return 1;
1755 
1756   return amd->mtime == st.st_mtime;
1757 }
1758 
1759 static int
amd_get_size(mu_mailbox_t mailbox,mu_off_t * psize)1760 amd_get_size (mu_mailbox_t mailbox, mu_off_t *psize)
1761 {
1762   struct _amd_data *amd = mailbox->data;
1763   if (amd->mailbox_size)
1764     return amd->mailbox_size (mailbox, psize);
1765   if (_amd_prop_fetch_off (amd, _MU_AMD_PROP_SIZE, psize))
1766     return compute_mailbox_size (amd, psize);
1767   return 0;
1768 }
1769 
1770 /* Return number of open streams residing in a message pool */
1771 static int
amd_pool_open_count(struct _amd_data * amd)1772 amd_pool_open_count (struct _amd_data *amd)
1773 {
1774   int cnt = amd->pool_last - amd->pool_first;
1775   if (cnt < 0)
1776     cnt += MAX_OPEN_STREAMS;
1777   return cnt;
1778 }
1779 
1780 /* Look up a _amd_message in the pool of open messages.
1781    If the message is found in the pool, returns the address of
1782    the pool slot occupied by it. Otherwise returns NULL. */
1783 static struct _amd_message **
amd_pool_lookup(struct _amd_message * mhm)1784 amd_pool_lookup (struct _amd_message *mhm)
1785 {
1786   struct _amd_data *amd = mhm->amd;
1787   int i;
1788 
1789   for (i = amd->pool_first; i != amd->pool_last; )
1790     {
1791       if (amd->msg_pool[i] == mhm)
1792 	return &amd->msg_pool[i];
1793       if (++i == MAX_OPEN_STREAMS)
1794 	i = 0;
1795     }
1796   return NULL;
1797 }
1798 
1799 /* Open a stream associated with the message mhm. If the stream is
1800    already open, do nothing.
1801    NOTE: We could have reused the NULL holes in the msg_pool, but
1802    that hardly is worth the effort, since the holes appear only when
1803    expunging. On the other hand this may be useful when MAX_OPEN_STREAMS
1804    size is very big. "Premature optimization is the root of all evil" */
1805 static int
amd_pool_open(struct _amd_message * mhm)1806 amd_pool_open (struct _amd_message *mhm)
1807 {
1808   int status;
1809   struct _amd_data *amd = mhm->amd;
1810   if (amd_pool_lookup (mhm))
1811     return 0;
1812   if (amd_pool_open_count (amd) == MAX_OPEN_STREAMS-1)
1813     {
1814       amd_message_stream_close (amd->msg_pool[amd->pool_first++]);
1815       amd->pool_first %= MAX_OPEN_STREAMS;
1816     }
1817   status = amd_message_stream_open (mhm);
1818   if (status)
1819     {
1820       mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1821 		("amd_pool_open: amd_message_stream_open=%s",
1822 		 mu_strerror (status)));
1823       return status;
1824     }
1825   amd->msg_pool[amd->pool_last++] = mhm;
1826   amd->pool_last %= MAX_OPEN_STREAMS;
1827   return 0;
1828 }
1829 
1830 static void
amd_pool_flush(struct _amd_data * amd)1831 amd_pool_flush (struct _amd_data *amd)
1832 {
1833   int i;
1834 
1835   for (i = amd->pool_first; i != amd->pool_last; )
1836     {
1837       if (amd->msg_pool[i])
1838 	amd_message_stream_close (amd->msg_pool[i]);
1839       if (++i == MAX_OPEN_STREAMS)
1840 	i = 0;
1841     }
1842   amd->pool_first = amd->pool_last = 0;
1843 }
1844 
1845 /* Attach a stream to a given message structure. The latter is supposed
1846    to be already added to the open message pool. */
1847 int
amd_message_stream_open(struct _amd_message * mhm)1848 amd_message_stream_open (struct _amd_message *mhm)
1849 {
1850   struct _amd_data *amd = mhm->amd;
1851   char *filename;
1852   int status;
1853   int flags = 0;
1854 
1855   status = amd->cur_msg_file_name (mhm, 1, &filename);
1856   if (status)
1857     {
1858       mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1859 		("amd_message_stream_open: cur_msg_file_name=%s",
1860 		 mu_strerror (status)));
1861       return status;
1862     }
1863 
1864   /* The message should be at least readable */
1865   if (amd->mailbox->flags & (MU_STREAM_WRITE|MU_STREAM_APPEND))
1866     flags |= MU_STREAM_RDWR;
1867   else
1868     flags |= MU_STREAM_READ;
1869   status = mu_file_stream_create (&mhm->stream, filename, flags);
1870   if (status)
1871     mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1872 	      ("amd_message_stream_open: mu_file_stream_create(%s)=%s",
1873 	       filename, mu_strerror (status)));
1874 
1875   free (filename);
1876 
1877   if (status != 0)
1878     return status;
1879 
1880   /* FIXME: Select buffer size dynamically */
1881   mu_stream_set_buffer (mhm->stream, mu_buffer_full, 16384);
1882 
1883   status = amd_scan_message (mhm);
1884   if (status)
1885     mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1886 	      ("amd_message_stream_open: amd_scan_message=%s",
1887 	       mu_strerror (status)));
1888 
1889   return status;
1890 }
1891 
1892 /* Close the stream associated with the given message. */
1893 void
amd_message_stream_close(struct _amd_message * mhm)1894 amd_message_stream_close (struct _amd_message *mhm)
1895 {
1896   if (mhm)
1897     {
1898       mu_stream_destroy (&mhm->stream);
1899     }
1900 }
1901 
1902 int
amd_check_message(struct _amd_message * mhm)1903 amd_check_message (struct _amd_message *mhm)
1904 {
1905   if (mhm->body_end == 0)
1906     return amd_pool_open (mhm);
1907   return 0;
1908 }
1909 
1910 /* Reading functions */
1911 static int
amd_body_stream_read(mu_stream_t is,char * buffer,size_t buflen,size_t * pnread)1912 amd_body_stream_read (mu_stream_t is, char *buffer, size_t buflen,
1913 		      size_t *pnread)
1914 {
1915   struct _amd_body_stream *amdstr = (struct _amd_body_stream *)is;
1916   mu_body_t body = amdstr->body;
1917   mu_message_t msg = mu_body_get_owner (body);
1918   struct _amd_message *mhm = mu_message_get_owner (msg);
1919   size_t nread = 0;
1920   int status = 0;
1921   mu_off_t ln;
1922 
1923   status = amd_pool_open (mhm);
1924   if (status)
1925     return status;
1926 
1927   if (buffer == NULL || buflen == 0)
1928     {
1929       *pnread = nread;
1930       return 0;
1931     }
1932 
1933   mu_monitor_rdlock (mhm->amd->mailbox->monitor);
1934 #ifdef WITH_PTHREAD
1935   /* read() is cancellation point since we're doing a potentially
1936      long operation.  Lets make sure we clean the state.  */
1937   pthread_cleanup_push (amd_cleanup, (void *)mhm->amd->mailbox);
1938 #endif
1939 
1940   ln = mhm->body_end - (mhm->body_start + amdstr->off);
1941   if (ln > 0)
1942     {
1943       nread = ((size_t)ln < buflen) ? (size_t)ln : buflen;
1944       status = mu_stream_seek (mhm->stream, mhm->body_start + amdstr->off,
1945 			       MU_SEEK_SET, NULL);
1946       if (status == 0)
1947 	{
1948 	  status = mu_stream_read (mhm->stream, buffer, nread, &nread);
1949 	  amdstr->off += nread;
1950 	}
1951     }
1952 
1953   *pnread = nread;
1954 
1955   mu_monitor_unlock (mhm->amd->mailbox->monitor);
1956 #ifdef WITH_PTHREAD
1957   pthread_cleanup_pop (0);
1958 #endif
1959 
1960   return status;
1961 }
1962 
1963 static int
amd_body_stream_seek(mu_stream_t str,mu_off_t off,mu_off_t * presult)1964 amd_body_stream_seek (mu_stream_t str, mu_off_t off, mu_off_t *presult)
1965 {
1966   int rc;
1967   size_t size;
1968   struct _amd_body_stream *amdstr = (struct _amd_body_stream *)str;
1969 
1970   rc = amd_body_size (amdstr->body, &size);
1971   if (rc)
1972     return rc;
1973 
1974   if (off < 0 || off > size)
1975     return ESPIPE;
1976 
1977   amdstr->off = off;
1978   if (presult)
1979     *presult = off;
1980   return 0;
1981 }
1982 
1983 /* Return corresponding sizes */
1984 
1985 static int
amd_body_stream_size(mu_stream_t stream,mu_off_t * psize)1986 amd_body_stream_size (mu_stream_t stream, mu_off_t *psize)
1987 {
1988   mu_body_t body = ((struct _amd_body_stream *)stream)->body;
1989   size_t size;
1990   int rc = amd_body_size (body, &size);
1991   if (rc == 0)
1992     *psize = size;
1993   return rc;
1994 }
1995 
1996 static int
amd_body_size(mu_body_t body,size_t * psize)1997 amd_body_size (mu_body_t body, size_t *psize)
1998 {
1999   int status;
2000   mu_message_t msg = mu_body_get_owner (body);
2001   struct _amd_message *mhm = mu_message_get_owner (msg);
2002   if (mhm == NULL)
2003     return EINVAL;
2004   status = amd_check_message (mhm);
2005   if (status)
2006     return status;
2007   if (psize)
2008     *psize = mhm->body_end - mhm->body_start;
2009   return 0;
2010 }
2011 
2012 static int
amd_body_lines(mu_body_t body,size_t * plines)2013 amd_body_lines (mu_body_t body, size_t *plines)
2014 {
2015   int status;
2016   mu_message_t msg = mu_body_get_owner (body);
2017   struct _amd_message *mhm = mu_message_get_owner (msg);
2018   if (mhm == NULL)
2019     return EINVAL;
2020   status = amd_check_message (mhm);
2021   if (status)
2022     return status;
2023   if (plines)
2024     *plines = mhm->body_lines;
2025   return 0;
2026 }
2027 
2028 /* Headers */
2029 static int
amd_header_fill(void * data,char ** pbuf,size_t * plen)2030 amd_header_fill (void *data, char **pbuf, size_t *plen)
2031 {
2032   char *buffer;
2033   size_t len;
2034   mu_message_t msg = data;
2035   struct _amd_message *mhm = mu_message_get_owner (msg);
2036   int status, rc;
2037   mu_off_t pos;
2038 
2039   status = amd_pool_open (mhm);
2040   if (status)
2041     return status;
2042 
2043   len = mhm->body_start;
2044   buffer = malloc (len);
2045   if (!buffer)
2046     return ENOMEM;
2047 
2048   status = mu_stream_seek (mhm->stream, 0, MU_SEEK_CUR, &pos);
2049   if (status)
2050     return status;
2051   status = mu_stream_seek (mhm->stream, 0, MU_SEEK_SET, NULL);
2052   if (status)
2053     return status;
2054 
2055   status = mu_stream_read (mhm->stream, buffer, len, NULL);
2056   rc = mu_stream_seek (mhm->stream, pos, MU_SEEK_SET, NULL);
2057 
2058   if (!status)
2059     status = rc;
2060 
2061   if (status)
2062     {
2063       free (buffer);
2064       return status;
2065     }
2066 
2067   *plen = len;
2068   *pbuf = buffer;
2069   return 0;
2070 }
2071 
2072 /* Attributes */
2073 static int
amd_get_attr_flags(mu_attribute_t attr,int * pflags)2074 amd_get_attr_flags (mu_attribute_t attr, int *pflags)
2075 {
2076   mu_message_t msg = mu_attribute_get_owner (attr);
2077   struct _amd_message *mhm = mu_message_get_owner (msg);
2078 
2079   if (mhm == NULL)
2080     return EINVAL;
2081   if (!(mhm->amd->capabilities & MU_AMD_STATUS))
2082     {
2083       /* If AMD implementation doesn't handle status (attribute) bits, they
2084 	 must be retrieved from the Status: header.  To ensure that, the
2085 	 message must be scanned: */
2086       int rc = amd_check_message (mhm);
2087       if (rc)
2088 	return rc;
2089     }
2090   if (pflags)
2091     *pflags = mhm->attr_flags;
2092   return 0;
2093 }
2094 
2095 static int
amd_set_attr_flags(mu_attribute_t attr,int flags)2096 amd_set_attr_flags (mu_attribute_t attr, int flags)
2097 {
2098   mu_message_t msg = mu_attribute_get_owner (attr);
2099   struct _amd_message *mhm = mu_message_get_owner (msg);
2100 
2101   if (mhm == NULL)
2102     return EINVAL;
2103   mhm->attr_flags |= flags;
2104   return 0;
2105 }
2106 
2107 static int
amd_unset_attr_flags(mu_attribute_t attr,int flags)2108 amd_unset_attr_flags (mu_attribute_t attr, int flags)
2109 {
2110   mu_message_t msg = mu_attribute_get_owner (attr);
2111   struct _amd_message *mhm = mu_message_get_owner (msg);
2112 
2113   if (mhm == NULL)
2114     return EINVAL;
2115   mhm->attr_flags &= ~flags;
2116   return 0;
2117 }
2118 
2119 
2120 int
amd_remove_dir(const char * name)2121 amd_remove_dir (const char *name)
2122 {
2123   DIR *dir;
2124   struct dirent *ent;
2125   char *namebuf;
2126   size_t namelen, namesize;
2127   int rc = 0;
2128   int has_subdirs = 0;
2129 
2130   namelen = strlen (name);
2131   namesize = namelen + 128;
2132   namebuf = malloc (namesize);
2133   if (!namebuf)
2134     return ENOMEM;
2135   memcpy (namebuf, name, namelen);
2136   if (namebuf[namelen - 1] != '/')
2137     namebuf[namelen++] = '/';
2138 
2139   dir = opendir (name);
2140   if (!dir)
2141     return errno;
2142   while ((ent = readdir (dir)))
2143     {
2144       struct stat st;
2145       size_t len;
2146 
2147       if (strcmp (ent->d_name, ".") == 0 ||
2148 	  strcmp (ent->d_name, "..") == 0)
2149 	continue;
2150       len = strlen (ent->d_name);
2151       if (namelen + len >= namesize)
2152 	{
2153 	  char *p;
2154 
2155 	  namesize += len + 1;
2156 	  p = realloc (namebuf, namesize);
2157 	  if (!p)
2158 	    {
2159 	      rc = ENOMEM;
2160 	      break;
2161 	    }
2162 	}
2163       strcpy (namebuf + namelen, ent->d_name);
2164       if (stat (namebuf, &st) == 0 && S_ISDIR (st.st_mode))
2165 	{
2166 	  has_subdirs = 1;
2167 	  continue;
2168 	}
2169 
2170       if (unlink (namebuf))
2171 	{
2172 	  rc = errno;
2173 	  mu_diag_output (MU_DIAG_WARNING,
2174 			  "failed to remove %s: %s",
2175 			  namebuf, mu_strerror (rc));
2176 	  break;
2177 	}
2178     }
2179   closedir (dir);
2180   free (namebuf);
2181 
2182   if (rc == 0 && !has_subdirs)
2183     {
2184       if (rmdir (name))
2185 	{
2186 	  rc = errno;
2187 	  /* POSIX.1-2001 allows EEXIST to be returned if the directory
2188 	     contained entries other than . and .. */
2189 	  if (rc == EEXIST)
2190 	    rc = ENOTEMPTY;
2191 	}
2192     }
2193   return rc;
2194 }
2195 
2196 int
amd_reset_uidvalidity(struct _amd_data * amd)2197 amd_reset_uidvalidity (struct _amd_data *amd)
2198 {
2199   struct timeval tv;
2200   gettimeofday (&tv, NULL);
2201   return _amd_prop_store_off (amd, _MU_AMD_PROP_UIDVALIDITY, tv.tv_sec);
2202 }
2203 
2204 int
amd_update_uidnext(struct _amd_data * amd,size_t * newval)2205 amd_update_uidnext (struct _amd_data *amd, size_t *newval)
2206 {
2207   int rc;
2208   size_t curval;
2209 
2210   rc = _amd_prop_fetch_size (amd, _MU_AMD_PROP_UIDNEXT, &curval);
2211   if (rc == MU_ERR_NOENT)
2212     curval = 1;
2213   else if (rc)
2214     return rc;
2215   if (*newval < curval)
2216     {
2217       *newval = curval;
2218       return 0;
2219     }
2220   return _amd_prop_store_off (amd, _MU_AMD_PROP_UIDNEXT, *newval);
2221 }
2222 
2223 int
amd_alloc_uid(struct _amd_data * amd,size_t * newval)2224 amd_alloc_uid (struct _amd_data *amd, size_t *newval)
2225 {
2226   int rc;
2227   size_t retval;
2228 
2229   rc = _amd_prop_fetch_size (amd, _MU_AMD_PROP_UIDNEXT, &retval);
2230   if (rc == MU_ERR_NOENT)
2231     retval = 1;
2232   else if (rc)
2233     return rc;
2234   rc = _amd_prop_store_off (amd, _MU_AMD_PROP_UIDNEXT, retval + 1);
2235   if (rc)
2236     return rc;
2237   *newval = retval;
2238   return 0;
2239 }
2240 
2241 
2242