Back to index

courier  0.68.2
imapscanclient.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2011 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if    HAVE_CONFIG_H
00007 #include      "config.h"
00008 #endif
00009 
00010 #include      <stdio.h>
00011 #include      <stdlib.h>
00012 #include      <string.h>
00013 #include      <fcntl.h>
00014 #include      <time.h>
00015 #include      <errno.h>
00016 #include      "numlib/numlib.h"
00017 
00018 #if    HAVE_UNISTD_H
00019 #include      <unistd.h>
00020 #endif
00021 #include      <sys/types.h>
00022 #include      <sys/stat.h>
00023 #if HAVE_SYS_WAIT_H
00024 #include      <sys/wait.h>
00025 #endif
00026 #ifndef WEXITSTATUS
00027 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
00028 #endif
00029 #ifndef WIFEXITED
00030 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
00031 #endif
00032 #if HAVE_DIRENT_H
00033 #include <dirent.h>
00034 #define NAMLEN(dirent) strlen((dirent)->d_name)
00035 #else
00036 #define dirent direct
00037 #define NAMLEN(dirent) (dirent)->d_namlen
00038 #if HAVE_SYS_NDIR_H
00039 #include <sys/ndir.h>
00040 #endif
00041 #if HAVE_SYS_DIR_H
00042 #include <sys/dir.h>
00043 #endif
00044 #if HAVE_NDIR_H
00045 #include <ndir.h>
00046 #endif
00047 #endif
00048 
00049 #include      "liblock/config.h"
00050 #include      "liblock/liblock.h"
00051 #include      "maildir/config.h"
00052 #include      "maildir/maildircreate.h"
00053 #include      "maildir/maildirmisc.h"
00054 #include      "maildir/maildirwatch.h"
00055 #include      "liblock/mail.h"
00056 
00057 #include      "imapscanclient.h"
00058 #include      "imaptoken.h"
00059 #include      "imapwrite.h"
00060 #include      "imapd.h"
00061 
00062 /*
00063 ** RFC 2060: "A good value to use for the unique identifier validity value is a
00064 ** 32-bit representation of the creation date/time of the mailbox."
00065 **
00066 ** Well, Y2038k is on the horizon, time to push to reset button.
00067 **
00068 */
00069 
00070 #ifndef IMAP_EPOCH
00071 #define IMAP_EPOCH   1000000000
00072 #endif
00073 
00074 static int do_imapscan_maildir2(struct imapscaninfo *, const char *,
00075                             int, int, struct uidplus_info *);
00076 void imapscanfail(const char *p);
00077 
00078 #if SMAP
00079 extern int smapflag;
00080 #endif
00081 
00082 extern int keywords();
00083 extern void set_time(const char *tmpname, time_t timestamp);
00084 
00085 static void imapscan_readKeywords(const char *maildir,
00086                               struct imapscaninfo *scaninfo);
00087 
00088 void imapscan_init(struct imapscaninfo *i)
00089 {
00090        memset(i, 0, sizeof(*i));
00091 
00092        if ((i->keywordList=malloc(sizeof(*i->keywordList))) == NULL)
00093               write_error_exit(0);
00094 
00095        libmail_kwhInit(i->keywordList);
00096 }
00097 
00098 void imapscan_copy(struct imapscaninfo *a,
00099                  struct imapscaninfo *b)
00100 {
00101        imapscan_free(a);
00102        *a=*b;
00103        imapscan_init(b);
00104 }
00105 
00106 struct libmail_kwMessage *imapscan_createKeyword(struct imapscaninfo *a,
00107                                          unsigned long n)
00108 {
00109        if (n >= a->nmessages)
00110               return NULL;
00111 
00112        if (a->msgs[n].keywordMsg == NULL)
00113        {
00114               struct libmail_kwMessage *m=libmail_kwmCreate();
00115 
00116               if (!m)
00117                      write_error_exit(0);
00118 
00119               m->u.userNum=n;
00120               a->msgs[n].keywordMsg=m;
00121        }
00122 
00123        return a->msgs[n].keywordMsg;
00124 }
00125 
00126 static int uselocks()
00127 {
00128        const  char *p;
00129 
00130        if ((p=getenv("IMAP_USELOCKS")) != 0 && *p != '1')
00131               return 0;
00132 
00133        return 1;
00134 }
00135 
00136 int imapmaildirlock(struct imapscaninfo *scaninfo,
00137                   const char *maildir,
00138                   int (*func)(void *),
00139                   void *void_arg)
00140 {
00141        char *newname;
00142        int tryAnyway;
00143        int rc;
00144 
00145        if (!uselocks())
00146               return (*func)(void_arg);
00147 
00148        if (scaninfo->watcher == NULL &&
00149            (scaninfo->watcher=maildirwatch_alloc(maildir)) == NULL)
00150               imapscanfail("maildirwatch");
00151 
00152        if ((newname=maildir_lock(maildir, scaninfo->watcher, &tryAnyway))
00153            == NULL)
00154        {
00155               if (!tryAnyway)
00156                      return -1;
00157 
00158               imapscanfail("maildir_lock");
00159        }
00160 
00161        rc=(*func)(void_arg);
00162 
00163        if (newname)
00164        {
00165               unlink(newname);
00166               free(newname);
00167               newname=NULL;
00168        }
00169        return rc;
00170 }
00171 
00172 
00173 struct imapscan_info {
00174        struct imapscaninfo *scaninfo;
00175        const char *dir;
00176        int leavenew;
00177        int ro;
00178        struct uidplus_info *uidplus;
00179 };
00180 
00181 
00182 static int imapscan_maildir_cb(void *);
00183 
00184 int imapscan_maildir(struct imapscaninfo *scaninfo,
00185                    const char *dir, int leavenew, int ro,
00186                    struct uidplus_info *uidplus)
00187 {
00188        struct imapscan_info ii;
00189 
00190        ii.scaninfo=scaninfo;
00191        ii.dir=dir;
00192        ii.leavenew=leavenew;
00193        ii.ro=ro;
00194        ii.uidplus=uidplus;
00195 
00196        return imapmaildirlock(scaninfo, dir, imapscan_maildir_cb, &ii);
00197 }
00198 
00199 int imapscan_maildir_cb(void *void_arg)
00200 {
00201        struct imapscan_info *ii=(struct imapscan_info *)void_arg;
00202        int rc=do_imapscan_maildir2(ii->scaninfo,
00203                                ii->dir,
00204                                ii->leavenew,
00205                                ii->ro,
00206                                ii->uidplus);
00207 
00208        if (rc)
00209               rc= -1;
00210        return rc;
00211 }
00212 
00213 /* This structure is a temporary home for the filenames */
00214 
00215 struct tempinfo {
00216        struct tempinfo *next;
00217        char *filename;
00218        unsigned long uid;
00219        int    found;
00220        int    isrecent;
00221        } ;
00222 
00223 static char *imapscan_namedir(const char *dir, const char *new_or_cur)
00224 {
00225 char   *p=malloc(strlen(dir)+strlen(new_or_cur)+2);
00226 
00227        if (!p)       write_error_exit(0);
00228        strcat(strcat(strcpy(p, dir), "/"), new_or_cur);
00229        return (p);
00230 }
00231 
00232 static int fnamcmp(const char *a, const char *b)
00233 {
00234        long ai, bi;
00235        char ca, cb;
00236 
00237        ai = atol(a);
00238        bi = atol(b);
00239        if(ai - bi)
00240               return ai - bi;
00241 
00242        do
00243        {
00244               ca= *a++;
00245               cb= *b++;
00246 
00247               if (ca == ':') ca=0;
00248               if (cb == ':') cb=0;
00249        } while (ca && cb && ca == cb);
00250 
00251 
00252        return ( (int)(unsigned char)ca - (int)(unsigned char)cb);
00253 }
00254 
00255 static int sort_by_filename(struct tempinfo **a, struct tempinfo **b)
00256 {
00257        return (fnamcmp( (*a)->filename, (*b)->filename));
00258 }
00259 
00260 static int sort_by_filename_status(struct tempinfo **a, struct tempinfo **b)
00261 {
00262        if ( (*a)->found && (*b)->found )
00263        {
00264               if ( (*a)->uid < (*b)->uid )
00265                      return (-1);
00266               if ( (*a)->uid > (*b)->uid )
00267                      return (1);
00268               return (0);   /* What the fuck??? */
00269        }
00270        if ( (*a)->found )   return (-1);
00271        if ( (*b)->found )   return (1);
00272 
00273        return (fnamcmp( (*a)->filename, (*b)->filename));
00274 }
00275 
00276 /* Binary search on an array of tempinfos which is sorted by filenames */
00277 
00278 static int search_by_filename(struct tempinfo **a, unsigned s, unsigned *i,
00279        const char *filename)
00280 {
00281 unsigned lo=0, hi=s, mid;
00282 int    rc;
00283 
00284        while (lo < hi)
00285        {
00286               mid=(hi+lo)/2;
00287               rc=fnamcmp( a[mid]->filename, filename);
00288               if (rc < 0)
00289               {
00290                      lo=mid+1;
00291                      continue;
00292               }
00293               if (rc > 0)
00294               {
00295                      hi=mid;
00296                      continue;
00297               }
00298               *i=mid;
00299               return (0);
00300        }
00301        return (-1);
00302 }
00303 
00304 void imapscanfail(const char *p)
00305 {
00306        fprintf(stderr, "ERR: Failed to create cache file: %s (%s)\n", p,
00307               getenv("AUTHENTICATED"));
00308 #if    HAVE_STRERROR
00309        fprintf(stderr, "ERR: Error: %s\n", strerror(errno));
00310 #endif
00311 
00312 #if    HAVE_FAM
00313        if (errno == EIO)
00314        {
00315               fprintf(stderr,
00316                      "ERR: Check for proper operation and configuration\n"
00317                      "ERR: of the File Access Monitor daemon (famd).\n");
00318        }
00319 #endif
00320 }
00321 
00322 static char *readbuf;
00323 static unsigned readbufsize=0;
00324 
00325 char *readline(unsigned i, FILE *fp)
00326 {
00327 int    c;
00328 
00329        for (;;)
00330        {
00331               if (i >= 10000)
00332                      --i;   /* DOS check */
00333 
00334               if (i >= readbufsize)
00335               {
00336               char   *p= readbuf ? realloc(readbuf, readbufsize+256):
00337                                    malloc(readbufsize+256);
00338 
00339                      if (!p)       write_error_exit(0);
00340                      readbuf=p;
00341                      readbufsize += 256;
00342               }
00343 
00344               c=getc(fp);
00345               if (c == EOF || c == '\n')
00346               {
00347                      readbuf[i]=0;
00348                      return (c == EOF ? 0:readbuf);
00349               }
00350               readbuf[i++]=c;
00351        }
00352 }
00353 
00354 static int do_imapscan_maildir2(struct imapscaninfo *scaninfo,
00355                             const char *dir, int leavenew, int ro,
00356                             struct uidplus_info *uidplus)
00357 {
00358 char   *dbfilepath, *newdbfilepath;
00359 struct tempinfo *tempinfo_list=0, **tempinfo_array=0, *tempinfoptr;
00360 struct tempinfo *newtempinfo_list=0;
00361 unsigned      tempinfo_cnt=0, i;
00362 FILE   *fp;
00363 char   *p, *q;
00364 unsigned long uidv, nextuid;
00365 int    version;
00366 struct stat   stat_buf;
00367 DIR    *dirp;
00368 struct dirent *de;
00369 unsigned long left_unseen=0;
00370 int    dowritecache=0;
00371 
00372        if (is_sharedsubdir(dir))
00373               maildir_shared_sync(dir);
00374 
00375 
00376        /* Step 0 - purge the tmp directory */
00377 
00378        maildir_purgetmp(dir);
00379 
00380        dbfilepath=malloc(strlen(dir)+sizeof("/" IMAPDB));
00381        if (!dbfilepath)     write_error_exit(0);
00382        strcat(strcpy(dbfilepath, dir), "/" IMAPDB);
00383 
00384        /*
00385        ** We may need to rebuild the UID cache file.  Create the new cache
00386        ** file in the tmp subdirectory.
00387        */
00388 
00389        {
00390               char   uniqbuf[80];
00391               static  unsigned tmpuniqcnt=0;
00392               struct maildir_tmpcreate_info createInfo;
00393               int fd;
00394 
00395               maildir_tmpcreate_init(&createInfo);
00396 
00397               createInfo.maildir=dir;
00398               createInfo.hostname=getenv("HOSTNAME");
00399               sprintf(uniqbuf, "imapuid_%u", tmpuniqcnt++);
00400               createInfo.uniq=uniqbuf;
00401               createInfo.doordie=1;
00402 
00403               if ((fd=maildir_tmpcreate_fd(&createInfo)) < 0)
00404               {
00405                      write_error_exit(0);
00406               }
00407               close(fd);
00408 
00409               newdbfilepath=createInfo.tmpname;
00410               createInfo.tmpname=NULL;
00411               maildir_tmpcreate_free(&createInfo);
00412        }
00413 
00414        /* Step 1 - read the cache file */
00415 
00416        if ((fp=fopen(dbfilepath, "r")) != 0 &&
00417               (p=readline(0, fp)) != 0 &&
00418               sscanf(p, "%d %lu %lu", &version, &uidv, &nextuid) == 3 &&
00419               version == IMAPDBVERSION)
00420        {
00421               while ((p=readline(0, fp)) != 0)
00422               {
00423               char   *q=strchr(p, ' ');
00424               unsigned long uid;
00425               struct tempinfo      *newtmpl;
00426 
00427                      if (!q)       continue;
00428                      *q++=0;
00429                      if (sscanf(p, "%lu", &uid) != 1)   continue;
00430                      if ((newtmpl=(struct tempinfo *)
00431                             malloc(sizeof(struct tempinfo))) == 0
00432                             || (newtmpl->filename=strdup(q)) == 0)
00433                      {
00434                             unlink(newdbfilepath);
00435                             write_error_exit(0);
00436                      }
00437                      newtmpl->next=tempinfo_list;
00438                      tempinfo_list=newtmpl;
00439                      newtmpl->uid=uid;
00440                      newtmpl->found=0;
00441                      newtmpl->isrecent=0;
00442                      ++tempinfo_cnt;
00443               }
00444               fclose(fp);
00445               fp=0;
00446        }
00447        else if(!ro)
00448        {
00449 
00450        /* First time - create the cache file */
00451 
00452               if (fp)       fclose(fp);
00453               nextuid=1;
00454               if ((fp=fopen(newdbfilepath, "w")) == 0 ||
00455                      fstat(fileno(fp), &stat_buf) != 0)
00456               {
00457                      if (fp)       fclose(fp);
00458                      imapscanfail(newdbfilepath);
00459 
00460                      /* bk: ignore error */
00461                      unlink(newdbfilepath);
00462                      unlink(dbfilepath);
00463                      fp = 0;
00464                      /*
00465                      free(dbfilepath);
00466                      unlink(newdbfilepath);
00467                      free(newdbfilepath);
00468                      return (-1);
00469                      */
00470               }
00471               uidv=stat_buf.st_mtime - IMAP_EPOCH;
00472               dowritecache=1;
00473        }
00474        else
00475        {
00476               nextuid=1;
00477               uidv=time(0) - IMAP_EPOCH;
00478        }
00479 
00480        while (uidplus)
00481        {
00482               struct tempinfo      *newtmpl;
00483 
00484               if (uidplus->tmpkeywords)
00485                      if (rename(uidplus->tmpkeywords,
00486                                uidplus->newkeywords) < 0)
00487                      {
00488                             struct libmail_kwGeneric g;
00489 
00490                             /*
00491                             ** Maybe courierimapkeywords needs to be
00492                             ** created.
00493                             */
00494 
00495                             libmail_kwgInit(&g);
00496                             libmail_kwgReadMaildir(&g, dir);
00497                             libmail_kwgDestroy(&g);
00498 
00499                             rename(uidplus->tmpkeywords,
00500                                    uidplus->newkeywords);
00501                      }
00502 
00503               maildir_movetmpnew(uidplus->tmpfilename,
00504                                uidplus->curfilename);
00505 
00506               if (uidplus->mtime)
00507                      set_time (uidplus->curfilename, uidplus->mtime);
00508 
00509               if ((newtmpl=(struct tempinfo *)
00510                    malloc(sizeof(struct tempinfo))) == 0
00511                   || (newtmpl->filename=strdup(strrchr(uidplus->curfilename,
00512                                                   '/')+1)) == 0)
00513               {
00514                      unlink(newdbfilepath);
00515                      write_error_exit(0);
00516               }
00517 
00518               if ((p=strrchr(newtmpl->filename, MDIRSEP[0])) != 0)
00519                      *p=0;
00520 
00521               newtmpl->next=tempinfo_list;
00522               tempinfo_list=newtmpl;
00523               newtmpl->uid=nextuid;
00524               uidplus->uid=nextuid;
00525               nextuid++;
00526               newtmpl->found=0;
00527               newtmpl->isrecent=0;
00528               ++tempinfo_cnt;
00529 
00530               uidplus=uidplus->next;
00531               dowritecache=1;
00532        }
00533 
00534        if (!fp && (fp=fopen(newdbfilepath, "w")) == 0)
00535        {
00536               imapscanfail(newdbfilepath);
00537 
00538               /* bk: ignore error */
00539               unlink(newdbfilepath);
00540               unlink(dbfilepath);
00541               /*
00542               free(dbfilepath);
00543               unlink(newdbfilepath);
00544               free(newdbfilepath);
00545               while (tempinfo_list)
00546               {
00547                      tempinfoptr=tempinfo_list;
00548                      tempinfo_list=tempinfoptr->next;
00549                      free(tempinfoptr->filename);
00550                      free(tempinfoptr);
00551               }
00552               return (-1);
00553               */
00554        }
00555 
00556        /*
00557        ** Convert the link list of cached files to an array, then
00558        ** sort it by filename.
00559        */
00560 
00561        if ((tempinfo_array=(struct tempinfo **)malloc(
00562               (tempinfo_cnt+1)*sizeof(struct tempinfo *))) == 0)
00563        {
00564               unlink(newdbfilepath);
00565               write_error_exit(0);
00566        }
00567 
00568        for (i=0, tempinfoptr=tempinfo_list; tempinfoptr;
00569               tempinfoptr=tempinfoptr->next, i++)
00570               tempinfo_array[i]=tempinfoptr;
00571 
00572        if (tempinfo_cnt)
00573               qsort(tempinfo_array, tempinfo_cnt,
00574                      sizeof(tempinfo_array[0]),
00575                      ( int (*)(const void *, const void *))
00576                             &sort_by_filename);
00577 
00578        /* Step 2 - read maildir/cur.  Search the array.  Mark found files. */
00579 
00580        p=imapscan_namedir(dir, "cur");
00581        dirp=opendir(p);
00582        free(p);
00583        while (dirp && (de=readdir(dirp)) != 0)
00584        {
00585        int    rc;
00586        struct tempinfo      *newtmpl;
00587 
00588               if (de->d_name[0] == '.')   continue;
00589 
00590               p=my_strdup(de->d_name);
00591 
00592               /* IMAPDB doesn't store the filename flags, so strip them */
00593               q=strrchr(p, MDIRSEP[0]);
00594               if (q) *q=0;
00595               rc=search_by_filename(tempinfo_array, tempinfo_cnt, &i, p);
00596               if (q) *q=MDIRSEP[0];
00597               if (rc == 0)
00598               {
00599                      tempinfo_array[i]->found=1;
00600                      free(tempinfo_array[i]->filename);
00601                      tempinfo_array[i]->filename=p;
00602                             /* Keep the full filename */
00603                      continue;
00604               }
00605 
00606               if ((newtmpl=(struct tempinfo *)
00607                      malloc(sizeof(struct tempinfo))) == 0)
00608               {
00609                      unlink(newdbfilepath);
00610                      write_error_exit(0);
00611               }
00612               newtmpl->filename=p;
00613               newtmpl->next=newtempinfo_list;
00614               newtmpl->found=0;
00615               newtmpl->isrecent=1;
00616               newtempinfo_list=newtmpl;
00617               dowritecache=1;
00618        }
00619        if (dirp)     closedir(dirp);
00620 
00621        /* Step 3 - purge messages that no longer exist in the maildir */
00622 
00623        free(tempinfo_array);
00624 
00625        for (tempinfo_array= &tempinfo_list; *tempinfo_array; )
00626        {
00627               if ( (*tempinfo_array)->found )
00628               {
00629                      tempinfo_array= & (*tempinfo_array)->next;
00630                      continue;
00631               }
00632               tempinfoptr= *tempinfo_array;
00633               *tempinfo_array=tempinfoptr->next;
00634               free(tempinfoptr->filename);
00635               free(tempinfoptr);
00636               --tempinfo_cnt;
00637               dowritecache=1;
00638        }
00639 
00640        /* Step 4 - add messages in cur that are not in the cache file */
00641 
00642        while (newtempinfo_list)
00643        {
00644               tempinfoptr=newtempinfo_list;
00645               newtempinfo_list=tempinfoptr->next;
00646 
00647               tempinfoptr->next=tempinfo_list;
00648               tempinfo_list=tempinfoptr;
00649               ++tempinfo_cnt;
00650        }
00651 
00652        /* Step 5 - read maildir/new.  */
00653 
00654        p=imapscan_namedir(dir, "new");
00655 
00656        if (leavenew)
00657        {
00658               dirp=opendir(p);
00659               while (dirp && (de=readdir(dirp)) != 0)
00660               {
00661                      if (de->d_name[0] == '.')   continue;
00662                      ++left_unseen;
00663               }
00664               if (dirp)     closedir(dirp);
00665        }
00666        else
00667               /*
00668               ** Some filesystems keel over if we delete files while
00669               ** reading the directory where the files are.
00670               ** Accomodate them by processing 20 files at a time.
00671               */
00672        {
00673               char *new_buf[20];
00674               char *cur_buf[20];
00675               int keepgoing;
00676               int n;
00677 
00678               do
00679               {
00680                      n=0;
00681                      keepgoing=0;
00682 
00683                      dirp=opendir(p);
00684                      while (dirp && (de=readdir(dirp)) != 0)
00685                      {
00686                             struct tempinfo      *newtmpl;
00687                             char   *newname, *curname;
00688                             char   *z;
00689 
00690                             if (de->d_name[0] == '.')   continue;
00691 
00692                             z=de->d_name;
00693 
00694                             newname=imapscan_namedir(p, z);
00695                             curname=malloc(strlen(newname)
00696                                           +sizeof(MDIRSEP "2,"));
00697                             if (!curname)
00698                             {
00699                                    unlink(newdbfilepath);
00700                                    write_error_exit(0);
00701                             }
00702                             strcpy(curname, newname);
00703                             z=strrchr(curname, '/');
00704 
00705                             memcpy(z-3, "cur", 3);
00706                             /* Mother of all hacks */
00707                             if (strchr(z, MDIRSEP[0]) == 0)
00708                                    strcat(z, MDIRSEP "2,");
00709 
00710                             new_buf[n]=newname;
00711                             cur_buf[n]=curname;
00712 
00713                             if ((newtmpl=(struct tempinfo *)
00714                                  malloc(sizeof(struct tempinfo))) == 0)
00715                             {
00716                                    unlink(newdbfilepath);
00717                                    write_error_exit(0);
00718                             }
00719                             newtmpl->filename=my_strdup(z+1);
00720                             newtmpl->next=tempinfo_list;
00721                             tempinfo_list=newtmpl;
00722                             ++tempinfo_cnt;
00723                             newtmpl->found=0;
00724                             newtmpl->isrecent=1;
00725                             dowritecache=1;
00726 
00727                             if (++n >= sizeof(cur_buf)/
00728                                 sizeof(cur_buf[0]))
00729                             {
00730                                    keepgoing=1;
00731                                    break;
00732                             }
00733                      }
00734 
00735                      if (dirp)     closedir(dirp);
00736 
00737                      while (n)
00738                      {
00739                             char *newname, *curname;
00740 
00741                             --n;
00742 
00743                             newname=new_buf[n];
00744                             curname=cur_buf[n];
00745 
00746                             if (rename(newname, curname))
00747                             {
00748                                    fprintf(stderr,
00749                                           "ERR: rename(%s,%s) failed:"
00750                                           " %s\n",
00751                                           newname, curname,
00752                                           strerror(errno));
00753                                    keepgoing=0;
00754                                    /* otherwise we could have infinite loop */
00755                             }
00756 
00757                             free(newname);
00758                             free(curname);
00759                      }
00760               } while (keepgoing);
00761        }
00762        free(p);
00763 
00764        /*
00765        ** Step 6: sort existing messages by UIDs, new messages will
00766        ** sort after all messages with UIDs, and new messages are
00767        ** sorted by filename, so that they end up roughly in the order
00768        ** they were received.
00769        */
00770 
00771        if ((tempinfo_array=(struct tempinfo **)malloc(
00772               (tempinfo_cnt+1)*sizeof(struct tempinfo *))) == 0)
00773        {
00774               unlink(newdbfilepath);
00775               write_error_exit(0);
00776        }
00777 
00778        for (i=0, tempinfoptr=tempinfo_list; tempinfoptr;
00779               tempinfoptr=tempinfoptr->next, i++)
00780               tempinfo_array[i]=tempinfoptr;
00781 
00782        if (tempinfo_cnt)
00783               qsort(tempinfo_array, tempinfo_cnt,
00784                      sizeof(tempinfo_array[0]),
00785                      ( int (*)(const void *, const void *))
00786                             &sort_by_filename_status);
00787 
00788        /* Assign new UIDs */
00789 
00790        for (i=0; i<tempinfo_cnt; i++)
00791               if ( !tempinfo_array[i]->found )
00792               {
00793                      tempinfo_array[i]->uid= nextuid++;
00794                      dowritecache=1;
00795               }
00796 
00797        /* bk: ignore if failed to open file */
00798        if (!ro && dowritecache && fp)
00799        {
00800               int need_fclose;
00801 
00802        /* Step 7 - write out the new cache file */
00803 
00804               version=IMAPDBVERSION;
00805               fprintf(fp, "%d %lu %lu\n", version, uidv, nextuid);
00806 
00807               for (i=0; i<tempinfo_cnt; i++)
00808               {
00809                      q=strrchr(tempinfo_array[i]->filename, MDIRSEP[0]);
00810                      if (q)  *q=0;
00811                      fprintf(fp, "%lu %s\n", tempinfo_array[i]->uid,
00812                             tempinfo_array[i]->filename);
00813                      if (q) *q=MDIRSEP[0];
00814               }
00815 
00816               need_fclose=1;
00817               if (fflush(fp) || ferror(fp) || ((need_fclose=0), fclose(fp)))
00818               {
00819                      imapscanfail(dir);
00820                      if (need_fclose)
00821                             fclose(fp);
00822                      /* bk: ignore if failed */
00823                      unlink(newdbfilepath);
00824                      unlink(dbfilepath);
00825                      /*
00826                      free(tempinfo_array);
00827                      free(dbfilepath);
00828                      unlink(newdbfilepath);
00829                      free(newdbfilepath);
00830                      while (tempinfo_list)
00831                      {
00832                             tempinfoptr=tempinfo_list;
00833                             tempinfo_list=tempinfoptr->next;
00834                             free(tempinfoptr->filename);
00835                             free(tempinfoptr);
00836                      }
00837                      return (-1);
00838                      */
00839               }
00840               /* bk */
00841               else
00842 
00843               rename(newdbfilepath, dbfilepath);
00844        }
00845        else
00846        {
00847               if (fp)
00848                      fclose(fp);
00849               unlink(newdbfilepath);
00850        }
00851        free(dbfilepath);
00852        free(newdbfilepath);
00853 
00854        /* Step 8 - create the final scaninfo array */
00855 
00856        scaninfo->msgs=0;
00857        if (tempinfo_cnt && (scaninfo->msgs=(struct imapscanmessageinfo *)
00858               malloc(tempinfo_cnt * sizeof(*scaninfo->msgs))) == 0)
00859               write_error_exit(0);
00860        scaninfo->nmessages=tempinfo_cnt;
00861        scaninfo->uidv=uidv;
00862        scaninfo->left_unseen=left_unseen;
00863        scaninfo->nextuid=nextuid+left_unseen;
00864 
00865        for (i=0; i<tempinfo_cnt; i++)
00866        {
00867               scaninfo->msgs[i].uid=tempinfo_array[i]->uid;
00868               scaninfo->msgs[i].filename=tempinfo_array[i]->filename;
00869               scaninfo->msgs[i].keywordMsg=NULL;
00870               scaninfo->msgs[i].copiedflag=0;
00871 
00872 #if SMAP
00873               if (smapflag)
00874                      scaninfo->msgs[i].recentflag=0;
00875               else
00876 #endif
00877                      scaninfo->msgs[i].recentflag=
00878                             tempinfo_array[i]->isrecent;
00879               scaninfo->msgs[i].changedflags=0;
00880 
00881               free(tempinfo_array[i]);
00882        }
00883        free(tempinfo_array);
00884 
00885        imapscan_readKeywords(dir, scaninfo);
00886 
00887 
00888        return (0);
00889 }
00890 
00891 static int try_maildir_open(const char *dir, struct imapscanmessageinfo *n)
00892 {
00893 int    fd;
00894 char   *filename=maildir_filename(dir, 0, n->filename);
00895 char   *p;
00896 
00897        if (!filename)
00898        {
00899               return (0);
00900        }
00901 
00902        p=strrchr(filename, '/')+1;
00903 
00904        if (strcmp(p, n->filename))
00905        {
00906               n->changedflags=1;
00907               free(n->filename);
00908               n->filename=malloc(strlen(p)+1);
00909               if (!n->filename)    write_error_exit(0);
00910               strcpy(n->filename, p);
00911        }
00912 
00913        fd=maildir_semisafeopen(filename, O_RDONLY, 0);
00914        free(filename);
00915        return (fd);
00916 }
00917 
00918 int imapscan_openfile(const char *dir, struct imapscaninfo *i, unsigned j)
00919 {
00920 struct imapscanmessageinfo *n;
00921 
00922        if (j >= i->nmessages)
00923        {
00924               errno=EINVAL;
00925               return (-1);
00926        }
00927 
00928        n=i->msgs+j;
00929 
00930        return (try_maildir_open(dir, n));
00931 }
00932 
00933 void imapscan_free(struct imapscaninfo *i)
00934 {
00935        unsigned      n;
00936 
00937        if (i->watcher)
00938        {
00939               maildirwatch_free(i->watcher);
00940               i->watcher=NULL;
00941        }
00942 
00943        if (i->msgs)
00944        {
00945               for (n=0; n<i->nmessages; n++)
00946               {
00947                      if (i->msgs[n].filename)
00948                             free(i->msgs[n].filename);
00949 
00950                      if (i->msgs[n].keywordMsg)
00951                             libmail_kwmDestroy(i->msgs[n].keywordMsg);
00952 
00953               }
00954               free(i->msgs);
00955               i->msgs=0;
00956        }
00957 
00958        if (i->keywordList)
00959        {
00960               if (libmail_kwhCheck(i->keywordList) < 0)
00961                      write_error_exit("INTERNAL ERROR: Keyword hashtable "
00962                                     "memory corruption.");
00963 
00964               free(i->keywordList);
00965               i->keywordList=NULL;
00966        }
00967 }
00968 
00969 /*
00970 ** Keyword-related stuff  See README.imapkeywords.html for more information.
00971 */
00972 
00973 extern char *current_mailbox;
00974 
00975 int imapscan_updateKeywords(const char *filename,
00976                          struct libmail_kwMessage *newKeyword)
00977 {
00978        char *tmpname, *newname;
00979        int rc;
00980 
00981        if (maildir_kwSave(current_mailbox, filename, newKeyword,
00982                         &tmpname, &newname, 0))
00983        {
00984               perror("maildir_kwSave");
00985               return -1;
00986        }
00987 
00988        rc=rename(tmpname, newname);
00989 
00990        if (rc)
00991        {
00992               perror(tmpname);
00993               unlink(tmpname);
00994        }
00995        free(tmpname);
00996        free(newname);
00997        return rc;
00998 }
00999 
01000 static unsigned long hashFilename(const char *fn, struct imapscaninfo *info)
01001 {
01002        unsigned long hashBucket=0;
01003 
01004        while (*fn && *fn != MDIRSEP[0])
01005        {
01006               hashBucket=(hashBucket << 1) ^ (hashBucket & 0x8000 ? 0x1301:0)
01007                      ^ (unsigned char)*fn++;
01008        }
01009        hashBucket=hashBucket & 0xFFFF;
01010 
01011        return hashBucket % info->nmessages; /* Cannot get here if its zero */
01012 }
01013 
01014 struct imapscanReadKeywordInfo {
01015        struct maildir_kwReadInfo ri;
01016 
01017        struct imapscaninfo *messages;
01018        int hashedFilenames;
01019 };
01020 
01021 static struct libmail_kwMessage **findMessageByFilename(const char *filename,
01022                                                int autocreate,
01023                                                size_t *indexNum,
01024                                                void *voidarg)
01025 {
01026        struct imapscanReadKeywordInfo *info=
01027               (struct imapscanReadKeywordInfo *)voidarg;
01028 
01029        size_t l;
01030        struct imapscanmessageinfo *i;
01031 
01032        struct imapscaninfo *scaninfo=info->messages;
01033 
01034        if (!info->hashedFilenames)
01035        {
01036               unsigned long n;
01037 
01038               for (n=0; n<scaninfo->nmessages; n++)
01039                      scaninfo->msgs[n].firstBucket=NULL;
01040 
01041               for (n=0; n<scaninfo->nmessages; n++)
01042               {
01043                      unsigned long bucket=hashFilename(scaninfo->msgs[n]
01044                                                    .filename,
01045                                                    scaninfo);
01046 
01047                      scaninfo->msgs[n].nextBucket=
01048                             scaninfo->msgs[bucket].firstBucket;
01049 
01050                      scaninfo->msgs[bucket].firstBucket=scaninfo->msgs+n;
01051               }
01052               info->hashedFilenames=1;
01053        }
01054 
01055        l=strlen(filename);
01056 
01057        for (i= scaninfo->nmessages ?
01058                    scaninfo->msgs[hashFilename(filename, scaninfo)]
01059                    .firstBucket:NULL; i; i=i->nextBucket)
01060        {
01061               if (strncmp(i->filename, filename, l))
01062                      continue;
01063 
01064               if (i->filename[l] == 0 ||
01065                   i->filename[l] == MDIRSEP[0])
01066                      break;
01067        }
01068 
01069        if (!i)
01070               return NULL;
01071 
01072        if (indexNum)
01073               *indexNum= i-scaninfo->msgs;
01074 
01075        if (!i->keywordMsg && autocreate)
01076               imapscan_createKeyword(info->messages, i-scaninfo->msgs);
01077 
01078        return &i->keywordMsg;
01079 }
01080 
01081 static size_t getMessageCount(void *voidarg)
01082 {
01083        struct imapscanReadKeywordInfo *info=
01084               (struct imapscanReadKeywordInfo *)voidarg;
01085 
01086        return info->messages->nmessages;
01087 }
01088 
01089 static const char *getMessageFilename(size_t n, void *voidarg)
01090 {
01091        struct imapscanReadKeywordInfo *info=
01092               (struct imapscanReadKeywordInfo *)voidarg;
01093 
01094        if (n >= info->messages->nmessages)
01095               return NULL;
01096 
01097        return info->messages->msgs[n].filename;
01098 }
01099 
01100 static void updateKeywords(size_t n, struct libmail_kwMessage *kw,
01101                         void *voidarg)
01102 {
01103        struct imapscanReadKeywordInfo *info=
01104               (struct imapscanReadKeywordInfo *)voidarg;
01105 
01106        if (n >= info->messages->nmessages)
01107               return;
01108 
01109        if (info->messages->msgs[n].keywordMsg)
01110               libmail_kwmDestroy(info->messages->msgs[n].keywordMsg);
01111 
01112        kw->u.userNum=n;
01113        info->messages->msgs[n].keywordMsg=kw;
01114 }
01115 
01116 static struct libmail_kwHashtable * getKeywordHashtable(void *voidarg)
01117 {
01118        struct imapscanReadKeywordInfo *info=
01119               (struct imapscanReadKeywordInfo *)voidarg;
01120 
01121        return info->messages->keywordList;
01122 }
01123 
01124 static struct libmail_kwMessage **findMessageByIndex(size_t indexNum,
01125                                             int autocreate,
01126                                             void *voidarg)
01127 {
01128        struct imapscanReadKeywordInfo *info=
01129               (struct imapscanReadKeywordInfo *)voidarg;
01130        struct imapscanmessageinfo *i;
01131 
01132        if (indexNum >= info->messages->nmessages)
01133               return NULL;
01134 
01135        i= &info->messages->msgs[indexNum];
01136 
01137        if (!i->keywordMsg && autocreate)
01138               imapscan_createKeyword(info->messages, indexNum);
01139 
01140        return &i->keywordMsg;
01141 }
01142 
01143 static void initri(struct imapscanReadKeywordInfo *rki)
01144 {
01145        memset(rki, 0, sizeof(*rki));
01146 
01147        rki->ri.findMessageByFilename= &findMessageByFilename;
01148        rki->ri.getMessageCount= &getMessageCount;
01149        rki->ri.findMessageByIndex= &findMessageByIndex;
01150        rki->ri.getKeywordHashtable= &getKeywordHashtable;
01151        rki->ri.getMessageFilename= &getMessageFilename;
01152        rki->ri.updateKeywords= &updateKeywords;
01153        rki->ri.voidarg= rki;
01154 }
01155 
01156 void imapscan_readKeywords(const char *maildir,
01157                         struct imapscaninfo *scaninfo)
01158 {
01159        struct imapscanReadKeywordInfo rki;
01160 
01161        initri(&rki);
01162 
01163        do
01164        {
01165               unsigned long i;
01166 
01167               for (i=0; i<scaninfo->nmessages; i++)
01168                      if (scaninfo->msgs[i].keywordMsg)
01169                      {
01170                             libmail_kwmDestroy(scaninfo->msgs[i]
01171                                                 .keywordMsg);
01172                             scaninfo->msgs[i].keywordMsg=NULL;
01173                      }
01174 
01175               rki.messages=scaninfo;
01176 
01177               if (maildir_kwRead(maildir, &rki.ri) < 0)
01178                      write_error_exit(0);
01179 
01180        } while (rki.ri.tryagain);
01181 }
01182 
01183 int imapscan_restoreKeywordSnapshot(FILE *fp, struct imapscaninfo *scaninfo)
01184 {
01185        struct imapscanReadKeywordInfo rki;
01186 
01187        initri(&rki);
01188 
01189        rki.messages=scaninfo;
01190        return maildir_kwImport(fp, &rki.ri);
01191 }
01192 
01193 int imapscan_saveKeywordSnapshot(FILE *fp, struct imapscaninfo *scaninfo)
01194 {
01195        struct imapscanReadKeywordInfo rki;
01196 
01197        initri(&rki);
01198 
01199        rki.messages=scaninfo;
01200        return maildir_kwExport(fp, &rki.ri);
01201 }