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