1 /*
2 ** Copyright 2003-2007 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
15 #include "maildir/config.h"
16 #include "maildir/maildircreate.h"
17 #include "maildir/maildirmisc.h"
18 #include "maildir/maildirwatch.h"
19 #include "numlib/numlib.h"
20
21 #if HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
24 #include "maildirkeywords.h"
25 #if HAVE_DIRENT_H
26 #include <dirent.h>
27 #define NAMLEN(dirent) strlen((dirent)->d_name)
28 #else
29 #define dirent direct
30 #define NAMLEN(dirent) (dirent)->d_namlen
31 #if HAVE_SYS_NDIR_H
32 #include <sys/ndir.h>
33 #endif
34 #if HAVE_SYS_DIR_H
35 #include <sys/dir.h>
36 #endif
37 #if HAVE_NDIR_H
38 #include <ndir.h>
39 #endif
40 #endif
41
42
43 int libmail_kwEnabled=1;
44
45 struct keywordUpdateInfo {
46 size_t highestN;
47 size_t highestT;
48 size_t totalCnt;
49 int foundNewest;
50 };
51
52 static void scan_updates(const char *dir,
53 time_t t,
54 time_t tn,
55 struct maildir_kwReadInfo *info,
56 struct keywordUpdateInfo **updateInfo);
57
58 static void read_updates(const char *dir,
59 struct maildir_kwReadInfo *info,
60 struct keywordUpdateInfo *updateInfo);
61
62 static void purge_old_updates(const char *dir,
63 time_t tn,
64 struct maildir_kwReadInfo *info,
65 struct keywordUpdateInfo *updateInfo);
66
67 static int save_updated_keywords(const char *maildir, const char *kwname,
68 struct maildir_kwReadInfo *info,
69 struct keywordUpdateInfo *updateInfo);
70
71 static void doReadKeywords2(const char *maildir, const char *dir,
72 struct maildir_kwReadInfo *rki);
73
74
75 struct readLine {
76 char *lineBuf;
77 size_t lineBufSize;
78 int errflag;
79 };
80
rl_init(struct readLine * p)81 static void rl_init(struct readLine *p)
82 {
83 p->lineBuf=NULL;
84 p->lineBufSize=0;
85 p->errflag=0;
86 }
87
rl_free(struct readLine * p)88 static void rl_free(struct readLine *p)
89 {
90 if (p->lineBuf)
91 free(p->lineBuf);
92 }
93
rl_read(struct readLine * p,FILE * f)94 static char *rl_read(struct readLine *p, FILE *f)
95 {
96 size_t n=0;
97 int ch;
98
99 for (;;)
100 {
101 if (n >= p->lineBufSize)
102 {
103 size_t o= n + 1024;
104 char *b= p->lineBuf ? realloc(p->lineBuf, o):malloc(o);
105
106 if (!b)
107 {
108 p->errflag=1;
109 return NULL;
110 }
111
112 p->lineBuf=b;
113 p->lineBufSize=o;
114 }
115
116 if ((ch=getc(f)) == EOF || ch == '\n')
117 break;
118 p->lineBuf[n++]=(char)ch;
119 }
120
121 p->lineBuf[n]=0;
122
123 return n == 0 && ch == EOF ? NULL:p->lineBuf;
124 }
125
maildir_kwRead(const char * maildir,struct maildir_kwReadInfo * rki)126 int maildir_kwRead(const char *maildir,
127 struct maildir_kwReadInfo *rki)
128 {
129 char *p=malloc(strlen(maildir)+sizeof("/" KEYWORDDIR));
130
131 if (!p)
132 return -1;
133
134 strcat(strcpy(p, maildir), "/" KEYWORDDIR);
135
136 rki->errorOccured=0;
137 doReadKeywords2(maildir, p, rki);
138 free(p);
139
140 return rki->errorOccured;
141 }
142
doReadKeywords2(const char * maildir,const char * dir,struct maildir_kwReadInfo * rki)143 static void doReadKeywords2(const char *maildir, const char *dir,
144 struct maildir_kwReadInfo *rki)
145 {
146 FILE *fp;
147 char *kwname=malloc(strlen(dir)+sizeof("/:list"));
148 struct keywordUpdateInfo *updateInfo;
149
150 time_t t=time(NULL);
151 time_t tn=t/300;
152
153 rki->updateNeeded=0;
154 rki->tryagain=0;
155
156 if (!kwname)
157 {
158 rki->errorOccured= -1;
159 return;
160 }
161
162 strcat(strcpy(kwname, dir), "/:list");
163
164 fp=fopen(kwname, "r");
165
166 if (!fp) /* Maybe the keyword directory needs creating? */
167 {
168 struct stat stat_buf;
169
170 mkdir(dir, 0700);
171
172 /* Give it same mode as the maildir */
173
174 if (stat(maildir, &stat_buf) == 0)
175 chmod(dir, stat_buf.st_mode & 0777);
176 }
177
178 /*
179 ** If keywords are disabled, we still need to create the keyword
180 ** directory, otherwise FAM-based IDLE will not work.
181 */
182
183 if (!libmail_kwEnabled)
184 {
185 if (fp)
186 fclose(fp);
187 free(kwname);
188 return;
189 }
190
191 if (fp && maildir_kwImport(fp, rki))
192 rki->updateNeeded=1;
193 if (fp)
194 fclose(fp);
195
196 updateInfo=malloc(sizeof(struct keywordUpdateInfo)
197 *( 1+(*rki->getMessageCount)(rki->voidarg)));
198
199 if (!updateInfo)
200 {
201 free(kwname);
202 return;
203 }
204
205 scan_updates(dir, t, tn, rki, &updateInfo);
206 read_updates(dir, rki, updateInfo);
207
208 if (!rki->tryagain && !rki->errorOccured)
209 {
210 if (save_updated_keywords(maildir, kwname, rki, updateInfo)
211 == 0 && !rki->errorOccured)
212 purge_old_updates(dir, tn, rki, updateInfo);
213 }
214
215 free(updateInfo);
216 free(kwname);
217 }
218
maildir_kwImport(FILE * fp,struct maildir_kwReadInfo * rki)219 int maildir_kwImport(FILE *fp, struct maildir_kwReadInfo *rki)
220 {
221 struct keywordIndex {
222 struct keywordIndex *next;
223 struct libmail_keywordEntry *kw;
224 } *firstKw=NULL, *lastKw=NULL, **index;
225 size_t numKw=0;
226 struct libmail_kwMessage *tmpMsg;
227 char *p;
228 int rc=0;
229
230 struct readLine rl;
231
232 if ((tmpMsg=libmail_kwmCreate()) == NULL)
233 {
234 rki->errorOccured=-1;
235 return 0;
236 }
237
238 rl_init(&rl);
239
240 while ((p=rl_read(&rl, fp)) != NULL && *p)
241 {
242 struct keywordIndex *ki=malloc(sizeof(*firstKw));
243
244 if (!ki)
245 {
246 rki->errorOccured=-1;
247 rl_free(&rl);
248 libmail_kwmDestroy(tmpMsg);
249 return 0;
250 }
251
252 if (lastKw)
253 lastKw->next=ki;
254 else
255 firstKw=ki;
256
257 lastKw=ki;
258 ki->next=NULL;
259 ++numKw;
260
261 ki->kw=libmail_kweFind((*rki->getKeywordHashtable)
262 (rki->voidarg), p, 1);
263 if (libmail_kwmSet(tmpMsg, ki->kw) < 0)
264 {
265 rki->errorOccured= -1;
266 break;
267 }
268 }
269
270 if (rki->errorOccured ||
271 (index=malloc(sizeof(*firstKw)*(numKw+1))) == NULL)
272 {
273 while ((lastKw=firstKw) != NULL)
274 {
275 firstKw=firstKw->next;
276 free(lastKw);
277 }
278 libmail_kwmDestroy(tmpMsg);
279 rki->errorOccured= -1;
280 rl_free(&rl);
281 return 0;
282 }
283
284 numKw=0;
285
286 while (firstKw)
287 {
288 index[numKw]=firstKw;
289 ++numKw;
290 firstKw=firstKw->next;
291 }
292 index[numKw]=0;
293
294 if (p)
295 while ((p=rl_read(&rl, fp)) != NULL)
296 {
297 char *q=strchr(p, ':');
298 struct libmail_kwMessage **i;
299 size_t l;
300 size_t n;
301
302 if (!q)
303 {
304 rc=1;
305 continue; /* Crap */
306 }
307
308 *q++=0;
309
310 i= (*rki->findMessageByFilename)(p, 0, &n,
311 rki->voidarg);
312
313 if (!i)
314 {
315 rc=1;
316 continue; /* Stale data */
317 }
318
319 if (*i) /* Already have it */
320 libmail_kwmDestroy(*i);
321 (*i)=NULL;
322
323 i=NULL;
324
325 while ((q=strtok(q, " ")) != NULL)
326 {
327 l=atoi(q);
328 q=NULL;
329
330 if (l < numKw)
331 {
332 if (!i)
333 i= (*rki->
334 findMessageByFilename)
335 (p, 1, &n,
336 rki->voidarg);
337 /* Autocreate it */
338
339 if (*i == NULL) /* ENOMEM */
340 {
341 rc= -1;
342 break;
343 }
344
345 if (libmail_kwmSet(*i, index[l]->kw)
346 < 0)
347 rki->errorOccured= -1;
348 }
349 }
350 }
351
352 rl_free(&rl);
353 for (numKw=0; index[numKw]; numKw++)
354 free(index[numKw]);
355
356 free(index);
357 libmail_kwmDestroy(tmpMsg);
358 return rc;
359 }
360
361 /* See the README */
362
scan_updates(const char * dir,time_t t,time_t tn,struct maildir_kwReadInfo * info,struct keywordUpdateInfo ** updateInfo)363 static void scan_updates(const char *dir,
364 time_t t,
365 time_t tn,
366 struct maildir_kwReadInfo *info,
367 struct keywordUpdateInfo **updateInfo)
368 {
369 DIR *dirp;
370 struct dirent *de;
371 unsigned long i;
372
373 size_t n= (*info->getMessageCount)(info->voidarg);
374
375 for (i=0; i<n; i++)
376 {
377 (*updateInfo)[i].highestN=0;
378 (*updateInfo)[i].highestT=0;
379 (*updateInfo)[i].totalCnt=0;
380 (*updateInfo)[i].foundNewest=0;
381 }
382
383 dirp=opendir(dir);
384 while (dirp && (de=readdir(dirp)) != NULL)
385 {
386 struct libmail_kwMessage **i;
387 unsigned long x=0;
388 char *p, *q;
389 size_t in;
390
391 if (de->d_name[0] == ':')
392 continue;
393
394 if (de->d_name[0] != '.')
395 i=(*info->findMessageByFilename)
396 (de->d_name, 0, &in, info->voidarg);
397
398 else if ((x=libmail_atotime_t(de->d_name+1)) == 0)
399 {
400 if (strncmp(de->d_name, ".tmp.", 5) == 0)
401 {
402 if ((x=libmail_atotime_t(de->d_name+5)) != 0
403 && x >= t - 120)
404 continue; /* New scratch file */
405
406
407 p=malloc(strlen(dir)+strlen(de->d_name)+2);
408
409 if (!p)
410 {
411 closedir(dirp);
412 info->errorOccured= -1;
413 return;
414 }
415
416 strcat(strcat(strcpy(p, dir), "/"),
417 de->d_name);
418 unlink(p);
419 free(p);
420 }
421 continue;
422 }
423 else
424 {
425 const char *p=strchr(de->d_name+1, '.');
426
427 if (!p)
428 continue;
429
430 i=(*info->findMessageByFilename)
431 (p+1, 0, &in, info->voidarg);
432 }
433
434 p=malloc(strlen(dir)+strlen(de->d_name)+2);
435
436 if (!p)
437 {
438 closedir(dirp);
439 info->errorOccured= -1;
440 return;
441 }
442
443 strcat(strcat(strcpy(p, dir), "/"), de->d_name);
444
445 if (!i)
446 {
447 struct stat stat_buf;
448
449 if (stat(p, &stat_buf) == 0 &&
450 stat_buf.st_mtime < t - 15 * 60)
451 unlink(p);
452 free(p);
453 continue;
454 }
455 free(p);
456
457
458 if (in >= n)
459 {
460 /* libmail_kwgReadMaildir autocrerate */
461
462 struct keywordUpdateInfo *u=
463 realloc(*updateInfo,
464 sizeof(**updateInfo) * (in+1));
465
466 if (!u)
467 {
468 closedir(dirp);
469 info->errorOccured= -1;
470 return;
471 }
472
473 *updateInfo=u;
474
475 while (n <= in)
476 {
477
478 (*updateInfo)[n].highestN=0;
479 (*updateInfo)[n].highestT=0;
480 (*updateInfo)[n].totalCnt=0;
481 (*updateInfo)[n].foundNewest=0;
482 ++n;
483 }
484 }
485
486
487 if (de->d_name[0] != '.')
488 {
489 x=tn+1;
490
491 (*updateInfo)[in].foundNewest=1;
492 }
493
494 ++(*updateInfo)[in].totalCnt;
495
496 if (x >= tn)
497 {
498 if (x > (*updateInfo)[in].highestT)
499 (*updateInfo)[in].highestT=x;
500 }
501 else if ((*updateInfo)[in].highestN < x)
502 {
503 if ((*updateInfo)[in].highestN > 0)
504 {
505 char b[NUMBUFSIZE];
506 char *r;
507
508 libmail_str_size_t((*updateInfo)[in].highestN, b);
509
510 r=de->d_name;
511 if (*r == '.')
512 r=strchr(r+1, '.')+1;
513
514 q=malloc(strlen(dir)+strlen(r)+
515 strlen(b)+4);
516
517 if (!q)
518 {
519 closedir(dirp);
520 info->errorOccured= -1;
521 return;
522 }
523
524 sprintf(q, "%s/.%s.%s", dir, b, r);
525 unlink(q);
526 free(q);
527 }
528
529 (*updateInfo)[in].highestN=x;
530 }
531 }
532
533 if (dirp)
534 closedir(dirp);
535 }
536
read_updates(const char * dir,struct maildir_kwReadInfo * info,struct keywordUpdateInfo * updateInfo)537 static void read_updates(const char *dir,
538 struct maildir_kwReadInfo *info,
539 struct keywordUpdateInfo *updateInfo)
540 {
541 unsigned long i;
542 size_t msgCnt= (*info->getMessageCount)(info->voidarg);
543 struct readLine rl;
544
545 struct libmail_kwHashtable *h=
546 (*info->getKeywordHashtable)(info->voidarg);
547
548 rl_init(&rl);
549 for (i=0; i<msgCnt; i++)
550 {
551 size_t n=updateInfo[i].highestN;
552 char *p, *q;
553 char b[NUMBUFSIZE];
554 FILE *fp;
555 struct libmail_kwMessage *km, **oldKm;
556 const char *fn;
557
558 if (n < updateInfo[i].highestT)
559 n=updateInfo[i].highestT;
560
561 if (n == 0)
562 continue;
563
564 libmail_str_size_t(n, b);
565
566 fn= (*info->getMessageFilename)(i, info->voidarg);
567
568 if (!fn)
569 continue; /* Shouldn't happen */
570
571 p=malloc(strlen(dir)+strlen(b)+strlen(fn)+4);
572
573 if (!p)
574 {
575 info->errorOccured= -1;
576 rl_free(&rl);
577 return;
578 }
579
580 if (updateInfo[i].foundNewest)
581 sprintf(p, "%s/%s", dir, fn);
582 else
583 sprintf(p, "%s/.%s.%s", dir, b, fn);
584
585
586 q=strrchr(strrchr(p, '/'), MDIRSEP[0]);
587 if (q)
588 *q=0;
589
590 fp=fopen(p, "r");
591 free(p);
592
593 if (!fp)
594 {
595 if (errno == ENOENT)
596 {
597 info->tryagain=1;
598 rl_free(&rl);
599 return;
600 }
601
602 continue;
603 }
604
605 if ((km=libmail_kwmCreate()) == NULL)
606 {
607 fclose(fp);
608 info->errorOccured= -1;
609 rl_free(&rl);
610 return;
611 }
612
613 while ((p=rl_read(&rl, fp)) != NULL && *p)
614 if (libmail_kwmSetName(h, km, p) < 0)
615 {
616 fclose(fp);
617 info->errorOccured= -1;
618 rl_free(&rl);
619 return;
620 }
621
622 if (km->firstEntry == NULL)
623 {
624 oldKm= (*info->findMessageByIndex)(i,
625 0, info->voidarg);
626 if (oldKm && *oldKm)
627 {
628 info->updateNeeded=1;
629 libmail_kwmDestroy(*oldKm);
630 *oldKm=NULL;
631 }
632 libmail_kwmDestroy(km);
633 }
634 else
635 {
636 oldKm= (*info->findMessageByIndex)(i, 1,
637 info->voidarg);
638
639 if (oldKm && *oldKm == NULL)
640 {
641 info->updateNeeded=1;
642 (*info->updateKeywords)(i, km, info->voidarg);
643 }
644 else if (!oldKm /* Shouldn't happen */
645 || libmail_kwmCmp(*oldKm, km) == 0)
646 {
647 libmail_kwmDestroy(km);
648 }
649 else
650 {
651 info->updateNeeded=1;
652 (*info->updateKeywords)(i, km, info->voidarg);
653 }
654 }
655
656 fclose(fp);
657 }
658 rl_free(&rl);
659 }
660
661 struct saveUpdateInfo {
662 FILE *fp;
663 unsigned long counter;
664 };
665
saveKeywordList(struct libmail_keywordEntry * ke,void * vp)666 static int saveKeywordList(struct libmail_keywordEntry *ke,
667 void *vp)
668 {
669 struct saveUpdateInfo *sui=(struct saveUpdateInfo *)vp;
670
671 fprintf(sui->fp, "%s\n", keywordName(ke));
672
673 ke->u.userNum= sui->counter;
674
675 ++sui->counter;
676 return 0;
677 }
678
maildir_kwExport(FILE * fp,struct maildir_kwReadInfo * info)679 int maildir_kwExport(FILE *fp, struct maildir_kwReadInfo *info)
680 {
681 struct saveUpdateInfo sui;
682 size_t i, n;
683
684 sui.fp=fp;
685 sui.counter=0;
686 libmail_kwEnumerate((*info->getKeywordHashtable)(info->voidarg),
687 saveKeywordList, &sui);
688
689 if (sui.counter == 0) /* No keywords set for any message */
690 return 0;
691
692 fprintf(fp, "\n");
693
694 n= (*info->getMessageCount)(info->voidarg);
695
696 for (i=0; i<n; i++)
697 {
698 const char *p;
699
700 struct libmail_kwMessage **km=
701 (*info->findMessageByIndex)(i, 0, info->voidarg);
702 struct libmail_kwMessageEntry *kme;
703
704 if (!km || !*km || (*km)->firstEntry == NULL)
705 continue;
706
707 for (p= (*info->getMessageFilename)(i, info->voidarg);
708 *p && *p != MDIRSEP[0]; p++)
709 putc(*p, fp);
710
711 putc(':', fp);
712 p="";
713
714 for (kme=(*km)->firstEntry; kme; kme=kme->next)
715 {
716 fprintf(fp, "%s%lu", p, kme->libmail_keywordEntryPtr
717 ->u.userNum);
718 p=" ";
719 }
720 fprintf(fp, "\n");
721 }
722
723 return 1;
724 }
725
save_updated_keywords(const char * maildir,const char * kwname,struct maildir_kwReadInfo * info,struct keywordUpdateInfo * updateInfo)726 static int save_updated_keywords(const char *maildir, const char *kwname,
727 struct maildir_kwReadInfo *info,
728 struct keywordUpdateInfo *updateInfo)
729 {
730 struct maildir_tmpcreate_info createInfo;
731 FILE *fp;
732
733 if (!info->updateNeeded)
734 return 0;
735
736 maildir_tmpcreate_init(&createInfo);
737
738 createInfo.maildir=maildir;
739 createInfo.hostname=getenv("HOSTNAME");
740 createInfo.doordie=1;
741
742 if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
743 {
744 perror("maildir_tmpcreate_fp");
745 return -1;
746 }
747
748 if (maildir_kwExport(fp, info) == 0)
749 {
750 fclose(fp);
751 unlink(createInfo.tmpname);
752 maildir_tmpcreate_free(&createInfo);
753 unlink(kwname);
754 return 0;
755 }
756 if (fflush(fp) < 0 || ferror(fp))
757 {
758 fclose(fp);
759 unlink(createInfo.tmpname);
760 perror(createInfo.tmpname);
761 maildir_tmpcreate_free(&createInfo);
762 return -1;
763 }
764 fclose(fp);
765
766 if (rename(createInfo.tmpname, kwname))
767 {
768 perror(createInfo.tmpname);
769 unlink(createInfo.tmpname);
770 perror(createInfo.tmpname);
771 maildir_tmpcreate_free(&createInfo);
772 return -1;
773 }
774 maildir_tmpcreate_free(&createInfo);
775 return 0;
776 }
777
purge_old_updates(const char * dir,time_t tn,struct maildir_kwReadInfo * info,struct keywordUpdateInfo * updateInfo)778 static void purge_old_updates(const char *dir,
779 time_t tn,
780 struct maildir_kwReadInfo *info,
781 struct keywordUpdateInfo *updateInfo)
782 {
783 size_t i;
784
785 time_t x;
786
787 size_t n= (*info->getMessageCount)(info->voidarg);
788
789 for (i=0; i<n; i++)
790 {
791 char *p, *q, *c;
792 char b[NUMBUFSIZE];
793
794 const char *fn= (*info->getMessageFilename)(i, info->voidarg);
795
796 if (!fn)
797 continue; /* Shouldn't happen */
798
799 if (updateInfo[i].foundNewest)
800 {
801 size_t l;
802
803 x=tn+1;
804
805 libmail_str_size_t(x, b);
806
807 l=strlen(dir)+strlen(b)+strlen(fn)+4;
808
809 p=malloc(l);
810 if (!p)
811 {
812 info->errorOccured= -1;
813 return;
814 }
815
816 q=malloc(l);
817 if (!q)
818 {
819 free(p);
820 info->errorOccured= -1;
821 return;
822 }
823
824 sprintf(p, "%s/%s", dir, fn);
825
826 sprintf(q, "%s/.%s.%s", dir, b, fn);
827
828
829 c=strrchr(strrchr(p, '/'), MDIRSEP[0]);
830
831 if (c) *c=0;
832 c=strrchr(strrchr(q, '/'), MDIRSEP[0]);
833
834 if (c) *c=0;
835
836 rename(p, q);
837 free(q);
838 free(p);
839 continue;
840 }
841
842 if (! (updateInfo[i].totalCnt == 1 &&
843 updateInfo[i].highestN))
844 continue;
845
846 libmail_str_size_t(updateInfo[i].highestN, b);
847
848 p=malloc(strlen(dir)+strlen(b)+strlen(fn)+4);
849
850 if (!p)
851 {
852 info->errorOccured= -1;
853 return;
854 }
855
856 sprintf(p, "%s/.%s.%s", dir, b, fn);
857
858 q=strrchr(strrchr(p, '/'), MDIRSEP[0]);
859 if (q) *q=0;
860 unlink(p);
861 free(p);
862 }
863 }
864
865
866 /***************/
867
868 static int maildir_kwSaveCommon(const char *maildir,
869 const char *filename,
870 struct libmail_kwMessage *newKeyword,
871 const char **newKeywordArray,
872 char **tmpname,
873 char **newname,
874 int tryAtomic);
875
maildir_kwSave(const char * maildir,const char * filename,struct libmail_kwMessage * newKeyword,char ** tmpname,char ** newname,int tryAtomic)876 int maildir_kwSave(const char *maildir,
877 const char *filename,
878 struct libmail_kwMessage *newKeyword,
879 char **tmpname,
880 char **newname,
881 int tryAtomic)
882 {
883 return maildir_kwSaveCommon(maildir, filename, newKeyword, NULL,
884 tmpname, newname, tryAtomic);
885 }
886
maildir_kwSaveArray(const char * maildir,const char * filename,const char ** flags,char ** tmpname,char ** newname,int tryAtomic)887 int maildir_kwSaveArray(const char *maildir,
888 const char *filename,
889 const char **flags,
890 char **tmpname,
891 char **newname,
892 int tryAtomic)
893 {
894 return maildir_kwSaveCommon(maildir, filename, NULL, flags,
895 tmpname, newname, tryAtomic);
896 }
897
maildir_kwSaveCommon(const char * maildir,const char * filename,struct libmail_kwMessage * newKeyword,const char ** newKeywordArray,char ** tmpname,char ** newname,int tryAtomic)898 static int maildir_kwSaveCommon(const char *maildir,
899 const char *filename,
900 struct libmail_kwMessage *newKeyword,
901 const char **newKeywordArray,
902 char **tmpname,
903 char **newname,
904 int tryAtomic)
905 {
906 struct maildir_tmpcreate_info createInfo;
907 FILE *fp;
908 char *n=malloc(strlen(maildir)+strlen(filename)+10+
909 sizeof(KEYWORDDIR)), *p;
910 struct libmail_kwMessageEntry *kme;
911
912 if (!n)
913 return -1;
914
915 strcat(strcat(strcpy(n, maildir),
916 "/" KEYWORDDIR "/"), filename);
917
918 p=strrchr(strrchr(n, '/'), MDIRSEP[0]);
919 if (p)
920 *p=0;
921
922
923 maildir_tmpcreate_init(&createInfo);
924
925 createInfo.maildir=maildir;
926 createInfo.msgsize=0;
927 createInfo.hostname=getenv("HOSTNAME");
928 createInfo.doordie=1;
929
930 if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
931 {
932 free(n);
933 return -1;
934 }
935
936 if (newKeywordArray)
937 {
938 size_t i;
939
940 for (i=0; newKeywordArray[i]; i++)
941 fprintf(fp, "%s\n", newKeywordArray[i]);
942 }
943 else for (kme=newKeyword ? newKeyword->firstEntry:NULL;
944 kme; kme=kme->next)
945 fprintf(fp, "%s\n", keywordName(kme->libmail_keywordEntryPtr));
946
947 errno=EIO;
948
949 if (fflush(fp) < 0 || ferror(fp))
950 {
951 fclose(fp);
952 free(n);
953 return -1;
954 }
955
956 fclose(fp);
957
958 *tmpname=createInfo.tmpname;
959 createInfo.tmpname=NULL;
960 *newname=n;
961 maildir_tmpcreate_free(&createInfo);
962
963 if (tryAtomic)
964 {
965 char timeBuf[NUMBUFSIZE];
966
967 char *n=malloc(strlen(*tmpname)
968 + sizeof(KEYWORDDIR) + NUMBUFSIZE+10);
969
970 if (!n)
971 {
972 free(*tmpname);
973 free(*newname);
974 return -1;
975 }
976
977 strcat(strcat(strcat(strcpy(n, maildir),
978 "/" KEYWORDDIR "/.tmp."),
979 libmail_str_time_t(time(NULL), timeBuf)),
980 strrchr( *tmpname, '/')+1);
981
982
983 if (rename( *tmpname, n) < 0)
984 {
985 free(n);
986 free(*tmpname);
987 free(*newname);
988 return -1;
989 }
990
991 free (*tmpname);
992 *tmpname=n;
993 }
994 return 0;
995 }
996