1 /*
2 ** Copyright 2003 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #if HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <ctype.h>
14 #include <fcntl.h>
15 #if HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #if HAVE_UTIME_H
19 #include <utime.h>
20 #endif
21 #if TIME_WITH_SYS_TIME
22 #include <sys/time.h>
23 #include <time.h>
24 #else
25 #if HAVE_SYS_TIME_H
26 #include <sys/time.h>
27 #else
28 #include <time.h>
29 #endif
30 #endif
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #include "config.h"
36 #include "maildirinfo.h"
37
38 #include "maildirmisc.h"
39 #include "maildirnewshared.h"
40 #include <courier-unicode.h>
41
maildir_info_destroy(struct maildir_info * info)42 void maildir_info_destroy(struct maildir_info *info)
43 {
44 if (info->homedir)
45 free(info->homedir);
46 if (info->maildir)
47 free(info->maildir);
48 if (info->owner)
49 free(info->owner);
50 info->homedir=NULL;
51 info->maildir=NULL;
52 info->owner=NULL;
53 }
54
55 struct imap_find_shared {
56 struct maildir_info *info;
57 const char *path;
58 size_t path_l;
59 char *homedir;
60 char *maildir;
61 };
62
imap_find_cb(struct maildir_newshared_enum_cb * cb)63 static int imap_find_cb(struct maildir_newshared_enum_cb *cb)
64 {
65 struct imap_find_shared *ifs=(struct imap_find_shared *)cb->cb_arg;
66
67 if (cb->homedir)
68 {
69 ifs->homedir=strdup(cb->homedir);
70 if (!ifs->homedir)
71 return -1;
72 }
73
74 if (cb->maildir)
75 {
76 ifs->maildir=strdup(cb->maildir);
77
78 if (!ifs->maildir)
79 {
80 if (cb->homedir)
81 {
82 free(ifs->homedir);
83 ifs->homedir=NULL;
84 }
85 return -1;
86 }
87 }
88
89 return 0;
90 }
91
maildir_info_imap_find(struct maildir_info * info,const char * path,const char * myId)92 int maildir_info_imap_find(struct maildir_info *info, const char *path,
93 const char *myId)
94 {
95 const char *p;
96 struct imap_find_shared ifs;
97 const char *indexfile;
98 char *indexfile_cpy;
99 struct maildir_shindex_cache *curcache;
100 const char *subhierarchy;
101
102 info->homedir=NULL;
103 info->maildir=NULL;
104 info->owner=NULL;
105
106 if (strchr(path, '/'))
107 {
108 errno=EINVAL;
109 return -1;
110 }
111
112 for (p=path; *p; p++)
113 if (*p == '.' && p[1] == '.')
114 {
115 errno=EINVAL;
116 return -1;
117 }
118
119 if (strncmp(path, SHARED, sizeof(SHARED)-1) == 0)
120 {
121 path += sizeof(SHARED)-1;
122
123 info->homedir=strdup(".");
124 if (!info->homedir)
125 return -1;
126
127 info->mailbox_type=MAILBOXTYPE_OLDSHARED;
128 info->owner=strdup("anonymous");
129
130 if (!info->owner)
131 {
132 maildir_info_destroy(info);
133 return -1;
134 }
135
136 /* We need to specialcase "shared" and "shared.name".
137 ** maildir_shareddir will return NULL for these cases, because
138 ** it will insist on "name.folder", but we need to return a
139 ** non NULL value to indicate that this is a valid hierarchy
140 ** name. We return a special value of an empty string, which
141 ** is checked for in situations where a valid folder is
142 ** required.
143 */
144
145 if (*path && *path != '.')
146 {
147 maildir_info_destroy(info);
148 errno=EINVAL;
149 return -1;
150 }
151
152 return 0;
153 }
154
155 if (strncasecmp(path, INBOX, sizeof(INBOX)-1) == 0)
156 {
157 switch (path[sizeof(INBOX)-1]) {
158 case 0:
159 case '.':
160 break;
161 default:
162 errno=EINVAL;
163 return -1;
164 }
165
166 info->homedir=strdup(".");
167
168 if (!info->homedir)
169 return -1;
170
171 info->maildir=strdup(path);
172 if (!info->maildir)
173 {
174 maildir_info_destroy(info);
175 return -1;
176 }
177
178 info->owner=malloc(strlen(myId)+sizeof("user="));
179
180 if (!info->owner)
181 {
182 maildir_info_destroy(info);
183 return -1;
184 }
185
186 info->mailbox_type=MAILBOXTYPE_INBOX;
187 strcat(strcpy(info->owner, "user="), myId);
188 return 0;
189 }
190
191 if (strncmp(path, NEWSHARED,
192 sizeof(NEWSHARED)-1) != 0)
193 {
194 errno=EINVAL;
195 return -1;
196 }
197
198 ifs.info=info;
199 ifs.path=path+sizeof(NEWSHARED)-1;
200
201 info->mailbox_type=MAILBOXTYPE_NEWSHARED;
202 info->homedir=NULL;
203 info->maildir=NULL;
204 info->owner=strdup("vendor=courier.internal");
205
206 if (!info->owner)
207 return -1;
208
209 ifs.homedir=NULL;
210 ifs.maildir=NULL;
211
212 indexfile=NULL;
213 indexfile_cpy=NULL;
214 curcache=NULL;
215 subhierarchy=NULL;
216
217 while (*ifs.path)
218 {
219 int rc, eof;
220 size_t i;
221
222 curcache=maildir_shared_cache_read(curcache, indexfile,
223 subhierarchy);
224
225 if (indexfile_cpy)
226 {
227 free(indexfile_cpy);
228 indexfile_cpy=NULL;
229 }
230
231 if (!curcache)
232 break;
233
234 p=strchr(ifs.path, '.');
235
236 if (p)
237 ifs.path_l=p-ifs.path;
238 else
239 ifs.path_l=strlen(ifs.path);
240
241
242 if (ifs.homedir)
243 free(ifs.homedir);
244 if (ifs.maildir)
245 free(ifs.maildir);
246
247 ifs.homedir=NULL;
248 ifs.maildir=NULL;
249
250 for (i=0; i < curcache->nrecords; i++)
251 {
252 char *n=maildir_info_imapmunge(curcache->
253 records[i].name);
254
255 if (n == NULL)
256 {
257 i=curcache->nrecords;
258 break;
259 }
260
261 if (strlen(n) == ifs.path_l &&
262 strncmp(n, ifs.path, ifs.path_l) == 0)
263 {
264 free(n);
265 break;
266 }
267 free(n);
268 }
269
270 if (i >= curcache->nrecords)
271 break;
272
273 curcache->indexfile.startingpos=
274 curcache->records[i].offset;
275 rc=maildir_newshared_nextAt(&curcache->indexfile,
276 &eof,
277 imap_find_cb, &ifs);
278
279 if (rc || eof)
280 {
281 fprintf(stderr, "ERR: Internal error -"
282 " maildir_newshared_nextAt: %s\n",
283 strerror(errno));
284 fflush(stderr);
285 break;
286 }
287
288 if (!ifs.homedir && !ifs.maildir)
289 break;
290
291 if (!ifs.homedir)
292 {
293 indexfile=indexfile_cpy=ifs.maildir;
294 ifs.maildir=NULL;
295 subhierarchy=curcache->records[i].name;
296
297 ifs.path += ifs.path_l;
298 if (*ifs.path)
299 ++ifs.path;
300 continue;
301 }
302
303 info->homedir=maildir_location(ifs.homedir,
304 ifs.maildir);
305
306 free(ifs.homedir);
307 free(ifs.maildir);
308
309 free(info->owner);
310
311 if (!info->homedir)
312 {
313 info->maildir=NULL;
314 info->owner=NULL;
315 return -1;
316 }
317
318 if (!subhierarchy || !*subhierarchy)
319 {
320 info->owner=strdup("vendor=courier.internal");
321 if (!info->owner)
322 {
323 free(info->homedir);
324 info->homedir=NULL;
325 info->maildir=NULL;
326 return -1;
327 }
328 }
329 else
330 {
331 char *owner_utf8;
332
333 info->owner=malloc(strlen(subhierarchy)
334 +sizeof("user="));
335
336 if (!info->owner)
337 {
338 free(info->homedir);
339 info->homedir=NULL;
340 info->maildir=NULL;
341 info->owner=NULL;
342 return -1;
343 }
344 strcpy(info->owner, "user=");
345 strcat(info->owner, subhierarchy);
346
347 /*
348 ** The folder path is in modified-UTF7. The owner is
349 ** obtained from shared hierarchy, but in ACL2 the
350 ** identifiers are in UTF8.
351 */
352
353 owner_utf8=
354 unicode_convert_tobuf(info->owner,
355 unicode_x_imap_modutf7,
356 "utf-8", NULL);
357
358 if (!owner_utf8)
359 {
360 free(info->homedir);
361 info->homedir=NULL;
362 return (0);
363 }
364
365 free(info->owner);
366 info->owner=owner_utf8;
367 }
368
369 ifs.path += ifs.path_l;
370
371 info->maildir=malloc(strlen(INBOX)+1+strlen(ifs.path));
372 if (!info->maildir)
373 {
374 free(info->owner);
375 free(info->homedir);
376 info->owner=NULL;
377 info->homedir=NULL;
378 return -1;
379 }
380 strcat(strcpy(info->maildir, INBOX), ifs.path);
381
382 if (maildir_info_suppress(info->homedir))
383 {
384
385 free(info->homedir);
386 free(info->maildir);
387 info->homedir=NULL;
388 info->maildir=NULL;
389 info->mailbox_type=MAILBOXTYPE_IGNORE;
390 free(info->owner);
391 info->owner=NULL;
392 info->owner=strdup("vendor=courier.internal");
393 if (!info->owner)
394 {
395 return -1;
396 }
397 }
398
399 return 0;
400 }
401
402 if (indexfile_cpy)
403 free(indexfile_cpy);
404 if (ifs.homedir)
405 {
406 free(ifs.homedir);
407 ifs.homedir=NULL;
408 }
409
410 if (ifs.maildir)
411 {
412 free(ifs.maildir);
413 ifs.maildir=NULL;
414 }
415 return 0;
416 }
417
418 /***************************************************************************/
419
420 /*
421 ** Maildir folders are named in IMAP-compatible modified-UTF8 encoding,
422 ** with periods as hierarchy delimiters. One exception: ".", "/", "~", and
423 ** ":" are also encoded using modified-UTF8, making folder names that contain
424 ** those characters incompatible with IMAP.
425 **
426 ** smap_to_utf8 crates a modified-UTF8-encoded folder name from a vector
427 ** of UTF-8 words.
428 **
429 ** input: "INBOX" "a" "b"
430 ** output: "INBOX.a.b"
431 **
432 */
433
smap_to_utf8(char ** ptr)434 static char *smap_to_utf8(char **ptr)
435 {
436 char *f=NULL;
437 char *n;
438
439 while ((n=*ptr++) != NULL && *n)
440 {
441 char *p=unicode_convert_tobuf(n, "utf-8",
442 unicode_x_smap_modutf8,
443 NULL);
444
445 if (!p)
446 {
447 if (f)
448 free(f);
449 return NULL;
450 }
451
452 n= f ? realloc(f, strlen(f)+strlen(p)+2):malloc(strlen(p)+1);
453
454 if (!n)
455 {
456 free(p);
457 if (f)
458 free(f);
459 return NULL;
460 }
461 if (f)
462 f=strcat(strcat(n, "."), p);
463 else
464 f=strcpy(n, p);
465 free(p);
466 }
467
468 if (!f)
469 errno=EINVAL;
470 return f;
471 }
472
473 /*
474 ** Convert modified-UTF8 folder name into an array of UTF-8 words, that
475 ** represent a folder name.
476 */
477
maildir_smapfn_fromutf8(const char * modutf8)478 char **maildir_smapfn_fromutf8(const char *modutf8)
479 {
480 char *p=strdup(modutf8), *q;
481 size_t n, i;
482 char **fn;
483
484 if (!p)
485 return NULL;
486
487 n=1;
488 for (i=0; p[i]; i++)
489 if (p[i] == '.' && p[i+1] && p[i+1] != '.')
490 {
491 ++n;
492 }
493
494 fn=malloc((n+1)*sizeof(char *));
495
496 if (!fn)
497 {
498 free(p);
499 return NULL;
500 }
501
502 n=0;
503 q=p;
504 do
505 {
506 for (i=0; q[i]; i++)
507 if (q[i] == '.' && q[i+1] && q[i+1] != '.')
508 {
509 q[i++]=0;
510 break;
511 }
512
513 fn[n]=unicode_convert_tobuf(q,
514 unicode_x_smap_modutf8,
515 "utf-8", NULL);
516 q += i;
517
518 if (!fn[n])
519 {
520 while (n)
521 free(fn[--n]);
522 free(fn);
523 free(p);
524 return NULL;
525 }
526 n++;
527 } while (*q);
528 fn[n]=0;
529 free(p);
530 return fn;
531 }
532
maildir_smapfn_free(char ** fn)533 void maildir_smapfn_free(char **fn)
534 {
535 size_t i;
536
537 for (i=0; fn[i]; i++)
538 free(fn[i]);
539 free(fn);
540 }
541
542 struct get_existing_folder_info {
543 char **fn;
544 char *pathname;
545 };
546
get_existing_callback(const char * f,void * vp)547 static void get_existing_callback(const char *f, void *vp)
548 {
549 char **fn;
550
551 struct get_existing_folder_info *gefi=
552 (struct get_existing_folder_info *)vp;
553 size_t i;
554 size_t j;
555
556 if (gefi->pathname)
557 return;
558
559 fn=maildir_smapfn_fromutf8(f);
560 if (!fn)
561 {
562 perror(f);
563 return;
564 }
565
566 for (i=0; gefi->fn[i]; i++)
567 if (fn[i] == NULL || strcmp(fn[i], gefi->fn[i]))
568 {
569 maildir_smapfn_free(fn);
570 return;
571 }
572
573 maildir_smapfn_free(fn);
574
575 for (j=0; i && f[j]; j++)
576 if (f[j] == '.' && f[j+1] && f[j+1] != '.')
577 {
578 --i;
579 if (i == 0)
580 break;
581 }
582
583 gefi->pathname=malloc(j+1);
584
585 if (!gefi->pathname)
586 {
587 perror("malloc");
588 return;
589 }
590
591 memcpy(gefi->pathname, f, j);
592 gefi->pathname[j]=0;
593 }
594
smap_path(const char * homedir,char ** words)595 static char *smap_path(const char *homedir,
596 char **words) /* words[0] better be INBOX! */
597 {
598 struct get_existing_folder_info gefi;
599 char *n, *p;
600 struct stat stat_buf;
601
602 if ((n=smap_to_utf8(words)) == NULL)
603 return NULL;
604
605 p=maildir_name2dir(homedir, n);
606
607 if (!p)
608 {
609 free(n);
610 return NULL;
611 }
612
613 if (stat(p, &stat_buf) == 0)
614 {
615 free(p);
616 return n;
617 }
618
619 gefi.fn=words;
620 gefi.pathname=NULL;
621
622 maildir_list(homedir ? homedir:".",
623 &get_existing_callback, &gefi);
624
625 if (gefi.pathname)
626 {
627 free(n);
628 free(p);
629
630 return gefi.pathname;
631 }
632
633 free(p);
634 return n;
635 }
636
maildir_info_smap_find(struct maildir_info * info,char ** folder,const char * myId)637 int maildir_info_smap_find(struct maildir_info *info, char **folder,
638 const char *myId)
639 {
640 char *p;
641 size_t n;
642 const char *indexfile;
643 struct maildir_shindex_cache *curcache;
644 const char *subhierarchy;
645 struct imap_find_shared ifs;
646 int rc, eof;
647 char *indexfile_cpy=NULL;
648
649 info->homedir=NULL;
650 info->maildir=NULL;
651 info->owner=NULL;
652 info->mailbox_type=MAILBOXTYPE_IGNORE;
653
654 if (folder[0] == NULL)
655 {
656 errno=EINVAL;
657 return -1;
658 }
659
660 if (strcmp(folder[0], PUBLIC))
661 {
662 if (strcmp(folder[0], INBOX))
663 {
664 errno=EINVAL;
665 return -1;
666 }
667
668 info->maildir=smap_path(NULL, folder);
669
670 if (info->maildir == NULL)
671 return -1;
672 info->homedir=strdup(".");
673 if (!info->homedir)
674 {
675 maildir_info_destroy(info);
676 return -1;
677 }
678
679 info->mailbox_type=MAILBOXTYPE_INBOX;
680
681 info->owner=malloc(strlen(myId)+sizeof("user="));
682
683 if (!info->owner)
684 {
685 maildir_info_destroy(info);
686 return -1;
687 }
688
689 strcat(strcpy(info->owner, "user="), myId);
690
691 return 0;
692 }
693
694 indexfile=NULL;
695 curcache=NULL;
696 subhierarchy=NULL;
697 n=1;
698 ifs.homedir=NULL;
699 ifs.maildir=NULL;
700
701 while (folder[n])
702 {
703 size_t i;
704
705 curcache=maildir_shared_cache_read(curcache, indexfile,
706 subhierarchy);
707
708 if (!curcache)
709 break;
710
711 for (i=0; i<curcache->nrecords; i++)
712 if (strcmp(curcache->records[i].name,
713 folder[n]) == 0)
714 break;
715
716 if (i >= curcache->nrecords)
717 break;
718 curcache->indexfile.startingpos=
719 curcache->records[i].offset;
720
721 if (ifs.homedir)
722 free(ifs.homedir);
723 if (ifs.maildir)
724 free(ifs.maildir);
725 ifs.homedir=NULL;
726 ifs.maildir=NULL;
727
728 rc=maildir_newshared_nextAt(&curcache->indexfile,
729 &eof,
730 imap_find_cb, &ifs);
731
732 if (rc || eof)
733 {
734 fprintf(stderr, "ERR: Internal error -"
735 " maildir_newshared_nextAt: %s\n",
736 strerror(errno));
737 fflush(stderr);
738 break;
739 }
740
741 if (!ifs.homedir && !ifs.maildir)
742 break;
743
744 if (!ifs.homedir)
745 {
746 if (indexfile_cpy)
747 free(indexfile_cpy);
748 indexfile=indexfile_cpy=ifs.maildir;
749 ifs.maildir=NULL;
750 subhierarchy=curcache->records[i].name;
751 ++n;
752 continue;
753 }
754
755 if (indexfile_cpy)
756 free(indexfile_cpy);
757 info->homedir=maildir_location(ifs.homedir,
758 ifs.maildir);
759 free(ifs.homedir);
760 free(ifs.maildir);
761
762 info->maildir=NULL;
763
764 if (maildir_info_suppress(info->homedir))
765 {
766
767 free(info->homedir);
768 info->homedir=NULL;
769 info->maildir=NULL;
770 info->mailbox_type=MAILBOXTYPE_IGNORE;
771 info->owner=NULL;
772 info->owner=strdup("vendor=courier.internal");
773 if (!info->owner)
774 {
775 maildir_info_destroy(info);
776 return -1;
777 }
778
779 return 0;
780 }
781
782
783 if (!subhierarchy || !*subhierarchy)
784 {
785 info->owner=strdup("vendor=courier.internal");
786 if (!info->owner)
787 {
788 maildir_info_destroy(info);
789 return -1;
790 }
791 }
792 else
793 {
794 info->owner=malloc(strlen(subhierarchy)
795 +sizeof("user="));
796
797 if (!info->owner)
798 {
799 free(info->homedir);
800 info->homedir=NULL;
801 info->maildir=NULL;
802 return -1;
803 }
804 strcpy(info->owner, "user=");
805 strcat(info->owner, subhierarchy);
806 }
807
808 p=folder[n];
809 folder[n]=INBOX;
810 info->maildir=smap_path(info->homedir, folder+n);
811 folder[n]=p;
812
813 if (!info->maildir)
814 {
815 free(info->homedir);
816 free(info->owner);
817 info->homedir=NULL;
818 info->maildir=NULL;
819 info->owner=NULL;
820 return -1;
821 }
822
823 info->mailbox_type=MAILBOXTYPE_NEWSHARED;
824 return 0;
825 }
826
827 if (ifs.homedir)
828 free(ifs.homedir);
829 if (ifs.maildir)
830 free(ifs.maildir);
831 if (indexfile_cpy)
832 free(indexfile_cpy);
833
834 if (folder[n] == 0)
835 {
836 info->mailbox_type=MAILBOXTYPE_NEWSHARED;
837 info->owner=strdup("vendor=courier.internal");
838 if (!info->owner)
839 {
840 maildir_info_destroy(info);
841 return -1;
842 }
843
844 /* Intermediate shared namespce */
845 return 0;
846 }
847
848 return -1;
849 }
850
851 static int complex_flag;
852
maildir_info_munge_complex(int f)853 void maildir_info_munge_complex(int f)
854 {
855 complex_flag=f;
856 }
857
858 static size_t munge_complex(const char *, char *);
859
maildir_info_imapmunge(const char * name)860 char *maildir_info_imapmunge(const char *name)
861 {
862 char *n=unicode_convert_tobuf(name, "utf-8",
863 unicode_x_imap_modutf7, NULL);
864 char *p;
865 size_t cnt;
866
867 if (!n)
868 return NULL;
869
870 if (!complex_flag)
871 {
872 for (p=n; *p; p++)
873 {
874 if (*p == '.' || *p == '/')
875 *p=' ';
876 }
877
878 return n;
879 }
880
881 cnt=munge_complex(n, NULL);
882 p=malloc(cnt);
883 if (!p)
884 {
885 free(n);
886 return NULL;
887 }
888
889 munge_complex(n, p);
890
891 free(n);
892 return p;
893 }
894
munge_complex(const char * orig,char * n)895 static size_t munge_complex(const char *orig, char *n)
896 {
897 size_t cnt=0;
898
899 while (*orig)
900 {
901 switch (*orig) {
902 case '.':
903 if (n)
904 {
905 *n++='\\';
906 *n++=':';
907 }
908 cnt += 2;
909 break;
910 case '/':
911 if (n)
912 {
913 *n++='\\';
914 *n++=';';
915 }
916 cnt += 2;
917 break;
918 case '\\':
919 if (n)
920 {
921 *n++='\\';
922 *n++='\\';
923 }
924 cnt += 2;
925 break;
926 default:
927 if (n) *n++ = *orig;
928 ++cnt;
929 }
930 ++orig;
931 }
932
933 if (n) *n=0;
934 return cnt+1;
935 }
936