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 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21 
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <mailutils/debug.h>
28 #include <mailutils/errno.h>
29 #include <mailutils/error.h>
30 #include <mailutils/folder.h>
31 #include <mailutils/iterator.h>
32 #include <mailutils/list.h>
33 #include <mailutils/locker.h>
34 #include <mailutils/observer.h>
35 #include <mailutils/property.h>
36 #include <mailutils/registrar.h>
37 #include <mailutils/stream.h>
38 #include <mailutils/url.h>
39 #include <mailutils/attribute.h>
40 #include <mailutils/message.h>
41 #include <mailutils/util.h>
42 
43 #include <mailutils/sys/mailbox.h>
44 #include <mailutils/sys/folder.h>
45 #include <mailutils/sys/url.h>
46 
47 static int
mailbox_folder_create(mu_mailbox_t mbox,const char * name,mu_record_t record)48 mailbox_folder_create (mu_mailbox_t mbox, const char *name,
49 		       mu_record_t record)
50 {
51   int rc;
52   mu_url_t url;
53 
54   if ((rc = mu_url_uplevel (mbox->url, &url)))
55     {
56       if (rc == MU_ERR_NOENT)
57 	{
58 	  rc = mu_url_dup (mbox->url, &url);
59 	  if (rc)
60 	    return rc;
61 	}
62       else
63 	return rc;
64     }
65 
66   rc = mu_folder_create_from_record (&mbox->folder, url, record);
67   if (rc)
68     mu_url_destroy (&url);
69   return rc;
70 }
71 
72 int
_mailbox_create_from_record(mu_mailbox_t * pmbox,mu_record_t record,mu_url_t url,mu_folder_t folder,const char * name)73 _mailbox_create_from_record (mu_mailbox_t *pmbox,
74 			     mu_record_t record,
75 			     mu_url_t url,
76 			     mu_folder_t folder,
77 			     const char *name)
78 {
79   int (*m_init) (mu_mailbox_t) = NULL;
80 
81   mu_record_get_mailbox (record, &m_init);
82   if (m_init)
83     {
84       int status;
85       int (*u_init) (mu_url_t) = NULL;
86       mu_mailbox_t mbox;
87 
88       /* Allocate memory for mbox.  */
89       mbox = calloc (1, sizeof (*mbox));
90       if (mbox == NULL)
91 	return ENOMEM;
92       mbox->notify_fd = -1;
93 
94       /* Initialize the internal lock now, so the concrete mailbox
95 	 could use it. */
96       status = mu_monitor_create (&mbox->monitor, 0, mbox);
97       if (status != 0)
98 	{
99 	  mu_mailbox_destroy (&mbox);
100 	  return status;
101 	}
102 
103       /* Make sure scheme contains actual mailbox scheme */
104       /* FIXME: It is appropriate not for all record types.  For now we
105 	 assume that if the record scheme ends with a plus sign, this
106 	 should not be done.  Probably it requires some flag in struct
107 	 _mu_record? */
108       if (strcmp (url->scheme, record->scheme))
109 	{
110 	  char *p = strdup (record->scheme);
111 	  if (!p)
112 	    {
113 	      mu_mailbox_destroy (&mbox);
114 	      return errno;
115 	    }
116 	  free (url->scheme);
117 	  url->scheme = p;
118 	}
119 
120       mu_record_get_url (record, &u_init);
121       if (u_init && (status = u_init (url)) != 0)
122 	{
123 	  mu_mailbox_destroy (&mbox);
124 	  return status;
125 	}
126 
127       mbox->url = url;
128 
129       if (folder)
130 	{
131 	  folder->ref++; /* FIXME: No ref/unref function for folders */
132 	  mbox->folder = folder;
133 	}
134       else
135 	/* Create the folder before initializing the concrete mailbox.
136 	   The mailbox needs it's back pointer. */
137 	status = mailbox_folder_create (mbox, name, record);
138 
139       if (status == 0)
140 	status = m_init (mbox);   /* Create the concrete mailbox type.  */
141 
142       if (status != 0)
143 	{
144 	  /* Take care not to destroy url.  Leave it to caller. */
145 	  mbox->url = NULL;
146 	  mu_mailbox_destroy (&mbox);
147 	}
148       else
149 	*pmbox = mbox;
150       return status;
151     }
152   return ENOSYS;
153 }
154 
155 static int
_create_mailbox0(mu_mailbox_t * pmbox,mu_url_t url,const char * name)156 _create_mailbox0 (mu_mailbox_t *pmbox, mu_url_t url, const char *name)
157 {
158   mu_record_t record = NULL;
159   int rc;
160 
161   rc = mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_FILE, &record, NULL);
162   if (rc == 0)
163     rc = _mailbox_create_from_record (pmbox, record, url, NULL, name);
164   return rc;
165 }
166 
167 static int
_create_mailbox(mu_mailbox_t * pmbox,const char * name)168 _create_mailbox (mu_mailbox_t *pmbox, const char *name)
169 {
170   int status;
171   mu_url_t url;
172 
173   status = mu_url_create (&url, name);
174   if (status)
175     return status;
176   status = _create_mailbox0 (pmbox, url, name);
177   if (status)
178     mu_url_destroy (&url);
179   return status;
180 }
181 
182 /* The Mailbox Factory.
183  */
184 int
mu_mailbox_create(mu_mailbox_t * pmbox,const char * name)185 mu_mailbox_create (mu_mailbox_t *pmbox, const char *name)
186 {
187   if (pmbox == NULL)
188     return MU_ERR_OUT_PTR_NULL;
189 
190   return _create_mailbox (pmbox, name);
191 }
192 
193 int
mu_mailbox_create_from_url(mu_mailbox_t * pmbox,mu_url_t url)194 mu_mailbox_create_from_url (mu_mailbox_t *pmbox, mu_url_t url)
195 {
196   if (pmbox == NULL)
197     return MU_ERR_OUT_PTR_NULL;
198   return _create_mailbox0 (pmbox, url, mu_url_to_string (url));
199 }
200 
201 int
mu_mailbox_create_from_record(mu_mailbox_t * pmbox,mu_record_t record,const char * name)202 mu_mailbox_create_from_record (mu_mailbox_t *pmbox, mu_record_t record,
203 			       const char *name)
204 {
205   mu_url_t url;
206   int rc;
207 
208   rc = mu_url_create (&url, name);
209   if (rc)
210     return rc;
211   rc = _mailbox_create_from_record (pmbox, record, url, NULL, name);
212   if (rc)
213     mu_url_destroy (&url);
214   return rc;
215 }
216 
217 int
mu_mailbox_create_at(mu_mailbox_t * pmbox,mu_folder_t folder,const char * name)218 mu_mailbox_create_at (mu_mailbox_t *pmbox, mu_folder_t folder,
219 		      const char *name)
220 {
221   int rc;
222   mu_url_t url;
223   const char *oldpath;
224 
225   rc = mu_url_dup (folder->url, &url);
226   if (rc)
227     return rc;
228   do
229     {
230       char *path;
231       size_t oldlen, len;
232       mu_record_t record;
233 
234       rc = mu_url_sget_path (url, &oldpath);
235       if (rc)
236 	break;
237 
238       oldlen = strlen (oldpath);
239       if (oldlen == 0)
240 	{
241 	  path = strdup (name);
242 	  if (!path)
243 	    {
244 	      rc = ENOMEM;
245 	      break;
246 	    }
247 	}
248       else
249 	{
250 	  if (oldpath[oldlen-1] == '/')
251 	    oldlen--;
252 	  len = oldlen + 1 + strlen (name) + 1;
253 	  path = malloc (len);
254 	  if (!path)
255 	    {
256 	      rc = ENOMEM;
257 	      break;
258 	    }
259 	  memcpy (path, oldpath, oldlen);
260 	  path[oldlen++] = '/';
261 	  strcpy (path + oldlen, name);
262 	}
263       rc = mu_url_set_path (url, path);
264       free (path);
265       if (rc)
266 	break;
267 
268       rc = mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_FILE,
269 				    &record, NULL);
270       if (rc)
271 	break;
272       rc = _mailbox_create_from_record (pmbox, record, url, folder, name);
273     }
274   while (0);
275 
276   if (rc)
277     mu_url_destroy (&url);
278   return rc;
279 }
280 
281 void
mu_mailbox_destroy(mu_mailbox_t * pmbox)282 mu_mailbox_destroy (mu_mailbox_t *pmbox)
283 {
284   if (pmbox && *pmbox)
285     {
286       mu_mailbox_t mbox = *pmbox;
287       mu_monitor_t monitor = mbox->monitor;
288 
289       /* Notify the observers.  */
290       if (mbox->observable)
291 	{
292 	  mu_observable_notify (mbox->observable, MU_EVT_MAILBOX_DESTROY,
293 				mbox);
294 	  mu_observable_destroy (&mbox->observable, mbox);
295 	}
296 
297       /* Call the concrete mailbox _destroy method. So it can clean itself.  */
298       if (mbox->_destroy)
299 	mbox->_destroy (mbox);
300 
301       mu_monitor_wrlock (monitor);
302 
303       /* Close the stream and nuke it */
304       if (mbox->stream)
305 	{
306 	  /* FIXME: Is this right, should the client be responsible
307 	     for closing the stream?  */
308 	  /* mu_stream_close (mbox->stream); */
309 	  mu_stream_destroy (&mbox->stream);
310 	}
311 
312       mu_url_destroy (&mbox->url);
313       mu_locker_destroy (&mbox->locker);
314       mu_folder_destroy (&mbox->folder);
315       mu_property_destroy (&mbox->property);
316       free (mbox->notify_user);
317       free (mbox->notify_sa);
318 
319       free (mbox);
320       *pmbox = NULL;
321       mu_monitor_unlock (monitor);
322       mu_monitor_destroy (&monitor, mbox);
323     }
324 }
325 
326 
327 /* -------------- stub functions ------------------- */
328 
329 int
mu_mailbox_open(mu_mailbox_t mbox,int flag)330 mu_mailbox_open (mu_mailbox_t mbox, int flag)
331 {
332   int rc;
333 
334   if (!mbox)
335     return EINVAL;
336   if (mbox->_open == NULL)
337     return MU_ERR_EMPTY_VFN;
338   if (mbox->flags & _MU_MAILBOX_OPEN)
339     return MU_ERR_OPEN;
340   if (flag & MU_STREAM_QACCESS)
341     {
342       /* Quick access mailboxes are read-only */
343       if (flag & (MU_STREAM_WRITE
344 		  | MU_STREAM_APPEND | MU_STREAM_CREAT))
345 	return EACCES;
346     }
347   rc = mbox->_open (mbox, flag);
348   if (rc == 0)
349     mbox->flags |= _MU_MAILBOX_OPEN;
350   return rc;
351 }
352 
353 int
mu_mailbox_close(mu_mailbox_t mbox)354 mu_mailbox_close (mu_mailbox_t mbox)
355 {
356   int rc;
357 
358   if (!mbox)
359     return EINVAL;
360   if (!(mbox->flags & _MU_MAILBOX_OPEN))
361     return MU_ERR_NOT_OPEN;
362   if (mbox == NULL || mbox->_close == NULL)
363     return MU_ERR_EMPTY_VFN;
364 
365   rc = mbox->_close (mbox);
366   if (rc == 0)
367     {
368       if (mbox->notify_fd >= 0)
369 	close (mbox->notify_fd);
370       mbox->flags &= ~_MU_MAILBOX_OPEN;
371     }
372   return rc;
373 }
374 
375 int
mu_mailbox_remove(mu_mailbox_t mbox)376 mu_mailbox_remove (mu_mailbox_t mbox)
377 {
378   if (!mbox)
379     return EINVAL;
380   if (mbox->flags & _MU_MAILBOX_OPEN)
381     return MU_ERR_OPEN;
382   if (mbox->flags & _MU_MAILBOX_REMOVED)
383     return MU_ERR_MBX_REMOVED;
384   if (!mbox->_remove)
385     {
386       /* Try the owning folder delete method.  See comment to mu_folder_delete
387 	 in folder.c.  This may result in a recursive call to mu_mailbox_remove
388 	 which is blocked by setting the _MU_MAILBOX_REMOVED flag. */
389 
390       int rc;
391       const char *path;
392 
393       rc = mu_url_sget_path (mbox->url, &path);
394       if (rc == 0)
395 	{
396 	  mbox->flags |= _MU_MAILBOX_REMOVED;
397 	  rc = mu_folder_delete (mbox->folder, path);
398 	  if (rc)
399 	    mbox->flags &= ~_MU_MAILBOX_REMOVED;
400 	}
401       return rc;
402     }
403   return mbox->_remove (mbox);
404 }
405 
406 int
mu_mailbox_flush(mu_mailbox_t mbox,int expunge)407 mu_mailbox_flush (mu_mailbox_t mbox, int expunge)
408 {
409   if (!mbox)
410     return EINVAL;
411   if (mbox->flags & _MU_MAILBOX_REMOVED)
412     return MU_ERR_MBX_REMOVED;
413   if (!(mbox->flags & _MU_MAILBOX_OPEN))
414     return _MU_MAILBOX_OPEN;
415   if (!(mbox->flags & (MU_STREAM_WRITE|MU_STREAM_APPEND)))
416     return 0;
417 
418   if (!(mbox->flags & MU_STREAM_APPEND))
419     {
420       size_t i, total = 0;
421 
422       mu_mailbox_messages_count (mbox, &total);
423       for (i = 1; i <= total; i++)
424 	{
425 	  mu_message_t msg = NULL;
426 	  mu_attribute_t attr = NULL;
427 	  mu_mailbox_get_message (mbox, i, &msg);
428 	  mu_message_get_attribute (msg, &attr);
429 	  mu_attribute_set_seen (attr);
430 	}
431       if (expunge)
432 	return mu_mailbox_expunge (mbox);
433     }
434   return mu_mailbox_sync (mbox);
435 }
436 
437 #define _MBOX_CHECK_FLAGS(mbox)			\
438   if (mbox == NULL)				\
439     return EINVAL;			        \
440   if (mbox->flags & _MU_MAILBOX_REMOVED)	\
441     return MU_ERR_MBX_REMOVED;			\
442   if (!(mbox->flags & _MU_MAILBOX_OPEN))	\
443     return MU_ERR_NOT_OPEN
444 
445 #define _MBOX_CHECK(mbox,method)		\
446   _MBOX_CHECK_FLAGS(mbox);			\
447   if (mbox->method == NULL)			\
448     return MU_ERR_EMPTY_VFN
449 
450 #define _MBOX_CHECK_Q(mbox,method)		\
451   _MBOX_CHECK(mbox,method);			\
452   if (mbox->flags & MU_STREAM_QACCESS)		\
453     return MU_ERR_BADOP
454 
455 /* messages */
456 int
mu_mailbox_append_message(mu_mailbox_t mbox,mu_message_t msg)457 mu_mailbox_append_message (mu_mailbox_t mbox, mu_message_t msg)
458 {
459   _MBOX_CHECK_Q (mbox, _append_message);
460   if (!(mbox->flags & (MU_STREAM_WRITE|MU_STREAM_APPEND)))
461     return EACCES;
462   return mbox->_append_message (mbox, msg);
463 }
464 
465 int
mu_mailbox_get_message(mu_mailbox_t mbox,size_t msgno,mu_message_t * pmsg)466 mu_mailbox_get_message (mu_mailbox_t mbox, size_t msgno,  mu_message_t *pmsg)
467 {
468   _MBOX_CHECK_Q (mbox, _get_message);
469   return mbox->_get_message (mbox, msgno, pmsg);
470 }
471 
472 int
mu_mailbox_quick_get_message(mu_mailbox_t mbox,mu_message_qid_t qid,mu_message_t * pmsg)473 mu_mailbox_quick_get_message (mu_mailbox_t mbox, mu_message_qid_t qid,
474 			      mu_message_t *pmsg)
475 {
476   _MBOX_CHECK (mbox, _quick_get_message);
477   if (!(mbox->flags & MU_STREAM_QACCESS))
478     return MU_ERR_BADOP;
479   return mbox->_quick_get_message (mbox, qid, pmsg);
480 }
481 
482 int
mu_mailbox_messages_count(mu_mailbox_t mbox,size_t * num)483 mu_mailbox_messages_count (mu_mailbox_t mbox, size_t *num)
484 {
485   _MBOX_CHECK_Q (mbox, _messages_count);
486   return mbox->_messages_count (mbox, num);
487 }
488 
489 int
mu_mailbox_messages_recent(mu_mailbox_t mbox,size_t * num)490 mu_mailbox_messages_recent (mu_mailbox_t mbox, size_t *num)
491 {
492   size_t i, count, n;
493   int rc;
494 
495   _MBOX_CHECK_FLAGS (mbox);
496   if (mbox->flags & MU_STREAM_QACCESS)
497     return MU_ERR_BADOP;
498   if (mbox->_messages_recent)
499     return mbox->_messages_recent (mbox, num);
500 
501   rc = mu_mailbox_messages_count (mbox, &count);
502   if (rc)
503     return rc;
504   n = 0;
505   for (i = 1; i < count; i++)
506     {
507       mu_message_t msg;
508       mu_attribute_t attr;
509 
510       rc = mu_mailbox_get_message (mbox, i, &msg);
511       if (rc)
512 	return rc;
513       rc = mu_message_get_attribute (msg, &attr);
514       if (rc)
515 	return rc;
516       if (mu_attribute_is_recent (attr))
517 	n++;
518     }
519   *num = n;
520   return 0;
521 }
522 
523 int
mu_mailbox_message_unseen(mu_mailbox_t mbox,size_t * num)524 mu_mailbox_message_unseen (mu_mailbox_t mbox, size_t *num)
525 {
526   int rc;
527   size_t i, count;
528 
529   _MBOX_CHECK_FLAGS (mbox);
530   if (mbox->flags & MU_STREAM_QACCESS)
531     return MU_ERR_BADOP;
532   if (mbox->_message_unseen)
533     return mbox->_message_unseen (mbox, num);
534 
535   rc = mu_mailbox_messages_count (mbox, &count);
536   if (rc)
537     return rc;
538   for (i = 1; i < count; i++)
539     {
540       mu_message_t msg;
541       mu_attribute_t attr;
542       int rc;
543 
544       rc = mu_mailbox_get_message (mbox, i, &msg);
545       if (rc)
546 	return rc;
547       rc = mu_message_get_attribute (msg, &attr);
548       if (rc)
549 	return rc;
550       if (!mu_attribute_is_seen (attr))
551 	{
552 	  *num = i;
553 	  return 0;
554 	}
555     }
556 
557   *num = 0;
558   return 0;
559 }
560 
561 int
mu_mailbox_sync(mu_mailbox_t mbox)562 mu_mailbox_sync (mu_mailbox_t mbox)
563 {
564   _MBOX_CHECK_Q (mbox, _sync);
565   if (!(mbox->flags & (MU_STREAM_WRITE|MU_STREAM_APPEND)))
566     return 0;
567   return mbox->_sync (mbox);
568 }
569 
570 /* Historic alias: */
571 int
mu_mailbox_expunge(mu_mailbox_t mbox)572 mu_mailbox_expunge (mu_mailbox_t mbox)
573 {
574   _MBOX_CHECK_Q (mbox, _expunge);
575   if (!(mbox->flags & (MU_STREAM_WRITE|MU_STREAM_APPEND)))
576     return EACCES;
577   return mbox->_expunge (mbox);
578 }
579 
580 int
mu_mailbox_is_updated(mu_mailbox_t mbox)581 mu_mailbox_is_updated (mu_mailbox_t mbox)
582 {
583   if (mbox == NULL ||
584       !(mbox->flags & _MU_MAILBOX_OPEN) ||
585       (mbox->flags & _MU_MAILBOX_REMOVED) ||
586       mbox->_is_updated == NULL)
587     return 1;
588   if (mbox->flags & MU_STREAM_QACCESS)
589     return 1;
590   return mbox->_is_updated (mbox);
591 }
592 
593 int
mu_mailbox_scan(mu_mailbox_t mbox,size_t msgno,size_t * pcount)594 mu_mailbox_scan (mu_mailbox_t mbox, size_t msgno, size_t *pcount)
595 {
596   _MBOX_CHECK_Q (mbox, _scan);
597   return mbox->_scan (mbox, msgno, pcount);
598 }
599 
600 int
mu_mailbox_get_size(mu_mailbox_t mbox,mu_off_t * psize)601 mu_mailbox_get_size (mu_mailbox_t mbox, mu_off_t *psize)
602 {
603   int status;
604 
605   _MBOX_CHECK_FLAGS (mbox);
606   if (mbox->flags & MU_STREAM_QACCESS)
607     return MU_ERR_BADOP;
608   if (mbox->_get_size == NULL
609       || (status = mbox->_get_size (mbox, psize)) == ENOSYS)
610     {
611       /* Fall back to brute-force method */
612       size_t i, total;
613       mu_off_t size = 0;
614 
615       status = mu_mailbox_messages_count (mbox, &total);
616       if (status)
617 	return status;
618       for (i = 1; i <= total; i++)
619 	{
620 	  mu_message_t msg;
621 	  size_t msgsize;
622 	  status = mu_mailbox_get_message (mbox, i, &msg);
623 	  if (status)
624 	    return status;
625 	  status = mu_message_size (msg, &msgsize);
626 	  if (status)
627 	    return status;
628 	  size += msgsize;
629 	}
630       *psize = size;
631     }
632   return status;
633 }
634 
635 int
mu_mailbox_uidvalidity(mu_mailbox_t mbox,unsigned long * pvalid)636 mu_mailbox_uidvalidity (mu_mailbox_t mbox, unsigned long *pvalid)
637 {
638   _MBOX_CHECK_Q (mbox, _get_uidvalidity);
639   return mbox->_get_uidvalidity (mbox, pvalid);
640 }
641 
642 int
mu_mailbox_uidvalidity_reset(mu_mailbox_t mbox)643 mu_mailbox_uidvalidity_reset (mu_mailbox_t mbox)
644 {
645   _MBOX_CHECK_Q (mbox, _set_uidvalidity);
646   return mbox->_set_uidvalidity (mbox, time (NULL));
647 }
648 
649 int
mu_mailbox_uidnext(mu_mailbox_t mbox,size_t * puidnext)650 mu_mailbox_uidnext (mu_mailbox_t mbox, size_t *puidnext)
651 {
652   _MBOX_CHECK_Q (mbox, _uidnext);
653   return mbox->_uidnext (mbox, puidnext);
654 }
655 
656 /* locking */
657 int
mu_mailbox_set_locker(mu_mailbox_t mbox,mu_locker_t locker)658 mu_mailbox_set_locker (mu_mailbox_t mbox, mu_locker_t locker)
659 {
660   if (mbox == NULL)
661     return EINVAL;
662   if (mbox->locker)
663     mu_locker_destroy (&mbox->locker);
664   mbox->locker = locker;
665   return 0;
666 }
667 
668 int
mu_mailbox_get_locker(mu_mailbox_t mbox,mu_locker_t * plocker)669 mu_mailbox_get_locker (mu_mailbox_t mbox, mu_locker_t *plocker)
670 {
671   if (mbox == NULL)
672     return EINVAL;
673   if (plocker == NULL)
674     return MU_ERR_OUT_PTR_NULL;
675   *plocker = mbox->locker;
676   return 0;
677 }
678 
679 int
mu_mailbox_get_flags(mu_mailbox_t mbox,int * flags)680 mu_mailbox_get_flags (mu_mailbox_t mbox, int *flags)
681 {
682   if (mbox == NULL)
683     return EINVAL;
684   if (!flags)
685     return MU_ERR_OUT_PTR_NULL;
686   *flags = mbox->flags & ~_MU_MAILBOX_MASK;
687   return 0;
688 }
689 
690 int
mu_mailbox_set_stream(mu_mailbox_t mbox,mu_stream_t stream)691 mu_mailbox_set_stream (mu_mailbox_t mbox, mu_stream_t stream)
692 {
693   if (mbox == NULL)
694     return EINVAL;
695   if (mbox->flags & MU_STREAM_QACCESS)
696     return MU_ERR_BADOP;
697   if (mbox->stream)
698     mu_stream_destroy (&mbox->stream);
699   mbox->stream = stream;
700   return 0;
701 }
702 
703 int
mu_mailbox_get_observable(mu_mailbox_t mbox,mu_observable_t * pobservable)704 mu_mailbox_get_observable (mu_mailbox_t mbox, mu_observable_t *pobservable)
705 {
706   if (mbox == NULL)
707     return EINVAL;
708   if (pobservable == NULL)
709     return MU_ERR_OUT_PTR_NULL;
710 
711   if (mbox->observable == NULL)
712     {
713       int status = mu_observable_create (&mbox->observable, mbox);
714       if (status != 0)
715 	return status;
716     }
717   *pobservable = mbox->observable;
718   return 0;
719 }
720 
721 int
mu_mailbox_set_property(mu_mailbox_t mbox,mu_property_t property)722 mu_mailbox_set_property (mu_mailbox_t mbox, mu_property_t property)
723 {
724   if (mbox == NULL)
725     return EINVAL;
726   if (mbox->property)
727     mu_property_unref (mbox->property);
728   mbox->property = property;
729   mu_property_ref (property);
730   return 0;
731 }
732 
733 int
mu_mailbox_get_property(mu_mailbox_t mbox,mu_property_t * pproperty)734 mu_mailbox_get_property (mu_mailbox_t mbox, mu_property_t *pproperty)
735 {
736   if (mbox == NULL)
737     return EINVAL;
738   if (pproperty == NULL)
739     return MU_ERR_OUT_PTR_NULL;
740 
741   if (mbox->property == NULL)
742     {
743       int status;
744 
745       if (mbox->_get_property)
746 	status = mbox->_get_property (mbox, &mbox->property);
747       else
748 	status = mu_property_create_init (&mbox->property,
749 					  mu_assoc_property_init, NULL);
750       if (status != 0)
751 	return status;
752     }
753   *pproperty = mbox->property;
754   return 0;
755 }
756 
757 int
mu_mailbox_get_url(mu_mailbox_t mbox,mu_url_t * purl)758 mu_mailbox_get_url (mu_mailbox_t mbox, mu_url_t *purl)
759 {
760   if (mbox == NULL)
761     return EINVAL;
762   if (purl == NULL)
763     return MU_ERR_OUT_PTR_NULL;
764   *purl = mbox->url;
765   return 0;
766 }
767 
768 int
mu_mailbox_get_folder(mu_mailbox_t mbox,mu_folder_t * pfolder)769 mu_mailbox_get_folder (mu_mailbox_t mbox, mu_folder_t *pfolder)
770 {
771   if (mbox == NULL)
772     return EINVAL;
773   if (pfolder == NULL)
774     return MU_ERR_OUT_PTR_NULL;
775   *pfolder = mbox->folder;
776   return 0;
777 }
778 
779 int
mu_mailbox_set_folder(mu_mailbox_t mbox,mu_folder_t folder)780 mu_mailbox_set_folder (mu_mailbox_t mbox, mu_folder_t folder)
781 {
782   if (mbox == NULL)
783     return EINVAL;
784   mbox->folder = folder;
785   return 0;
786 }
787 
788 int
mu_mailbox_lock(mu_mailbox_t mbox)789 mu_mailbox_lock (mu_mailbox_t mbox)
790 {
791   mu_locker_t lock = NULL;
792   mu_mailbox_get_locker (mbox, &lock);
793   return mu_locker_lock (lock);
794 }
795 
796 int
mu_mailbox_unlock(mu_mailbox_t mbox)797 mu_mailbox_unlock (mu_mailbox_t mbox)
798 {
799   mu_locker_t lock = NULL;
800   mu_mailbox_get_locker (mbox, &lock);
801   return mu_locker_unlock (lock);
802 }
803 
804 int
mu_mailbox_get_uidls(mu_mailbox_t mbox,mu_list_t * plist)805 mu_mailbox_get_uidls (mu_mailbox_t mbox, mu_list_t *plist)
806 {
807   mu_list_t list;
808   int status;
809 
810   if (mbox == NULL)
811     return EINVAL;
812   if (plist == NULL)
813     return MU_ERR_OUT_PTR_NULL;
814   status = mu_list_create (&list);
815   if (status)
816     return status;
817   mu_list_set_destroy_item (list, mu_list_free_item);
818   if (mbox->_get_uidls)
819     status = mbox->_get_uidls (mbox, list);
820   else
821     {
822       size_t i, total;
823 
824       status = mu_mailbox_messages_count (mbox, &total);
825       if (status)
826 	return status;
827       for (i = 1; i <= total; i++)
828 	{
829 	  mu_message_t msg = NULL;
830 	  char buf[MU_UIDL_BUFFER_SIZE];
831 	  size_t n;
832 	  struct mu_uidl *uidl;
833 
834 	  status = mu_mailbox_get_message (mbox, i, &msg);
835 	  if (status)
836 	    break;
837 	  status = mu_message_get_uidl (msg, buf, sizeof (buf), &n);
838 	  if (status)
839 	    break;
840 	  uidl = malloc (sizeof (uidl[0]));
841 	  if (!uidl)
842 	    {
843 	      status = ENOMEM;
844 	      break;
845 	    }
846 	  uidl->msgno = i;
847 	  strncpy (uidl->uidl, buf, MU_UIDL_BUFFER_SIZE);
848 	  status = mu_list_append (list, uidl);
849 	  if (status)
850 	    {
851 	      free (uidl);
852 	      break;
853 	    }
854 	}
855     }
856   *plist = list;
857   return status;
858 }
859 
860 
861 /* Auxiliary function. Performs binary search for a message with the
862    given UID number */
863 static int
_uid_bsearch(mu_mailbox_t mbox,size_t start,size_t stop,size_t uid,size_t * msgno)864 _uid_bsearch (mu_mailbox_t mbox, size_t start, size_t stop, size_t uid,
865 	      size_t *msgno)
866 {
867   mu_message_t mid_msg = NULL;
868   size_t num = 0, middle;
869   int rc;
870 
871   middle = (start + stop) / 2;
872   rc = mu_mailbox_get_message (mbox, middle, &mid_msg);
873   if (rc)
874     return rc;
875   rc = mu_message_get_uid (mid_msg, &num);
876   if (rc)
877     return rc;
878 
879   if (num == uid)
880     {
881       *msgno = middle;
882       return 0;
883     }
884 
885   if (start >= stop)
886     return MU_ERR_NOENT;
887 
888   if (num > uid)
889     return _uid_bsearch (mbox, start, middle - 1, uid, msgno);
890   else /*if (num < seqno)*/
891     return _uid_bsearch (mbox, middle + 1, stop, uid, msgno);
892 }
893 
894 static int
_search_message_uid(mu_mailbox_t mbox,size_t uid,size_t * result)895 _search_message_uid (mu_mailbox_t mbox, size_t uid, size_t *result)
896 {
897   int rc;
898   size_t num, count;
899   mu_message_t msg;
900 
901   rc = mu_mailbox_get_message (mbox, 1, &msg);
902   if (rc)
903     return rc;
904   rc = mu_message_get_uid (msg, &num);
905   if (rc)
906     return rc;
907   if (uid < num)
908     return MU_ERR_NOENT;
909   else if (uid == num)
910     {
911       *result = 1;
912       return 0;
913     }
914 
915   rc = mu_mailbox_messages_count (mbox, &count);
916   if (rc)
917     return rc;
918   rc = mu_mailbox_get_message (mbox, count, &msg);
919   if (rc)
920     return rc;
921   rc = mu_message_get_uid (msg, &num);
922   if (rc)
923     return rc;
924 
925   if (uid > num)
926     return MU_ERR_NOENT;
927   else if (uid == num)
928     {
929       *result = count;
930       return 0;
931     }
932   return _uid_bsearch (mbox, 1, count, uid, result);
933 }
934 
935 /* Translate message UIDs to message numbers and vice versa. */
936 int
mu_mailbox_translate(mu_mailbox_t mbox,int cmd,size_t from,size_t * to)937 mu_mailbox_translate (mu_mailbox_t mbox, int cmd, size_t from, size_t *to)
938 {
939   int rc = ENOSYS;
940   mu_message_t msg;
941 
942   if (mbox == NULL)
943     return EINVAL;
944   if (to == NULL)
945     return MU_ERR_OUT_PTR_NULL;
946   if (mbox->flags & MU_STREAM_QACCESS)
947     return MU_ERR_BADOP;
948 
949   if (mbox->_translate)
950     rc = mbox->_translate (mbox, cmd, from, to);
951   if (rc == ENOSYS)
952     {
953       switch (cmd)
954 	{
955 	case MU_MAILBOX_UID_TO_MSGNO:
956 	  rc = _search_message_uid (mbox, from, to);
957 	  break;
958 
959 	case MU_MAILBOX_MSGNO_TO_UID:
960 	  rc = mu_mailbox_get_message (mbox, from, &msg);
961 	  if (rc)
962 	    return rc;
963 	  rc = mu_message_get_uid (msg, to);
964 	  break;
965 
966 	default:
967 	  break;
968 	}
969     }
970   return rc;
971 }
972 
973 int
mu_mailbox_access_time(mu_mailbox_t mbox,time_t * return_time)974 mu_mailbox_access_time (mu_mailbox_t mbox, time_t *return_time)
975 {
976   _MBOX_CHECK_Q (mbox, _get_atime);
977   if (!return_time)
978     return MU_ERR_OUT_PTR_NULL;
979   return mbox->_get_atime (mbox, return_time);
980 }
981 
982