summaryrefslogtreecommitdiffstats
path: root/maildir/maildirkeywords2.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /maildir/maildirkeywords2.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'maildir/maildirkeywords2.c')
-rw-r--r--maildir/maildirkeywords2.c995
1 files changed, 995 insertions, 0 deletions
diff --git a/maildir/maildirkeywords2.c b/maildir/maildirkeywords2.c
new file mode 100644
index 0000000..6923a7a
--- /dev/null
+++ b/maildir/maildirkeywords2.c
@@ -0,0 +1,995 @@
+/*
+** Copyright 2003-2007 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "maildir/config.h"
+#include "maildir/maildircreate.h"
+#include "maildir/maildirmisc.h"
+#include "maildir/maildirwatch.h"
+#include "numlib/numlib.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "maildirkeywords.h"
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#if HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#if HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif
+
+
+int libmail_kwEnabled=1;
+
+struct keywordUpdateInfo {
+ size_t highestN;
+ size_t highestT;
+ size_t totalCnt;
+ int foundNewest;
+};
+
+static void scan_updates(const char *dir,
+ time_t t,
+ time_t tn,
+ struct maildir_kwReadInfo *info,
+ struct keywordUpdateInfo **updateInfo);
+
+static void read_updates(const char *dir,
+ struct maildir_kwReadInfo *info,
+ struct keywordUpdateInfo *updateInfo);
+
+static void purge_old_updates(const char *dir,
+ time_t tn,
+ struct maildir_kwReadInfo *info,
+ struct keywordUpdateInfo *updateInfo);
+
+static int save_updated_keywords(const char *maildir, const char *kwname,
+ struct maildir_kwReadInfo *info,
+ struct keywordUpdateInfo *updateInfo);
+
+static void doReadKeywords2(const char *maildir, const char *dir,
+ struct maildir_kwReadInfo *rki);
+
+
+struct readLine {
+ char *lineBuf;
+ size_t lineBufSize;
+ int errflag;
+};
+
+static void rl_init(struct readLine *p)
+{
+ p->lineBuf=NULL;
+ p->lineBufSize=0;
+ p->errflag=0;
+}
+
+static void rl_free(struct readLine *p)
+{
+ if (p->lineBuf)
+ free(p->lineBuf);
+}
+
+static char *rl_read(struct readLine *p, FILE *f)
+{
+ size_t n=0;
+ int ch;
+
+ for (;;)
+ {
+ if (n >= p->lineBufSize)
+ {
+ size_t o= n + 1024;
+ char *b= p->lineBuf ? realloc(p->lineBuf, o):malloc(o);
+
+ if (!b)
+ {
+ p->errflag=1;
+ return NULL;
+ }
+
+ p->lineBuf=b;
+ p->lineBufSize=o;
+ }
+
+ if ((ch=getc(f)) == EOF || ch == '\n')
+ break;
+ p->lineBuf[n++]=(char)ch;
+ }
+
+ p->lineBuf[n]=0;
+
+ return n == 0 && ch == EOF ? NULL:p->lineBuf;
+}
+
+int maildir_kwRead(const char *maildir,
+ struct maildir_kwReadInfo *rki)
+{
+ char *p=malloc(strlen(maildir)+sizeof("/" KEYWORDDIR));
+
+ if (!p)
+ return -1;
+
+ strcat(strcpy(p, maildir), "/" KEYWORDDIR);
+
+ rki->errorOccured=0;
+ doReadKeywords2(maildir, p, rki);
+ free(p);
+
+ return rki->errorOccured;
+}
+
+static void doReadKeywords2(const char *maildir, const char *dir,
+ struct maildir_kwReadInfo *rki)
+{
+ FILE *fp;
+ char *kwname=malloc(strlen(dir)+sizeof("/:list"));
+ struct keywordUpdateInfo *updateInfo;
+
+ time_t t=time(NULL);
+ time_t tn=t/300;
+
+ rki->updateNeeded=0;
+ rki->tryagain=0;
+
+ if (!kwname)
+ {
+ rki->errorOccured= -1;
+ return;
+ }
+
+ strcat(strcpy(kwname, dir), "/:list");
+
+ fp=fopen(kwname, "r");
+
+ if (!fp) /* Maybe the keyword directory needs creating? */
+ {
+ struct stat stat_buf;
+
+ mkdir(dir, 0700);
+
+ /* Give it same mode as the maildir */
+
+ if (stat(maildir, &stat_buf) == 0)
+ chmod(dir, stat_buf.st_mode & 0777);
+ }
+
+ /*
+ ** If keywords are disabled, we still need to create the keyword
+ ** directory, otherwise FAM-based IDLE will not work.
+ */
+
+ if (!libmail_kwEnabled)
+ {
+ if (fp)
+ fclose(fp);
+ free(kwname);
+ return;
+ }
+
+ if (fp && maildir_kwImport(fp, rki))
+ rki->updateNeeded=1;
+ if (fp)
+ fclose(fp);
+
+ updateInfo=malloc(sizeof(struct keywordUpdateInfo)
+ *( 1+(*rki->getMessageCount)(rki->voidarg)));
+
+ if (!updateInfo)
+ {
+ free(kwname);
+ return;
+ }
+
+ scan_updates(dir, t, tn, rki, &updateInfo);
+ read_updates(dir, rki, updateInfo);
+
+ if (!rki->tryagain && !rki->errorOccured)
+ {
+ if (save_updated_keywords(maildir, kwname, rki, updateInfo)
+ == 0 && !rki->errorOccured)
+ purge_old_updates(dir, tn, rki, updateInfo);
+ }
+
+ free(updateInfo);
+ free(kwname);
+}
+
+int maildir_kwImport(FILE *fp, struct maildir_kwReadInfo *rki)
+{
+ struct keywordIndex {
+ struct keywordIndex *next;
+ struct libmail_keywordEntry *kw;
+ } *firstKw=NULL, *lastKw=NULL, **index;
+ size_t numKw=0;
+ struct libmail_kwMessage *tmpMsg;
+ char *p;
+ int rc=0;
+
+ struct readLine rl;
+
+ if ((tmpMsg=libmail_kwmCreate()) == NULL)
+ {
+ rki->errorOccured=-1;
+ return 0;
+ }
+
+ rl_init(&rl);
+
+ while ((p=rl_read(&rl, fp)) != NULL && *p)
+ {
+ struct keywordIndex *ki=malloc(sizeof(*firstKw));
+
+ if (!ki)
+ {
+ rki->errorOccured=-1;
+ rl_free(&rl);
+ libmail_kwmDestroy(tmpMsg);
+ return 0;
+ }
+
+ if (lastKw)
+ lastKw->next=ki;
+ else
+ firstKw=ki;
+
+ lastKw=ki;
+ ki->next=NULL;
+ ++numKw;
+
+ ki->kw=libmail_kweFind((*rki->getKeywordHashtable)
+ (rki->voidarg), p, 1);
+ if (libmail_kwmSet(tmpMsg, ki->kw) < 0)
+ {
+ rki->errorOccured= -1;
+ break;
+ }
+ }
+
+ if (rki->errorOccured ||
+ (index=malloc(sizeof(*firstKw)*(numKw+1))) == NULL)
+ {
+ while ((lastKw=firstKw) != NULL)
+ {
+ firstKw=firstKw->next;
+ free(lastKw);
+ }
+ libmail_kwmDestroy(tmpMsg);
+ rki->errorOccured= -1;
+ rl_free(&rl);
+ return 0;
+ }
+
+ numKw=0;
+
+ while (firstKw)
+ {
+ index[numKw]=firstKw;
+ ++numKw;
+ firstKw=firstKw->next;
+ }
+ index[numKw]=0;
+
+ if (p)
+ while ((p=rl_read(&rl, fp)) != NULL)
+ {
+ char *q=strchr(p, ':');
+ struct libmail_kwMessage **i;
+ size_t l;
+ size_t n;
+
+ if (!q)
+ {
+ rc=1;
+ continue; /* Crap */
+ }
+
+ *q++=0;
+
+ i= (*rki->findMessageByFilename)(p, 0, &n,
+ rki->voidarg);
+
+ if (!i)
+ {
+ rc=1;
+ continue; /* Stale data */
+ }
+
+ if (*i) /* Already have it */
+ libmail_kwmDestroy(*i);
+ (*i)=NULL;
+
+ i=NULL;
+
+ while ((q=strtok(q, " ")) != NULL)
+ {
+ l=atoi(q);
+ q=NULL;
+
+ if (l < numKw)
+ {
+ if (!i)
+ i= (*rki->
+ findMessageByFilename)
+ (p, 1, &n,
+ rki->voidarg);
+ /* Autocreate it */
+
+ if (*i == NULL) /* ENOMEM */
+ {
+ rc= -1;
+ break;
+ }
+
+ if (libmail_kwmSet(*i, index[l]->kw)
+ < 0)
+ rki->errorOccured= -1;
+ }
+ }
+ }
+
+ rl_free(&rl);
+ for (numKw=0; index[numKw]; numKw++)
+ free(index[numKw]);
+
+ free(index);
+ libmail_kwmDestroy(tmpMsg);
+ return rc;
+}
+
+/* See the README */
+
+static void scan_updates(const char *dir,
+ time_t t,
+ time_t tn,
+ struct maildir_kwReadInfo *info,
+ struct keywordUpdateInfo **updateInfo)
+{
+ DIR *dirp;
+ struct dirent *de;
+ unsigned long i;
+
+ size_t n= (*info->getMessageCount)(info->voidarg);
+
+ for (i=0; i<n; i++)
+ {
+ (*updateInfo)[i].highestN=0;
+ (*updateInfo)[i].highestT=0;
+ (*updateInfo)[i].totalCnt=0;
+ (*updateInfo)[i].foundNewest=0;
+ }
+
+ dirp=opendir(dir);
+ while (dirp && (de=readdir(dirp)) != NULL)
+ {
+ struct libmail_kwMessage **i;
+ unsigned long x=0;
+ char *p, *q;
+ size_t in;
+
+ if (de->d_name[0] == ':')
+ continue;
+
+ if (de->d_name[0] != '.')
+ i=(*info->findMessageByFilename)
+ (de->d_name, 0, &in, info->voidarg);
+
+ else if ((x=libmail_atotime_t(de->d_name+1)) == 0)
+ {
+ if (strncmp(de->d_name, ".tmp.", 5) == 0)
+ {
+ if ((x=libmail_atotime_t(de->d_name+5)) != 0
+ && x >= t - 120)
+ continue; /* New scratch file */
+
+
+ p=malloc(strlen(dir)+strlen(de->d_name)+2);
+
+ if (!p)
+ {
+ closedir(dirp);
+ info->errorOccured= -1;
+ return;
+ }
+
+ strcat(strcat(strcpy(p, dir), "/"),
+ de->d_name);
+ unlink(p);
+ free(p);
+ }
+ continue;
+ }
+ else
+ {
+ const char *p=strchr(de->d_name+1, '.');
+
+ if (!p)
+ continue;
+
+ i=(*info->findMessageByFilename)
+ (p+1, 0, &in, info->voidarg);
+ }
+
+ p=malloc(strlen(dir)+strlen(de->d_name)+2);
+
+ if (!p)
+ {
+ closedir(dirp);
+ info->errorOccured= -1;
+ return;
+ }
+
+ strcat(strcat(strcpy(p, dir), "/"), de->d_name);
+
+ if (!i)
+ {
+ struct stat stat_buf;
+
+ if (stat(p, &stat_buf) == 0 &&
+ stat_buf.st_mtime < t - 15 * 60)
+ unlink(p);
+ free(p);
+ continue;
+ }
+ free(p);
+
+
+ if (in >= n)
+ {
+ /* libmail_kwgReadMaildir autocrerate */
+
+ struct keywordUpdateInfo *u=
+ realloc(*updateInfo,
+ sizeof(**updateInfo) * (in+1));
+
+ if (!u)
+ {
+ closedir(dirp);
+ info->errorOccured= -1;
+ return;
+ }
+
+ *updateInfo=u;
+
+ while (n <= in)
+ {
+
+ (*updateInfo)[n].highestN=0;
+ (*updateInfo)[n].highestT=0;
+ (*updateInfo)[n].totalCnt=0;
+ (*updateInfo)[n].foundNewest=0;
+ ++n;
+ }
+ }
+
+
+ if (de->d_name[0] != '.')
+ {
+ x=tn+1;
+
+ (*updateInfo)[in].foundNewest=1;
+ }
+
+ ++(*updateInfo)[in].totalCnt;
+
+ if (x >= tn)
+ {
+ if (x > (*updateInfo)[in].highestT)
+ (*updateInfo)[in].highestT=x;
+ }
+ else if ((*updateInfo)[in].highestN < x)
+ {
+ if ((*updateInfo)[in].highestN > 0)
+ {
+ char b[NUMBUFSIZE];
+ char *r;
+
+ libmail_str_size_t((*updateInfo)[in].highestN, b);
+
+ r=de->d_name;
+ if (*r == '.')
+ r=strchr(r+1, '.')+1;
+
+ q=malloc(strlen(dir)+strlen(r)+
+ strlen(b)+4);
+
+ if (!q)
+ {
+ closedir(dirp);
+ info->errorOccured= -1;
+ return;
+ }
+
+ sprintf(q, "%s/.%s.%s", dir, b, r);
+ unlink(q);
+ free(q);
+ }
+
+ (*updateInfo)[in].highestN=x;
+ }
+ }
+
+ if (dirp)
+ closedir(dirp);
+}
+
+static void read_updates(const char *dir,
+ struct maildir_kwReadInfo *info,
+ struct keywordUpdateInfo *updateInfo)
+{
+ unsigned long i;
+ size_t msgCnt= (*info->getMessageCount)(info->voidarg);
+ struct readLine rl;
+
+ struct libmail_kwHashtable *h=
+ (*info->getKeywordHashtable)(info->voidarg);
+
+ rl_init(&rl);
+ for (i=0; i<msgCnt; i++)
+ {
+ size_t n=updateInfo[i].highestN;
+ char *p, *q;
+ char b[NUMBUFSIZE];
+ FILE *fp;
+ struct libmail_kwMessage *km, **oldKm;
+ const char *fn;
+
+ if (n < updateInfo[i].highestT)
+ n=updateInfo[i].highestT;
+
+ if (n == 0)
+ continue;
+
+ libmail_str_size_t(n, b);
+
+ fn= (*info->getMessageFilename)(i, info->voidarg);
+
+ if (!fn)
+ continue; /* Shouldn't happen */
+
+ p=malloc(strlen(dir)+strlen(b)+strlen(fn)+4);
+
+ if (!p)
+ {
+ info->errorOccured= -1;
+ rl_free(&rl);
+ return;
+ }
+
+ if (updateInfo[i].foundNewest)
+ sprintf(p, "%s/%s", dir, fn);
+ else
+ sprintf(p, "%s/.%s.%s", dir, b, fn);
+
+
+ q=strrchr(strrchr(p, '/'), MDIRSEP[0]);
+ if (q)
+ *q=0;
+
+ fp=fopen(p, "r");
+ free(p);
+
+ if (!fp)
+ {
+ if (errno == ENOENT)
+ {
+ info->tryagain=1;
+ rl_free(&rl);
+ return;
+ }
+
+ continue;
+ }
+
+ if ((km=libmail_kwmCreate()) == NULL)
+ {
+ fclose(fp);
+ info->errorOccured= -1;
+ rl_free(&rl);
+ return;
+ }
+
+ while ((p=rl_read(&rl, fp)) != NULL && *p)
+ if (libmail_kwmSetName(h, km, p) < 0)
+ {
+ fclose(fp);
+ info->errorOccured= -1;
+ rl_free(&rl);
+ return;
+ }
+
+ if (km->firstEntry == NULL)
+ {
+ oldKm= (*info->findMessageByIndex)(i,
+ 0, info->voidarg);
+ if (oldKm && *oldKm)
+ {
+ info->updateNeeded=1;
+ libmail_kwmDestroy(*oldKm);
+ *oldKm=NULL;
+ }
+ libmail_kwmDestroy(km);
+ }
+ else
+ {
+ oldKm= (*info->findMessageByIndex)(i, 1,
+ info->voidarg);
+
+ if (oldKm && *oldKm == NULL)
+ {
+ info->updateNeeded=1;
+ (*info->updateKeywords)(i, km, info->voidarg);
+ }
+ else if (!oldKm /* Shouldn't happen */
+ || libmail_kwmCmp(*oldKm, km) == 0)
+ {
+ libmail_kwmDestroy(km);
+ }
+ else
+ {
+ info->updateNeeded=1;
+ (*info->updateKeywords)(i, km, info->voidarg);
+ }
+ }
+
+ fclose(fp);
+ }
+ rl_free(&rl);
+}
+
+struct saveUpdateInfo {
+ FILE *fp;
+ unsigned long counter;
+};
+
+static int saveKeywordList(struct libmail_keywordEntry *ke,
+ void *vp)
+{
+ struct saveUpdateInfo *sui=(struct saveUpdateInfo *)vp;
+
+ fprintf(sui->fp, "%s\n", keywordName(ke));
+
+ ke->u.userNum= sui->counter;
+
+ ++sui->counter;
+ return 0;
+}
+
+int maildir_kwExport(FILE *fp, struct maildir_kwReadInfo *info)
+{
+ struct saveUpdateInfo sui;
+ size_t i, n;
+
+ sui.fp=fp;
+ sui.counter=0;
+ libmail_kwEnumerate((*info->getKeywordHashtable)(info->voidarg),
+ saveKeywordList, &sui);
+
+ if (sui.counter == 0) /* No keywords set for any message */
+ return 0;
+
+ fprintf(fp, "\n");
+
+ n= (*info->getMessageCount)(info->voidarg);
+
+ for (i=0; i<n; i++)
+ {
+ const char *p;
+
+ struct libmail_kwMessage **km=
+ (*info->findMessageByIndex)(i, 0, info->voidarg);
+ struct libmail_kwMessageEntry *kme;
+
+ if (!km || !*km || (*km)->firstEntry == NULL)
+ continue;
+
+ for (p= (*info->getMessageFilename)(i, info->voidarg);
+ *p && *p != MDIRSEP[0]; p++)
+ putc(*p, fp);
+
+ putc(':', fp);
+ p="";
+
+ for (kme=(*km)->firstEntry; kme; kme=kme->next)
+ {
+ fprintf(fp, "%s%lu", p, kme->libmail_keywordEntryPtr
+ ->u.userNum);
+ p=" ";
+ }
+ fprintf(fp, "\n");
+ }
+
+ return 1;
+}
+
+static int save_updated_keywords(const char *maildir, const char *kwname,
+ struct maildir_kwReadInfo *info,
+ struct keywordUpdateInfo *updateInfo)
+{
+ struct maildir_tmpcreate_info createInfo;
+ FILE *fp;
+
+ if (!info->updateNeeded)
+ return 0;
+
+ maildir_tmpcreate_init(&createInfo);
+
+ createInfo.maildir=maildir;
+ createInfo.hostname=getenv("HOSTNAME");
+ createInfo.doordie=1;
+
+ if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
+ {
+ perror("maildir_tmpcreate_fp");
+ return -1;
+ }
+
+ if (maildir_kwExport(fp, info) == 0)
+ {
+ fclose(fp);
+ unlink(createInfo.tmpname);
+ maildir_tmpcreate_free(&createInfo);
+ unlink(kwname);
+ return 0;
+ }
+ if (fflush(fp) < 0 || ferror(fp))
+ {
+ fclose(fp);
+ unlink(createInfo.tmpname);
+ perror(createInfo.tmpname);
+ maildir_tmpcreate_free(&createInfo);
+ return -1;
+ }
+ fclose(fp);
+
+ if (rename(createInfo.tmpname, kwname))
+ {
+ perror(createInfo.tmpname);
+ unlink(createInfo.tmpname);
+ perror(createInfo.tmpname);
+ maildir_tmpcreate_free(&createInfo);
+ return -1;
+ }
+ maildir_tmpcreate_free(&createInfo);
+ return 0;
+}
+
+static void purge_old_updates(const char *dir,
+ time_t tn,
+ struct maildir_kwReadInfo *info,
+ struct keywordUpdateInfo *updateInfo)
+{
+ size_t i;
+
+ time_t x;
+
+ size_t n= (*info->getMessageCount)(info->voidarg);
+
+ for (i=0; i<n; i++)
+ {
+ char *p, *q, *c;
+ char b[NUMBUFSIZE];
+
+ const char *fn= (*info->getMessageFilename)(i, info->voidarg);
+
+ if (!fn)
+ continue; /* Shouldn't happen */
+
+ if (updateInfo[i].foundNewest)
+ {
+ size_t l;
+
+ x=tn+1;
+
+ libmail_str_size_t(x, b);
+
+ l=strlen(dir)+strlen(b)+strlen(fn)+4;
+
+ p=malloc(l);
+ if (!p)
+ {
+ info->errorOccured= -1;
+ return;
+ }
+
+ q=malloc(l);
+ if (!q)
+ {
+ free(p);
+ info->errorOccured= -1;
+ return;
+ }
+
+ sprintf(p, "%s/%s", dir, fn);
+
+ sprintf(q, "%s/.%s.%s", dir, b, fn);
+
+
+ c=strrchr(strrchr(p, '/'), MDIRSEP[0]);
+
+ if (c) *c=0;
+ c=strrchr(strrchr(q, '/'), MDIRSEP[0]);
+
+ if (c) *c=0;
+
+ rename(p, q);
+ free(q);
+ free(p);
+ continue;
+ }
+
+ if (! (updateInfo[i].totalCnt == 1 &&
+ updateInfo[i].highestN))
+ continue;
+
+ libmail_str_size_t(updateInfo[i].highestN, b);
+
+ p=malloc(strlen(dir)+strlen(b)+strlen(fn)+4);
+
+ if (!p)
+ {
+ info->errorOccured= -1;
+ return;
+ }
+
+ sprintf(p, "%s/.%s.%s", dir, b, fn);
+
+ q=strrchr(strrchr(p, '/'), MDIRSEP[0]);
+ if (q) *q=0;
+ unlink(p);
+ free(p);
+ }
+}
+
+
+/***************/
+
+static int maildir_kwSaveCommon(const char *maildir,
+ const char *filename,
+ struct libmail_kwMessage *newKeyword,
+ const char **newKeywordArray,
+ char **tmpname,
+ char **newname,
+ int tryAtomic);
+
+int maildir_kwSave(const char *maildir,
+ const char *filename,
+ struct libmail_kwMessage *newKeyword,
+ char **tmpname,
+ char **newname,
+ int tryAtomic)
+{
+ return maildir_kwSaveCommon(maildir, filename, newKeyword, NULL,
+ tmpname, newname, tryAtomic);
+}
+
+int maildir_kwSaveArray(const char *maildir,
+ const char *filename,
+ const char **flags,
+ char **tmpname,
+ char **newname,
+ int tryAtomic)
+{
+ return maildir_kwSaveCommon(maildir, filename, NULL, flags,
+ tmpname, newname, tryAtomic);
+}
+
+static int maildir_kwSaveCommon(const char *maildir,
+ const char *filename,
+ struct libmail_kwMessage *newKeyword,
+ const char **newKeywordArray,
+ char **tmpname,
+ char **newname,
+ int tryAtomic)
+{
+ struct maildir_tmpcreate_info createInfo;
+ FILE *fp;
+ char *n=malloc(strlen(maildir)+strlen(filename)+10+
+ sizeof(KEYWORDDIR)), *p;
+ struct libmail_kwMessageEntry *kme;
+
+ if (!n)
+ return -1;
+
+ strcat(strcat(strcpy(n, maildir),
+ "/" KEYWORDDIR "/"), filename);
+
+ p=strrchr(strrchr(n, '/'), MDIRSEP[0]);
+ if (p)
+ *p=0;
+
+
+ maildir_tmpcreate_init(&createInfo);
+
+ createInfo.maildir=maildir;
+ createInfo.msgsize=0;
+ createInfo.hostname=getenv("HOSTNAME");
+ createInfo.doordie=1;
+
+ if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
+ {
+ free(n);
+ return -1;
+ }
+
+ if (newKeywordArray)
+ {
+ size_t i;
+
+ for (i=0; newKeywordArray[i]; i++)
+ fprintf(fp, "%s\n", newKeywordArray[i]);
+ }
+ else for (kme=newKeyword ? newKeyword->firstEntry:NULL;
+ kme; kme=kme->next)
+ fprintf(fp, "%s\n", keywordName(kme->libmail_keywordEntryPtr));
+
+ errno=EIO;
+
+ if (fflush(fp) < 0 || ferror(fp))
+ {
+ fclose(fp);
+ free(n);
+ return -1;
+ }
+
+ fclose(fp);
+
+ *tmpname=createInfo.tmpname;
+ createInfo.tmpname=NULL;
+ *newname=n;
+ maildir_tmpcreate_free(&createInfo);
+
+ if (tryAtomic)
+ {
+ char timeBuf[NUMBUFSIZE];
+
+ char *n=malloc(strlen(*tmpname)
+ + sizeof(KEYWORDDIR) + NUMBUFSIZE+10);
+
+ if (!n)
+ {
+ free(*tmpname);
+ free(*newname);
+ return -1;
+ }
+
+ strcat(strcat(strcat(strcpy(n, maildir),
+ "/" KEYWORDDIR "/.tmp."),
+ libmail_str_time_t(time(NULL), timeBuf)),
+ strrchr( *tmpname, '/')+1);
+
+
+ if (rename( *tmpname, n) < 0)
+ {
+ free(n);
+ free(*tmpname);
+ free(*newname);
+ return -1;
+ }
+
+ free (*tmpname);
+ *tmpname=n;
+ }
+ return 0;
+}