Back to index

courier  0.68.2
maildir.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2011 Double Precision, Inc.  See COPYING for
00003 ** distribution information.
00004 */
00005 
00006 
00007 /*
00008 */
00009 #include      "config.h"
00010 #include      <stdio.h>
00011 #include      <string.h>
00012 #include      <stdlib.h>
00013 #include      <ctype.h>
00014 #include      <fcntl.h>
00015 #include      <errno.h>
00016 #include      "sqwebmail.h"
00017 #include      "maildir.h"
00018 #include      "folder.h"
00019 #include      "rfc822/rfc822.h"
00020 #include      "rfc822/rfc2047.h"
00021 #include      "rfc2045/rfc2045.h"
00022 #include      "cgi/cgi.h"
00023 #include      "pref.h"
00024 #include      "sqconfig.h"
00025 #include      "dbobj.h"
00026 #include      "auth.h"
00027 #include      "acl.h"
00028 #include      "maildir/maildirquota.h"
00029 #include      "maildir/maildirrequota.h"
00030 #include      "maildir/maildirgetquota.h"
00031 #include      "maildir/maildirmisc.h"
00032 #include      "maildir/maildircreate.h"
00033 #include      "maildir/maildirinfo.h"
00034 #include      "maildir/maildiraclt.h"
00035 #include      "maildir/maildirsearch.h"
00036 #include      "htmllibdir.h"
00037 
00038 #if    HAVE_UNISTD_H
00039 #include      <unistd.h>
00040 #endif
00041 #include      "unicode/unicode.h"
00042 
00043 #if    HAVE_DIRENT_H
00044 #include      <dirent.h>
00045 #define       NAMLEN(dirent)       strlen(dirent->d_name)
00046 #else
00047 #define       dirent direct
00048 #define       NAMLEN(dirent)       ((dirent)->d_namlen)
00049 #if    HAVE_SYS_NDIR_H
00050 #include      <sys/ndir.h>
00051 #endif
00052 #if    HAVE_SYS_DIR_H
00053 #include      <sys/dir.h>
00054 #endif
00055 #if    HAVE_NDIR_H
00056 #include      <ndir.h>
00057 #endif
00058 #endif
00059 
00060 #include      <sys/types.h>
00061 #include      <sys/stat.h>
00062 #if    HAVE_UTIME_H
00063 #include      <utime.h>
00064 #endif
00065 
00066 #include      "unicode/unicode.h"
00067 
00068 #include      "strftime.h"
00069 
00070 static time_t current_time;
00071 
00072 extern time_t rfc822_parsedt(const char *);
00073 extern const char *sqwebmail_content_charset;
00074 extern const char *sqwebmail_mailboxid;
00075 
00076 static char *folderdatname=0;      /* Which folder has been cached */
00077 static struct dbobj folderdat;
00078 static const char *folderdatmode;  /* "R" or "W" */
00079 
00080 static time_t cachemtime;   /* Modification time of the cache file */
00081 static unsigned long new_cnt, all_cnt;
00082 static int isoldestfirst;
00083 static char sortorder;
00084 
00085 static void createmdcache(const char *, const char *);
00086 
00087 static void maildir_getfoldermsgs(const char *);
00088 
00089 /* Change timestamp on a file */
00090 
00091 static const char *parse_ul(const char *p, unsigned long *ul)
00092 {
00093 int    err=1;
00094 
00095        while (p && isspace((int)(unsigned char)*p))     ++p;
00096        *ul=0;
00097        while (p && *p >= '0' && *p <= '9')
00098        {
00099               *ul *= 10;
00100               *ul += (*p-'0');
00101               err=0;
00102               ++p;
00103        }
00104        if (err)      return (0);
00105        return (p);
00106 }
00107 
00108 static void change_timestamp(const char *filename, time_t t)
00109 {
00110 #if    HAVE_UTIME
00111 struct utimbuf       ut;
00112 
00113        ut.actime=ut.modtime=t;
00114        utime(filename, &ut);
00115 #else
00116 #if    HAVE_UTIMES
00117 struct timeval tv;
00118 
00119        tv.tv_sec=t;
00120        tv.tv_usec=0;
00121        utimes(filename, &tv);
00122 #else
00123 #error You do not have utime or utimes function.  Upgrade your operating system.
00124 #endif
00125 #endif
00126 }
00127 
00128 /* Translate folder name into directory name */
00129 
00130 static char *xlate_mdir(const char *foldername)
00131 {
00132        struct maildir_info minfo;
00133        char   *p;
00134 
00135        if (maildir_info_imap_find(&minfo, foldername,
00136                                login_returnaddr())<0)
00137        {
00138               cginocache();
00139               printf("Content-Type: text/plain\n\n"
00140                      "Unable to translate mailbox: %s\n",
00141                      foldername);
00142               cleanup();
00143               fake_exit(1);
00144        }
00145 
00146        if (minfo.homedir == NULL || minfo.maildir == NULL)
00147        {
00148               maildir_info_destroy(&minfo);
00149               cginocache();
00150               printf("Content-Type: text/plain\n\n"
00151                      "Mailbox \"%s\" is not supported.\n",
00152                      foldername);
00153               cleanup();
00154               fake_exit(1);
00155               enomem();
00156        }
00157 
00158        p=maildir_name2dir(minfo.homedir, minfo.maildir);
00159 
00160        maildir_info_destroy(&minfo);
00161        return p;
00162 }
00163 
00164 static char *xlate_shmdir(const char *foldername)
00165 {
00166        struct maildir_info minfo;
00167        char   *p;
00168 
00169        if (maildir_info_imap_find(&minfo, foldername,
00170                                login_returnaddr())<0)
00171        {
00172               printf("Content-Type: text/plain\n\n%s\n", foldername);
00173               enomem();
00174        }
00175 
00176        if (minfo.mailbox_type == MAILBOXTYPE_OLDSHARED)
00177               return maildir_shareddir(".", strchr(foldername, '.')+1);
00178 
00179        if (minfo.homedir == NULL || minfo.maildir == NULL)
00180        {
00181               maildir_info_destroy(&minfo);
00182               enomem();
00183        }
00184 
00185        p=maildir_name2dir(minfo.homedir, minfo.maildir);
00186 
00187        maildir_info_destroy(&minfo);
00188        return p;
00189 }
00190 
00191 /* Display message size in meaningfull form */
00192 
00193 static void cat_n(char *buf, unsigned long n)
00194 {
00195 char   bb[MAXLONGSIZE+1];
00196 char   *p=bb+sizeof(bb)-1;
00197 
00198        *p=0;
00199        do
00200        {
00201               *--p = "0123456789"[n % 10];
00202               n=n/10;
00203        } while (n);
00204        strcat(buf, p);
00205 }
00206 
00207 const char *showsize(unsigned long n)
00208 {
00209 static char   sizebuf[MAXLONGSIZE+10];
00210 
00211        /* If size is less than 1K bytes, display it as 0.xK */
00212 
00213        if (n < 1024)
00214        {
00215               strcpy(sizebuf, "0.");
00216               cat_n(sizebuf, (int)(10 * n / 1024 ));
00217               strcat(sizebuf, "K");
00218        }
00219        /* If size is less than 1 meg, display is as xK */
00220 
00221        else if (n < 1024 * 1024)
00222        {
00223               *sizebuf=0;
00224               cat_n(sizebuf, (unsigned long)(n+512)/1024);
00225               strcat(sizebuf, "K");
00226        }
00227 
00228        /* Otherwise, display in megabytes */
00229 
00230        else
00231        {
00232        unsigned long nm=(double)n / (1024.0 * 1024.0) * 10;
00233 
00234               *sizebuf=0;
00235               cat_n( sizebuf, nm / 10);
00236               strcat(sizebuf, ".");
00237               cat_n( sizebuf, nm % 10);
00238               strcat(sizebuf, "M");
00239        }
00240        return (sizebuf);
00241 }
00242 
00243 /* Put together a filename from up to three parts */
00244 
00245 char *alloc_filename(const char *dir1, const char *dir2, const char *filename)
00246 {
00247 char   *p;
00248 
00249        if (!dir1)    dir1="";
00250        if (!dir2)    dir2="";
00251 
00252        p=malloc(strlen(dir1)+strlen(dir2)+strlen(filename)+3);
00253 
00254        if (!p)       enomem();
00255 
00256        strcpy(p, dir1);
00257        if (*dir2)
00258        {
00259               if (*p)       strcat(p, "/");
00260               strcat(p, dir2);
00261        }
00262        if (*filename)
00263        {
00264               if (*p)       strcat(p, "/");
00265               strcat(p, filename);
00266        }
00267 
00268        return (p);
00269 }
00270 
00271 /*
00272 ** char *maildir_find(const char *maildir, const char *filename)
00273 **     - find a message in a maildir
00274 **
00275 ** Return the full path to the indicated message.  If the message flags
00276 ** in filename have changed, we search for the given message.
00277 */
00278 
00279 char *maildir_find(const char *folder, const char *filename)
00280 {
00281 char   *p;
00282 char   *d=xlate_shmdir(folder);
00283 int    fd;
00284 
00285        if (!d)       return (0);
00286        p=maildir_filename(d, 0, filename);
00287        free(d);
00288 
00289        if (!p)       enomem();
00290 
00291        if ((fd=open(p, O_RDONLY)) >= 0)
00292        {
00293               close(fd);
00294               return (p);
00295        }
00296        free(p);
00297        return (0);
00298 }
00299 
00300 /*
00301 ** char *maildir_basename(const char *filename)
00302 **
00303 ** - return base name of the file (strip off cur or new, strip of trailing :)
00304 */
00305 
00306 char *maildir_basename(const char *filename)
00307 {
00308 const char *q=strrchr(filename, '/');
00309 char   *p, *r;
00310 
00311        if (q) ++q;
00312        else   q=filename;
00313        p=alloc_filename("", "", q);
00314        if ((r=strchr(p, ':')) != 0)       *r='\0';
00315        return (p);
00316 }
00317 
00318 /* Display message creation time.  If less than one week old (more or less)
00319 ** show day of the week, and time of day, otherwise show day, month, year
00320 */
00321 
00322 static char *displaydate(time_t t)
00323 {
00324 struct tm *tmp=localtime(&t);
00325 static char   datebuf[40];
00326 const char *date_yfmt;
00327 const char *date_wfmt;
00328 
00329                       
00330        date_yfmt = getarg ("DSPFMT_YDATE");
00331        if (*date_yfmt == 0)
00332               date_yfmt = "%d %b %Y";
00333 
00334        date_wfmt = getarg ("DSPFMT_WDATE");
00335        if (*date_wfmt == 0)
00336               date_wfmt = "%a %I:%M %p";
00337 
00338        datebuf[0]='\0';
00339        if (tmp)
00340        {
00341               strftime(datebuf, sizeof(datebuf)-1,
00342                      (t < current_time - 6 * 24 * 60 * 60 ||
00343                      t > current_time + 12 * 60 * 60
00344                      ? date_yfmt : date_wfmt), tmp);
00345               datebuf[sizeof(datebuf)-1]=0;
00346        }
00347        return (datebuf);
00348 }
00349 
00350 /*
00351 ** Add a flag to a maildir filename
00352 */
00353 
00354 static char *maildir_addflagfilename(const char *filename, char flag)
00355 {
00356 char   *new_filename=malloc(strlen(filename)+5);
00357               /* We can possibly add as many as four character */
00358 char   *p;
00359 char   *q;
00360 
00361        strcpy(new_filename, filename);
00362        p=strrchr(new_filename, '/');
00363        if (!p)       p=new_filename;
00364        if ((q=strchr(p, ':')) == 0)
00365               strcat(new_filename, ":2,");
00366        else if (q[1] != '2' && q[2] != ',')
00367               strcpy(p, ":2,");
00368        p=strchr(p, ':');
00369        if (strchr(p, flag))
00370        {
00371               free(new_filename);
00372               return (0);          /* Already set */
00373        }
00374 
00375        p += 2;
00376        while (*p && *p < flag)     p++;
00377        q=p+strlen(p);
00378        while ((q[1]=*q) != *p)
00379               --q;
00380        *p=flag;
00381        return (new_filename);
00382 }
00383 
00384 static void closedb()
00385 {
00386        if (folderdatname)
00387        {
00388               dbobj_close(&folderdat);
00389               free(folderdatname);
00390               folderdatname=0;
00391        }
00392 }
00393 
00394 static char *foldercachename(const char *folder)
00395 {
00396        char *f;
00397 
00398        if (strchr(folder, '/'))
00399        {
00400               enomem();
00401               return NULL;
00402        }
00403 
00404        f=malloc(sizeof(MAILDIRCURCACHE "/" DBNAME ".")+strlen(folder));
00405        if (!f)
00406               enomem();
00407 
00408        return strcat(strcpy(f, MAILDIRCURCACHE "/" DBNAME "."),
00409                     folder);
00410 }
00411 
00412 static int opencache(const char *folder, const char *mode)
00413 {
00414 char   *cachename;
00415 size_t l;
00416 char   *p;
00417 char   *q;
00418 char   *r;
00419 unsigned long ul;
00420 
00421        {
00422               char   *maildir=xlate_shmdir(folder);
00423 
00424               if (!maildir) return (-1);
00425               free(maildir);
00426        }
00427 
00428        cachename=foldercachename(folder);
00429 
00430        if (folderdatname && strcmp(folderdatname, cachename) == 0)
00431        {
00432               if (strcmp(mode, "W") == 0 &&
00433                      strcmp(folderdatmode, "W"))
00434                      ;
00435                      /*
00436                      ** We want to open for write, folder is open for
00437                      ** read
00438                      */
00439               else
00440               {
00441                      free(cachename);
00442                      return (0);
00443                             /* We already have this folder cache open */
00444               }
00445        }
00446        closedb();
00447        folderdatmode=mode;
00448 
00449        dbobj_init(&folderdat);
00450        if (dbobj_open(&folderdat, cachename, mode))     return (-1);
00451        folderdatname=cachename;
00452 
00453        if ((p=dbobj_fetch(&folderdat, "HEADER", 6, &l, "")) == 0)
00454               return (0);
00455        q=malloc(l+1);
00456        if (!q)       enomem();
00457        memcpy(q, p, l);
00458        q[l]=0;
00459        free(p);
00460 
00461        cachemtime=0;
00462        new_cnt=0;
00463        all_cnt=0;
00464        isoldestfirst=0;
00465        sortorder=0;
00466 
00467        for (p=q; (p=strtok(p, "\n")) != 0; p=0)
00468        {
00469               if ((r=strchr(p, '=')) == 0)       continue;
00470               *r++=0;
00471               if (strcmp(p, "SAVETIME") == 0 &&
00472                      parse_ul(r, &ul))
00473                      cachemtime=ul;
00474               else if (strcmp(p, "COUNT") == 0)
00475                      parse_ul(r, &all_cnt);
00476               else if (strcmp(p, "NEWCOUNT") == 0)
00477                      parse_ul(r, &new_cnt);
00478               else if (strcmp(p, "SORT") == 0)
00479               {
00480               unsigned long ul;
00481               const char *s;
00482 
00483                      if ((s=parse_ul(r, &ul)) != 0)
00484                      {
00485                             isoldestfirst=ul;
00486                             sortorder= *s;
00487                      }
00488               }
00489        }
00490        free(q);
00491        return (0);
00492 }
00493 
00494 /* And, remove a flag */
00495 
00496 static void maildir_remflagname(char *filename, char flag)
00497 {
00498 char   *p;
00499 
00500        p=strrchr(filename, '/');
00501        if (!p)       p=filename;
00502        if ((p=strchr(p, ':')) == 0)       return;
00503        else if (p[1] != '2' && p[2] != ',')
00504               return;
00505 
00506        p=strchr(p, ':');
00507        p += 3;
00508 
00509        while (*p && isalpha((int)(unsigned char)*p))
00510        {
00511               if (*p == flag)
00512               {
00513                      while ( (*p=p[1]) != 0)
00514                             p++;
00515                      return;
00516               }
00517               p++;
00518        }
00519 }
00520 
00521 static MSGINFO *get_msginfo(unsigned long n)
00522 {
00523 char   namebuf[MAXLONGSIZE+40];
00524 char   *p;
00525 size_t len;
00526 unsigned long ul;
00527 
00528 static char *buf=0;
00529 size_t bufsize=0;
00530 static MSGINFO msginfo_buf;
00531 
00532        sprintf(namebuf, "REC%lu", n);
00533 
00534        p=dbobj_fetch(&folderdat, namebuf, strlen(namebuf), &len, "");
00535        if (!p)       return (0);
00536 
00537        if (!buf || len > bufsize)
00538        {
00539               buf= buf ? realloc(buf, len+1):malloc(len+1);
00540               if (!buf)     enomem();
00541               bufsize=len;
00542        }
00543        memcpy(buf, p, len);
00544        buf[len]=0;
00545        free(p);
00546 
00547        memset(&msginfo_buf, 0, sizeof(msginfo_buf));
00548        msginfo_buf.filename= msginfo_buf.date_s= msginfo_buf.from_s=
00549        msginfo_buf.subject_s= msginfo_buf.size_s="";
00550 
00551        for (p=buf; (p=strtok(p, "\n")) != 0; p=0)
00552        {
00553        char   *q=strchr(p, '=');
00554 
00555               if (!q)       continue;
00556               *q++=0;
00557 
00558               if (strcmp(p, "FILENAME") == 0)
00559                      msginfo_buf.filename=q;
00560               else if (strcmp(p, "FROM") == 0)
00561                      msginfo_buf.from_s=q;
00562               else if (strcmp(p, "SUBJECT") == 0)
00563                      msginfo_buf.subject_s=q;
00564               else if (strcmp(p, "SIZES") == 0)
00565                      msginfo_buf.size_s=q;
00566               else if (strcmp(p, "DATE") == 0 &&
00567                      parse_ul(q, &ul))
00568               {
00569                      msginfo_buf.date_n=ul;
00570                      msginfo_buf.date_s=displaydate(msginfo_buf.date_n);
00571               }
00572               else if (strcmp(p, "SIZEN") == 0 &&
00573                      parse_ul(q, &ul))
00574                      msginfo_buf.size_n=ul;
00575               else if (strcmp(p, "TIME") == 0 &&
00576                      parse_ul(q, &ul))
00577                      msginfo_buf.mi_mtime=ul;
00578               else if (strcmp(p, "INODE") == 0 &&
00579                      parse_ul(q, &ul))
00580                      msginfo_buf.mi_ino=ul;
00581        }
00582        return (&msginfo_buf);
00583 }
00584 
00585 static MSGINFO *get_msginfo_alloc(unsigned long n)
00586 {
00587 MSGINFO       *msginfop=get_msginfo(n);
00588 MSGINFO *p;
00589 
00590        if (!msginfop)       return (0);
00591 
00592        if ((p= (MSGINFO *) malloc(sizeof(*p))) == 0)
00593               enomem();
00594 
00595        memset(p, 0, sizeof(*p));
00596        if ((p->filename=strdup(msginfop->filename)) == 0 ||
00597               (p->date_s=strdup(msginfop->date_s)) == 0 ||
00598               (p->from_s=strdup(msginfop->from_s)) == 0 ||
00599               (p->subject_s=strdup(msginfop->subject_s)) == 0 ||
00600               (p->size_s=strdup(msginfop->size_s)) == 0)
00601               enomem();
00602        p->date_n=msginfop->date_n;
00603        p->size_n=msginfop->size_n;
00604        p->mi_mtime=msginfop->mi_mtime;
00605        p->mi_ino=msginfop->mi_ino;
00606        return (p);
00607 }
00608 
00609 static void put_msginfo(MSGINFO *m, unsigned long n)
00610 {
00611 char   namebuf[MAXLONGSIZE+40];
00612 char   *rec;
00613 
00614        sprintf(namebuf, "REC%lu", n);
00615 
00616        rec=malloc(strlen(m->filename)+strlen(m->from_s)+
00617               strlen(m->subject_s)+strlen(m->size_s)+MAXLONGSIZE*4+
00618               sizeof("FILENAME=\nFROM=\nSUBJECT=\nSIZES=\nDATE=\n"
00619                      "SIZEN=\nTIME=\nINODE=\n")+100);
00620        if (!rec)     enomem();
00621 
00622        sprintf(rec, "FILENAME=%s\nFROM=%s\nSUBJECT=%s\nSIZES=%s\n"
00623               "DATE=%lu\n"
00624               "SIZEN=%lu\n"
00625               "TIME=%lu\n"
00626               "INODE=%lu\n",
00627               m->filename,
00628               m->from_s,
00629               m->subject_s,
00630               m->size_s,
00631               (unsigned long)m->date_n,
00632               (unsigned long)m->size_n,
00633               (unsigned long)m->mi_mtime,
00634               (unsigned long)m->mi_ino);
00635 
00636        if (dbobj_store(&folderdat, namebuf, strlen(namebuf),
00637               rec, strlen(rec), "R"))
00638               enomem();
00639        free(rec);
00640 }
00641 
00642 static void update_foldermsgs(const char *folder, const char *newname, size_t pos)
00643 {
00644 MSGINFO       *p;
00645 char *n;
00646 
00647        n=strrchr(newname, '/')+1;
00648        if (opencache(folder, "W") || (p=get_msginfo(pos)) == 0)
00649        {
00650               error("Internal error in update_foldermsgs");
00651               return;
00652        }
00653 
00654        p->filename=n;
00655 
00656        put_msginfo(p, pos);
00657 }
00658 
00659 static void maildir_markflag(const char *folder, size_t pos, char flag)
00660 {
00661 MSGINFO       *p;
00662 char   *filename;
00663 char   *new_filename;
00664 
00665        if (opencache(folder, "W") || (p=get_msginfo(pos)) == 0)
00666        {
00667               error("Internal error in maildir_markflag");
00668               return;
00669        }
00670 
00671        filename=maildir_find(folder, p->filename);
00672        if (!filename)       return;
00673 
00674        if ((new_filename=maildir_addflagfilename(filename, flag)) != 0)
00675        {
00676               rename(filename, new_filename);
00677               update_foldermsgs(folder, new_filename, pos);
00678               free(new_filename);
00679        }
00680        free(filename);
00681 }
00682 
00683 void maildir_markread(const char *folder, size_t pos)
00684 {
00685        char acl_buf[2];
00686 
00687        strcpy(acl_buf, ACL_SEEN);
00688        acl_computeRightsOnFolder(folder, acl_buf);
00689        if (acl_buf[0])
00690               maildir_markflag(folder, pos, 'S');
00691 }
00692 
00693 void maildir_markreplied(const char *folder, const char *message)
00694 {
00695        char   *filename;
00696        char   *new_filename;
00697        char acl_buf[2];
00698 
00699        strcpy(acl_buf, ACL_WRITE);
00700        acl_computeRightsOnFolder(folder, acl_buf);
00701 
00702        if (acl_buf[0] == 0)
00703               return;
00704 
00705        filename=maildir_find(folder, message);
00706 
00707        if (filename &&
00708               (new_filename=maildir_addflagfilename(filename, 'R')) != 0)
00709        {
00710               rename(filename, new_filename);
00711               free(new_filename);
00712        }
00713        if (filename) free(filename);
00714 }
00715 
00716 char *maildir_posfind(const char *folder, size_t *pos)
00717 {
00718 MSGINFO       *p;
00719 char   *filename;
00720 
00721        if (opencache(folder, "R") || (p=get_msginfo( *pos)) == 0)
00722        {
00723               error("Internal error in maildir_posfind");
00724               return (0);
00725        }
00726 
00727        filename=maildir_find(folder, p->filename);
00728        return(filename);
00729 }
00730 
00731 
00732 int maildir_name2pos(const char *folder, const char *filename, size_t *pos)
00733 {
00734        char *p, *q;
00735        size_t len;
00736 
00737        maildir_reload(folder);
00738        if (opencache(folder, "R"))
00739        {
00740               error("Internal error in maildir_name2pos");
00741               return (0);
00742        }
00743 
00744        p=malloc(strlen(filename)+10);
00745        if (!p)
00746               enomem();
00747        strcat(strcpy(p, "FILE"), filename);
00748        q=strchr(p, ':');
00749        if (q)
00750               *q=0;
00751 
00752        q=dbobj_fetch(&folderdat, p, strlen(p), &len, "");
00753        free(p);
00754 
00755        if (!q)
00756               return (-1);
00757 
00758        *pos=0;
00759        for (p=q; len; --len, p++)
00760        {
00761               if (isdigit((int)(unsigned char)*p))
00762                      *pos = *pos * 10 + (*p-'0');
00763        }
00764        free(q);
00765        return (0);
00766 }
00767 
00768 void maildir_msgpurge(const char *folder, size_t pos)
00769 {
00770 char *filename=maildir_posfind(folder, &pos);
00771 
00772        if (filename)
00773        {
00774               unlink(filename);
00775               free(filename);
00776        }
00777 }
00778 
00779 void maildir_msgpurgefile(const char *folder, const char *msgid)
00780 {
00781 char *filename=maildir_find(folder, msgid);
00782 
00783        if (filename)
00784        {
00785               char *d=xlate_shmdir(folder);
00786 
00787               if (d)
00788               {
00789                      if (strncmp(folder, SHARED ".", sizeof(SHARED))
00790                          && maildirquota_countfolder(d) &&
00791                          maildirquota_countfile(filename))
00792                      {
00793                             unsigned long filesize=0;
00794 
00795                             if (maildir_parsequota(filename, &filesize))
00796                             {
00797                                    struct stat stat_buf;
00798 
00799                                    if (stat(filename, &stat_buf) == 0)
00800                                           filesize=stat_buf.st_size;
00801                             }
00802 
00803                             if (filesize > 0)
00804                                    maildir_quota_deleted(".",
00805                                                        -(int64_t)filesize,
00806                                                        -1);
00807                      }
00808                      free(d);
00809               }
00810               unlink(filename);
00811               free(filename);
00812        }
00813 }
00814 
00815 /*
00816 ** A message is moved to a different folder as follows.
00817 ** The message is linked to the destination folder, then marked with a 'T'
00818 ** flag in the original folder.  Later, all T-marked messages are deleted.
00819 */
00820 
00821 static int msgcopy(int fromfd, int tofd)
00822               /* ... Except moving to/from sharable folder actually
00823               ** involves copying.
00824               */
00825 {
00826 char   buf[8192];
00827 int    i, j;
00828 char   *p;
00829 
00830        while ((i=read(fromfd, buf, sizeof(buf))) > 0)
00831        {
00832               p=buf;
00833               while (i)
00834               {
00835                      j=write(tofd, p, i);
00836                      if (j <= 0)   return (-1);
00837                      p += j;
00838                      i -= j;
00839               }
00840        }
00841        return (i);
00842 }
00843 
00844 static int do_msgmove(const char *from,
00845                     const char *file, const char *dest, size_t pos,
00846                     int check_acls)
00847 {
00848        char *destdir, *fromdir;
00849        const char *p;
00850        char *basename;
00851        struct stat stat_buf;
00852        char   *new_filename;
00853        unsigned long filesize=0;
00854        int    no_link=0;
00855        struct maildirsize quotainfo;
00856        char *newname;
00857        int from_shared, dest_shared;
00858        char acl_buf[4];
00859 
00860        if (stat(file, &stat_buf) || stat_buf.st_nlink != 1)
00861        {
00862               unlink(file); /* Already moved, or crashed in the middle of
00863                            ** moving the file, so clean up.
00864                            */
00865               return (0);
00866        }
00867 
00868 
00869        /* Update quota */
00870 
00871        destdir=xlate_shmdir(dest);
00872        if (!destdir) enomem();
00873 
00874        fromdir=xlate_shmdir(from);
00875        if (!fromdir) enomem();
00876 
00877        from_shared=strncmp(from, SHARED ".", sizeof(SHARED)) == 0;
00878        dest_shared=strncmp(dest, SHARED ".", sizeof(SHARED)) == 0;
00879 
00880        strcpy(acl_buf, ACL_SEEN ACL_WRITE ACL_DELETEMSGS);
00881        if (check_acls)
00882               acl_computeRightsOnFolder(dest, acl_buf);
00883 
00884        if (strcmp(from, dest))
00885        {
00886               if (maildir_parsequota(file, &filesize))
00887                      filesize=stat_buf.st_size;
00888                      /* Recover from possible corruption */
00889 
00890               if ((dest_shared
00891                    || !maildirquota_countfolder(destdir)) &&
00892                   maildirquota_countfile(file))
00893               {
00894 
00895                      if (!from_shared)
00896                             maildir_quota_deleted(".", -(int64_t)filesize, -1);
00897               }
00898               else if (!dest_shared && maildirquota_countfolder(destdir) &&
00899                       (from_shared || !maildirquota_countfolder(fromdir))
00900                       )
00901                      /* Moving FROM trash */
00902               {
00903 
00904                      if (maildir_quota_add_start(".", &quotainfo,
00905                                               filesize, 1, NULL))
00906                      {
00907                             free(fromdir);
00908                             return (-1);
00909                      }
00910 
00911                      maildir_quota_add_end(&quotainfo, filesize, 1);
00912               }
00913        }
00914 
00915        free(fromdir);
00916 
00917        if (from_shared || dest_shared)
00918        {
00919               struct maildir_tmpcreate_info createInfo;
00920               int    fromfd, tofd;
00921               char   *l;
00922 
00923               if (dest_shared)     /* Copy to the sharable folder */
00924               {
00925               char   *p=malloc(strlen(destdir)+sizeof("/shared"));
00926 
00927                      if (!p)
00928                      {
00929                             free(destdir);
00930                             enomem();
00931                      }
00932                      strcat(strcpy(p, destdir), "/shared");
00933                      free(destdir);
00934                      destdir=p;
00935               }
00936 
00937               maildir_tmpcreate_init(&createInfo);
00938 
00939               createInfo.maildir=destdir;
00940               createInfo.uniq="copy";
00941               createInfo.doordie=1;
00942 
00943               if ( dest_shared )
00944                      umask (0022);
00945 
00946               if ((tofd=maildir_tmpcreate_fd(&createInfo)) < 0)
00947               {
00948                      free(destdir);
00949                      error(strerror(errno));
00950               }
00951 
00952               if (dest_shared)
00953               /* We need to copy it directly into /cur of the dest folder */
00954               {
00955                      memcpy(strrchr(createInfo.newname, '/')-3, "cur", 3);
00956                                           /* HACK!!!!!!!!!!!! */
00957               }
00958 
00959               if ((fromfd=maildir_semisafeopen(file, O_RDONLY, 0)) < 0)
00960               {
00961                      int terrno = errno;
00962                      free(destdir);
00963                      close(tofd);
00964                      unlink(createInfo.tmpname);
00965                      maildir_tmpcreate_free(&createInfo);
00966                      error3(__FILE__, __LINE__, "Failed to open for read:",
00967                             file, terrno);
00968               }
00969 
00970               umask (0077);
00971               if (msgcopy(fromfd, tofd))
00972               {
00973                      int terrno = errno;
00974                      close(fromfd);
00975                      close(tofd);
00976                      free(destdir);
00977                      unlink(createInfo.tmpname);
00978                      maildir_tmpcreate_free(&createInfo);
00979                      error3(__FILE__, __LINE__, "Failed to copy message",
00980                             "", terrno);
00981               }
00982               close(fromfd);
00983               close(tofd);
00984 
00985        /*
00986        ** When we attempt to DELETE a message in the sharable folder,
00987        ** attempt to remove the UNDERLYING message
00988        */
00989 
00990               if (from_shared && (l=maildir_getlink(file)) != 0)
00991               {
00992                      if (unlink(l))
00993                      {
00994                             /* Not our message */
00995 
00996                             if (strcmp(dest, INBOX "." TRASH) == 0)
00997                             {
00998                                    free(l);
00999                                    unlink(createInfo.tmpname);
01000                                    maildir_tmpcreate_free(&createInfo);
01001                                    free(destdir);
01002                                    return (0);
01003                             }
01004                      }
01005                      free(l);
01006               }
01007 
01008               if (strchr(acl_buf, ACL_DELETEMSGS[0]) == 0)
01009                      maildir_remflagname(createInfo.newname, 'T');
01010               if (strchr(acl_buf, ACL_SEEN[0]) == 0)
01011                      maildir_remflagname(createInfo.newname, 'S');
01012               if (strchr(acl_buf, ACL_WRITE[0]) == 0)
01013               {
01014                      maildir_remflagname(createInfo.newname, 'F');
01015                      maildir_remflagname(createInfo.newname, 'D');
01016                      maildir_remflagname(createInfo.newname, 'R');
01017               }
01018 
01019               if (maildir_movetmpnew(createInfo.tmpname, createInfo.newname))
01020               {
01021                      free(destdir);
01022                      unlink(createInfo.tmpname);
01023                      maildir_tmpcreate_free(&createInfo);
01024                      error(strerror(errno));
01025               }
01026               no_link=1;    /* Don't call link(), below */
01027               maildir_tmpcreate_free(&createInfo);
01028        }
01029 
01030        p=strrchr(file, '/');
01031        if (p) ++p;
01032        else   p=file;
01033 
01034        if ( (basename=strdup(p)) == NULL)
01035               enomem();
01036        maildir_remflagname(basename, 'T');       /* Remove any deleted flag for new name */
01037 
01038        if (strchr(acl_buf, ACL_SEEN[0]) == 0)
01039               maildir_remflagname(basename, 'S');
01040        if (strchr(acl_buf, ACL_WRITE[0]) == 0)
01041        {
01042               maildir_remflagname(basename, 'F');
01043               maildir_remflagname(basename, 'D');
01044               maildir_remflagname(basename, 'R');
01045        }
01046        newname=alloc_filename(destdir, "cur", basename);
01047        free(destdir);
01048        free(basename);
01049 
01050        /* When DELETE is called for a message in TRASH, from and dest will
01051        ** be the same, so we just mark the file as Trashed, to be removed
01052        ** in checknew.
01053        */
01054 
01055        if (no_link == 0 && strcmp(from, dest))
01056        {
01057               if (link(file, newname))
01058               {
01059                      free(newname);
01060                      return (-1);
01061               }
01062        }
01063        free(newname);
01064 
01065        if ((new_filename=maildir_addflagfilename(file, 'T')) != 0)
01066        {
01067               rename(file, new_filename);
01068               update_foldermsgs(from, new_filename, pos);
01069               free(new_filename);
01070        }
01071        return (0);
01072 }
01073 
01074 void maildir_msgdeletefile(const char *folder, const char *file, size_t pos)
01075 {
01076 char *filename=maildir_find(folder, file);
01077 
01078        if (filename)
01079        {
01080               (void)do_msgmove(folder, filename, INBOX "." TRASH, pos, 0);
01081               free(filename);
01082        }
01083 }
01084 
01085 int maildir_msgmovefile(const char *folder, const char *file, const char *dest,
01086        size_t pos)
01087 {
01088        char *filename=maildir_find(folder, file);
01089        int    rc;
01090 
01091        if (!filename)       return (0);
01092        rc=do_msgmove(folder, filename, dest, pos, 1);
01093        free(filename);
01094        return (rc);
01095 }
01096 
01097 static char *foldercountfilename(const char *folder)
01098 {
01099        char *f=malloc(sizeof(MAILDIRCURCACHE "/cnt.") + strlen(folder));
01100 
01101        if (!f)
01102               enomem();
01103 
01104        strcat(strcpy(f, MAILDIRCURCACHE "/cnt."), folder);
01105        return f;
01106 }
01107 
01108 /*
01109 ** Grab new messages from new.
01110 */
01111 
01112 static void maildir_checknew(const char *folder, const char *dir)
01113 {
01114        char   *dirbuf;
01115        struct stat   stat_buf;
01116        DIR    *dirp;
01117        struct dirent *dire;
01118        char   acl_buf[2];
01119 
01120        /* Delete old files in tmp */
01121 
01122        maildir_purgetmp(dir);
01123 
01124        /* Move everything from new to cur */
01125 
01126        dirbuf=alloc_filename(dir, "new", "");
01127 
01128        for (dirp=opendir(dirbuf); dirp && (dire=readdir(dirp)) != 0; )
01129        {
01130        char   *oldname, *newname;
01131        char   *p;
01132 
01133               if (dire->d_name[0] == '.') continue;
01134 
01135               oldname=alloc_filename(dirbuf, dire->d_name, "");
01136 
01137               newname=malloc(strlen(oldname)+4);
01138               if (!newname) enomem();
01139 
01140               strcat(strcat(strcpy(newname, dir), "/cur/"), dire->d_name);
01141               p=strrchr(newname, '/');
01142               if ((p=strchr(p, ':')) != NULL)    *p=0;  /* Someone screwed up */
01143               strcat(newname, ":2,");
01144               rename(oldname, newname);
01145               free(oldname);
01146               free(newname);
01147        }
01148        if (dirp)     closedir(dirp);
01149        free(dirbuf);
01150 
01151        /* Look for any messages mark as deleted.  When we delete a message
01152        ** we link it into the Trash folder, and mark the original with a T,
01153        ** which we delete when we check for new messages.
01154        */
01155 
01156        dirbuf=alloc_filename(dir, "cur", "");
01157        if (stat(dirbuf, &stat_buf))
01158        {
01159               free(dirbuf);
01160               return;
01161        }
01162 
01163        /* If the count cache file is still current, the directory hasn't
01164        ** changed, so we don't need to scan it for deleted messages.  When
01165        ** a message is deleted, the rename bumps up the timestamp.
01166        **
01167        ** This depends on dodirscan() being called after this function,
01168        ** which updates MAILDIRCOUNTCACHE
01169        */
01170 
01171        {
01172               char *f=foldercountfilename(folder);
01173               struct stat c_stat_buf;
01174 
01175               if (stat(f, &c_stat_buf) == 0 && c_stat_buf.st_mtime >
01176                   stat_buf.st_mtime)
01177               {
01178                      free(f);
01179                      free(dirbuf);
01180                      return;
01181               }
01182               free(f);
01183        }
01184 
01185        strcpy(acl_buf, ACL_EXPUNGE);
01186        acl_computeRightsOnFolder(folder, acl_buf);
01187 
01188        for (dirp=opendir(dirbuf); dirp && (dire=readdir(dirp)) != 0; )
01189        {
01190        char   *p;
01191 
01192               if (dire->d_name[0] == '.') continue;
01193 
01194               if (maildirfile_type(dire->d_name) == MSGTYPE_DELETED &&
01195                   acl_buf[0])
01196               {
01197                      p=alloc_filename(dirbuf, "", dire->d_name);
01198 
01199 
01200                      /*
01201                      ** Because of the funky way we do things,
01202                      ** if we were compiled with --enable-trashquota,
01203                      ** purging files from Trash should decrease the
01204                      ** quota
01205                      */
01206 
01207                      if (strcmp(folder, INBOX "." TRASH) == 0 &&
01208                          maildirquota_countfolder(dir))
01209                      {
01210                             struct stat stat_buf;
01211                             unsigned long filesize=0;
01212 
01213                             if (maildir_parsequota(dire->d_name,
01214                                                  &filesize))
01215                                    if (stat(p, &stat_buf) == 0)
01216                                           filesize=stat_buf.st_size;
01217 
01218                             if (filesize > 0)
01219                                    maildir_quota_deleted(".",
01220                                                        -(int64_t)filesize,
01221                                                        -1);
01222                      }
01223 
01224                      maildir_unlinksharedmsg(p);
01225                             /* Does The Right Thing if this is a shared
01226                             ** folder
01227                             */
01228                             
01229                      free(p);
01230               }
01231        }
01232        if (dirp)     closedir(dirp);
01233        free(dirbuf);
01234 }
01235 
01236 /*
01237 ** Automatically purge deleted messages.
01238 */
01239 
01240 static int goodcache(const char *foldername)
01241 {
01242        struct maildir_info minfo;
01243        char *folderdir;
01244        struct stat stat_buf;
01245 
01246        if (maildir_info_imap_find(&minfo, foldername, login_returnaddr())<0
01247            || minfo.homedir == NULL || minfo.maildir == NULL)
01248               return 0;
01249 
01250        maildir_info_destroy(&minfo);
01251 
01252        folderdir=xlate_shmdir(foldername);
01253 
01254        if (stat(folderdir, &stat_buf) < 0)
01255        {
01256               free(folderdir);
01257               return 0;
01258        }
01259        free(folderdir);
01260        return 1;
01261 }
01262 
01263 void maildir_autopurge()
01264 {
01265        char   *dir;
01266        char   *dirbuf;
01267        struct stat   stat_buf;
01268        DIR    *dirp;
01269        struct dirent *dire;
01270        char   *filename;
01271        char buffer[80];
01272        size_t n, i;
01273        FILE *fp;
01274 
01275        /* This is called when logging in.  Version 0.18 supports maildir
01276        ** quotas, so automatically upgrade all folders.
01277        */
01278 
01279        for (dirp=opendir("."); dirp && (dire=readdir(dirp)) != 0; )
01280        {
01281               if (dire->d_name[0] != '.') continue;
01282               if (strcmp(dire->d_name, "..") == 0)      continue;
01283 
01284               if (strcmp(dire->d_name, "."))
01285               {
01286                      filename=alloc_filename(dire->d_name,
01287                                           "maildirfolder", "");
01288                      if (!filename)       enomem();
01289                      close(open(filename, O_RDWR|O_CREAT, 0644));
01290                      free(filename);
01291               }
01292 
01293               /* Eliminate obsoleted cache files */
01294 
01295               filename=alloc_filename(dire->d_name, MAILDIRCOUNTCACHE, "");
01296 
01297               if (!filename)       enomem();
01298               unlink(filename);
01299               free(filename);
01300 
01301               filename=alloc_filename(dire->d_name, MAILDIRCURCACHE, "");
01302 
01303               if (!filename)       enomem();
01304               unlink(filename);
01305               free(filename);
01306 
01307               filename=alloc_filename(dire->d_name, "", 
01308                                    MAILDIRCURCACHE "." DBNAME);
01309               if (!filename)       enomem();
01310               unlink(filename);
01311               free(filename);
01312        }
01313 
01314        /* Version 0.24 top level remove */
01315 
01316        unlink(MAILDIRCURCACHE);
01317 
01318        /* Version 4 top level remove */
01319 
01320        unlink(MAILDIRCURCACHE "." DBNAME);
01321        mkdir (MAILDIRCURCACHE, 0700);
01322 
01323        if (dirp)
01324               closedir(dirp);
01325 
01326        /*
01327        ** Periodically purge stale cache files of nonexistent folders.
01328        ** This is done by using a counter that runs from 0 up until the
01329        ** # of files in MAILDIRCURCACHE.  At each login, we check if
01330        ** file #n is for an existing folder.  If not, the stale file is
01331        ** removed.
01332        */
01333 
01334        n=0;
01335 
01336        if ((fp=fopen(MAILDIRCURCACHE "/.purgecnt", "r")) != NULL)
01337        {
01338               if (fgets(buffer, sizeof(buffer), fp) != NULL)
01339                      n=atoi(buffer);
01340               fclose(fp);
01341        }
01342 
01343        i=0;
01344 
01345        for (dirp=opendir(MAILDIRCURCACHE); dirp && (dire=readdir(dirp)); )
01346        {
01347               char *folderdir;
01348 
01349               if (dire->d_name[0] == '.')
01350                      continue;
01351 
01352               if (strncmp(dire->d_name, DBNAME ".", sizeof(DBNAME)) == 0 ||
01353                   strncmp(dire->d_name, "cnt.", 4) == 0)
01354               {
01355                      if (i == n)
01356                      {
01357                             if (!goodcache(strchr(dire->d_name, '.')+1))
01358                             {
01359                                    folderdir=malloc(sizeof(MAILDIRCURCACHE
01360                                                         "/")
01361                                                   + strlen(dire->
01362                                                           d_name));
01363                                    if (!folderdir)
01364                                           enomem();
01365                                    strcat(strcpy(folderdir,
01366                                                 MAILDIRCURCACHE "/"),
01367                                           dire->d_name);
01368                                    unlink(folderdir);
01369                                    free(folderdir);
01370                             }
01371                             break;
01372                      }
01373                      ++i;
01374                      continue;
01375               }
01376 
01377               folderdir=malloc(sizeof(MAILDIRCURCACHE "/")
01378                              + strlen(dire->d_name));
01379               if (!folderdir)
01380                      enomem();
01381               strcat(strcpy(folderdir, MAILDIRCURCACHE "/"), dire->d_name);
01382               unlink(folderdir);
01383               free(folderdir);
01384        }
01385        if (dirp)
01386               closedir(dirp);
01387 
01388        if (i == n)
01389               ++i;
01390        else
01391               i=0;
01392 
01393 
01394        if ((fp=fopen(MAILDIRCURCACHE "/.purgecnt.tmp", "w")) == NULL ||
01395            fprintf(fp, "%lu\n", (unsigned long)i) < 0 ||
01396            fflush(fp) < 0)
01397               enomem();
01398 
01399        fclose(fp);
01400        if (rename(MAILDIRCURCACHE "/.purgecnt.tmp",
01401                  MAILDIRCURCACHE "/.purgecnt") < 0)
01402               enomem();
01403 
01404        dir=xlate_mdir(INBOX "." TRASH);
01405 
01406        /* Delete old files in tmp */
01407 
01408        time(&current_time);
01409        dirbuf=alloc_filename(dir, "cur", "");
01410        free(dir);
01411 
01412        for (dirp=opendir(dirbuf); dirp && (dire=readdir(dirp)) != 0; )
01413        {
01414               if (dire->d_name[0] == '.') continue;
01415               filename=alloc_filename(dirbuf, dire->d_name, "");
01416               if (stat(filename, &stat_buf) == 0 &&
01417                   pref_autopurge &&
01418                   stat_buf.st_ctime < current_time
01419                   - pref_autopurge * 24 * 60 * 60)
01420               {
01421                      if (maildirquota_countfolder(dirbuf) &&
01422                          maildirquota_countfile(filename))
01423                      {
01424                             unsigned long filesize=0;
01425 
01426                             if (maildir_parsequota(filename, &filesize))
01427                                    filesize=stat_buf.st_size;
01428 
01429                             if (filesize > 0)
01430                                    maildir_quota_deleted(".",
01431                                                        -(int64_t)filesize,
01432                                                        -1);
01433                      }
01434 
01435                      unlink(filename);
01436               }
01437 
01438               free(filename);
01439        }
01440        if (dirp)     closedir(dirp);
01441        free(dirbuf);
01442 
01443        maildir_purgemimegpg();
01444 }
01445 
01446 /*
01447 ** MIME-GPG decoding creates a temporary file in tmp, which is preserved
01448 ** (in the event of subsequent multipart/related accesses).  The filenames
01449 ** include ':', to mark them as used for this purpose.  Rather than wait
01450 ** 36 hours for them to get cleaned up, as part of a normal maildir tmp
01451 ** purge, we can blow them off right now.
01452 */
01453 
01454 void maildir_purgemimegpg()
01455 {
01456        DIR    *dirp;
01457        struct dirent *dire;
01458        char *p;
01459 
01460        for (dirp=opendir("tmp"); dirp && (dire=readdir(dirp)) != 0; )
01461        {
01462               if (strstr(dire->d_name, ":mimegpg:") == 0 &&
01463                   strstr(dire->d_name, ":calendar:") == 0)     continue;
01464 
01465               p=malloc(sizeof("tmp/")+strlen(dire->d_name));
01466 
01467               if (p)
01468               {
01469                      strcat(strcpy(p, "tmp/"), dire->d_name);
01470                      unlink(p);
01471                      free(p);
01472               }
01473        }
01474 
01475        if (dirp)
01476               closedir(dirp);
01477 }
01478 
01479 /* Ditto for search results */
01480 
01481 void maildir_purgesearch()
01482 {
01483        DIR    *dirp;
01484        struct dirent *dire;
01485        char *p;
01486 
01487        for (dirp=opendir("tmp"); dirp && (dire=readdir(dirp)) != 0; )
01488        {
01489               if (strstr(dire->d_name, ":search:") == 0)       continue;
01490 
01491               p=malloc(sizeof("tmp/")+strlen(dire->d_name));
01492 
01493               if (p)
01494               {
01495                      strcat(strcpy(p, "tmp/"), dire->d_name);
01496                      unlink(p);
01497                      free(p);
01498               }
01499        }
01500 
01501        if (dirp)
01502               closedir(dirp);
01503 }
01504 
01505 static int subjectcmp(const char *a, const char *b)
01506 {
01507        int    aisre;
01508        int    bisre;
01509        int    n;
01510        char   *as;
01511        char   *bs;
01512 
01513        as=rfc822_display_hdrvalue_tobuf("subject", a, "utf-8",
01514                                     NULL, NULL);
01515 
01516        if (!as)
01517               as=strdup(a);
01518 
01519        bs=rfc822_display_hdrvalue_tobuf("subject", b, "utf-8",
01520                                     NULL, NULL);
01521 
01522        if (!bs)
01523               bs=strdup(b);
01524 
01525        if (as)
01526        {
01527               char *p=rfc822_coresubj(as, &aisre);
01528 
01529               free(as);
01530               as=p;
01531        }
01532 
01533        if (bs)
01534        {
01535               char *p=rfc822_coresubj(bs, &bisre);
01536 
01537               free(bs);
01538               bs=p;
01539        }
01540 
01541        if (!as || !bs)
01542        {
01543               if (as)       free(as);
01544               if (bs)       free(bs);
01545               enomem();
01546        }
01547 
01548        n=strcasecmp(as, bs);
01549        free(as);
01550        free(bs);
01551 
01552        if (aisre)
01553               aisre=1;      /* Just to be sure */
01554 
01555        if (bisre)
01556               bisre=1;      /* Just to be sure */
01557 
01558        if (n == 0)   n=aisre - bisre;
01559        return (n);
01560 }
01561 
01562 /*
01563 ** Messages supposed to be arranged in the reverse chronological order of
01564 ** arrival.
01565 **
01566 ** Instead of stat()ing every file in the directory, we depend on the
01567 ** naming convention that are specified for the Maildir.  Therefore, we rely
01568 ** on Maildir writers observing the required naming conventions.
01569 */
01570 
01571 static int messagecmp(const MSGINFO **pa, const MSGINFO **pb)
01572 {
01573 int    gt=1, lt=-1;
01574 int    n;
01575 const MSGINFO *a= *pa;
01576 const MSGINFO *b= *pb;
01577 
01578        if (pref_flagisoldest1st)
01579        {
01580               gt= -1;
01581               lt= 1;
01582        }
01583 
01584        switch (pref_flagsortorder) {
01585        case 'F':
01586               n=strcasecmp(a->from_s, b->from_s);
01587               if (n) return (n);
01588               break;
01589        case 'S':
01590               n=subjectcmp(a->subject_s, b->subject_s);
01591               if (n) return (n);
01592               break;
01593        }
01594        if (a->date_n < b->date_n)  return (gt);
01595        if (a->date_n > b->date_n)  return (lt);
01596 
01597        if (a->mi_ino < b->mi_ino)  return (gt);
01598        if (a->mi_ino > b->mi_ino)  return (lt);
01599        return (0);
01600 }
01601 
01602 /*
01603 ** maildirfile_type(directory, filename) - return one of the following:
01604 **
01605 **   MSGTYPE_NEW - new message
01606 **   MSGTYPE_DELETED - trashed message
01607 **   '\0' - all other kinds
01608 */
01609 
01610 char maildirfile_type(const char *p)
01611 {
01612 const char *q=strrchr(p, '/');
01613 int    seen_trash=0, seen_r=0, seen_s=0;
01614 
01615        if (q) p=q;
01616 
01617        if ( !(p=strchr(p, ':')) || *++p != '2' || *++p != ',')
01618               return (MSGTYPE_NEW);              /* No :2,info */
01619                             ;
01620        ++p;
01621        while (p && isalpha((int)(unsigned char)*p))
01622               switch (*p++) {
01623               case 'T':
01624                      seen_trash=1;
01625                      break;
01626               case 'R':
01627                      seen_r=1;
01628                      break;
01629               case 'S':
01630                      seen_s=1;
01631                      break;
01632               }
01633 
01634        if (seen_trash)
01635               return (MSGTYPE_DELETED);   /* Trashed message */
01636        if (seen_s)
01637        {
01638               if (seen_r)   return (MSGTYPE_REPLIED);
01639               return (0);
01640        }
01641 
01642        return (MSGTYPE_NEW);
01643 }
01644 
01645 static int docount(const char *fn, unsigned *new_cnt, unsigned *other_cnt)
01646 {
01647 const char *filename=strrchr(fn, '/');
01648 char   c;
01649 
01650        if (filename) ++filename;
01651        else          filename=fn;
01652 
01653        if (*filename == '.')       return (0);   /* We don't want this one */
01654 
01655        c=maildirfile_type(filename);
01656 
01657        if (c == MSGTYPE_NEW)
01658               ++ *new_cnt;
01659        else
01660               ++ *other_cnt;
01661        return (1);
01662 }
01663 
01664 MSGINFO **maildir_read(const char *dirname, unsigned nfiles,
01665                      size_t *starting_pos,
01666                      int *morebefore, int *moreafter)
01667 {
01668 MSGINFO       **msginfo;
01669 size_t i;
01670 
01671        if ((msginfo=malloc(sizeof(MSGINFO *)*nfiles)) == 0)
01672               enomem();
01673        for (i=0; i<nfiles; i++)
01674               msginfo[i]=0;
01675 
01676        if (opencache(dirname, "W"))       return (msginfo);
01677 
01678        if (nfiles > all_cnt)       nfiles=all_cnt;
01679        if (*starting_pos + nfiles > all_cnt)
01680               *starting_pos=all_cnt-nfiles;
01681 
01682        *morebefore = *starting_pos > 0;
01683 
01684        for (i=0; i<nfiles; i++)
01685        {
01686               if (*starting_pos + i >= all_cnt)  break;
01687               if ((msginfo[i]= get_msginfo_alloc(*starting_pos + i)) == 0)
01688                      break;
01689 
01690               msginfo[i]->msgnum=*starting_pos+i;
01691        }
01692        *moreafter= *starting_pos + i < all_cnt;
01693        return (msginfo);
01694 }
01695 
01696 #define save_int(n, fp) do {                                          \
01697               size_t i;                                        \
01698               size_t cnt=sizeof((n));                                 \
01699               putc(cnt, (fp));                                 \
01700               for (i=0; i<cnt; i++)                                   \
01701                      putc((n) >> (cnt-1-i)*8, (fp));                  \
01702        } while(0);
01703 
01704 static void save_str(const char *str, FILE *fp)
01705 {
01706        size_t l=strlen(str);
01707 
01708        save_int(l, fp);
01709        fprintf(fp, "%s", str);
01710 }
01711 
01712 #define load_int(n, fp) do {                                   \
01713        int i;                                           \
01714        int cnt=getc(fp);                                \
01715        (n)=0;                                           \
01716        if (cnt != EOF)                                         \
01717               for (i=0; i<cnt; ++i)                            \
01718                      (n)=(n) << 8 | (unsigned char)getc(fp);   \
01719        } while(0);
01720 
01721 static char *load_str(FILE *fp)
01722 {
01723        size_t l;
01724        size_t i;
01725        char *str;
01726 
01727        load_int(l, fp);
01728 
01729        if (feof(fp))
01730               l=0;
01731 
01732        str=malloc(l+1);
01733 
01734        if (!str)
01735               enomem();
01736 
01737        for (i=0; i<l; i++)
01738        {
01739               char c=getc(fp);
01740 
01741               str[i]=c;
01742        }
01743 
01744        str[i]=0;
01745        return str;
01746 }
01747 
01748 static void save_msginfo(const MSGINFO *p, MATCHEDSTR *context, FILE *fp)
01749 {
01750        size_t context_cnt;
01751        MATCHEDSTR *c;
01752 
01753        save_int(p->msgnum, fp);
01754        save_str(p->filename, fp);
01755        save_str(p->date_s, fp);
01756        save_str(p->from_s, fp);
01757        save_str(p->subject_s, fp);
01758        save_str(p->size_s, fp);
01759        save_int(p->date_n, fp);
01760        save_int(p->size_n, fp);
01761        save_int(p->mi_mtime, fp);
01762        save_int(p->mi_ino, fp);
01763 
01764        for (context_cnt=0, c=context; c && c->match; ++c, ++context_cnt)
01765               ;
01766 
01767        save_int(context_cnt, fp);
01768 
01769        for (c=context; c && c->match; ++c)
01770        {
01771               save_str(c->prefix, fp);
01772               save_str(c->match, fp);
01773               save_str(c->suffix, fp);
01774        }
01775 }
01776 
01777 static void load_msginfo(MSGINFO **retinfo,
01778                       MATCHEDSTR **retmatches,
01779                       FILE *fp)
01780 {
01781        MSGINFO *p=malloc(sizeof(MSGINFO));
01782        size_t context_cnt;
01783        MATCHEDSTR *c;
01784 
01785        if (p == 0)
01786               enomem();
01787 
01788        load_int(p->msgnum, fp);
01789        p->filename=load_str(fp);
01790        p->date_s=load_str(fp);
01791        p->from_s=load_str(fp);
01792        p->subject_s=load_str(fp);
01793        p->size_s=load_str(fp);
01794        load_int(p->date_n, fp);
01795        load_int(p->size_n, fp);
01796        load_int(p->mi_mtime, fp);
01797        load_int(p->mi_ino, fp);
01798 
01799        *retinfo=p;
01800 
01801        load_int(context_cnt, fp);
01802 
01803        *retmatches=NULL;
01804 
01805        if (context_cnt)
01806        {
01807               *retmatches=c=malloc(sizeof(MATCHEDSTR)*(context_cnt+1));
01808 
01809               for (; context_cnt; --context_cnt)
01810               {
01811                      char *prefix=load_str(fp);
01812                      char *match=load_str(fp);
01813                      char *suffix=load_str(fp);
01814 
01815                      if (c)
01816                      {
01817                             c->prefix=prefix;
01818                             c->match=match;
01819                             c->suffix=suffix;
01820                             ++c;
01821                      }
01822                      else
01823                      {
01824                             free(prefix);
01825                             free(match);
01826                             free(suffix);
01827                      }
01828               }
01829 
01830               if (c)
01831               {
01832                      c->prefix=NULL;
01833                      c->match=NULL;
01834                      c->suffix=NULL;
01835               }
01836        }
01837 }
01838 
01839 static void execute_maildir_search(const char *dirname,
01840                                size_t pos,
01841                                const char *searchtxt,
01842                                const char *charset,
01843                                unsigned nfiles,
01844                                MSGINFO ***retval,
01845                                MATCHEDSTR ***retcontext,
01846                                unsigned long *last_message_searched);
01847 
01848 #define SEARCHFORMATVER 1
01849 
01850 void maildir_search(const char *dirname,
01851                   size_t pos,
01852                   const char *searchtxt,
01853                   const char *charset,
01854                   unsigned nfiles)
01855 {
01856        struct maildir_tmpcreate_info createInfo;
01857 
01858        MSGINFO **p;
01859        MATCHEDSTR **pcontext;
01860        char *filename;
01861        FILE *fp;
01862        unsigned i;
01863 
01864        unsigned long last_message_searched=0;
01865 
01866        execute_maildir_search(dirname, pos, searchtxt, charset,
01867                             nfiles, &p, &pcontext, &last_message_searched);
01868 
01869        maildir_purgesearch();
01870 
01871        maildir_tmpcreate_init(&createInfo);
01872 
01873        createInfo.uniq=":search:";
01874        createInfo.doordie=1;
01875 
01876        if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
01877        {
01878               if (p)
01879                      maildir_free(p, nfiles);
01880               error("Can't create new file.");
01881        }
01882 
01883        filename=createInfo.tmpname;
01884        createInfo.tmpname=NULL;
01885        maildir_tmpcreate_free(&createInfo);
01886 
01887        chmod(filename, 0600);
01888 
01889        {
01890               char ver=SEARCHFORMATVER;
01891 
01892               save_int(ver, fp);
01893               save_int(last_message_searched, fp);
01894        }
01895 
01896        for (i=0; i<nfiles; i++)
01897               if (p[i])
01898                      save_msginfo(p[i], pcontext[i], fp);
01899        fflush(fp);
01900        if (ferror(fp) || fclose(fp))
01901               error("Cannot create temp file");
01902 
01903        cgi_put(SEARCHRESFILENAME, strrchr(filename, '/')+1);
01904 
01905        if (p)
01906               maildir_free(p, nfiles);
01907        if (pcontext)
01908               matches_free(pcontext, nfiles);
01909 }
01910 
01911 void maildir_loadsearch(unsigned nfiles,
01912                      MSGINFO ***retmsginfo,
01913                      MATCHEDSTR ***retmatches,
01914                      unsigned long *last_message_searched)
01915 {
01916        MSGINFO       **msginfo;
01917        MATCHEDSTR **matches;
01918 
01919        unsigned i;
01920        const char *filename;
01921        char *buf;
01922        FILE *fp;
01923        char ver;
01924 
01925        if ((msginfo=malloc(sizeof(MSGINFO *)*nfiles)) == 0)
01926               enomem();
01927 
01928        if ((matches=malloc(sizeof(MATCHEDSTR *)*nfiles)) == 0)
01929        {
01930               free(msginfo);
01931               enomem();
01932        }
01933 
01934        for (i=0; i<nfiles; i++)
01935        {
01936               msginfo[i]=0;
01937               matches[i]=0;
01938        }
01939 
01940        filename=cgi(SEARCHRESFILENAME);
01941        CHECKFILENAME(filename);
01942 
01943        buf=malloc(strlen(filename)+5);
01944 
01945        if (!buf)
01946        {
01947               free(msginfo);
01948               enomem();
01949        }
01950 
01951        strcat(strcpy(buf, "tmp/"), filename);
01952 
01953        fp=fopen(buf, "r");
01954        free(buf);
01955 
01956        if (fp)
01957        {
01958               load_int(ver, fp);
01959 
01960               if (ver == SEARCHFORMATVER)
01961               {
01962                      load_int(*last_message_searched, fp);
01963                      for (i=0; i<nfiles; ++i)
01964                      {
01965                             int ch=getc(fp);
01966 
01967                             if (ch == EOF)
01968                                    break;
01969                             ungetc(ch, fp);
01970 
01971                             load_msginfo(&msginfo[i], &matches[i], fp);
01972                      }
01973               }
01974        }
01975        fclose(fp);
01976 
01977        for (i=0; i<nfiles; ++i)
01978        {
01979               MSGINFO *p;
01980 
01981               if (msginfo[i] == 0)
01982                      continue;
01983 
01984               p=get_msginfo(msginfo[i]->msgnum);
01985 
01986               if (p && p->mi_ino == msginfo[i]->mi_ino) /* Safety first */
01987               {
01988                      char *f=strdup(p->filename);
01989                      if (f)
01990                      {
01991                             if (msginfo[i]->filename)
01992                                    free(msginfo[i]->filename);
01993                             msginfo[i]->filename=f;
01994                      }
01995               }
01996        }
01997 
01998        *retmsginfo=msginfo;
01999        *retmatches=matches;
02000 }
02001 
02002 static const char spaces[]=" \t\r\n";
02003 
02004 #define SEARCH_MATCH_CONTEXT_LEN   20
02005 
02006 /* After matching, save surrounding context here */
02007 
02008 struct searchresults_match_context {
02009        struct searchresults_match_context *next;
02010 
02011        unicode_char *match_context_before;
02012        unicode_char *match_context;
02013 
02014        unicode_char *match_context_after;
02015        size_t match_context_after_len;
02016        size_t match_context_after_max_len;
02017 };
02018 
02019 struct searchresults {
02020 
02021        char prevchar;
02022        size_t foundcnt;
02023 
02024        int finished;
02025 
02026        char utf8buf[512];
02027        size_t utf8buf_cnt;
02028 
02029        unicode_char *context_buf;
02030        size_t context_buf_len;
02031 
02032        size_t context_buf_head;
02033        size_t context_buf_tail;
02034 
02035        struct searchresults_match_context *matched_context_head;
02036        struct searchresults_match_context **matched_context_tail;
02037 
02038        struct maildir_searchengine *se;
02039 
02040 };
02041 
02042 static MATCHEDSTR *creatematches(struct searchresults *sr);
02043 
02044 static int searchresults_init(struct searchresults *sr,
02045                            struct maildir_searchengine *se);
02046 
02047 static void searchresults_destroy(struct searchresults *sr);
02048 
02049 static int do_maildir_search(const char *filename,
02050                           struct searchresults *sr);
02051 
02052 
02053 static void execute_maildir_search(const char *dirname,
02054                                size_t pos,
02055                                const char *searchtxt,
02056                                const char *charset,
02057                                unsigned nfiles,
02058                                MSGINFO ***retval,
02059                                MATCHEDSTR ***retcontext,
02060                                unsigned long *last_message_searched)
02061 {
02062        MSGINFO       **msginfo;
02063        MATCHEDSTR **matches;
02064        unsigned long i;
02065        unsigned j;
02066        char *utf8str;
02067        char *p, *q;
02068 
02069        if ((*retval=msginfo=malloc(sizeof(MSGINFO *)*nfiles)) == 0)
02070               enomem();
02071 
02072        if ((*retcontext=matches=malloc(sizeof(MATCHEDSTR *)*nfiles)) == 0)
02073        {
02074               free(msginfo);
02075               enomem();
02076        }
02077 
02078        for (i=0; i<nfiles; i++)
02079        {
02080               msginfo[i]=0;
02081               matches[i]=0;
02082        }
02083 
02084        if (opencache(dirname, "W"))       return;
02085 
02086        if (pos >= all_cnt) return;
02087 
02088        utf8str=libmail_u_convert_toutf8(searchtxt, charset, NULL);
02089 
02090        if (!utf8str)
02091               return;
02092 
02093        /* Normalize whitespace in the search string */
02094 
02095        p=q=utf8str;
02096 
02097        while (*p && strchr(spaces, *p))
02098               ++p;
02099 
02100        while (*p)
02101        {
02102               while (*p && !strchr(spaces, *p))
02103               {
02104                      *q++ = *p++;
02105               }
02106 
02107               while (*p && strchr(spaces, *p))
02108                      ++p;
02109 
02110               if (*p)
02111                      *q++=' ';
02112        }
02113        *q=0;
02114 
02115        if (*utf8str)
02116        {
02117               struct maildir_searchengine se;
02118               int rc=-1;
02119               unicode_char *ustr;
02120               size_t ustr_size;
02121               libmail_u_convert_handle_t h;
02122 
02123               maildir_search_init(&se);
02124 
02125               h=libmail_u_convert_tou_init("utf-8",
02126                                         &ustr,
02127                                         &ustr_size,
02128                                         1);
02129 
02130               if (h)
02131               {
02132                      libmail_u_convert(h, utf8str, strlen(utf8str));
02133                      if (libmail_u_convert_deinit(h, NULL) == 0)
02134                      {
02135                             size_t n;
02136 
02137                             for (n=0; ustr[n]; ++n)
02138                                    ustr[n]=unicode_lc(ustr[n]);
02139 
02140                             rc=maildir_search_start_unicode(&se, ustr);
02141                             free(ustr);
02142                      }
02143               }
02144 
02145               j=0;
02146 
02147               for (i=0, j=0; rc == 0 && pos+i<all_cnt && j<nfiles; ++i)
02148               {
02149                      MSGINFO *info=get_msginfo_alloc(pos+i);
02150                      char *filename;
02151 
02152                      if (!info)
02153                             continue;
02154 
02155                      *last_message_searched= pos+i;
02156 
02157                      filename=maildir_find(dirname, info->filename);
02158 
02159                      maildir_search_reset(&se);
02160 
02161                      if (filename)
02162                      {
02163                             struct searchresults sr;
02164 
02165                             info->msgnum=pos+i;
02166 
02167                             if (searchresults_init(&sr, &se) == 0)
02168                             {
02169                                    if (do_maildir_search(filename, &sr))
02170                                    {
02171                                           msginfo[j]=info;
02172                                           matches[j]=creatematches(&sr);
02173                                           info=NULL;
02174                                           ++j;
02175                                    }
02176 
02177                                    searchresults_destroy(&sr);
02178                             }
02179                             free(filename);
02180                      }
02181 
02182                      if (info)
02183                             maildir_nfreeinfo(info);
02184               }
02185               maildir_search_destroy(&se);
02186        }
02187 
02188        free(utf8str);
02189 }
02190 
02191 #define sr_context_buf_index_inc(sr,n) ( ( (n)+1 ) % ( (sr)->context_buf_len))
02192 #define sr_context_buf_index_dec(sr,n) ( ( (n)+ (sr)->context_buf_len - 1 ) % ( (sr)->context_buf_len))
02193 
02194 static int searchresults_init(struct searchresults *sr,
02195                            struct maildir_searchengine *se)
02196 {
02197        sr->prevchar=0;
02198        sr->foundcnt=0;
02199        sr->finished=0;
02200        sr->se=se;
02201        sr->utf8buf_cnt=0;
02202 
02203        sr->context_buf_len=maildir_search_len(se)+SEARCH_MATCH_CONTEXT_LEN*2+1;
02204        sr->context_buf=malloc(sr->context_buf_len * sizeof(unicode_char));
02205 
02206        if (sr->context_buf == NULL)
02207               return -1;
02208        sr->context_buf_head=0;
02209        sr->context_buf_tail=0;
02210 
02211        sr->matched_context_head=NULL;
02212        sr->matched_context_tail=&sr->matched_context_head;
02213 
02214        return 0;
02215 }
02216 
02217 static void searchresults_destroy(struct searchresults *sr)
02218 {
02219        while (sr->matched_context_head)
02220        {
02221               struct searchresults_match_context *c=sr->matched_context_head;
02222 
02223               sr->matched_context_head=c->next;
02224 
02225               free(c->match_context_before);
02226               free(c->match_context_after);
02227               free(c->match_context);
02228        }
02229 
02230        free(sr->context_buf);
02231 }
02232 
02233 /* Save context before, and the matched context */
02234 
02235 static void search_found_save_context(struct searchresults *sr)
02236 {
02237        struct searchresults_match_context *c=
02238               malloc(sizeof(struct searchresults_match_context));
02239        size_t n, i, j;;
02240 
02241        if (c == NULL)
02242               return;
02243 
02244        if ((c->match_context_before=malloc((SEARCH_MATCH_CONTEXT_LEN+1)
02245                                        * sizeof(unicode_char))) == NULL)
02246        {
02247               free(c);
02248               return;
02249        }
02250 
02251        if ((c->match_context=malloc((maildir_search_len(sr->se)+1)
02252                                  * sizeof(unicode_char))) == NULL)
02253        {
02254               free(c->match_context_before);
02255               free(c);
02256               return;
02257        }
02258 
02259        if ((c->match_context_after=malloc((SEARCH_MATCH_CONTEXT_LEN+1)
02260                                       * sizeof(unicode_char))) == NULL)
02261        {
02262               free(c->match_context);
02263               free(c->match_context_before);
02264               free(c);
02265               return;
02266        }
02267 
02268        c->next=NULL;
02269 
02270        *sr->matched_context_tail=c;
02271        sr->matched_context_tail= &c->next;
02272 
02273        /*
02274        ** Subtract from the head of the context buffer to arrive at the
02275        ** start of the matched context
02276        */
02277 
02278        n=sr->context_buf_head;
02279 
02280        for (i=maildir_search_len(sr->se); i > 0; --i)
02281        {
02282               if (n == sr->context_buf_tail)
02283                      break; /* Shouldn't happen */
02284 
02285               n=sr_context_buf_index_dec(sr, n);
02286        }
02287 
02288        /* From here to the head is the matched context */
02289 
02290        j=0;
02291        for (i=n; i != sr->context_buf_head; )
02292        {
02293               c->match_context[j++]=sr->context_buf[i];
02294               i=sr_context_buf_index_inc(sr, i);
02295        }
02296        c->match_context[j]=0;
02297 
02298        /* Now, look before the start of the matched context */
02299 
02300        for (i=n, j=0; j<SEARCH_MATCH_CONTEXT_LEN; ++j)
02301        {
02302               if (i == sr->context_buf_tail)
02303                      break; /* Possible */
02304 
02305               i=sr_context_buf_index_dec(sr, i);
02306        }
02307 
02308        j=0;
02309        while (i != n)
02310        {
02311               c->match_context_before[j++]=sr->context_buf[i];
02312               i=sr_context_buf_index_inc(sr, i);
02313        }
02314        c->match_context_before[j]=0;
02315 
02316        c->match_context_after_len=0;
02317        c->match_context_after_max_len=SEARCH_MATCH_CONTEXT_LEN;
02318 }
02319 
02320 static int do_search_utf8(struct searchresults *res)
02321 {
02322        unicode_char *uc=NULL;
02323        size_t n;
02324        libmail_u_convert_handle_t h;
02325 
02326        if (res->utf8buf_cnt == 0)
02327               return 0;
02328 
02329        if (res->finished)
02330               return -1;
02331 
02332        res->utf8buf[res->utf8buf_cnt]=0;
02333        res->utf8buf_cnt=0;
02334 
02335        h=libmail_u_convert_tou_init("utf-8",
02336                                  &uc,
02337                                  &n,
02338                                  1);
02339 
02340        if (h)
02341        {
02342               libmail_u_convert(h, res->utf8buf, strlen(res->utf8buf));
02343               if (libmail_u_convert_deinit(h, NULL) == 0)
02344                      ;
02345               else
02346                      uc=NULL;
02347        }
02348 
02349        for (n=0; uc && uc[n]; n++)
02350        {
02351               struct searchresults_match_context *c;
02352 
02353               unicode_char origch=uc[n];
02354               unicode_char ch=unicode_lc(origch);
02355 
02356               maildir_search_step_unicode(res->se, ch);
02357 
02358               /* Record the context, one char at a time */
02359 
02360               res->context_buf[res->context_buf_head]=origch;
02361               res->context_buf_head =
02362                      sr_context_buf_index_inc(res, res->context_buf_head);
02363 
02364               if (res->context_buf_head == res->context_buf_tail)
02365                      res->context_buf_tail =
02366                             sr_context_buf_index_inc(res, res->context_buf_tail);
02367               /* Accumulate post-match context for matched hits */
02368 
02369               for (c=res->matched_context_head; c; c=c->next)
02370               {
02371                      if (c->match_context_after_len <
02372                          c->match_context_after_max_len)
02373                             c->match_context_after[c->match_context_after_len++]=origch;
02374               }
02375 
02376               if (maildir_search_found(res->se))
02377               {
02378                      search_found_save_context(res);
02379                      if (++res->foundcnt > 3)
02380                      {
02381                             res->finished=1;
02382                             break;
02383                      }
02384 
02385                      maildir_search_reset(res->se);
02386               }
02387        }
02388 
02389        if (uc)
02390               free(uc);
02391 
02392        if (res->finished)
02393               return 1;
02394 
02395        return 0;            
02396 }
02397 
02398 static int do_search(const char *str, size_t n, void *arg)
02399 {
02400        struct searchresults *res=(struct searchresults *)arg;
02401 
02402        int rc=0;
02403 
02404        while (n && rc == 0)
02405        {
02406               char ch=*str++;
02407               --n;
02408 
02409               if (strchr(spaces, ch))
02410               {
02411                      ch=' ';
02412 
02413                      if (res->prevchar == ' ')
02414                             continue;
02415               }
02416               res->prevchar=ch;
02417 
02418               if (res->utf8buf_cnt >= sizeof(res->utf8buf)-1)
02419               {
02420                      size_t n;
02421                      char save_ch;
02422                      size_t save_n;
02423 
02424                      for (n=res->utf8buf_cnt-1;
02425                           n > sizeof(res->utf8buf_cnt)/2; --n)
02426                             if ((unsigned char)((res->utf8buf[n] ^ 0xC0)
02427                                               & 0xC0))
02428                                    break;
02429 
02430                      save_n=res->utf8buf_cnt;
02431                      save_ch=res->utf8buf[n];
02432 
02433                      res->utf8buf_cnt=n;
02434 
02435                      rc=do_search_utf8(res);
02436 
02437                      if (rc)
02438                             return rc;
02439 
02440                      res->utf8buf[n]=save_ch;
02441 
02442                      while (n < save_n)
02443                      {
02444                             res->utf8buf[res->utf8buf_cnt++]=
02445                                    res->utf8buf[n];
02446                             ++n;
02447                      }
02448               }
02449 
02450               res->utf8buf[res->utf8buf_cnt]=ch;
02451               ++res->utf8buf_cnt;
02452        }
02453 
02454        return rc;
02455 }
02456 
02457 static int do_maildir_search(const char *filename,
02458                           struct searchresults *sr)
02459 {
02460        struct rfc2045src *src;
02461        struct rfc2045_decodemsgtoutf8_cb cb;
02462        int fd=maildir_semisafeopen(filename, O_RDONLY, 0);
02463        struct rfc2045 *rfc2045p;
02464 
02465        if (fd < 0)
02466               return 0;
02467 
02468        rfc2045p=rfc2045_fromfd(fd);
02469 
02470        if (rfc2045p == NULL)
02471        {
02472               close(fd);
02473               return 0;
02474        }
02475 
02476        memset(&cb, 0, sizeof(cb));
02477        cb.output_func=do_search;
02478        cb.arg=sr;
02479 
02480        if ((src=rfc2045src_init_fd(fd)) != NULL)
02481        {
02482               if (rfc2045_decodemsgtoutf8(src, rfc2045p, &cb) == 0)
02483                      do_search_utf8(sr);
02484               rfc2045src_deinit(src);
02485        }
02486 
02487        rfc2045_free(rfc2045p);
02488        close(fd);
02489        return sr->foundcnt ? 1:0;
02490 }
02491 
02492 static void savematch(struct searchresults_match_context *smc,
02493                     MATCHEDSTR **curptr);
02494 
02495 static MATCHEDSTR *creatematches(struct searchresults *sr)
02496 {
02497        size_t n=0;
02498        struct searchresults_match_context *smc;
02499        MATCHEDSTR *retval, *retptr;
02500 
02501        /* Count, allocate the array */
02502        for (smc=sr->matched_context_head; smc; smc=smc->next)
02503               ++n;
02504 
02505        if ((retval=malloc(sizeof(MATCHEDSTR)*(n+1))) == NULL)
02506               return NULL;
02507 
02508        retptr=retval;
02509 
02510        for (smc=sr->matched_context_head; smc; smc=smc->next)
02511        {
02512               savematch(smc, &retptr);
02513        }
02514 
02515        /* Last one */
02516 
02517        retptr->prefix=NULL;
02518        retptr->match=NULL;
02519        retptr->suffix=NULL;
02520        return retval;
02521 }
02522 
02523 static char *match_conv(const unicode_char *uc)
02524 {
02525        char *cbuf;
02526        size_t csize;
02527        libmail_u_convert_handle_t h;
02528        size_t i;
02529 
02530        if ((h=libmail_u_convert_fromu_init("utf-8", &cbuf, &csize, 1)) == NULL)
02531               return NULL;
02532 
02533        for (i=0; uc[i]; ++i)
02534               ;
02535 
02536        libmail_u_convert_uc(h, uc, i);
02537 
02538        if (libmail_u_convert_deinit(h, NULL))
02539               return NULL;
02540        return cbuf;
02541 }
02542 
02543 static void savematch(struct searchresults_match_context *smc,
02544                     MATCHEDSTR **curptr)
02545 {
02546        if ( ((*curptr)->prefix=match_conv(smc->match_context_before))
02547             == NULL)
02548               return;
02549 
02550        if ( ((*curptr)->match=match_conv(smc->match_context)) == NULL)
02551        {
02552               free( (*curptr)->prefix );
02553               return;
02554        }
02555 
02556        smc->match_context_after[smc->match_context_after_len]=0;
02557 
02558        if ( ((*curptr)->suffix=match_conv(smc->match_context_after))
02559             == NULL)
02560        {
02561               free( (*curptr)->match );
02562               free( (*curptr)->prefix );
02563               return;
02564        }
02565        ++ (*curptr);
02566 }
02567 
02568 static void dodirscan(const char *, const char *, unsigned *, unsigned *);
02569 
02570 void maildir_count(const char *folder,
02571                  unsigned *new_ptr,
02572                  unsigned *other_ptr)
02573 {
02574        struct maildir_info minfo;
02575        char *dir;
02576 
02577        *new_ptr=0;
02578        *other_ptr=0;
02579 
02580        if (maildir_info_imap_find(&minfo, folder,
02581                                login_returnaddr()) < 0)
02582               return;
02583 
02584        if (minfo.mailbox_type == MAILBOXTYPE_OLDSHARED)
02585        {
02586               dir=maildir_shareddir(".", strchr(folder, '.')+1);
02587 
02588               if (!dir)
02589               {
02590                      maildir_info_destroy(&minfo);
02591                      return;
02592               }
02593 
02594               maildir_shared_sync(dir);
02595        }
02596        else
02597        {
02598               if (minfo.homedir == NULL || minfo.maildir == NULL)
02599               {
02600                      maildir_info_destroy(&minfo);
02601                      return;
02602               }
02603 
02604               dir=maildir_name2dir(minfo.homedir, minfo.maildir);
02605 
02606               if (!dir)
02607               {
02608                      maildir_info_destroy(&minfo);
02609                      return;
02610               }
02611        }
02612 
02613        maildir_info_destroy(&minfo);
02614        maildir_checknew(folder, dir);
02615        dodirscan(folder, dir, new_ptr, other_ptr);
02616        free(dir);
02617 }
02618 
02619 unsigned maildir_countof(const char *folder)
02620 {
02621        maildir_getfoldermsgs(folder);
02622        return (all_cnt);
02623 }
02624 
02625 static void dodirscan(const char *folder,
02626                     const char *dir, unsigned *new_cnt,
02627                     unsigned *other_cnt)
02628 {
02629        DIR *dirp;
02630        struct dirent *de;
02631        char   *curname;
02632        struct stat cur_stat;
02633        struct stat c_stat;
02634        const  char *p;
02635        char   cntbuf[MAXLONGSIZE*2+4];
02636        char   *cntfilename;
02637        FILE   *fp;
02638        struct maildir_tmpcreate_info createInfo;
02639 
02640        *new_cnt=0;
02641        *other_cnt=0;
02642        curname=alloc_filename(dir, "cur", "");
02643 
02644        if (stat(curname, &cur_stat))
02645        {
02646               free(curname);
02647               return;
02648        }
02649 
02650        cntfilename=foldercountfilename(folder);
02651        fp=fopen(cntfilename, "r");
02652 
02653        if (fp)
02654        {
02655               char buf[BUFSIZ];
02656 
02657               if (fstat(fileno(fp), &c_stat) == 0 &&
02658                   c_stat.st_mtime > cur_stat.st_mtime &&
02659                   fgets(buf, sizeof(buf), fp))
02660               {
02661                      unsigned long n;
02662                      unsigned long o;
02663 
02664                      if ((p=parse_ul(buf, &n)) && (p=parse_ul(p, &o)))
02665                      {
02666                             *new_cnt=n;
02667                             *other_cnt=o;
02668                             free(curname);
02669                             fclose(fp);
02670                             free(cntfilename);
02671                             return;       /* Valid cache of count */
02672                      }
02673               }
02674               fclose(fp);
02675        }
02676 
02677        dirp=opendir(curname);
02678        while (dirp && (de=readdir(dirp)) != NULL)
02679               docount(de->d_name, new_cnt, other_cnt);
02680        if (dirp)     closedir(dirp);
02681        sprintf(cntbuf, "%u %u\n", *new_cnt, *other_cnt);
02682 
02683        maildir_tmpcreate_init(&createInfo);
02684        createInfo.maildir=".";
02685        createInfo.uniq="count";
02686        createInfo.doordie=1;
02687 
02688        fp=maildir_tmpcreate_fp(&createInfo);
02689        if (!fp)
02690        {
02691               free(curname);
02692               free(cntfilename);
02693               maildir_tmpcreate_free(&createInfo);
02694               return;
02695        }
02696 
02697        fprintf(fp, "%s", cntbuf);
02698        fclose(fp);
02699        
02700        if (rename(createInfo.tmpname, cntfilename) < 0 ||
02701            stat(cntfilename, &c_stat) < 0)
02702        {
02703               unlink(cntfilename);
02704               free(curname);
02705               free(cntfilename);
02706               maildir_tmpcreate_free(&createInfo);
02707               return;
02708        }
02709        maildir_tmpcreate_free(&createInfo);
02710 
02711 
02712        if (c_stat.st_mtime != cur_stat.st_mtime)
02713        {
02714               struct stat stat2;
02715 
02716               if (stat(curname, &stat2)
02717                   || stat2.st_mtime != cur_stat.st_mtime)
02718               {
02719                      /* cur directory changed while we were there, punt */
02720 
02721                      c_stat.st_mtime=cur_stat.st_mtime;
02722                      /* Reset it below */
02723               }
02724        }
02725 
02726        if (c_stat.st_mtime == cur_stat.st_mtime)
02727               /* Potential race condition */
02728        {
02729               change_timestamp(cntfilename, c_stat.st_mtime-1);
02730                             /* ... So rebuild it next time */
02731        }
02732        free(curname);
02733        free(cntfilename);
02734 }
02735 
02736 void maildir_free(MSGINFO **files, unsigned nfiles)
02737 {
02738 unsigned i;
02739 
02740        for (i=0; i<nfiles; i++)
02741        {
02742               if ( files[i] )
02743                      maildir_nfreeinfo( files[i] );
02744        }
02745        free(files);
02746 }
02747 
02748 static char *buf=0;
02749 size_t bufsize=0, buflen=0;
02750 
02751 static void addbuf(int c)
02752 {
02753        if (buflen == bufsize)
02754        {
02755        char   *newbuf= buf ? realloc(buf, bufsize+512):malloc(bufsize+512);
02756 
02757               if (!newbuf)  enomem();
02758               buf=newbuf;
02759               bufsize += 512;
02760        }
02761        buf[buflen++]=c;
02762 }
02763 
02764 char *maildir_readline(FILE *fp)
02765 {
02766 int    c;
02767 
02768        buflen=0;
02769        while ((c=getc(fp)) != '\n' && c >= 0)
02770               if (buflen < 8192)
02771                      addbuf(c);
02772        if (c < 0 && buflen == 0)   return (NULL);
02773        addbuf(0);
02774        return (buf);
02775 }
02776 
02777 char *maildir_readheader_nolc(FILE *fp, char **value)
02778 {
02779        int c;
02780 
02781        buflen=0;
02782 
02783        while ((c=getc(fp)) != EOF)
02784        {
02785               if (c != '\n')
02786               {
02787                      addbuf(c);
02788                      continue;
02789               }
02790               c=getc(fp);
02791               if (c >= 0) ungetc(c, fp);
02792               if (c < 0 || c == '\n' || !isspace(c)) break;
02793               addbuf('\n');
02794        }
02795        addbuf(0);
02796 
02797        if (c == EOF && buf[0] == 0) return (0);
02798 
02799        for ( *value=buf; **value; (*value)++)
02800        {
02801               if (**value == ':')
02802               {
02803                      **value='\0';
02804                      ++*value;
02805                      break;
02806               }
02807        }
02808        while (**value && isspace((int)(unsigned char)**value)) ++*value;
02809        return(buf);
02810 }
02811 
02812 char   *maildir_readheader_mimepart(FILE *fp, char **value, int preserve_nl,
02813               off_t *mimepos, const off_t *endpos)
02814 {
02815        int    c;
02816        int    eatspaces=0;
02817 
02818        buflen=0;
02819 
02820        if (mimepos && *mimepos >= *endpos)       return (0);
02821 
02822        while (mimepos == 0 || *mimepos < *endpos)
02823        {
02824               if ((c=getc(fp)) != '\n' && c >= 0)
02825               {
02826                      if (c != ' ' && c != '\t' && c != '\r')
02827                             eatspaces=0;
02828 
02829                      if (!eatspaces)
02830                             addbuf(c);
02831                      if (mimepos)  ++ *mimepos;
02832                      continue;
02833               }
02834               if ( c == '\n' && mimepos)  ++ *mimepos;
02835               if (buflen == 0)     return (0);
02836               if (c < 0)    break;
02837               c=getc(fp);
02838               if (c >= 0)   ungetc(c, fp);
02839               if (c < 0 || c == '\n' || !isspace(c))    break;
02840               addbuf(preserve_nl ? '\n':' ');
02841               if (!preserve_nl)
02842                      eatspaces=1;
02843        }
02844        addbuf(0);
02845 
02846        for ( *value=buf; **value; (*value)++)
02847        {
02848               if (**value == ':')
02849               {
02850                      **value='\0';
02851                      ++*value;
02852                      break;
02853               }
02854               **value=tolower(**value);
02855        }
02856        while (**value && isspace((int)(unsigned char)**value)) ++*value;
02857        return(buf);
02858 }
02859 
02860 char   *maildir_readheader(FILE *fp, char **value, int preserve_nl)
02861 {
02862        return (maildir_readheader_mimepart(fp, value, preserve_nl, 0, 0));
02863 }
02864 
02865 /*****************************************************************************
02866 
02867 The MSGINFO structure contains the summary of the headers found in all
02868 messages in the cur directory.
02869 
02870 Instead of opening each message every time we need to serve the directory
02871 contents, the messages are scanned once, and a cache file is built
02872 containing the contents.
02873 
02874 *****************************************************************************/
02875 
02876 /* Deallocate an individual MSGINFO structure */
02877 
02878 void maildir_nfreeinfo(MSGINFO *mi)
02879 {
02880        if (mi->filename)    free(mi->filename);
02881        if (mi->date_s)      free(mi->date_s);
02882        if (mi->from_s)      free(mi->from_s);
02883        if (mi->subject_s)   free(mi->subject_s);
02884        if (mi->size_s)      free(mi->size_s);
02885        free(mi);
02886 }
02887 
02888 /* Initialize a MSGINFO structure by reading the message headers */
02889 
02890 MSGINFO *maildir_ngetinfo(const char *filename)
02891 {
02892 FILE   *fp;
02893 MSGINFO       *mi;
02894 struct stat stat_buf;
02895 char   *hdr, *val;
02896 const char *p;
02897 int    is_sent_header=0;
02898 char   *fromheader=0;
02899 int    fd;
02900 
02901        /* Hack - see if we're reading a message from the Sent or Drafts
02902               folder */
02903 
02904        p=strrchr(filename, '/');
02905        if ((p && p - filename >=
02906               sizeof(SENT) + 5 && strncmp(p - (sizeof(SENT) + 5),
02907                      "/." SENT "/", sizeof(SENT)+2) == 0)
02908               || strncmp(filename, "." SENT "/", sizeof(SENT)+1) == 0
02909               || strncmp(filename, "./." SENT ".", sizeof(SENT)+3) == 0
02910               || strncmp(filename, "." SENT ".", sizeof(SENT)+1) == 0)
02911               is_sent_header=1;
02912        if ((p && p - filename >=
02913               sizeof(DRAFTS) + 5 && strncmp(p-(sizeof(DRAFTS) + 5),
02914                      "/." DRAFTS "/", sizeof(DRAFTS)+2) == 0)
02915               || strncmp(filename, "." DRAFTS "/", sizeof(DRAFTS)+1) == 0)
02916               is_sent_header=1;
02917 
02918        if ((mi=(MSGINFO *)malloc(sizeof(MSGINFO))) == 0)
02919               enomem();
02920 
02921        memset(mi, '\0', sizeof(*mi));
02922 
02923        fp=0;
02924        fd=maildir_semisafeopen(filename, O_RDONLY, 0);
02925        if (fd >= 0)
02926               if ((fp=fdopen(fd, "r")) == 0)
02927                      close(fd);
02928 
02929        if (fp == NULL)
02930        {
02931               free(mi);
02932               return (NULL);
02933        }
02934 
02935        /* mi->filename shall be the base filename, normalized as :2, */
02936 
02937        if ((p=strrchr(filename, '/')) != NULL)
02938               p++;
02939        else   p=filename;
02940 
02941        if (!(mi->filename=strdup(p)))
02942               enomem();
02943 
02944        if (fstat(fileno(fp), &stat_buf) == 0)
02945        {
02946               mi->mi_mtime=stat_buf.st_mtime;
02947               mi->mi_ino=stat_buf.st_ino;
02948               mi->size_n=stat_buf.st_size;
02949               mi->size_s=strdup( showsize(stat_buf.st_size));
02950               mi->date_n=mi->mi_mtime;    /* Default if no Date: */
02951               if (!mi->size_s)     enomem();
02952        }
02953        else
02954        {
02955               free(mi->filename);
02956               fclose(fp);
02957               free(mi);
02958               return (0);
02959        }
02960 
02961 
02962        while ((hdr=maildir_readheader(fp, &val, 0)) != 0)
02963        {
02964               if (strcmp(hdr, "subject") == 0)
02965               {
02966                      char *uibuf=rfc822_display_hdrvalue_tobuf("subject",
02967                                                           val,
02968                                                           "utf-8",
02969                                                           NULL, NULL);
02970 
02971                      if (mi->subject_s)   free(mi->subject_s);
02972 
02973                      mi->subject_s=uibuf;
02974                      if (!mi->subject_s)  enomem();
02975               }
02976 
02977               if (strcmp(hdr, "date") == 0 && mi->date_s == 0)
02978               {
02979               time_t t=rfc822_parsedt(val);
02980 
02981                      if (t)
02982                      {
02983                             mi->date_n=t;
02984                             mi->date_s=strdup(displaydate(mi->date_n));
02985                             if (!mi->date_s)     enomem();
02986                      }
02987               }
02988 
02989               if ((is_sent_header ?
02990                      strcmp(hdr, "to") == 0 || strcmp(hdr, "cc") == 0:
02991                      strcmp(hdr, "from") == 0) && fromheader == 0)
02992               {
02993                      struct rfc822t *from_addr;
02994                      struct rfc822a *from_addra;
02995                      char   *p;
02996                      int dotflag=0;
02997                      int cnt;
02998 
02999                      from_addr=rfc822t_alloc_new(val, NULL, NULL);
03000                      if (!from_addr)      enomem();
03001                      from_addra=rfc822a_alloc(from_addr);
03002                      if (!from_addra)     enomem();
03003 
03004                      p=NULL;
03005 
03006                      for (cnt=0; cnt<from_addra->naddrs; ++cnt)
03007                      {
03008                             if (from_addra->addrs[cnt].tokens == NULL)
03009                                    continue;
03010 
03011                             if (p)
03012                             {
03013                                    dotflag=1;
03014                                    break;
03015                             }
03016 
03017                             p=rfc822_display_name_tobuf(from_addra, cnt,
03018                                                      "utf-8");
03019                      }
03020 
03021                      if (p)
03022                      {
03023                             if (fromheader)      free(fromheader);
03024                             if ((fromheader=malloc(strlen(p)+7)) == 0)
03025                                    enomem();
03026                             strcpy(fromheader, p);
03027                             if (dotflag)
03028                                    strcat(fromheader, "...");
03029 
03030                             free(p);
03031                      }
03032 
03033                      rfc822a_free(from_addra);
03034                      rfc822t_free(from_addr);
03035               }
03036 
03037               if (mi->date_s && fromheader && mi->subject_s)
03038                      break;
03039        }
03040        fclose(fp);
03041 
03042        mi->from_s=fromheader;
03043        if (!mi->date_s)
03044               mi->date_s=strdup(displaydate(mi->date_n));
03045        if (!mi->date_s)     enomem();
03046        if (!mi->from_s && !(mi->from_s=strdup("")))     enomem();
03047        if (!mi->subject_s && !(mi->subject_s=strdup("")))      enomem();
03048        return (mi);
03049 }
03050 
03051 /************************************************************************/
03052 
03053 /* Save cache file */
03054 
03055 static unsigned long save_cnt, savenew_cnt;
03056 static time_t save_time;
03057 
03058 static char   *save_dbname;
03059 static char   *save_tmpdbname;
03060 struct dbobj  tmpdb;
03061 
03062 static void maildir_save_start(const char *folder,
03063                             const char *maildir, time_t t)
03064 {
03065        int fd;
03066        struct maildir_tmpcreate_info createInfo;
03067 
03068        save_dbname=foldercachename(folder);
03069 
03070        save_time=t;
03071 #if 1
03072        {
03073          int f = -1;
03074          char *tmpfname = alloc_filename(maildir,
03075                                       "", MAILDIRCURCACHE ".nfshack");
03076          if (tmpfname) {
03077            f = open(tmpfname, O_CREAT|O_WRONLY, 0600);
03078            free(tmpfname);
03079          }
03080          if (f != -1) {
03081            struct stat s;
03082            if (write(f, ".", 1) != 1)
03083                   ; /* ignore */
03084            fsync(f);
03085            if (fstat(f, &s) == 0)
03086              save_time = s.st_mtime;
03087            close(f);
03088            unlink(tmpfname);
03089          }
03090        }
03091 #endif
03092 
03093        maildir_tmpcreate_init(&createInfo);
03094        createInfo.maildir=maildir;
03095        createInfo.uniq="sqwebmail-db";
03096        createInfo.doordie=1;
03097 
03098        if ((fd=maildir_tmpcreate_fd(&createInfo)) < 0)
03099        {
03100               fprintf(stderr, "ERR: Can't create cache file %s: %s\n",
03101                      maildir, strerror(errno));
03102               error(strerror(errno));
03103        }
03104        close(fd);
03105 
03106        save_tmpdbname=createInfo.tmpname;
03107        createInfo.tmpname=NULL;
03108        maildir_tmpcreate_free(&createInfo);
03109 
03110        dbobj_init(&tmpdb);
03111 
03112        if (dbobj_open(&tmpdb, save_tmpdbname, "N")) {
03113               fprintf(stderr, "ERR: Can't create cache file |%s|: %s\n", save_tmpdbname, strerror(errno));
03114               error("Can't create cache file.");
03115        }
03116 
03117        save_cnt=0;
03118        savenew_cnt=0;
03119 }
03120 
03121 static void maildir_saveinfo(MSGINFO *m)
03122 {
03123 char   *rec, *p;
03124 char   recnamebuf[MAXLONGSIZE+40];
03125 
03126        rec=malloc(strlen(m->filename)+strlen(m->from_s)+
03127               strlen(m->subject_s)+strlen(m->size_s)+MAXLONGSIZE*4+
03128               sizeof("FILENAME=\nFROM=\nSUBJECT=\nSIZES=\nDATE=\n"
03129                      "SIZEN=\nTIME=\nINODE=\n")+100);
03130        if (!rec)     enomem();
03131 
03132        sprintf(rec, "FILENAME=%s\nFROM=%s\nSUBJECT=%s\nSIZES=%s\n"
03133               "DATE=%lu\n"
03134               "SIZEN=%lu\n"
03135               "TIME=%lu\n"
03136               "INODE=%lu\n",
03137               m->filename,
03138               m->from_s,
03139               m->subject_s,
03140               m->size_s,
03141               (unsigned long)m->date_n,
03142               (unsigned long)m->size_n,
03143               (unsigned long)m->mi_mtime,
03144               (unsigned long)m->mi_ino);
03145        sprintf(recnamebuf, "REC%lu", (unsigned long)save_cnt);
03146        if (dbobj_store(&tmpdb, recnamebuf, strlen(recnamebuf),
03147               rec, strlen(rec), "R"))
03148               enomem();
03149        free(rec);
03150 
03151        /* Reverse lookup */
03152        rec=malloc(strlen(m->filename)+10);
03153        if (!rec)
03154               enomem();
03155        strcat(strcpy(rec, "FILE"), m->filename);
03156        if ((p=strchr(rec, ':')) != 0)
03157               *p=0;
03158        sprintf(recnamebuf, "%lu", (unsigned long)save_cnt);
03159        if (dbobj_store(&tmpdb, rec, strlen(rec),
03160                      recnamebuf, strlen(recnamebuf), "R"))
03161               enomem();
03162 
03163        save_cnt++;
03164        if (maildirfile_type(m->filename) == MSGTYPE_NEW)
03165               savenew_cnt++;
03166 }
03167 
03168 static void maildir_save_end(const char *maildir)
03169 {
03170 char   *curname;
03171 char   *rec;
03172 
03173        curname=alloc_filename(maildir, "", "cur");
03174 
03175        rec=malloc(MAXLONGSIZE*4+sizeof(
03176                      "SAVETIME=\n"
03177                      "COUNT=\n"
03178                      "NEWCOUNT=\n"
03179                      "SORT=\n")+100);
03180 
03181        if (!rec)     enomem();
03182        sprintf(rec,
03183               "SAVETIME=%lu\nCOUNT=%lu\nNEWCOUNT=%lu\nSORT=%d%c\n",
03184               (unsigned long)save_time,
03185               (unsigned long)save_cnt,
03186               (unsigned long)savenew_cnt,
03187                      pref_flagisoldest1st,
03188                      pref_flagsortorder);
03189        if (dbobj_store(&tmpdb, "HEADER", 6, rec, strlen(rec), "R"))
03190               enomem();
03191        dbobj_close(&tmpdb);
03192        free(rec);
03193 
03194        rename(save_tmpdbname, save_dbname);
03195        unlink(save_tmpdbname);
03196 
03197        free(curname);
03198        free(save_dbname);
03199        free(save_tmpdbname);
03200 }
03201 
03202 void maildir_savefoldermsgs(const char *folder)
03203 {
03204 }
03205 
03206 /************************************************************************/
03207 
03208 struct MSGINFO_LIST {
03209        struct MSGINFO_LIST  *next;
03210        MSGINFO *minfo;
03211        } ;
03212 
03213 static void createmdcache(const char *folder, const char *maildir)
03214 {
03215 char   *curname;
03216 DIR *dirp;
03217 struct dirent *de;
03218 struct MSGINFO_LIST *milist, *newmi;
03219 MSGINFO       *mi;
03220 unsigned long cnt=0;
03221 
03222        curname=alloc_filename(maildir, "", "cur");
03223 
03224        time(&current_time);
03225 
03226        maildir_save_start(folder, maildir, current_time);
03227 
03228        milist=0;
03229        dirp=opendir(curname);
03230        while (dirp && (de=readdir(dirp)) != NULL)
03231        {
03232        char   *filename;
03233 
03234               if (de->d_name[0] == '.')
03235                      continue;
03236 
03237               filename=alloc_filename(curname, "", de->d_name);
03238               mi=maildir_ngetinfo(filename);
03239               free(filename);
03240               if (!mi)      continue;
03241 
03242               if (!(newmi=malloc(sizeof(struct MSGINFO_LIST)))) enomem();
03243               newmi->next= milist;
03244               milist=newmi;
03245               newmi->minfo=mi;
03246               ++cnt;
03247        }
03248        if (dirp)     closedir(dirp);
03249        free(curname);
03250 
03251        if (milist)
03252        {
03253        MSGINFO **miarray=malloc(sizeof(MSGINFO *) * cnt);
03254        unsigned long i;
03255 
03256               if (!miarray) enomem();
03257               i=0;
03258               while (milist)
03259               {
03260                      miarray[i++]=milist->minfo;
03261                      newmi=milist;
03262                      milist=newmi->next;
03263                      free(newmi);
03264               }
03265 
03266               qsort(miarray, cnt, sizeof(*miarray),
03267                      ( int (*)(const void *, const void *)) messagecmp);
03268               for (i=0; i<cnt; i++)
03269               {
03270                      maildir_saveinfo(miarray[i]);
03271                      maildir_nfreeinfo(miarray[i]);
03272               }
03273               free(miarray);
03274        }
03275 
03276        maildir_save_end(maildir);
03277 }
03278 
03279 static int chkcache(const char *folder)
03280 {
03281        if (opencache(folder, "W")) return (-1);
03282 
03283        if (isoldestfirst != pref_flagisoldest1st)       return (-1);
03284        if (sortorder != pref_flagsortorder)             return (-1);
03285        return (0);
03286 }
03287 
03288 static void   maildir_getfoldermsgs(const char *folder)
03289 {
03290 char   *dir=xlate_shmdir(folder);
03291 
03292        if (!dir)     return;
03293 
03294        while ( chkcache(folder) )
03295        {
03296               closedb();
03297               createmdcache(folder, dir);
03298        }
03299        free(dir);
03300 }
03301 
03302 void   maildir_remcache(const char *folder)
03303 {
03304        char   *dir=xlate_shmdir(folder);
03305        char   *cachename=foldercachename(folder);
03306 
03307        unlink(cachename);
03308        if (folderdatname && strcmp(folderdatname, cachename) == 0)
03309               closedb();
03310        free(cachename);
03311        free(dir);
03312 }
03313 
03314 void   maildir_reload(const char *folder)
03315 {
03316 char   *dir=xlate_shmdir(folder);
03317 char   *curname;
03318 struct stat   stat_buf;
03319 
03320        if (!dir)     return;
03321 
03322        curname=alloc_filename(dir, "cur", ".");
03323        time(&current_time);
03324 
03325        /* Remove old cache file when: */
03326 
03327        if (opencache(folder, "W") == 0)
03328        {
03329               if ( stat(curname, &stat_buf) != 0 ||
03330                      stat_buf.st_mtime >= cachemtime)
03331               {
03332                      closedb();
03333                      createmdcache(folder, dir);
03334               }
03335        }
03336        free(dir);
03337        maildir_getfoldermsgs(folder);
03338        free(curname);
03339 }
03340 
03341 /*
03342        maildir_listfolders(char ***) - read all the folders in the mailbox.
03343        maildir_freefolders(char ***) - deallocate memory
03344 */
03345 
03346 static void addfolder(const char *name, char ***buf, size_t *size, size_t *cnt)
03347 {
03348        if (*cnt >= *size)
03349        {
03350        char   **newbuf= *buf ? realloc(*buf, (*size + 10) * sizeof(char *))
03351                      : malloc( (*size+10) * sizeof(char *));
03352 
03353               if (!newbuf)  enomem();
03354               *buf=newbuf;
03355               *size += 10;
03356        }
03357 
03358        (*buf)[*cnt]=0;
03359        if ( name && ((*buf)[*cnt]=strdup(name)) == 0)   enomem();
03360        ++*cnt;
03361 }
03362 
03363 /*
03364 **  Return a sorted list of folders.
03365 **
03366 */
03367 
03368 struct add_shared_info {
03369        char ***p;
03370        size_t *s;
03371        size_t *c;
03372        const char *inbox_pfix;
03373        } ;
03374 
03375 static void list_callback(const char *n, void *vp)
03376 {
03377        struct add_shared_info *i=
03378               (struct add_shared_info *)vp;
03379        char *o;
03380 
03381        while (*n)
03382        {
03383               if (*n == '.')
03384                      break;
03385               ++n;
03386        }
03387 
03388        o=malloc(strlen(i->inbox_pfix)+strlen(n)+1);
03389        if (!o)
03390               enomem();
03391        strcat(strcpy(o, i->inbox_pfix), n);
03392 
03393        addfolder(o, i->p, i->s, i->c);
03394 
03395        free(o);
03396 }
03397 
03398 static void list_shared_callback(const char *n, void *vp)
03399 {
03400        struct add_shared_info *i=
03401               (struct add_shared_info *)vp;
03402        char *p=malloc(sizeof(SHARED ".") + strlen(n));
03403 
03404        if (!p)
03405               enomem();
03406        strcat(strcpy(p, SHARED "."), n);
03407 
03408        addfolder(p, i->p, i->s, i->c);
03409        free(p);
03410 }
03411 
03412 static void list_sharable_callback(const char *n, void *vp)
03413 {
03414        struct add_shared_info *i=
03415               (struct add_shared_info *)vp;
03416        char *p=malloc(sizeof(SHARED ".") + strlen(n));
03417        size_t j;
03418 
03419        if (!p)
03420               enomem();
03421        strcat(strcpy(p, SHARED "."), n);
03422 
03423        for (j=0; j< *i->c; j++)
03424               if (strcmp( (*i->p)[j], p) == 0)
03425               {
03426                      free(p);
03427                      return;
03428               }
03429 
03430        addfolder(p, i->p, i->s, i->c);
03431        free(p);
03432 }
03433 
03434 static int shcomparefunc( char **a, char **b)
03435 {
03436        char   *ca= *a, *cb= *b;
03437 
03438        return (strcasecmp(ca, cb));
03439 }
03440 
03441 void maildir_listfolders(const char *inbox_pfix,
03442                       const char *homedir,
03443                       char ***fp)
03444 {
03445        size_t fbsize=0;
03446        size_t fbcnt=0;
03447        struct add_shared_info info;
03448        size_t sh_cnt;
03449 
03450        *fp=0;
03451 
03452        info.p=fp;
03453        info.s= &fbsize;
03454        info.c= &fbcnt;
03455        info.inbox_pfix=inbox_pfix;
03456 
03457        if (!homedir)
03458               homedir=".";
03459 
03460        /*
03461        ** Sort unsubscribed folders AFTER all subscribed folders.
03462        ** This is done by grabbing INBOX, then shared subscribed folders
03463        ** first, memorizing the folder cnt, adding unsubscribed folders,
03464        ** then sorting the unsubscribed folders separately.
03465        */
03466 
03467        maildir_list(homedir, list_callback, &info);
03468 
03469        if (strcmp(homedir, ".") == 0)
03470               maildir_list_shared(".", list_shared_callback, &info);
03471 
03472        sh_cnt=fbcnt;
03473        if (strcmp(homedir, ".") == 0)
03474               maildir_list_sharable(".", list_sharable_callback, &info);
03475 
03476        qsort( (*fp), sh_cnt, sizeof(**fp),
03477               (int (*)(const void *, const void *))shcomparefunc);
03478 
03479        qsort( (*fp)+sh_cnt, fbcnt-sh_cnt, sizeof(**fp),
03480               (int (*)(const void *, const void *))shcomparefunc);
03481        addfolder(NULL, fp, &fbsize, &fbcnt);
03482 }
03483 
03484 void maildir_freefolders(char ***fp)
03485 {
03486 size_t cnt;
03487 
03488        for (cnt=0; (*fp)[cnt]; cnt++)
03489               free( (*fp)[cnt] );
03490        free(*fp);
03491 }
03492 
03493 int maildir_create(const char *foldername)
03494 {
03495        char   *dir;
03496        int    rc= -1;
03497 
03498        dir=xlate_mdir(foldername);
03499        if (!dir)
03500               return 0;
03501 
03502        if (mkdir(dir, 0700) == 0)
03503        {
03504        char *tmp=alloc_filename(dir, "tmp", "");
03505 
03506               if (mkdir(tmp, 0700) == 0)
03507               {
03508               char *tmp2=alloc_filename(dir, "new", "");
03509 
03510                      if (mkdir(tmp2, 0700) == 0)
03511                      {
03512                      char *tmp3=alloc_filename(dir, "cur", "");
03513 
03514                             if (mkdir(tmp3, 0700) == 0)
03515                             {
03516                             char *tmp4=alloc_filename(dir, "maildirfolder",
03517                                    "");
03518 
03519                                    close(open(tmp4, O_RDWR|O_CREAT, 0600));
03520                                    rc=0;
03521                                    free(tmp4);
03522                             }
03523                             free(tmp3);
03524                      }
03525                      if (rc)       rmdir(tmp2);
03526                      free (tmp2);
03527               }
03528               if (rc)       rmdir(tmp);
03529               free(tmp);
03530        }
03531        if (rc)       rmdir(dir);
03532        free(dir);
03533        return (rc);
03534 }
03535 
03536 int maildir_delete(const char *foldername, int deletecontent)
03537 {
03538        char   *dir, *tmp, *new, *cur;
03539        int    rc=0;
03540 
03541        struct maildir_info minfo;
03542 
03543        if (maildir_info_imap_find(&minfo, foldername, login_returnaddr())<0)
03544               return -1;
03545 
03546        if (strcmp(minfo.maildir, INBOX) == 0 ||
03547            strcmp(minfo.maildir, INBOX "." SENT) == 0 ||
03548            strcmp(minfo.maildir, INBOX "." TRASH) == 0 ||
03549            strcmp(minfo.maildir, INBOX "." DRAFTS) == 0 ||
03550            (dir=maildir_name2dir(minfo.homedir, minfo.maildir)) == NULL)
03551 
03552        {
03553               maildir_info_destroy(&minfo);
03554               return (-1);
03555        }
03556 
03557        tmp=alloc_filename(dir, "tmp", "");
03558        cur=alloc_filename(dir, "cur", "");
03559        new=alloc_filename(dir, "new", "");
03560 
03561        if (!deletecontent)
03562        {
03563               if (rmdir(new) || rmdir(cur))
03564               {
03565                      mkdir(new, 0700);
03566                      mkdir(cur, 0700);
03567                      rc= -1;
03568               }
03569        }
03570 
03571        if (rc == 0 && maildir_del(dir))
03572               rc= -1;
03573 
03574        if (rc == 0)
03575               maildir_acl_delete(minfo.homedir,
03576                                strchr(minfo.maildir, '.'));
03577 
03578        maildir_info_destroy(&minfo);
03579        free(tmp);
03580        free(new);
03581        free(cur);
03582        free(dir);
03583        return (rc);
03584 }
03585 
03586 /* ------------------------------------------------------------------- */
03587 
03588 /* Here's where we create a new message in a maildir.  First maildir_createmsg
03589 ** is called.  Then, the message contents are defined via maildir_writemsg,
03590 ** then maildir_closemsg is called. */
03591 
03592 static char writebuf[BUFSIZ];
03593 static char *writebufptr;
03594 static int writebufcnt, writebufleft;
03595 static int writeerr;
03596 off_t writebufpos;
03597 int    writebuf8bit;
03598 
03599 int    maildir_createmsg(const char *foldername, const char *seq,
03600               char **retname)
03601 {
03602        char   *p;
03603        char   *dir=xlate_mdir(foldername);
03604        char   *filename;
03605        int    n;
03606        struct maildir_tmpcreate_info createInfo;
03607 
03608        /* Create a new file in the tmp directory. */
03609 
03610        maildir_tmpcreate_init(&createInfo);
03611 
03612        createInfo.maildir=dir;
03613        createInfo.uniq=seq;
03614        createInfo.doordie=1;
03615 
03616        if ((n=maildir_tmpcreate_fd(&createInfo)) < 0)
03617        {
03618               error("maildir_createmsg: cannot create temp file.");
03619        }
03620 
03621        /*
03622        ** Ok, new maildir semantics: filename in new is different than in tmp.
03623        ** Originally this whole scheme was designed with the filenames being
03624        ** the same.  We can fix it like this:
03625        */
03626 
03627        close(n);
03628 
03629        memcpy(strrchr(createInfo.newname, '/')-3, "tmp", 3); /* Hack */
03630 
03631        if (rename(createInfo.tmpname, createInfo.newname) < 0 ||
03632            (n=open(createInfo.newname, O_RDWR)) < 0)
03633        {
03634               error("maildir_createmsg: cannot create temp file.");
03635        }
03636 
03637 
03638        filename=createInfo.newname;
03639        createInfo.newname=NULL;
03640 
03641        maildir_tmpcreate_free(&createInfo);
03642 
03643        p=strrchr(filename, '/');
03644        *retname=strdup(p+1);
03645 
03646        if (*retname == 0)
03647        {
03648               close(n);
03649               free(filename);
03650               enomem();
03651        }
03652        free(filename);
03653 
03654        /* Buffer writes */
03655 
03656        writebufcnt=0;
03657        writebufpos=0;
03658        writebuf8bit=0;
03659        writebufleft=0;
03660        writeerr=0;
03661        return (n);
03662 }
03663 
03664 /* Like createmsg, except we're rewriting the contents of this message here,
03665 ** so we might as well use the same name. */
03666 
03667 int maildir_recreatemsg(const char *folder, const char *name, char **baseptr)
03668 {
03669 char   *dir=xlate_mdir(folder);
03670 char   *base;
03671 char   *p;
03672 int    n;
03673 
03674        base=maildir_basename(name);
03675        p=alloc_filename(dir, "tmp", base);
03676 
03677        free(dir);
03678        *baseptr=base;
03679        n=maildir_safeopen(p, O_CREAT|O_RDWR|O_TRUNC, 0644);
03680        if (n < 0)    free(base);
03681        free(p);
03682        writebufcnt=0;
03683        writebufleft=0;
03684        writeerr=0;
03685        writebufpos=0;
03686        writebuf8bit=0;
03687        return (n);
03688 }
03689 
03690 
03691 /* Flush write buffer */
03692 
03693 static void writeflush(int n)
03694 {
03695 const char    *q=writebuf;
03696 int    c;
03697 
03698        /* Keep calling write() until there's an error, or we're all done */
03699 
03700        while (!writeerr && writebufcnt)
03701        {
03702               c=write(n, q, writebufcnt);
03703               if ( c <= 0)
03704                      writeerr=1;
03705               else
03706               {
03707                      q += c;
03708                      writebufcnt -= c;
03709               }
03710        }
03711 
03712        /* We have an empty buffer now */
03713 
03714        writebufcnt=0;
03715        writebufleft=sizeof(writebuf);
03716        writebufptr=writebuf;
03717 }
03718 
03719        /* Add whatever we have to the buffer */
03720 
03721 /* Write to the message file.  The writes are buffered, and we will set a
03722 ** flag if there's error writing to the message file.
03723 */
03724 
03725 void maildir_writemsgstr(int n, const char *p)
03726 {
03727        maildir_writemsg(n, p, strlen(p));
03728 }
03729 
03730 void   maildir_writemsg(int n, const char *p, size_t cnt)
03731 {
03732 int    c;
03733 size_t i;
03734 
03735        writebufpos += cnt;  /* I'm optimistic */
03736        for (i=0; i<cnt; i++)
03737               if (p[i] & 0x80)     writebuf8bit=1;
03738 
03739        while (cnt)
03740        {
03741               /* Flush buffer if it's full.  No need to flush if we've
03742               ** already had an error before. */
03743 
03744               if (writebufleft == 0)      writeflush(n);
03745 
03746               c=writebufleft;
03747               if (c > cnt)  c=cnt;
03748               memcpy(writebufptr, p, c);
03749               writebufptr += c;
03750               p += c;
03751               cnt -= c;
03752               writebufcnt += c;
03753               writebufleft -= c;
03754        }
03755 }
03756 
03757 /* The new message has been written out.  Move the new file from the tmp
03758 ** directory to either the new directory (if 'new' is set), or to the
03759 ** cur directory.
03760 **
03761 ** The caller might have encountered an error condition.  If 'isok' is zero,
03762 ** we just delete the file.  If we had a write error, we delete the file
03763 ** as well.  We return -1 in both cases, or 0 if the new file has been
03764 ** succesfully moved into its final resting place.
03765 */
03766 
03767 int    maildir_writemsg_flush(int n )
03768 {
03769        writeflush(n);
03770        return (writeerr);
03771 }
03772 
03773 int    maildir_closemsg(int n,     /* File descriptor */
03774        const char *folder,  /* Folder */
03775        const char *retname, /* Filename in folder */
03776        int isok,     /* 0 - discard it (I changed my mind),
03777                         1 - keep it
03778                        -1 - keep it even if we'll exceed the quota
03779                      */
03780        unsigned long prevsize      /* Prev size of this msg, used in quota calc */
03781               )
03782 {
03783        char   *dir=xlate_mdir(folder);
03784        char   *oldname=alloc_filename(dir, "tmp", retname);
03785        char   *newname;
03786        struct stat   stat_buf;
03787 
03788  
03789        writeflush(n);       /* If there's still anything in the buffer */
03790        if (fstat(n, &stat_buf))
03791        {
03792               close(n);
03793               unlink(oldname);
03794               enomem();
03795        }
03796 
03797        newname=maildir_find(folder, retname);
03798               /* If we called recreatemsg before */
03799 
03800        if (!newname)
03801        {
03802               newname=alloc_filename(dir, "cur:2,S", retname);
03803               /* Hack of the century          ^^^^ */
03804               strcat(strcat(strcat(strcpy(newname, dir), "/cur/"),
03805                      retname), ":2,S");
03806        }
03807 
03808        if (writeerr)
03809        {
03810               close(n);
03811               unlink(oldname);
03812               enomem();
03813        }
03814 
03815        close(n);
03816 
03817        if (isok)
03818        {
03819               if (prevsize < stat_buf.st_size)
03820               {
03821                      struct maildirsize info;
03822 
03823                      if (maildir_quota_add_start(".", &info,
03824                                               stat_buf.st_size-prevsize,
03825                                               prevsize == 0 ? 1:0,
03826                                               NULL))
03827                      {
03828                             if (isok < 0) /* Force it */
03829                             {
03830                                    maildir_quota_deleted(".", (int64_t)
03831                                                        (stat_buf.st_size
03832                                                         -prevsize),
03833                                                        prevsize == 0
03834                                                        ? 1:0);
03835                                    isok= -2;
03836                             }
03837                             else
03838                                    isok=0;
03839                      }
03840                      maildir_quota_add_end(&info, stat_buf.st_size-prevsize,
03841                                          prevsize == 0 ? 1:0);
03842               }
03843               else if (prevsize != stat_buf.st_size)
03844               {
03845                      maildir_quota_deleted(".", (int64_t)
03846                                          (stat_buf.st_size-prevsize),
03847                                          prevsize == 0 ? 1:0);
03848               }
03849        }
03850 
03851        if (isok)
03852               rename(oldname, newname);
03853               
03854        unlink(oldname);
03855 
03856        if (isok)
03857        {
03858        char   *realnewname=maildir_requota(newname, stat_buf.st_size);
03859 
03860               if (strcmp(newname, realnewname))
03861                      rename(newname, realnewname);
03862               free(realnewname);
03863        }
03864        free(dir);
03865        free(oldname);
03866        free(newname);
03867        return (isok && isok != -2? 0:-1);
03868 }
03869 
03870 void   maildir_deletenewmsg(int n, const char *folder, const char *retname)
03871 {
03872 char   *dir=xlate_mdir(folder);
03873 char   *oldname=alloc_filename(dir, "tmp", retname);
03874 
03875        close(n);
03876        unlink(oldname);
03877        free(oldname);
03878        free(dir);
03879 }
03880 
03881 void maildir_cleanup()
03882 {
03883        closedb();
03884 }
03885 
03886 void matches_free(MATCHEDSTR **p, unsigned n)
03887 {
03888        size_t i;
03889 
03890        for (i=0; p && i<n; ++i)
03891        {
03892               MATCHEDSTR *q;
03893 
03894               for (q=p[i]; q && q->match; ++q)
03895               {
03896                      free(q->prefix);
03897                      free(q->match);
03898                      free(q->suffix);
03899               }
03900               if (p[i])
03901                      free(p[i]);
03902        }
03903        if (p)
03904               free(p);
03905 }
03906 
03907 /*
03908 ** Convert folder names to modified-UTF7 encoding.
03909 */
03910 
03911 char *folder_toutf7(const char *foldername)
03912 {
03913        char *p;
03914        int converr;
03915 
03916        p=libmail_u_convert_tobuf(foldername, sqwebmail_content_charset,
03917                               unicode_x_imap_modutf7, &converr);
03918 
03919        if (p && converr)
03920        {
03921               free(p);
03922               p=NULL;
03923        }
03924 
03925        if (p)
03926               return (p);
03927 
03928        p=strdup(foldername);
03929        if (!p)
03930               enomem();
03931        return (p);
03932 }
03933 
03934 char *folder_fromutf7(const char *foldername)
03935 {
03936        char *p;
03937        int converr;
03938 
03939        p=libmail_u_convert_tobuf(foldername,
03940                               unicode_x_imap_modutf7,
03941                               sqwebmail_content_charset,
03942                               &converr);
03943 
03944        if (p && converr)
03945        {
03946               free(p);
03947               p=NULL;
03948        }
03949 
03950        if (p)
03951               return (p);
03952 
03953        p=strdup(foldername);
03954        if (!p)
03955               enomem();
03956        return (p);
03957 }