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