Back to index

courier  0.68.2
imapd.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2011 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if    HAVE_CONFIG_H
00007 #include      "config.h"
00008 #endif
00009 #include      <stdio.h>
00010 #include      <stdlib.h>
00011 #include      <string.h>
00012 #include      <errno.h>
00013 #include      <ctype.h>
00014 #include      <signal.h>
00015 #include      <fcntl.h>
00016 #include      <pwd.h>
00017 #if    HAVE_UNISTD_H
00018 #include      <unistd.h>
00019 #endif
00020 #if HAVE_DIRENT_H
00021 #include <dirent.h>
00022 #define NAMLEN(dirent) strlen((dirent)->d_name)
00023 #else
00024 #define dirent direct
00025 #define NAMLEN(dirent) (dirent)->d_namlen
00026 #if HAVE_SYS_NDIR_H
00027 #include <sys/ndir.h>
00028 #endif
00029 #if HAVE_SYS_DIR_H
00030 #include <sys/dir.h>
00031 #endif
00032 #if HAVE_NDIR_H
00033 #include <ndir.h>
00034 #endif
00035 #endif
00036 #if    HAVE_UTIME_H
00037 #include      <utime.h>
00038 #endif
00039 #if TIME_WITH_SYS_TIME
00040 #include      <sys/time.h>
00041 #include      <time.h>
00042 #else
00043 #if HAVE_SYS_TIME_H
00044 #include      <sys/time.h>
00045 #else
00046 #include      <time.h>
00047 #endif
00048 #endif
00049 #if HAVE_LOCALE_H
00050 #include      <locale.h>
00051 #endif
00052 
00053 #include      <courierauth.h>
00054 #include      "maildir/maildiraclt.h"
00055 #include      "maildir/maildirnewshared.h"
00056 
00057 #include      <sys/types.h>
00058 #include      <sys/stat.h>
00059 
00060 #include      "imaptoken.h"
00061 #include      "imapwrite.h"
00062 #include      "imapscanclient.h"
00063 
00064 #include      "mysignal.h"
00065 #include      "imapd.h"
00066 #include      "fetchinfo.h"
00067 #include      "searchinfo.h"
00068 #include      "storeinfo.h"
00069 #include      "mailboxlist.h"
00070 #include      "thread.h"
00071 #include      "outbox.h"
00072 
00073 #include      "maildir/config.h"
00074 #include      "maildir/maildiraclt.h"
00075 #include      "maildir/maildircreate.h"
00076 #include      "maildir/maildirrequota.h"
00077 #include      "maildir/maildirgetquota.h"
00078 #include      "maildir/maildirquota.h"
00079 #include      "maildir/maildirmisc.h"
00080 #include      "maildir/maildirwatch.h"
00081 #include      "maildir/maildirkeywords.h"
00082 #include      "maildir/maildirinfo.h"
00083 #include      "maildir/loginexec.h"
00084 
00085 #include      "unicode/unicode.h"
00086 #include      "maildir/maildirkeywords.h"
00087 #include      "courierauth.h"
00088 
00089 #define KEYWORD_IMAPVERBOTTEN " (){%*\"\\]"
00090 #define KEYWORD_SMAPVERBOTTEN ","
00091 
00092 extern time_t rfc822_parsedt(const char *);
00093 extern void fetchflags(unsigned long);
00094 extern unsigned long header_count, body_count;
00095 extern time_t start_time;
00096 extern void smap();
00097 extern void smap_fetchflags(unsigned long);
00098 
00099 extern int do_fetch(unsigned long, int, void *);
00100 extern void fetch_free_cached();
00101 extern int keywords();
00102 extern int fastkeywords();
00103 extern void imapscanfail(const char *);
00104 extern void bye_msg(const char *);
00105 
00106 extern void mainloop();
00107 extern void initcapability();
00108 extern void imapcapability();
00109 extern int magictrash();
00110 
00111 #if SMAP
00112 int smapflag=0;
00113 
00114 extern void snapshot_save();
00115 extern void snapshot_needed();
00116 
00117 #endif
00118 
00119 static const char *protocol;
00120 
00121 char *dot_trash = "." TRASH;
00122 char *trash = TRASH;
00123 
00124 char *current_mailbox=0;    /* .folder */
00125 FILE *debugfile=0;
00126 #if 0
00127 char *imapscanpath;
00128 #endif
00129 
00130 int current_temp_fd=-1;
00131 const char *current_temp_fn=NULL;
00132 
00133 struct imapscaninfo current_maildir_info;
00134 int current_mailbox_ro;
00135 char *current_mailbox_acl;
00136 
00137 dev_t homedir_dev;
00138 ino_t homedir_ino;
00139 
00140 void rfc2045_error(const char *p)
00141 {
00142        if (write(2, p, strlen(p)) < 0)
00143               _exit(1);
00144        _exit(0);
00145 }
00146 
00147 
00148 extern int maildirsize_read(const char *,int *,off_t *,unsigned *,unsigned *,struct stat *);
00149 
00150 int maildir_info_suppress(const char *maildir)
00151 {
00152        struct stat stat_buf;
00153 
00154        if (stat(maildir, &stat_buf) < 0 ||
00155            /* maildir inaccessible, perhaps another server? */
00156 
00157            (stat_buf.st_dev == homedir_dev &&
00158             stat_buf.st_ino == homedir_ino)
00159                   /* Exclude ourselves from the shared list */
00160 
00161            )
00162        {
00163               return 1;
00164        }
00165 
00166        return 0;
00167 }
00168 
00169 
00170 void quotainfo_out(const char* qroot)
00171 {
00172        char    quotabuf[QUOTABUFSIZE];
00173        char   qresult[200]="";
00174        char   qbuf[200];
00175        
00176        if ((maildir_getquota(".", quotabuf) == 0) && (strcmp(qroot,"ROOT") == 0))
00177        {
00178               struct maildirsize quotainfo;
00179 
00180               if (maildir_openquotafile(&quotainfo, ".") == 0)
00181                      maildir_closequotafile(&quotainfo);
00182               else
00183                      quotainfo.quota.nbytes=quotainfo.size.nbytes=
00184                             quotainfo.quota.nmessages=
00185                             quotainfo.size.nmessages=0;
00186                      
00187               if (quotainfo.quota.nbytes > 0)
00188               {
00189                      sprintf(qbuf,"STORAGE %ld %ld",
00190                             (long)((quotainfo.size.nbytes+1023)/1024),
00191                             (long)((quotainfo.quota.nbytes+1023)/1024));
00192                      strcat(qresult,qbuf);
00193               }
00194               if (quotainfo.quota.nmessages > 0)
00195               {
00196                      sprintf(qbuf,"MESSAGE %d %d",
00197                             quotainfo.size.nmessages,
00198                             quotainfo.quota.nmessages);
00199                      if (strcmp(qresult,"")!=0) strcat(qresult," ");
00200                      strcat(qresult,qbuf);
00201               }
00202        }
00203 
00204        writes("* ");
00205        writes("QUOTA \"");
00206        writes(qroot);
00207        writes("\"");
00208        if (strcmp(qresult,"")!=0)
00209        {
00210               writes(" (");
00211               writes(qresult);
00212               writes(")");
00213        };
00214        writes("\r\n");
00215 }
00216 
00217 int is_trash(const char *m)
00218 {
00219        if (strcmp(m, dot_trash))
00220        {
00221               /*
00222                * not trying to delete .Trash but folder inside of .Trash
00223                */
00224               return (0);
00225        }
00226        else
00227        {
00228               /*
00229                * trying to delete .Trash - stop them
00230                */
00231               return (1);
00232        }
00233 }
00234 
00235 void emptytrash()
00236 {
00237        char   *dir, *all_settings, *next_folder, *folder, *p;
00238        unsigned l;
00239 
00240        all_settings=getenv("IMAP_EMPTYTRASH");
00241 
00242        if (!all_settings)
00243               return;
00244 
00245        all_settings=strdup(all_settings);
00246        if (!all_settings)
00247               return;
00248 
00249        if (strchr(all_settings, ':') == 0 &&
00250            strchr(all_settings, ',') == 0)
00251        {
00252               l=atoi(all_settings);
00253 
00254               if (l <= 0)
00255                      l=1;
00256 
00257               maildir_getnew(".", trash, NULL, NULL);
00258               if ((dir=maildir_folderdir(".", trash)))
00259               {
00260                      maildir_purge(dir, l * 24 * 60 * 60);
00261                      free(dir);
00262               }
00263               free(all_settings);
00264               return;
00265        }
00266 
00267        for (folder=all_settings; folder && *folder; )
00268        {
00269               if (*folder == ',')
00270               {
00271                      ++folder;
00272                      continue;
00273               }
00274               next_folder=strchr(folder, ',');
00275               if (next_folder)
00276                      *next_folder++=0;
00277 
00278               p=strchr(folder, ':');
00279               if (!p)
00280               {
00281                      folder=next_folder;
00282                      continue;
00283               }
00284 
00285               *p++=0;
00286 
00287               l=atoi(p);
00288               if (l <= 0)   l=1;
00289 
00290               maildir_getnew(".", folder, NULL, NULL);
00291               if ((dir=maildir_folderdir(".", folder)))
00292               {
00293                      maildir_purge(dir, l * 24 * 60 * 60);
00294                      free(dir);
00295               }
00296               folder=next_folder;
00297        }
00298        free(all_settings);
00299 }
00300 
00301 #if 0
00302 int is_draft(const char *m)
00303 {
00304 #if 1
00305        /* Fix some PINE bugs first */
00306 
00307        if (strcmp(m, "." DRAFTS))  return (0);
00308        return (1);
00309 #else
00310        return (0);
00311 #endif
00312 }
00313 #endif
00314 
00315 int is_reserved(const char *m)
00316 {
00317        if (strncmp(m, "./", 2) == 0) m += 2;
00318 
00319        if (is_trash(m))     return (1);
00320        return (0);
00321 }
00322 
00323 int is_reserved_name(const char *name)
00324 {
00325        if (strncmp(name, INBOX, strlen(INBOX)) == 0)
00326               return is_trash(name+strlen(INBOX));
00327        return 0;
00328 }
00329 
00330 char *decode_valid_mailbox(const char *p, int autosubscribe)
00331 {
00332        struct maildir_info mi;
00333        char *q, *r;
00334 
00335        if (maildir_info_imap_find(&mi, p, getenv("AUTHENTICATED")) < 0)
00336        {
00337               return NULL;
00338        }
00339 
00340        if (mi.homedir && mi.maildir)
00341        {
00342               q=maildir_name2dir(mi.homedir, mi.maildir);
00343 
00344               if (q)
00345               {
00346                      r=malloc(strlen(q)+sizeof("/."));
00347                      if (!r)       write_error_exit(0);
00348                      strcat(strcpy(r, q), "/.");
00349                      if (access(r, 0) == 0)
00350                      {
00351                             free(r);
00352                             maildir_info_destroy(&mi);
00353                             return q;
00354                      }
00355                      free(r);
00356                      free(q);
00357               }
00358               maildir_info_destroy(&mi);
00359               return NULL;
00360        }
00361 
00362        if (mi.mailbox_type == MAILBOXTYPE_OLDSHARED)
00363        {
00364               const char *q;
00365               char *r;
00366 
00367               if ((q=strchr(p, '.')) == NULL)
00368               {
00369                      maildir_info_destroy(&mi);
00370                      errno=EINVAL;
00371                      return NULL;
00372               }
00373 
00374               r=maildir_shareddir(".", q+1);
00375               if (!r)
00376               {
00377                      maildir_info_destroy(&mi);
00378                      errno=EINVAL;
00379                      return NULL;
00380               }
00381 
00382               if (access(r, 0) == 0)
00383               {
00384                      maildir_info_destroy(&mi);
00385                      return r;
00386               }
00387 
00388               maildir_shared_subscribe(".", q+1);
00389               if (access(r, 0) == 0)
00390               {
00391                      maildir_info_destroy(&mi);
00392                      return r;
00393               }
00394 
00395               free(r);
00396               maildir_info_destroy(&mi);
00397               return NULL;
00398        }
00399        maildir_info_destroy(&mi);
00400        return (NULL);
00401 }
00402 
00403 static time_t decode_date_time(char *p)
00404 {
00405 unsigned      i;
00406 
00407        /* Convert to format rfc822_parsedt likes */
00408 
00409        for (i=1; p[i] != ' '; i++)
00410        {
00411               if (!p[i])    return (0);
00412               if (p[i] == '-')     p[i]=' ';
00413        }
00414        return (rfc822_parsedt(p));
00415 }
00416 
00417 int get_flagname(const char *p, struct imapflags *flags)
00418 {
00419        if (strcasecmp(p, "\\SEEN") == 0)
00420               flags->seen=1;
00421        else if (strcasecmp(p, "\\ANSWERED") == 0)
00422               flags->answered=1;
00423        else if (strcasecmp(p, "\\DRAFT") == 0)
00424               flags->drafts=1;
00425        else if (strcasecmp(p, "\\DELETED") == 0)
00426               flags->deleted=1;
00427        else if (strcasecmp(p, "\\FLAGGED") == 0)
00428               flags->flagged=1;
00429        else return (-1);
00430        return (0);
00431 }
00432 
00433 int valid_keyword(const char *kw)
00434 {
00435        const char *p;
00436 
00437        if (!keywords())
00438               return 0;
00439 
00440        /* Check for valid keyword names */
00441 
00442        for (p=kw; *p; p++)
00443        {
00444               if ((unsigned char)*p <= ' '
00445                   || strchr(KEYWORD_IMAPVERBOTTEN, *p))
00446                      return 0;
00447        }
00448        return 1;
00449 }
00450 
00451 int get_keyword(struct libmail_kwMessage **kwPtr, const char *kw)
00452 {
00453        if (libmail_kwmSetName(current_maildir_info.keywordList, *kwPtr, kw) < 0)
00454               write_error_exit(0);
00455 
00456        return 0;
00457 }
00458 
00459 
00460 int get_flagsAndKeywords(struct imapflags *flags,
00461                       struct libmail_kwMessage **kwPtr)
00462 {
00463 struct imaptoken *t;
00464 
00465        while ((t=nexttoken_nouc())->tokentype == IT_ATOM)
00466        {
00467               if (get_flagname(t->tokenbuf, flags))
00468               {
00469                      if (!valid_keyword(t->tokenbuf))
00470                             return -1;
00471 
00472                      if (get_keyword(kwPtr, t->tokenbuf))
00473                             return -1;
00474               }
00475        }
00476        return (t->tokentype == IT_RPAREN ? 0:-1);
00477 }
00478 
00479 void get_message_flags(
00480        struct imapscanmessageinfo *mi,
00481        char *buf, struct imapflags *flags)
00482 {
00483        const char *filename=mi->filename;
00484 
00485        const char *DRAFT="\\Draft";
00486        const char *FLAGGED="\\Flagged";
00487        const char *REPLIED="\\Answered";
00488        const char *SEEN="\\Seen";
00489        const char *DELETED="\\Deleted";
00490        const char *RECENT="\\Recent";
00491 
00492        const char *SPC=" ";
00493 
00494        if (buf)
00495               *buf=0;
00496 
00497        if (flags)
00498               flags->seen=flags->answered=flags->deleted=flags->flagged
00499               =flags->recent=flags->drafts=0;
00500 
00501        if ((filename=strrchr(filename, MDIRSEP[0])) == 0
00502               || strncmp(filename, MDIRSEP "2,", 3))    return;
00503 
00504 #if SMAP
00505        if (smapflag)
00506        {
00507               SPC=",";
00508               DRAFT="DRAFT";
00509               FLAGGED="MARKED";
00510               REPLIED="REPLIED";
00511               SEEN="SEEN";
00512               DELETED="DELETED";
00513               RECENT="RECENT";
00514        }
00515 #endif
00516 
00517        if (strchr(filename, 'D'))
00518        {
00519               if (buf) strcat(buf, DRAFT);
00520               if (flags)  flags->drafts=1;
00521        }
00522 
00523        if (strchr(filename, 'F'))
00524        {
00525               if (buf) strcat(strcat(buf, *buf ? SPC:""), FLAGGED);
00526               if (flags)    flags->flagged=1;
00527        }
00528        if (strchr(filename, 'R'))
00529        {
00530               if (buf) strcat(strcat(buf, *buf ? SPC:""), REPLIED);
00531               if (flags)    flags->answered=1;
00532        }
00533 
00534        if (strchr(filename, 'S') != NULL)
00535        {
00536               if (buf) strcat(strcat(buf, *buf ? SPC:""), SEEN);
00537               if (flags)    flags->seen=1;
00538        }
00539 
00540        if (strchr(filename, 'T'))
00541        {
00542               if (buf) strcat(strcat(buf, *buf ? SPC:""), DELETED);
00543               if (flags)    flags->deleted=1;
00544        }
00545 
00546        if (mi->recentflag)
00547        {
00548               if (flags) flags->recent=1;
00549               if (buf) strcat(strcat(buf, *buf ? SPC:""), RECENT);
00550        }
00551 }
00552 
00553 static char *parse_mailbox_error(const char *tag,
00554        struct imaptoken *curtoken,
00555        int ok_hierarchy,    /* RFC 2060 errata - DELETE can take
00556                             ** a trailing hierarchy separator if the
00557                             ** IMAP server supports subfolders of
00558                             ** a real folder (such as this one).
00559                             */
00560 
00561        int autosubscribe)   /* Really DUMP clients that do a LIST,
00562                             ** and don't bother to check if a folder is
00563                             ** subscribed to, or not (Pine)
00564                             */
00565 {
00566 char   *mailbox;
00567 
00568        if (curtoken->tokentype != IT_NUMBER &&
00569               curtoken->tokentype != IT_ATOM &&
00570               curtoken->tokentype != IT_QUOTED_STRING)
00571        {
00572               mailbox=0;
00573        }
00574        else
00575        {
00576               if (ok_hierarchy && (mailbox=strrchr(curtoken->tokenbuf,
00577                      HIERCH)) && mailbox[1] == 0)
00578                             *mailbox=0;
00579 
00580               mailbox=decode_valid_mailbox(curtoken->tokenbuf,
00581                      autosubscribe);
00582        }
00583 
00584        if ( mailbox == 0)
00585        {
00586               writes(tag);
00587               writes(" NO Mailbox does not exist, or must be subscribed to.\r\n");
00588               return (0);
00589        }
00590        return (mailbox);
00591 }
00592 
00593 /*
00594               STORE NEW MESSAGE INTO A MAILBOX
00595 */
00596 
00597 void append_flags(char *buf, struct imapflags *flags)
00598 {
00599        if (flags->drafts)   strcat(buf, "D");
00600        if (flags->flagged)  strcat(buf, "F");
00601        if (flags->answered) strcat(buf, "R");
00602        if (flags->seen)     strcat(buf, "S");
00603        if (flags->deleted)  strcat(buf, "T");
00604 }
00605 
00606        /* First, figure out the filenames used in tmp and new */
00607 
00608 FILE *maildir_mkfilename(const char *mailbox, struct imapflags *flags,
00609                       unsigned long s, char **tmpname, char **newname)
00610 {
00611        char   *p;
00612        char   uniqbuf[80];
00613        static unsigned uniqcnt=0;
00614        FILE   *fp;
00615        struct maildir_tmpcreate_info createInfo;
00616 
00617        sprintf(uniqbuf, "%u", uniqcnt++);
00618 
00619        maildir_tmpcreate_init(&createInfo);
00620        createInfo.openmode=0666;
00621        createInfo.maildir=mailbox;
00622        createInfo.uniq=uniqbuf;
00623        createInfo.msgsize=s;
00624        createInfo.hostname=getenv("HOSTNAME");
00625        createInfo.doordie=1;
00626 
00627        if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
00628               return NULL;
00629 
00630        *tmpname=createInfo.tmpname;
00631        *newname=createInfo.newname;
00632 
00633        createInfo.tmpname=NULL;
00634        createInfo.newname=NULL;
00635        maildir_tmpcreate_free(&createInfo);
00636 
00637        strcpy(uniqbuf, MDIRSEP "2,");
00638        append_flags(uniqbuf, flags);
00639 
00640        /* Ok, this message will really go to cur, not new */
00641 
00642        p=malloc(strlen(*newname)+strlen(uniqbuf)+1);
00643        if (!p)       write_error_exit(0);
00644        strcpy(p, *newname);
00645        memcpy(strrchr(p, '/')-3, "cur", 3);      /* HACK OF THE MILLENIA */
00646        strcat(p, uniqbuf);
00647        free(*newname);
00648        *newname=p;
00649        return fp;
00650 }
00651 
00652 void set_time(const char *tmpname, time_t timestamp)
00653 {
00654 #if    HAVE_UTIME
00655        if (timestamp)
00656        {
00657        struct utimbuf ub;
00658 
00659               ub.actime=ub.modtime=timestamp;
00660               utime(tmpname, &ub);
00661        }
00662 #else
00663 #if    HAVE_UTIMES
00664        if (timestamp)
00665        {
00666        struct timeval       tv;
00667 
00668               tv.tv_sec=timestamp;
00669               tv.tv_usec=0;
00670               utimes(tmpname, &tv);
00671        }
00672 #endif
00673 #endif
00674 }
00675 
00676 static int store_mailbox(const char *tag, const char *mailbox,
00677                       struct       imapflags *flags,
00678                       struct libmail_kwMessage *keywords,
00679                       time_t       timestamp,
00680                       unsigned long nbytes,
00681                       unsigned long *new_uidv,
00682                       unsigned long *new_uid)
00683 {
00684 char   *tmpname;
00685 char   *newname;
00686 char   *p;
00687 char    *e;
00688 FILE   *fp;
00689 unsigned long n;
00690 static const char nowrite[]=" NO [ALERT] Cannot create message - no write permission or out of disk space.\r\n";
00691 int    lastnl;
00692 int     rb;
00693 
00694        fp=maildir_mkfilename(mailbox, flags, 0, &tmpname, &newname);
00695 
00696        if (!fp)
00697        {
00698               writes(tag);
00699               writes(nowrite);
00700               return -1;
00701        }
00702 
00703        writes("+ OK\r\n");
00704        writeflush();
00705        lastnl=0;
00706 
00707        current_temp_fd=fileno(fp);
00708        current_temp_fn=tmpname;
00709 
00710        while (nbytes)
00711        {
00712               read_string(&p, &n, nbytes);
00713               nbytes -= n;
00714               if (p[n-1] == '\n') lastnl = 1;
00715               else lastnl = 0;
00716               
00717               while (n)
00718               {
00719                      e = memchr(p, '\r', n);
00720                      if (e && p == e)
00721                      {
00722                             rb=1;
00723                      }
00724                      else if (e)
00725                      {
00726                             rb = fwrite(p, 1, e-p, fp);
00727                      }
00728                      else
00729                      {      
00730                             rb = fwrite(p, 1, n, fp);
00731                      }
00732                      n -= rb;
00733                      p += rb;
00734               }
00735        }
00736        if (!lastnl) putc('\n', fp);
00737 
00738        current_temp_fd=-1;
00739        current_temp_fn=NULL;
00740 
00741        if (fflush(fp) || ferror(fp))
00742        {
00743               fprintf(stderr,
00744                         "ERR: error storing a message, user=%s, errno=%d\n",
00745                                 getenv("AUTHENTICATED"), errno);
00746 
00747               fclose(fp);
00748               unlink(tmpname);
00749               writes(tag);
00750               writes(nowrite);
00751               free(tmpname);
00752               free(newname);
00753               return (-1);
00754        }
00755 
00756        nbytes=ftell(fp);
00757        if (nbytes == (unsigned long)-1 ||
00758               (p=maildir_requota(newname, nbytes)) == 0)
00759               
00760        {
00761               fclose(fp);
00762               unlink(tmpname);
00763               writes(tag);
00764               writes(nowrite);
00765               free(tmpname);
00766               free(newname);
00767               return (-1);
00768        }
00769 
00770        free(newname);
00771 
00772        fclose(fp);
00773 
00774        if (maildirquota_countfolder(mailbox) &&
00775            maildirquota_countfile(p))
00776        {
00777               struct maildirsize quotainfo;
00778 
00779               if (maildir_quota_add_start(mailbox, &quotainfo, nbytes, 1,
00780                                        getenv("MAILDIRQUOTA")))
00781               {
00782                      unlink(tmpname);
00783                      free(tmpname);
00784                      free(p);
00785                      writes(tag);
00786                      writes(" NO [ALERT] You exceeded your mail quota.\r\n");
00787                      return (-1);
00788               }
00789               maildir_quota_add_end(&quotainfo, nbytes, 1);
00790        }
00791 
00792        if (check_outbox(tmpname, mailbox))
00793        {
00794               unlink(tmpname);
00795               writes(tag);
00796               writes(" NO [ALERT] Unable to send E-mail message.\r\n");
00797               free(tmpname);
00798               free(p);
00799               return (-1);
00800        }
00801 
00802        {
00803               struct imapscaninfo new_maildir_info;
00804               struct uidplus_info new_uidplus_info;
00805               int rc;
00806 
00807               imapscan_init(&new_maildir_info);
00808 
00809               memset(&new_uidplus_info, 0, sizeof(new_uidplus_info));
00810 
00811               new_uidplus_info.tmpfilename=tmpname;
00812               new_uidplus_info.curfilename=p;
00813               new_uidplus_info.mtime=timestamp;
00814 
00815               if (keywords && keywords->firstEntry)
00816               {
00817                      if (maildir_kwSave(mailbox,
00818                                       strrchr(p, '/')+1,
00819                                       keywords,
00820                                       &new_uidplus_info.tmpkeywords,
00821                                       &new_uidplus_info.newkeywords,
00822                                       0))
00823                      {
00824                             unlink(tmpname);
00825                             writes(tag);
00826                             writes(" NO [ALERT] ");
00827                             writes(strerror(errno));
00828                             free(tmpname);
00829                             free(p);
00830                             return (-1);
00831                      }
00832               }
00833 
00834               rc=imapscan_maildir(&new_maildir_info, mailbox, 0, 0,
00835                                 &new_uidplus_info);
00836 
00837               if (new_uidplus_info.tmpkeywords)
00838                      free(new_uidplus_info.tmpkeywords);
00839 
00840               if (new_uidplus_info.newkeywords)
00841                      free(new_uidplus_info.newkeywords);
00842 
00843               if (rc)
00844               {
00845                      free(tmpname);
00846                      free(p);
00847                      writes(tag);
00848                      writes(nowrite);
00849                      return -1;
00850               }
00851 
00852               *new_uidv=new_maildir_info.uidv;
00853               *new_uid=new_uidplus_info.uid;
00854               imapscan_free(&new_maildir_info);
00855        }
00856 
00857        free(tmpname);
00858        free(p);
00859        return (0);
00860 }
00861 
00862 
00863 /************** Create and delete folders **************/
00864 
00865 #if 0
00866 static int checksubdir(const char *s)
00867 {
00868 DIR    *dirp;
00869 struct dirent *de;
00870 
00871        dirp=opendir(s);
00872        while (dirp && (de=readdir(dirp)) != 0)
00873               if (de->d_name[0] != '.')
00874               {
00875                      closedir(dirp);
00876                      return (1);
00877               }
00878        if (dirp)     closedir(dirp);
00879        return (0);
00880 }
00881 #endif
00882 
00883 int mddelete(const char *s)
00884 {
00885 int    rc;
00886 
00887        trap_signals();
00888        rc=maildir_del(s);
00889        if (release_signals())      _exit(0);
00890        return (rc);
00891 }
00892 
00893 int mdcreate(const char *mailbox)
00894 {
00895        trap_signals();
00896        if (maildir_make(mailbox, 0700, 0700, 1) < 0)
00897        {
00898               if (release_signals())      _exit(0);
00899               return (-1);
00900        }
00901 
00902        if (release_signals())      _exit(0);
00903        return (0);
00904 }
00905 
00906 /****************************************************************************/
00907 
00908 /* do_msgset parses a message set, and calls a processing func for each msg */
00909 
00910 /****************************************************************************/
00911 
00912 static int do_msgset(const char *msgset,
00913        int (*msgfunc)(unsigned long, int, void *),
00914        void *msgfunc_arg, int isuid)
00915 {
00916 unsigned long i, j;
00917 int    rc;
00918 unsigned long last=0;
00919 
00920        if (current_maildir_info.nmessages > 0)
00921        {
00922               last=current_maildir_info.nmessages;
00923               if (isuid)
00924               {
00925                      last=current_maildir_info.msgs[last-1].uid;
00926               }
00927        }
00928 
00929        while (isdigit((int)(unsigned char)*msgset) || *msgset == '*')
00930        {
00931               i=0;
00932               if (*msgset == '*')
00933               {
00934                      i=last;
00935                      ++msgset;
00936               }
00937               else while (isdigit((int)(unsigned char)*msgset))
00938               {
00939                      i=i*10 + (*msgset++-'0');
00940               }
00941               if (*msgset != ':')
00942                      j=i;
00943               else
00944               {
00945                      j=0;
00946                      ++msgset;
00947                      if (*msgset == '*')
00948                      {
00949                             j=last;
00950                             ++msgset;
00951                      }
00952                      else while (isdigit((int)(unsigned char)*msgset))
00953                      {
00954                             j=j*10 + (*msgset++-'0');
00955                      }
00956               }
00957               if (j < i)
00958               {
00959 #if 0
00960        /* BUGS issue */
00961                      writes("* NO Invalid message set: ");
00962                      writen(i);
00963                      writes(":");
00964                      writen(j);
00965                      writes("\r\n");
00966 #endif
00967               }
00968               else if (isuid)
00969               {
00970               unsigned long k;
00971 
00972                      for (k=0; k<current_maildir_info.nmessages; k++)
00973                             if (current_maildir_info.msgs[k].uid >= i)
00974                                    break;
00975                      if (k >= current_maildir_info.nmessages ||
00976                             current_maildir_info.msgs[k].uid > j)
00977                      {
00978 #if 0
00979        /* BUGS issue */
00980                             writes("* NO Invalid message: UID ");
00981                             writen(i);
00982                             if (j > i)
00983                             {
00984                                    writes(":");
00985                                    writen(j);
00986                             }
00987                             writes("\r\n");
00988 #endif
00989                      }
00990                      else while (k < current_maildir_info.nmessages &&
00991                             current_maildir_info.msgs[k].uid <= j)
00992                      {
00993                             if ((rc=(*msgfunc)(k+1, 1, msgfunc_arg)) != 0)
00994                                    return (rc);
00995                             ++k;
00996                      }
00997               }
00998               else
00999               {
01000                      do
01001                      {
01002                             if (i == 0 ||
01003                                 i > current_maildir_info.nmessages)
01004                             {
01005                                    writes("* NO Invalid message sequence number: ");
01006                                    writen(i);
01007                                    writes("\r\n");
01008                                    break;
01009                             }
01010 
01011                             if ((rc=(*msgfunc)(i, 0, msgfunc_arg)) != 0)
01012                                    return (rc);
01013                      } while (i++ < j);
01014               }
01015 
01016               if (*msgset++ != ',')       break;
01017        }
01018        return (0);
01019 }
01020 
01023 static int write_keyword_name(struct libmail_keywordEntry *, void *);
01024 
01025 static void mailboxflags(int ro)
01026 {
01027 #if SMAP
01028        if (smapflag)
01029               return;
01030 #endif
01031 
01032        writes("* FLAGS (");
01033 
01034        if (current_maildir_info.keywordList)
01035        {
01036               void (*writefunc)(const char *)=writes;
01037 
01038               libmail_kwEnumerate(current_maildir_info.keywordList,
01039                                 &write_keyword_name, &writefunc);
01040        }
01041 
01042        writes("\\Draft \\Answered \\Flagged"
01043               " \\Deleted \\Seen \\Recent)\r\n");
01044        writes("* OK [PERMANENTFLAGS (");
01045 
01046 
01047        if (ro)
01048        {
01049               writes(")] No permanent flags permitted\r\n");
01050        }
01051        else
01052        {
01053               if (current_maildir_info.keywordList)
01054               {
01055                      void (*writefunc)(const char *)=writes;
01056 
01057                      libmail_kwEnumerate(current_maildir_info
01058                                        .keywordList,
01059                                        &write_keyword_name,
01060                                        &writefunc);
01061               }
01062 
01063               if (keywords())
01064                      writes("\\* ");
01065 
01066               writes("\\Draft \\Answered \\Flagged \\Deleted \\Seen)] Limited\r\n");
01067        }
01068 }
01069 
01070 static int write_keyword_name(struct libmail_keywordEntry *kw, void *dummy)
01071 {
01072        void (**writefunc)(const char *)=(void (**)(const char *))dummy;
01073 
01074        (**writefunc)(keywordName(kw));
01075        (**writefunc)(" ");
01076        return 0;
01077 }
01078 
01079 /****************************************************************************/
01080 
01081 /* Show how many messages are in the mailbox                                */
01082 
01083 /****************************************************************************/
01084 
01085 static void mailboxmetrics()
01086 {
01087 unsigned long i,j;
01088 
01089 #if SMAP
01090        if (smapflag)
01091        {
01092               writes("* EXISTS ");
01093               writen(current_maildir_info.nmessages);
01094               writes("\n");
01095               return;
01096        }
01097 #endif
01098 
01099 
01100        writes("* ");
01101        writen(current_maildir_info.nmessages);
01102        writes(" EXISTS\r\n");
01103 
01104        writes("* ");
01105        i=0;
01106 
01107        for (j=0; j<current_maildir_info.nmessages; j++)
01108               if (current_maildir_info.msgs[j].recentflag)
01109                      ++i;
01110        writen(i);
01111        writes(" RECENT\r\n");
01112 }
01113 
01114 /****************************************************************************/
01115 
01116 /* Do the NOOP stuff                                                        */
01117 
01118 /****************************************************************************/
01119 
01120 struct noopKeywordUpdateInfo {
01121        struct libmail_kwHashtable *keywordList;
01122        struct libmail_kwMessage *messagePtr;
01123 };
01124 
01125 static int noopAddKeyword(struct libmail_keywordEntry *ke, void *vp)
01126 {
01127        struct noopKeywordUpdateInfo *kui=
01128               (struct noopKeywordUpdateInfo *)vp;
01129 
01130        libmail_kwmSetName(kui->keywordList, kui->messagePtr, keywordName(ke));
01131        /*
01132        ** ke originates from a different keyword namespace, so must use
01133        ** its name.
01134        */
01135        return 0;
01136 }
01137 
01138 void doNoop(int real_noop)
01139 {
01140        struct imapscaninfo new_maildir_info;
01141        unsigned long i, j;
01142        int    needstats=0;
01143        unsigned long expunged;
01144 #if SMAP
01145        unsigned long smap_expunge_count=0;
01146        unsigned long smap_expunge_bias=0;
01147 
01148        unsigned long smap_last=0;
01149        unsigned long smap_range=0;
01150 
01151        int takeSnapshot=1;
01152 #endif
01153        struct noopKeywordUpdateInfo kui;
01154 
01155        imapscan_init(&new_maildir_info); 
01156 
01157        if (imapscan_maildir(&new_maildir_info, current_mailbox, 0,
01158                           current_mailbox_ro, NULL))
01159               return;
01160 
01161        j=0;
01162        expunged=0;
01163        for (i=0; i<current_maildir_info.nmessages; i++)
01164        {
01165               struct libmail_kwMessage *a, *b;
01166 
01167               while (j < new_maildir_info.nmessages &&
01168                      new_maildir_info.msgs[j].uid <
01169                             current_maildir_info.msgs[i].uid)
01170               {
01171                      /* How did this happen??? */
01172 
01173                      new_maildir_info.msgs[j].changedflags=1;
01174                      ++j;
01175                      needstats=1;
01176 #if SMAP
01177                      takeSnapshot=0;
01178 #endif
01179               }
01180 
01181               if (j >= new_maildir_info.nmessages ||
01182                      new_maildir_info.msgs[j].uid >
01183                             current_maildir_info.msgs[i].uid)
01184               {
01185 #if SMAP
01186                      if (smapflag)
01187                      {
01188                             takeSnapshot=0;
01189 
01190                             if (smap_expunge_count > 100)
01191                             {
01192                                    if (smap_range > 0)
01193                                    {
01194                                           writes("-");
01195                                           writen(smap_last + smap_range
01196                                                  - smap_expunge_bias
01197                                                  + 1);
01198                                    }
01199                                    writes("\n");
01200                                    smap_expunge_count=0;
01201                             }
01202 
01203                             if (smap_expunge_count == 0)
01204                             {
01205                                    smap_expunge_bias=expunged;
01206 
01207                                    writes("* EXPUNGE ");
01208                                    writen(i+1-smap_expunge_bias);
01209                                    smap_last=i;
01210                                    smap_range=0;
01211                             }
01212                             else if (smap_last + smap_range + 1 == i)
01213                             {
01214                                    ++smap_range;
01215                             }
01216                             else
01217                             {
01218                                    if (smap_range > 0)
01219                                    {
01220                                           writes("-");
01221                                           writen(smap_last + smap_range
01222                                                  - smap_expunge_bias
01223                                                  + 1);
01224                                    }
01225                                    writes(" ");
01226                                    writen(i+1-smap_expunge_bias);
01227                                    smap_last=i;
01228                                    smap_range=0;
01229                             }
01230                             ++smap_expunge_count;
01231                      }
01232                      else
01233 #endif
01234                      {
01235                             writes("* ");
01236                             writen(i+1-expunged);
01237                             writes(" EXPUNGE\r\n");
01238                             needstats=1;
01239                      }
01240 
01241                      ++expunged;
01242                      continue;
01243               }
01244 
01245               /* Must be the same */
01246 
01247               a=current_maildir_info.msgs[i].keywordMsg;
01248               b=new_maildir_info.msgs[j].keywordMsg;
01249 
01250               if (strcmp(current_maildir_info.msgs[i].filename,
01251                      new_maildir_info.msgs[j].filename) ||
01252                   (a && !b) || (!a && b) ||
01253                   (a && b && libmail_kwmCmp(a, b)))
01254               {
01255                      new_maildir_info.msgs[j].changedflags=1;
01256 #if SMAP
01257                      takeSnapshot=0;
01258 #endif
01259               }
01260               if (current_maildir_info.msgs[i].recentflag)
01261                      new_maildir_info.msgs[j].recentflag=1;
01262 #if SMAP
01263               if (smapflag)
01264                      new_maildir_info.msgs[j].recentflag=0;
01265 #endif
01266               new_maildir_info.msgs[j].copiedflag=
01267                      current_maildir_info.msgs[i].copiedflag;
01268               ++j;
01269        }
01270 
01271 #if SMAP
01272        if (smapflag && smap_expunge_count)
01273        {
01274               if (smap_range > 0)
01275               {
01276                      writes("-");
01277                      writen(smap_last + smap_range - smap_expunge_bias + 1);
01278               }
01279               writes("\n");
01280        }
01281 
01282 #endif
01283 
01284        while (j < new_maildir_info.nmessages)
01285        {
01286 #if SMAP
01287               if (smapflag)
01288                      takeSnapshot=0;
01289 #endif
01290               ++j;
01291               needstats=1;
01292        }
01293 
01294        new_maildir_info.keywordList->keywordAddedRemoved=0;
01295 
01296 
01297        /**********************************************************
01298         **
01299         ** current_maildir_info: existing keywords
01300         ** new_maildir_info: new keywords
01301         **
01302         ** Process new/deleted keywords as follows:
01303         */
01304 
01305        /*
01306        ** 1. Make sure that the old keyword list includes any new keywords.
01307        */
01308 
01309        kui.keywordList=current_maildir_info.keywordList;
01310        kui.messagePtr=libmail_kwmCreate();
01311 
01312        if (!kui.messagePtr)
01313               write_error_exit(0);
01314 
01315        current_maildir_info.keywordList->keywordAddedRemoved=0;
01316        libmail_kwEnumerate(new_maildir_info.keywordList,
01317                       noopAddKeyword, &kui);
01318 
01319        if (current_maildir_info.keywordList->keywordAddedRemoved)
01320               mailboxflags(current_mailbox_ro);
01321        libmail_kwmDestroy(kui.messagePtr);
01322 
01323 
01324        /*
01325        ** 2. Temporarily add all existing keywords to the new keyword list.
01326        */
01327 
01328        kui.keywordList=new_maildir_info.keywordList;
01329        kui.messagePtr=libmail_kwmCreate();
01330 
01331        if (!kui.messagePtr)
01332               write_error_exit(0);
01333        libmail_kwEnumerate(current_maildir_info.keywordList,
01334                       noopAddKeyword, &kui);
01335 
01336        imapscan_copy(&current_maildir_info, &new_maildir_info);
01337        imapscan_free(&new_maildir_info);
01338 
01339 #if SMAP
01340        if (takeSnapshot)
01341        {
01342               if (real_noop && smapflag)
01343                      snapshot_save();
01344        }
01345        else
01346               snapshot_needed();
01347 #endif
01348 
01349        if (needstats)
01350               mailboxmetrics();
01351 
01352        for (j=0; j < current_maildir_info.nmessages; j++)
01353               if (current_maildir_info.msgs[j].changedflags)
01354               {
01355 #if SMAP
01356                      if (smapflag)
01357                             smap_fetchflags(j);
01358                      else
01359 #endif
01360                             fetchflags(j);
01361               }
01362 
01363        /*
01364        ** After sending changed flags to the client, see if any keywords
01365        ** have gone away.
01366        */
01367 
01368        current_maildir_info.keywordList->keywordAddedRemoved=0;
01369        libmail_kwmDestroy(kui.messagePtr);
01370        if (current_maildir_info.keywordList->keywordAddedRemoved)
01371               mailboxflags(current_mailbox_ro);
01372 }
01373 
01374 static char *alloc_filename(const char *mbox, const char *name)
01375 {
01376 char   *p=malloc(strlen(mbox)+strlen(name)+sizeof("/cur/"));
01377 
01378        if (!p)       write_error_exit(0);
01379 
01380        strcat(strcat(strcpy(p, mbox), "/cur/"), name);
01381        return (p);
01382 }
01383 
01384 /****************************************************************************/
01385 
01386 /* Do the IDLE stuff                                                        */
01387 
01388 /****************************************************************************/
01389 
01390 extern int doidle(time_t, int);
01391 
01392 int imapenhancedidle(void)
01393 {
01394        struct maildirwatch *w;
01395        struct maildirwatch_contents c;
01396        int rc;
01397        int started=0;
01398 
01399        if (!current_mailbox)
01400               return (-1);
01401 
01402        if ((w=maildirwatch_alloc(current_mailbox)) == NULL)
01403        {
01404               perror(current_mailbox);
01405               fprintf(stderr, "This may be a problem with FAM or Gamin\n");
01406               return (-1);
01407        }
01408 
01409        rc=maildirwatch_start(w, &c);
01410 
01411        if (rc < 0)
01412        {
01413               perror("maildirwatch_start");
01414               maildirwatch_free(w);
01415               return (-1);
01416        }
01417 
01418        if (rc > 0)
01419        {
01420               maildirwatch_free(w);
01421               return (-1); /* Fallback */
01422        }
01423 
01424 #if SMAP
01425        if (smapflag)
01426        {
01427               writes("+OK Entering ENHANCED idle mode\n");
01428        }
01429        else
01430 #endif
01431               writes("+ entering ENHANCED idle mode\r\n");
01432        writeflush();
01433 
01434        for (;;)
01435        {
01436               if (!started)
01437               {
01438                      int fd;
01439                      int rc;
01440 
01441                      rc=maildirwatch_started(&c, &fd);
01442 
01443                      if (rc > 0)
01444                      {
01445                             started=1;
01446                             doNoop(0);
01447                             writeflush();
01448                      }
01449                      else
01450                      {
01451                             if (rc < 0)
01452                                    perror("maildirwatch_started");
01453                             if (doidle(60, fd))
01454                                    break;
01455                      }
01456                      continue;
01457               }
01458 
01459               if (started < 0) /* Error fallback mode*/
01460               {
01461                      if (doidle(60, -1))
01462                             break;
01463               }
01464               else
01465               {
01466                      int changed;
01467                      int fd;
01468                      int timeout;
01469 
01470                      if (maildirwatch_check(&c, &changed, &fd, &timeout)
01471                          == 0)
01472                      {
01473                             if (!changed)
01474                             {
01475                                    if (doidle(timeout, fd))
01476                                           break;
01477                                    continue;
01478                             }
01479 
01480                             maildirwatch_end(&c);
01481                             doNoop(0);
01482 
01483                             rc=maildirwatch_start(w, &c);
01484 
01485                             if (rc < 0)
01486                             {
01487                                    perror("maildirwatch_start");
01488                                    started= -1;
01489                             }
01490                             else
01491                                    started=0;
01492                      }
01493                      else
01494                      {
01495                             started= -1;
01496                      }
01497               }
01498 
01499               doNoop(0);
01500               writeflush();
01501        }
01502 
01503        maildirwatch_end(&c);
01504        maildirwatch_free(w);
01505        return (0);
01506 }
01507 
01508 void imapidle(void)
01509 {
01510        const char * envp = getenv("IMAP_IDLE_TIMEOUT");
01511        const int idleTimeout = envp ? atoi(envp) : 60;
01512 
01513 #if SMAP
01514        if (smapflag)
01515        {
01516               writes("+OK Entering idle mode...\n");
01517        }
01518        else
01519 #endif
01520               writes("+ entering idle mode\r\n");
01521        if (current_mailbox)
01522               doNoop(0);
01523        writeflush();
01524        while (!doidle(idleTimeout, -1))
01525        {
01526               if (current_mailbox)
01527                      doNoop(0);
01528               writeflush();
01529        }
01530 }
01531 
01532 /****************************************************************************/
01533 
01534 /* Do the EXPUNGE stuff                                                     */
01535 
01536 /****************************************************************************/
01537 
01538 void do_expunge(unsigned long from, unsigned long to, int force);
01539 
01540 static int uid_expunge(unsigned long msgnum, int uidflag, void *void_arg)
01541 {
01542        do_expunge(msgnum-1, msgnum, 0);
01543        return 0;
01544 }
01545 
01546 void expunge()
01547 {
01548        do_expunge(0, current_maildir_info.nmessages, 0);
01549 }
01550 
01551 void do_expunge(unsigned long expunge_start,
01552               unsigned long expunge_end,
01553               int force)
01554 {
01555 unsigned long j;
01556 struct imapflags flags;
01557 char   *old_name;
01558 int    move_to_trash=0;
01559 struct stat stat_buf;
01560 const char *cp=getenv("IMAP_LOG_DELETIONS");
01561 int log_deletions= cp && *cp != '0';
01562 
01563        fetch_free_cache();
01564 
01565        if (magictrash() &&
01566            !is_trash(strncmp(current_mailbox, "./", 2) == 0?
01567                     current_mailbox+2:current_mailbox))
01568               move_to_trash=1;
01569 
01570        for (j=expunge_start; j < expunge_end; j++)
01571        {
01572               int file_counted=0;
01573 
01574               get_message_flags(current_maildir_info.msgs+j, 0, &flags);
01575 
01576               if (!flags.deleted && !force)
01577                      continue;
01578 
01579               old_name=alloc_filename(current_mailbox,
01580                      current_maildir_info.msgs[j].filename);
01581 
01582               if (stat(old_name, &stat_buf) < 0)
01583               {
01584                      free(old_name);
01585                      continue;
01586               }
01587 
01588               if (maildirquota_countfolder(current_mailbox) &&
01589                   maildirquota_countfile(old_name))
01590                      file_counted=1;
01591 
01592               if (is_sharedsubdir(current_mailbox))
01593               {
01594                      maildir_unlinksharedmsg(old_name);
01595               }
01596               else if (move_to_trash && current_maildir_info
01597                       .msgs[j].copiedflag == 0)
01598               {
01599               char   *new_name;
01600               char   *p;
01601               int will_count=0;
01602 
01603                      new_name=alloc_filename(dot_trash,
01604                             current_maildir_info.msgs[j].filename);
01605 
01606                      if (maildirquota_countfolder(dot_trash))
01607                             will_count=1;
01608 
01609                      if (file_counted != will_count)
01610                      {
01611                             unsigned long filesize=0;
01612 
01613                             if (maildir_parsequota(old_name, &filesize))
01614                             {
01615                                    if (stat(old_name, &stat_buf))
01616                                           stat_buf.st_size=0;
01617                                    filesize=(unsigned long)
01618                                           stat_buf.st_size;
01619                             }
01620 
01621                             maildir_quota_deleted(".",
01622                                                 (long)filesize *
01623                                                 (will_count
01624                                                  -file_counted),
01625                                                 will_count
01626                                                 -file_counted);
01627                      }
01628 
01629                      if ((p=strrchr(new_name, '/')) &&
01630                          (p=strrchr(p, MDIRSEP[0])) &&
01631                          strncmp(p, MDIRSEP "2,", 3) == 0)
01632                      {
01633                             char *q;
01634 
01635                             /* Don't mark it as deleted in the Trash */
01636 
01637                             if ((q=strchr(p, 'T')) != NULL)
01638                                    while ((*q=q[1]) != 0)
01639                                           ++q;
01640 
01641                             /* Don't mark it as a draft msg in the Trash */
01642 
01643                             if ((q=strchr(p, 'D')) != NULL)
01644                                    while ((*q=q[1]) != 0)
01645                                           ++q;
01646                      }
01647 
01648                      if (log_deletions)
01649                             fprintf(stderr, "INFO: EXPUNGED, user=%s, ip=[%s], old_name=%s, new_name=%s: only new_name will be left\n",
01650                                    getenv("AUTHENTICATED"),
01651                                    getenv("TCPREMOTEIP"),
01652                                    old_name, new_name);
01653 
01654                      if (rename(old_name, new_name))
01655                      {
01656                             mdcreate(dot_trash);
01657                             rename(old_name, new_name);
01658                      }
01659 
01660                      unlink(old_name);
01661                      /* triggers linux kernel bug if also moved to Trash by
01662                      sqwebmail */
01663 
01664                      free(new_name);
01665               }
01666               else
01667               {
01668                      unlink(old_name);
01669 
01670                      if (file_counted)
01671                      {
01672                             unsigned long filesize=0;
01673 
01674                             if (maildir_parsequota(old_name, &filesize))
01675                             {
01676                                    if (stat(old_name, &stat_buf))
01677                                           stat_buf.st_size=0;
01678                                    filesize=(unsigned long)
01679                                           stat_buf.st_size;
01680                             }
01681 
01682                             maildir_quota_deleted(".",-(long)filesize, -1);
01683                      }
01684               }
01685 
01686               if (log_deletions)
01687                      fprintf(stderr, "INFO: EXPUNGED, user=%s, ip=[%s], old_name=%s\n",
01688                             getenv("AUTHENTICATED"),
01689                             getenv("TCPREMOTEIP"),
01690                             old_name);
01691               free(old_name);
01692        }
01693 }
01694 
01695 
01696 static FILE *newsubscribefile(char **tmpname)
01697 {
01698        char    uniqbuf[80];
01699        static  unsigned tmpuniqcnt=0;
01700        FILE   *fp;
01701        struct maildir_tmpcreate_info createInfo;
01702 
01703        maildir_tmpcreate_init(&createInfo);
01704 
01705        sprintf(uniqbuf, "imapsubscribe%u", tmpuniqcnt++);
01706 
01707        createInfo.uniq=uniqbuf;
01708        createInfo.hostname=getenv("HOSTNAME");
01709        createInfo.doordie=1;
01710 
01711        if ((fp=maildir_tmpcreate_fp(&createInfo)) == NULL)
01712               write_error_exit(0);
01713 
01714        *tmpname=createInfo.tmpname;
01715        createInfo.tmpname=NULL;
01716        maildir_tmpcreate_free(&createInfo);
01717 
01718        return (fp);
01719 }
01720 
01721 static int sub_strcmp(const char *a, const char *b)
01722 {
01723        if (strncasecmp(a, "inbox", 5) == 0 &&
01724               strncasecmp(b, "inbox", 5) == 0)
01725        {
01726               a += 5;
01727               b += 5;
01728        }
01729        return (strcmp(a, b));
01730 }
01731 
01732 static void subscribe(const char *f)
01733 {
01734        char *newf;
01735        FILE *newfp=newsubscribefile(&newf);
01736        FILE *oldfp;
01737 
01738        if ((oldfp=fopen(SUBSCRIBEFILE, "r")) != 0)
01739        {
01740        char   buf[BUFSIZ];
01741 
01742               while (fgets(buf, sizeof(buf), oldfp) != 0)
01743               {
01744               char *p=strchr(buf, '\n');
01745 
01746                      if (p) *p=0;
01747                      if (sub_strcmp(buf, f) == 0)
01748                      {
01749                             fclose(oldfp);
01750                             fclose(newfp);
01751                             unlink(newf);
01752                             free(newf);
01753                             return;       /* Already subscribed */
01754                      }
01755                      fprintf(newfp, "%s\n", buf);
01756               }
01757               fclose(oldfp);
01758        }
01759        fprintf(newfp, "%s\n", f);
01760        if (fflush(newfp) || ferror(newfp))
01761               write_error_exit(0);
01762        fclose(newfp);
01763        rename(newf, SUBSCRIBEFILE);
01764        free(newf);
01765 }
01766 
01767 static void unsubscribe(const char *f)
01768 {
01769        char *newf;
01770        FILE *newfp=newsubscribefile(&newf);
01771        FILE *oldfp;
01772 
01773        if ((oldfp=fopen(SUBSCRIBEFILE, "r")) != 0)
01774        {
01775        char   buf[BUFSIZ];
01776 
01777               while (fgets(buf, sizeof(buf), oldfp) != 0)
01778               {
01779               char *p=strchr(buf, '\n');
01780 
01781                      if (p) *p=0;
01782                      if (sub_strcmp(buf, f) == 0)
01783                             continue;
01784                      fprintf(newfp, "%s\n", buf);
01785               }
01786               fclose(oldfp);
01787        }
01788        if (fflush(newfp) || ferror(newfp))
01789               write_error_exit(0);
01790        fclose(newfp);
01791        rename(newf, SUBSCRIBEFILE);
01792        free(newf);
01793 }
01794 
01795 /*
01796 ** Count selected messages (if there's >1 copy to OUTBOX should fail).
01797 */
01798 
01799 static int do_count(unsigned long n, int byuid, void *voidptr)
01800 {
01801        const char *p=getenv("OUTBOX_MULTIPLE_SEND");
01802 
01803        ++ *(int *)voidptr;
01804 
01805        if (p && atoi(p))
01806               *(int *)voidptr=1; /* Suppress the error, below */
01807 
01808        return 0;
01809 }
01810 
01811 static void dirsync(const char *folder)
01812 {
01813 #if EXPLICITDIRSYNC
01814 
01815        char *p=malloc(strlen(folder)+sizeof("/new"));
01816        int fd;
01817 
01818        if (!p)
01819               write_error_exit(0);
01820 
01821        p=strcat(strcpy(p, folder), "/new");
01822 
01823        fd=open(p, O_RDONLY);
01824 
01825        if (fd >= 0)
01826        {
01827               fsync(fd);
01828               close(fd);
01829        }
01830 
01831        p=strcat(strcpy(p, folder), "/cur");
01832 
01833        fd=open(p, O_RDONLY);
01834 
01835        if (fd >= 0)
01836        {
01837               fsync(fd);
01838               close(fd);
01839        }
01840 
01841        free(p);
01842 #endif
01843 }
01844 
01845 /*
01846 ** Keyword updates for +FLAGS and -FLAGS
01847 */
01848 
01849 static int addRemoveKeywords1(void *);
01850 
01851 static int addRemoveKeywords2(int (*callback_func)(void *, void *),
01852                            void *callback_func_arg,
01853                            struct storeinfo *storeinfo_s,
01854                            int *tryagain);
01855 
01856 struct addremove_info {
01857        int (*callback_func)(void *, void *);
01858        void *callback_func_arg;
01859        struct storeinfo *storeinfo_s;
01860        int *tryagain;
01861 };
01862 
01863 
01864 int addRemoveKeywords(int (*callback_func)(void *, void *),
01865                     void *callback_func_arg,
01866                     struct storeinfo *storeinfo_s)
01867 {
01868        int tryagain;
01869        struct addremove_info ai;
01870 
01871        if (!keywords())
01872               return 0;
01873 
01874        if (current_mailbox_acl &&
01875            strchr(current_mailbox_acl, ACL_WRITE[0]) == NULL)
01876               return 0; /* No permission */
01877        do
01878        {
01879               ai.callback_func=callback_func;
01880               ai.callback_func_arg=callback_func_arg;
01881               ai.storeinfo_s=storeinfo_s;
01882               ai.tryagain= &tryagain;
01883 
01884               if (imapmaildirlock(&current_maildir_info,
01885                                 current_mailbox,
01886                                 addRemoveKeywords1,
01887                                 &ai))
01888                      return -1;
01889        } while (tryagain);
01890 
01891        return 0;
01892 }
01893 
01894 static int addRemoveKeywords1(void *void_arg)
01895 {
01896        struct addremove_info *ai=(struct addremove_info *)void_arg;
01897 
01898        return addRemoveKeywords2(ai->callback_func,
01899                               ai->callback_func_arg,
01900                               ai->storeinfo_s,
01901                               ai->tryagain);
01902 }
01903 
01904 int doAddRemoveKeywords(unsigned long, int, void *);
01905 
01906 struct addRemoveKeywordInfo {
01907        struct libmail_kwGeneric kwg;
01908        struct storeinfo *storeinfo;
01909 };
01910 
01911 static int addRemoveKeywords2(int (*callback_func)(void *, void *),
01912                            void *callback_func_arg,
01913                            struct storeinfo *storeinfo_s,
01914                            int *tryagain)
01915 {
01916        struct addRemoveKeywordInfo arki;
01917        int rc;
01918 
01919        *tryagain=0;
01920 
01921        libmail_kwgInit(&arki.kwg);
01922 
01923        arki.storeinfo=storeinfo_s;
01924 
01925        rc=libmail_kwgReadMaildir(&arki.kwg, current_mailbox);
01926 
01927        if (rc == 0)
01928               rc= (*callback_func)(callback_func_arg, &arki);
01929 
01930        if (rc < 0)
01931        {
01932               libmail_kwgDestroy(&arki.kwg);
01933               return -1;
01934        }
01935 
01936        if (rc > 0) /* Race */
01937        {
01938               libmail_kwgDestroy(&arki.kwg);
01939               *tryagain=1;
01940               return 0;
01941        }
01942 
01943        libmail_kwgDestroy(&arki.kwg);
01944        return 0;
01945 }
01946 
01947 int doAddRemoveKeywords(unsigned long n, int uid, void *vp)
01948 {
01949        struct addRemoveKeywordInfo *arki=
01950               (struct addRemoveKeywordInfo *)vp;
01951        struct libmail_kwGenericEntry *ge=
01952               libmail_kwgFindByName(&arki->kwg,
01953                                   current_maildir_info.msgs[--n].filename);
01954        char *tmpname=NULL, *newname=NULL;
01955        struct stat stat_buf;
01956        int rc;
01957 
01958        if (!ge || ge->keywords == NULL)
01959        {
01960               if (arki->storeinfo->plusminus == '+')
01961               {
01962                      rc=maildir_kwSave(current_mailbox,
01963                                      current_maildir_info.msgs[n].
01964                                      filename,
01965                                      arki->storeinfo->keywords,
01966                                      &tmpname, &newname, 1);
01967 
01968                      if (rc < 0)
01969                             return -1;
01970               }
01971        }
01972        else if (arki->storeinfo->plusminus == '+')
01973        {
01974               int flag=0;
01975               struct libmail_kwMessageEntry *kme;
01976 
01977               for (kme=arki->storeinfo->keywords->firstEntry;
01978                    kme; kme=kme->next)
01979               {
01980                      if ((rc=libmail_kwmSet(ge->keywords,
01981                                           kme->libmail_keywordEntryPtr))
01982                          < 0)
01983                      {
01984                             write_error_exit(0);
01985                             return 0;
01986                      }
01987 
01988                      if (rc == 0)
01989                             flag=1;
01990               }
01991 
01992               if (flag)
01993               {
01994                      rc=maildir_kwSave(current_mailbox,
01995                                      current_maildir_info.msgs[n].
01996                                      filename,
01997                                      ge->keywords,
01998                                      &tmpname, &newname, 1);
01999 
02000                      if (rc < 0)
02001                             return -1;
02002               }
02003        }
02004        else
02005        {
02006               int flag=0;
02007               struct libmail_kwMessageEntry *kme;
02008 
02009               for (kme=arki->storeinfo->keywords->firstEntry;
02010                    kme; kme=kme->next)
02011               {
02012                      struct libmail_keywordEntry *kwe;
02013 
02014                      if ((kwe=libmail_kweFind(&arki->kwg.kwHashTable,
02015                                            keywordName(kme->
02016                                                       libmail_keywordEntryPtr),
02017                                            0)) != NULL &&
02018 
02019                          libmail_kwmClear(ge->keywords, kwe) == 0)
02020                             flag=1;
02021               }
02022 
02023               if (flag)
02024               {
02025                      rc=maildir_kwSave(current_mailbox,
02026                                      current_maildir_info.msgs[n].
02027                                      filename,
02028                                      ge->keywords,
02029                                      &tmpname, &newname, 1);
02030 
02031                      if (rc < 0)
02032                             return -1;
02033               }
02034 
02035 
02036        }
02037 
02038        if (tmpname)
02039        {
02040               current_maildir_info.msgs[n].changedflags=1;
02041 
02042               if (link(tmpname, newname) < 0)
02043               {
02044                      unlink(tmpname);
02045                      free(tmpname);
02046                      free(newname);
02047 
02048                      return (errno == EEXIST ? 1:-1);
02049               }
02050 
02051               if (stat(tmpname, &stat_buf) == 0 &&
02052                   stat_buf.st_nlink == 2)
02053               {
02054                      unlink(tmpname);
02055                      free(tmpname);
02056                      free(newname);
02057                      return 0;
02058               }
02059               unlink(tmpname);
02060               free(tmpname);
02061               free(newname);
02062               return 1;
02063        }
02064 
02065        return 0;
02066 }
02067 
02068 /* IMAP interface to add/remove keyword */
02069 
02070 struct imap_addRemoveKeywordInfo {
02071        char *msgset;
02072        int uid;
02073 };
02074 
02075 static int markmessages(unsigned long n, int i, void *dummy)
02076 {
02077        --n;
02078        current_maildir_info.msgs[n].storeflag=1;
02079        return 0;
02080 }
02081 
02082 static int imap_addRemoveKeywords(void *myVoidArg, void *addRemoveVoidArg)
02083 {
02084        struct imap_addRemoveKeywordInfo *i=
02085               (struct imap_addRemoveKeywordInfo *)myVoidArg;
02086        unsigned long j;
02087 
02088        for (j=0; j<current_maildir_info.nmessages; j++)
02089               current_maildir_info.msgs[j].storeflag=0;
02090 
02091        do_msgset(i->msgset, markmessages, NULL, i->uid);
02092 
02093        for (j=0; j<current_maildir_info.nmessages; j++)
02094        {
02095               int rc;
02096 
02097               if (!current_maildir_info.msgs[j].storeflag)
02098                      continue;
02099 
02100               rc=doAddRemoveKeywords(j+1, i->uid, addRemoveVoidArg);
02101               if (rc)
02102                      return rc;
02103        }
02104        return 0;
02105 }
02106 
02107 /*
02108 ** After adding messages to a maildir, compute their new UIDs.
02109 */
02110 
02111 static int uidplus_fill(const char *mailbox,
02112                      struct uidplus_info *uidplus_list,
02113                      unsigned long *uidv)
02114 {
02115        struct imapscaninfo scan_info;
02116 
02117        imapscan_init(&scan_info);
02118 
02119        if (imapscan_maildir(&scan_info, mailbox, 0, 0, uidplus_list))
02120               return -1;
02121 
02122        *uidv=scan_info.uidv;
02123 
02124        imapscan_free(&scan_info);
02125        return 0;
02126 }
02127 
02128 static void uidplus_writemsgset(struct uidplus_info *uidplus_list,
02129                             int new_uids)
02130 {
02131 #define UIDN(u) ( new_uids ? (u)->uid:(u)->old_uid )
02132 
02133        unsigned long uid_start, uid_end;
02134        const char *comma="";
02135 
02136        writes(" ");
02137        while (uidplus_list)
02138        {
02139               uid_start=UIDN(uidplus_list);
02140               uid_end=uid_start;
02141 
02142               while (uidplus_list->next &&
02143                      UIDN(uidplus_list->next) == uid_end + 1)
02144               {
02145                      uidplus_list=uidplus_list->next;
02146                      ++uid_end;
02147               }
02148 
02149               writes(comma);
02150               writen(uid_start);
02151               if (uid_end != uid_start)
02152               {
02153                      writes(":");
02154                      writen(uid_end);
02155               }
02156               comma=",";
02157               uidplus_list=uidplus_list->next;
02158        }
02159 
02160 #undef UIDN
02161 }
02162 
02163 static void uidplus_free(struct uidplus_info *uidplus_list)
02164 {
02165        struct uidplus_info *u;
02166 
02167        while ((u=uidplus_list) != NULL)
02168        {
02169               uidplus_list=u->next;
02170               free(u->tmpfilename);
02171               free(u->curfilename);
02172 
02173               if (u->tmpkeywords)
02174                      free(u->tmpkeywords);
02175               if (u->newkeywords)
02176                      free(u->newkeywords);
02177               free(u);
02178        }
02179 }
02180 
02181 /* Abort a partially-filled uidplus */
02182 
02183 static void uidplus_abort(struct uidplus_info *uidplus_list)
02184 {
02185        struct uidplus_info *u;
02186 
02187        while ((u=uidplus_list) != NULL)
02188        {
02189               uidplus_list=u->next;
02190               unlink(u->tmpfilename);
02191               unlink(u->curfilename);
02192 
02193               if (u->tmpkeywords)
02194               {
02195                      unlink(u->tmpkeywords);
02196                      free(u->tmpkeywords);
02197               }
02198 
02199               if (u->newkeywords)
02200               {
02201                      unlink(u->newkeywords);
02202                      free(u->newkeywords);
02203               }
02204 
02205               free(u->tmpfilename);
02206               free(u->curfilename);
02207               free(u);
02208        }
02209 }
02210 
02211 static void rename_callback(const char *old_path, const char *new_path)
02212 {
02213 struct imapscaninfo minfo;
02214 
02215        char *p=malloc(strlen(new_path)+sizeof("/" IMAPDB));
02216 
02217        if (!p)
02218               write_error_exit(0);
02219 
02220        strcat(strcpy(p, new_path), "/" IMAPDB);
02221        unlink(p);
02222        free(p);
02223        imapscan_init(&minfo);
02224        imapscan_maildir(&minfo, new_path, 0,0, NULL);
02225        imapscan_free(&minfo);
02226 }
02227 
02228 static int broken_uidvs()
02229 {
02230        const char *p=getenv("IMAP_BROKENUIDV");
02231 
02232        return (p && atoi(p) != 0);
02233 }
02234 
02235 static void logoutmsg()
02236 {
02237        bye_msg("INFO: LOGOUT");
02238 }
02239 
02240 void bye()
02241 {
02242        if (current_temp_fd >= 0)
02243               close(current_temp_fd);
02244        if (current_temp_fn)
02245               unlink(current_temp_fn);
02246 
02247        if (current_mailbox)
02248               free(current_mailbox);
02249        imapscan_free(&current_maildir_info);
02250        maildirwatch_cleanup();
02251        fetch_free_cached();
02252        exit(0);
02253 }
02254 
02255 char *get_myrightson(const char *mailbox);
02256 
02257 static void list_acl(const char *mailbox,
02258                    maildir_aclt_list *);
02259 static int get_acllist(maildir_aclt_list *l,
02260                      const char *mailbox,
02261                      char **computed_mailbox_owner);
02262 
02263 static void list_myrights(const char *mailbox,
02264                        const char *myrights);
02265 static void list_postaddress(const char *mailbox);
02266 
02267 static void accessdenied(const char *cmd, const char *folder,
02268                       const char *acl_required);
02269 
02270 static int list_callback(const char *hiersep,
02271                       const char *mailbox,
02272                       int flags,
02273                       void *void_arg)
02274 {
02275        const char *sep="";
02276        const char *cmd=(const char *)void_arg;
02277        maildir_aclt_list l;
02278 
02279        char *myrights=NULL;
02280 
02281        /*
02282        ** If we're going to list ACLs, grab the ACLs now, because
02283        ** get_acllist2() may end up calling myrights(), which generates
02284        ** its own output.
02285        */
02286 
02287        if (flags & (LIST_MYRIGHTS | LIST_ACL))
02288        {
02289               myrights=get_myrightson(mailbox);
02290 
02291               if (flags & LIST_MYRIGHTS)
02292               {
02293                      if (!maildir_acl_canlistrights(myrights))
02294                      {
02295                             flags &= ~LIST_MYRIGHTS;
02296 #if 0
02297                             /* F.Y.I. only, should not be enabled
02298                                'cause make check may fail on systems
02299                                which return directory entries in a
02300                                different order */
02301 
02302                             writes("* ACLFAILED \"");
02303                             writeqs(mailbox);
02304                             writes("\"");
02305                             accessdenied("LIST(MYRIGHTS)",
02306                                         mailbox,
02307                                         ACL_LOOKUP
02308                                         ACL_READ
02309                                         ACL_INSERT
02310                                         ACL_CREATE
02311                                         ACL_DELETEFOLDER
02312                                         ACL_EXPUNGE
02313                                         ACL_ADMINISTER);
02314 #endif
02315                      }
02316               }
02317 
02318               if (flags & LIST_ACL)
02319               {
02320                      if (!strchr(myrights, ACL_ADMINISTER[0]))
02321                      {
02322                             flags &= ~LIST_ACL;
02323 #if 0
02324                             /* F.Y.I. only, should not be enabled
02325                                'cause make check may fail on systems
02326                                which return directory entries in a
02327                                different order */
02328 
02329                             writes("* ACLFAILED \"");
02330                             writeqs(mailbox);
02331                             writes("\"");
02332                             accessdenied("LIST(ACL)",
02333                                         mailbox,
02334                                         ACL_ADMINISTER);
02335 #endif
02336                      }
02337               }
02338        }
02339 
02340        if (flags & LIST_ACL)
02341        {
02342               if (get_acllist(&l, mailbox, NULL) < 0)
02343               {
02344                      fprintf(stderr,
02345                             "ERR: Error reading ACLs for %s: %s\n",
02346                             mailbox, strerror(errno));
02347                      flags &= ~LIST_ACL;
02348               }
02349        }
02350 
02351        writes("* ");
02352        writes(cmd);
02353        writes(" (");
02354 
02355 #define DO_FLAG(flag, flagname) \
02356        if (flags & (flag)) { writes(sep); writes(flagname); \
02357                             sep=" "; }
02358 
02359        DO_FLAG(MAILBOX_NOSELECT, "\\Noselect");
02360        DO_FLAG(MAILBOX_UNMARKED, "\\Unmarked");
02361        DO_FLAG(MAILBOX_MARKED, "\\Marked");
02362        DO_FLAG(MAILBOX_NOCHILDREN, "\\HasNoChildren");
02363        DO_FLAG(MAILBOX_NOINFERIORS, "\\Noinferiors");
02364        DO_FLAG(MAILBOX_CHILDREN, "\\HasChildren");
02365 #undef DO_FLAG
02366 
02367        writes(") ");
02368        writes(hiersep);
02369        writes(" \"");
02370        writeqs(mailbox);
02371        writes("\"");
02372 
02373        if (flags & (LIST_ACL|LIST_MYRIGHTS|LIST_POSTADDRESS))
02374        {
02375               writes(" (");
02376               if (flags & LIST_ACL)
02377                      list_acl(mailbox, &l);
02378               if (flags & LIST_MYRIGHTS)
02379                      list_myrights(mailbox, myrights);
02380               if (flags & LIST_POSTADDRESS)
02381                      list_postaddress(mailbox);
02382               writes(")");
02383        }
02384 
02385        writes("\r\n");
02386        if (myrights)
02387               free(myrights);
02388        if (flags & LIST_ACL)
02389               maildir_aclt_list_destroy(&l);
02390 
02391        return 0;
02392 }
02393 
02394 static void list_postaddress(const char *mailbox)
02395 {
02396        writes("(POSTADDRESS NIL)");
02397 }
02398 
02399 /*
02400 ** Wrapper for maildir_aclt_read and maildir_aclt_write
02401 */
02402 
02403 static int acl_read_folder(maildir_aclt_list *aclt_list,
02404                         const char *maildir,
02405                         const char *path)
02406 {
02407        char *p, *q;
02408        int rc;
02409 
02410        /* Handle legacy shared. namespace */
02411 
02412        if (strcmp(path, SHARED) == 0)
02413        {
02414               maildir_aclt_list_init(aclt_list);
02415               if (maildir_aclt_list_add(aclt_list, "anyone",
02416                                      ACL_LOOKUP, NULL) < 0)
02417               {
02418                      maildir_aclt_list_destroy(aclt_list);
02419                      return -1;
02420               }
02421               return 0;
02422        }
02423 
02424        if (strncmp(path, SHARED ".", sizeof(SHARED)) == 0)
02425        {
02426               maildir_aclt_list_init(aclt_list);
02427 
02428               if (strchr(path+sizeof(SHARED), '.') == 0)
02429               {
02430                      if (maildir_aclt_list_add(aclt_list,
02431                                             "anyone",
02432                                             ACL_LOOKUP, NULL) < 0)
02433                      {
02434                             maildir_aclt_list_destroy(aclt_list);
02435                             return -1;
02436                      }
02437               }
02438 
02439               p=maildir_shareddir(maildir, path+sizeof(SHARED));
02440               if (!p)
02441               {
02442                      if (maildir_aclt_list_add(aclt_list,
02443                                             "anyone",
02444                                             ACL_LOOKUP, NULL) < 0)
02445                      {
02446                             maildir_aclt_list_destroy(aclt_list);
02447                             return -1;
02448                      }
02449                      return 0;
02450               }
02451               q=malloc(strlen(p)+sizeof("/new"));
02452               if (!q)
02453               {
02454                      free(p);
02455                      maildir_aclt_list_destroy(aclt_list);
02456                      return -1;
02457               }
02458               strcat(strcpy(q, p), "/new");
02459               free(p);
02460 
02461               if (maildir_aclt_list_add(aclt_list,
02462                                      "anyone",
02463                                      access(q, W_OK) == 0 ?
02464                                      ACL_LOOKUP ACL_READ
02465                                      ACL_SEEN ACL_WRITE ACL_INSERT
02466 
02467                                      ACL_DELETEFOLDER /* Wrong, but needed for ACL1 compatibility */
02468 
02469                                      ACL_DELETEMSGS ACL_EXPUNGE:
02470                                      ACL_LOOKUP ACL_READ ACL_SEEN
02471                                      ACL_WRITE, NULL) < 0)
02472               {
02473                      free(q);
02474                      maildir_aclt_list_destroy(aclt_list);
02475                      return -1;
02476               }
02477               free(q);
02478               return 0;
02479        }
02480 
02481        p=maildir_name2dir(".", path);
02482 
02483        if (!p)
02484               return -1;
02485 
02486        rc=maildir_acl_read(aclt_list, maildir, p[0] == '.' && p[1] == '/'
02487                          ? p+2:p);
02488        free(p);
02489        return rc;
02490 }
02491 
02492 static int acl_write_folder(maildir_aclt_list *aclt_list,
02493                          const char *maildir,
02494                          const char *path,
02495 
02496                          const char *owner,
02497                          const char **err_failedrights)
02498 {
02499        char *p;
02500        int rc;
02501 
02502        if (strcmp(path, SHARED) == 0 ||
02503            strncmp(path, SHARED ".", sizeof(SHARED ".")-1) == 0)
02504        {
02505               /* Legacy */
02506 
02507               return 1;
02508        }
02509 
02510        p=maildir_name2dir(".", path);
02511 
02512        if (!p)
02513               return -1;
02514 
02515        rc=maildir_acl_write(aclt_list, maildir, p[0] == '.' && p[1] == '/'
02516                           ? p+2:p,
02517                           owner, err_failedrights);
02518        free(p);
02519        return rc;
02520 }
02521 
02522 static void writeacl(const char *);
02523 
02524 static void myrights()
02525 {
02526        writes("* OK [MYRIGHTS \"");
02527        writeacl(current_mailbox_acl);
02528        writes("\"] ACL\r\n");
02529 }
02530 
02531 static int list_acl_cb(const char *ident,
02532                      const maildir_aclt *acl,
02533                      void *cb_arg);
02534 
02535 char *compute_myrights(maildir_aclt_list *l,
02536                      const char *l_owner);
02537 
02538 static int get_acllist(maildir_aclt_list *l,
02539                      const char *p,
02540                      char **computed_mailbox_owner)
02541 {
02542        int rc;
02543        char *v;
02544 
02545        struct maildir_info mi;
02546        char *dummy_mailbox_owner=NULL;
02547 
02548        if (!computed_mailbox_owner)
02549               computed_mailbox_owner= &dummy_mailbox_owner;
02550 
02551        if (maildir_info_imap_find(&mi, p, getenv("AUTHENTICATED")) < 0)
02552               return -1;
02553 
02554        v=NULL;
02555 
02556        if (mi.homedir && mi.maildir)
02557        {
02558               rc=acl_read_folder(l, mi.homedir, mi.maildir);
02559               v=maildir_name2dir(mi.homedir, mi.maildir);
02560        }
02561        else if (mi.mailbox_type == MAILBOXTYPE_OLDSHARED)
02562        {
02563               rc=acl_read_folder(l, ".", p); /* It handles it */
02564        }
02565        else if (mi.mailbox_type == MAILBOXTYPE_NEWSHARED)
02566        {
02567               /* Intermediate #shared node.  Punt */
02568 
02569               maildir_aclt_list_init(l);
02570               rc=0;
02571 
02572               if (maildir_aclt_list_add(l, "anyone",
02573                                      ACL_LOOKUP, NULL) < 0)
02574               {
02575                      maildir_aclt_list_destroy(l);
02576                      rc=-1;
02577               }
02578        }
02579        else
02580        {
02581               /* Unknown mailbox type, no ACLs */
02582 
02583               maildir_aclt_list_init(l);
02584               rc=0;
02585        }
02586 
02587        if (rc)
02588        {
02589               if (v)
02590                      free(v);
02591               maildir_info_destroy(&mi);
02592               return -1;
02593        }
02594 
02595        *computed_mailbox_owner=my_strdup(mi.owner);
02596 
02597        maildir_info_destroy(&mi);
02598 
02599        /* Detect if ACLs on the currently-open folder have changed */
02600 
02601        if (rc == 0 && current_mailbox &&
02602 #if SMAP
02603            !smapflag &&
02604 #endif
02605 
02606            v && strcmp(v, current_mailbox) == 0)
02607        {
02608               char *new_acl=compute_myrights(l, *computed_mailbox_owner);
02609 
02610               if (new_acl && strcmp(new_acl, current_mailbox_acl))
02611               {
02612                      free(current_mailbox_acl);
02613                      current_mailbox_acl=new_acl;
02614                      new_acl=NULL;
02615                      myrights();
02616               }
02617 
02618               if (new_acl)
02619                      free(new_acl);
02620        }
02621        if (v)
02622               free(v);
02623        if (dummy_mailbox_owner)
02624               free(dummy_mailbox_owner);
02625        return rc;
02626 }
02627 
02628 static void list_acl(const char *mailbox,
02629                    maildir_aclt_list *l)
02630 {
02631        writes("(\"ACL\" (");
02632        maildir_aclt_list_enum(l, list_acl_cb, NULL);
02633        writes("))");
02634 }
02635 
02636 static void writeacl(const char *aclstr)
02637 {
02638        char *p, *q, *r;
02639        const char *cp;
02640        int n=0;
02641 
02642        for (cp=aclstr; *cp; cp++)
02643               if (*cp == ACL_EXPUNGE[0])
02644                      n |= 1;
02645               else if (*cp == ACL_DELETEMSGS[0])
02646                      n |= 2;
02647               else if (*cp == ACL_DELETEFOLDER[0])
02648                      n |= 4;
02649 
02650        if (n != 7)
02651        {
02652               writeqs(aclstr);
02653               return;
02654        }
02655 
02656        p=my_strdup(aclstr);
02657 
02658        *strchr(p, ACL_EXPUNGE[0])= ACL_DELETE_SPECIAL[0];
02659 
02660        q=r=p;
02661        while (*q)
02662        {
02663               if (*q != ACL_EXPUNGE[0] && *q != ACL_DELETEMSGS[0] &&
02664                   *q != ACL_DELETEFOLDER[0])
02665                      *r++= *q;
02666               ++q;
02667        }
02668        *r=0;
02669        writeqs(p);
02670        free(p);
02671 }
02672        
02673 static int list_acl_cb(const char *ident,
02674                      const maildir_aclt *acl,
02675                      void *cb_arg)
02676 {
02677        writes("(\"");
02678        writeqs(ident);
02679        writes("\" \"");
02680        writeacl(maildir_aclt_ascstr(acl));
02681        writes("\")");
02682        return 0;
02683 }
02684 
02685 static void writeacl1(char *p)
02686 {
02687        char *q, *r;
02688 
02689        if (strchr(p, ACL_EXPUNGE[0]) &&
02690            strchr(p, ACL_DELETEMSGS[0]) &&
02691            strchr(p, ACL_DELETEFOLDER[0]))
02692               *strchr(p, ACL_EXPUNGE[0])=ACL_DELETE_SPECIAL[0];
02693        /* See no evil */
02694 
02695 
02696        for (q=r=p; *q; q++)
02697        {
02698               if (*q == ACL_EXPUNGE[0] ||
02699                   *q == ACL_DELETEMSGS[0] ||
02700                   *q == ACL_DELETEFOLDER[0])
02701               {
02702                      continue;
02703               }
02704 
02705               *r++= *q;
02706        }
02707        *r=0;
02708        writeqs(p);
02709 }
02710 
02711 static int getacl_cb(const char *ident,
02712                    const maildir_aclt *acl,
02713                    void *cb_arg)
02714 {
02715        char *p;
02716        int isneg=0;
02717 
02718        if (*ident == '-')
02719        {
02720               isneg=1;
02721               ++ident;
02722        }
02723 
02724        if (strchr(ident, '='))
02725        {
02726               if (strncmp(ident, "user=", 5))
02727                      return 0; /* Hear no evil */
02728               ident += 5;
02729        }
02730 
02731        writes(" \"");
02732        if (isneg)
02733               writes("-");
02734        writeqs(ident);
02735        writes("\" \"");
02736 
02737 
02738        p=my_strdup(maildir_aclt_ascstr(acl));
02739 
02740        writeacl1(p);
02741 
02742        free(p);
02743        writes("\"");
02744        return 0;
02745 }
02746 
02747 char *get_myrightson(const char *mailbox)
02748 {
02749        maildir_aclt_list l;
02750        char *mailbox_owner;
02751        char *rights;
02752 
02753        if (get_acllist(&l, mailbox, &mailbox_owner) < 0)
02754               return NULL;
02755 
02756        rights=compute_myrights(&l, mailbox_owner);
02757        free(mailbox_owner);
02758        maildir_aclt_list_destroy(&l);
02759        return rights;
02760 }
02761 
02762 
02763 char *compute_myrights(maildir_aclt_list *l, const char *l_owner)
02764 {
02765        maildir_aclt aa;
02766        char *a;
02767 
02768        if (maildir_acl_computerights(&aa, l, getenv("AUTHENTICATED"),
02769                                   l_owner) < 0)
02770               return NULL;
02771 
02772        a=my_strdup(maildir_aclt_ascstr(&aa));
02773        maildir_aclt_destroy(&aa);
02774        return a;
02775 }
02776 
02777 void check_rights(const char *mailbox, char *rights_buf)
02778 {
02779        char *r=get_myrightson(mailbox);
02780        char *p, *q;
02781 
02782        if (!r)
02783        {
02784               fprintf(stderr, "ERR: Error reading ACLs for %s: %s\n",
02785                      mailbox, strerror(errno));
02786               *rights_buf=0;
02787               return;
02788        }
02789 
02790        for (p=q=rights_buf; *p; p++)
02791        {
02792               if (strchr(r, *p) == NULL)
02793                      continue;
02794 
02795               *q++ = *p;
02796        }
02797        *q=0;
02798        free(r);
02799 }
02800 
02801 static void list_myrights(const char *mailbox,
02802                        const char *r)
02803 {
02804 
02805        writes("(\"MYRIGHTS\" ");
02806 
02807        if (r == NULL)
02808        {
02809               fprintf(stderr, "ERR: Error reading ACLs for %s: %s\n",
02810                      mailbox, strerror(errno));
02811               writes(" NIL");
02812        }
02813        else
02814        {
02815               writes("\"");
02816               writeacl(r);
02817               writes("\"");
02818        }
02819        writes(")");
02820 }
02821 
02822 
02823 struct temp_acl_mailbox_list {
02824        struct temp_acl_mailbox_list *next;
02825        char *mailbox;
02826        char *hier;
02827        int flags;
02828 };
02829 
02830 /*
02831 ** Callback function from aclmailbox_scan that saves the listed mailboxes into
02832 ** a temporary link list.
02833 */
02834 
02835 static int aclmailbox_scan(const char *hiersep,
02836                         const char *mailbox,
02837                         int flags,
02838                         void *void_arg)
02839 {
02840        struct temp_acl_mailbox_list **p
02841               =(struct temp_acl_mailbox_list **)void_arg,
02842               *q=malloc(sizeof(struct temp_acl_mailbox_list));
02843 
02844        if (!q || !(q->mailbox=malloc(strlen(mailbox)+1)))
02845        {
02846               if (q) free(q);
02847               return -1;
02848        }
02849        if (!(q->hier=strdup(hiersep)))
02850        {
02851               free(q->mailbox);
02852               free(q);
02853               return -1;
02854        }
02855        strcpy(q->mailbox, mailbox);
02856        q->next= *p;
02857        q->flags=flags;
02858        *p=q;
02859        return 0;
02860 }
02861 
02862 static void free_temp_acl_mailbox(struct temp_acl_mailbox_list *p)
02863 {
02864        free(p->mailbox);
02865        free(p->hier);
02866 }
02867 
02868 static int cmp_mb(const void *a, const void *b)
02869 {
02870        return (strcmp ( ((struct temp_acl_mailbox_list *)a)->mailbox,
02871                       ((struct temp_acl_mailbox_list *)b)->mailbox));
02872 }
02873 
02874 /*
02875 ** Combine mailbox patterns; remove duplicate mailboxes.
02876 **
02877 ** mailbox_merge takes the temp_acl_mailbox_list generated from a pattern,
02878 ** combines it with the current mailbox list (kept as an array), sorts the
02879 ** end result, then removes dupes.
02880 */
02881 
02882 static int aclmailbox_merge(struct temp_acl_mailbox_list *l,
02883                          struct temp_acl_mailbox_list **mailbox_list)
02884 {
02885        size_t n, o;
02886        struct temp_acl_mailbox_list *p;
02887        struct temp_acl_mailbox_list *newa;
02888 
02889        /* Count # of mailboxes so far */
02890 
02891        for (n=0; *mailbox_list && (*mailbox_list)[n].mailbox; n++)
02892               ;
02893 
02894        /* Count # of new mailboxes */
02895 
02896        for (p=l, o=0; p; p=p->next)
02897               ++o;
02898 
02899        if (n + o == 0)
02900               return 0; /* The list is empty */
02901 
02902        /* Expand the array */
02903 
02904        if ((newa= *mailbox_list == NULL ? malloc( (n+o+1)*sizeof(*l)):
02905             realloc(*mailbox_list, (n+o+1)*sizeof(*l))) == NULL)
02906               return -1;
02907 
02908        *mailbox_list=newa;
02909        while (l)
02910        {
02911               newa[n]= *l;
02912 
02913               if ((newa[n].mailbox=strdup(l->mailbox)) == NULL)
02914                      return -1;
02915 
02916               if ((newa[n].hier=strdup(l->hier)) == NULL)
02917               {
02918                      free(newa[n].mailbox);
02919                      newa[n].mailbox=NULL;
02920                      return -1;
02921               }
02922 
02923               ++n;
02924               memset(&newa[n], 0, sizeof(newa[n]));
02925 
02926               l=l->next;
02927        }
02928        qsort(newa, n, sizeof(*l), cmp_mb);
02929 
02930        /* Remove dupes */
02931 
02932        for (n=o=0; newa[n].mailbox; n++)
02933        {
02934               if (newa[n+1].mailbox &&
02935                   strcmp(newa[n].mailbox, newa[n+1].mailbox) == 0)
02936               {
02937                      free_temp_acl_mailbox(&newa[n]);
02938                      continue;
02939               }
02940               newa[o]=newa[n];
02941               ++o;
02942        }
02943        memset(&newa[o], 0, sizeof(newa[o]));
02944 
02945        return 0;
02946 }
02947 
02948 static int aclstore(const char *, struct temp_acl_mailbox_list *);
02949 static int aclset(const char *, struct temp_acl_mailbox_list *);
02950 static int acldelete(const char *, struct temp_acl_mailbox_list *);
02951 
02952 static void free_mailboxlist(struct temp_acl_mailbox_list *mailboxlist)
02953 {
02954        size_t i;
02955        for (i=0; mailboxlist && mailboxlist[i].mailbox; i++)
02956        {
02957               free(mailboxlist[i].hier);
02958               free(mailboxlist[i].mailbox);
02959        }
02960        if (mailboxlist)
02961               free(mailboxlist);
02962 }
02963 
02964 static void free_tempmailboxlist(struct temp_acl_mailbox_list *mailboxlist)
02965 {
02966        struct temp_acl_mailbox_list *p;
02967 
02968        while ((p=mailboxlist) != NULL)
02969        {
02970               mailboxlist=p->next;
02971               free_temp_acl_mailbox(p);
02972               free(p);
02973        }
02974 }
02975 
02976 static int aclcmd(const char *tag)
02977 {
02978        char aclcmd[11];
02979        struct temp_acl_mailbox_list *mailboxlist;
02980        struct temp_acl_mailbox_list *mblist;
02981        struct imaptoken *curtoken;
02982        size_t i;
02983        int rc;
02984        int (*aclfunc)(const char *, struct temp_acl_mailbox_list *);
02985 
02986        /* Expect ACL followed only by: STORE/DELETE/SET */
02987 
02988        if ((curtoken=nexttoken())->tokentype != IT_ATOM ||
02989            strlen(curtoken->tokenbuf) > sizeof(aclcmd)-1)
02990        {
02991               errno=EINVAL;
02992               return -1;
02993        }
02994 
02995        strcpy(aclcmd, curtoken->tokenbuf);
02996 
02997        mailboxlist=NULL;
02998 
02999        switch ((curtoken=nexttoken_nouc())->tokentype) {
03000        case IT_LPAREN:
03001               while ((curtoken=nexttoken_nouc())->tokentype != IT_RPAREN)
03002               {
03003                      if (curtoken->tokentype != IT_QUOTED_STRING &&
03004                             curtoken->tokentype != IT_ATOM &&
03005                             curtoken->tokentype != IT_NUMBER)
03006                      {
03007                             errno=EINVAL;
03008                             return -1;
03009                      }
03010 
03011                      mblist=NULL;
03012 
03013                      if (mailbox_scan("", curtoken->tokenbuf, 0,
03014                                     aclmailbox_scan, &mblist) ||
03015                          aclmailbox_merge(mblist, &mailboxlist))
03016                      {
03017                             free_tempmailboxlist(mblist);
03018                             free_mailboxlist(mailboxlist);
03019                             return -1;
03020 
03021                      }
03022 
03023                      free_tempmailboxlist(mblist);
03024               }
03025               break;
03026        case IT_QUOTED_STRING:
03027        case IT_ATOM:
03028        case IT_NUMBER:
03029 
03030               mblist=NULL;
03031               if (mailbox_scan("", curtoken->tokenbuf, LIST_CHECK1FOLDER,
03032                              aclmailbox_scan, &mblist) ||
03033                   aclmailbox_merge(mblist, &mailboxlist))
03034 
03035               {
03036                      free_tempmailboxlist(mblist);
03037                      free_mailboxlist(mailboxlist);
03038                      return -1;
03039               }
03040               free_tempmailboxlist(mblist);
03041               break;
03042        }
03043 
03044        rc=0;
03045 
03046        aclfunc=strcmp(aclcmd, "STORE") == 0 ? aclstore:
03047               strcmp(aclcmd, "DELETE") == 0 ? acldelete:
03048               strcmp(aclcmd, "SET") == 0 ? aclset:NULL;
03049 
03050        rc= aclfunc ? (*aclfunc)(tag, mailboxlist): -1;
03051 
03052        if (rc == 0)
03053        {
03054               for (i=0; mailboxlist && mailboxlist[i].mailbox; i++)
03055               {
03056                      if (mailboxlist[i].mailbox[0])
03057                             list_callback(mailboxlist[i].hier,
03058                                          mailboxlist[i].mailbox,
03059                                          LIST_ACL | mailboxlist[i].flags,
03060                                          "LIST");
03061               }
03062        }
03063        free_mailboxlist(mailboxlist);
03064 
03065        if (rc == 0)
03066        {
03067               writes(tag);
03068               writes(" OK ACL ");
03069               writes(aclcmd);
03070               writes(" completed.\r\n");
03071        }
03072        else
03073        {
03074               errno=EINVAL;
03075        }
03076        return rc;
03077 }
03078 
03079 static int fix_acl_delete(int (*func)(maildir_aclt *, const char *,
03080                                   const maildir_aclt *),
03081                        maildir_aclt *newacl,
03082                        const char *aclstr)
03083 {
03084        char *p, *q;
03085        int rc;
03086 
03087        if (strchr(aclstr, ACL_DELETE_SPECIAL[0]) == NULL)
03088               return (*func)(newacl, aclstr, NULL);
03089 
03090 
03091        p=malloc(strlen(aclstr)+sizeof(ACL_DELETEFOLDER
03092                                    ACL_DELETEMSGS
03093                                    ACL_EXPUNGE));
03094        if (!p)
03095               return -1;
03096 
03097        q=p;
03098        while (*aclstr)
03099        {
03100               if (*aclstr != ACL_DELETE_SPECIAL[0])
03101                      *q++ = *aclstr;
03102               ++aclstr;
03103        }
03104 
03105        strcpy(q, ACL_DELETEFOLDER ACL_DELETEMSGS ACL_EXPUNGE);
03106 
03107        rc=(*func)(newacl, p, NULL);
03108        free(p);
03109        return rc;
03110 }
03111 
03112 static int fix_acl_delete2(maildir_aclt_list *aclt_list,
03113                         const char *identifier,
03114                         const char *aclstr)
03115 {
03116        char *p, *q;
03117        int rc;
03118 
03119        if (strchr(aclstr, ACL_DELETE_SPECIAL[0]) == NULL)
03120               return maildir_aclt_list_add(aclt_list, identifier, aclstr,
03121                                         NULL);
03122 
03123 
03124        p=malloc(strlen(aclstr)+sizeof(ACL_DELETEFOLDER
03125                                    ACL_DELETEMSGS
03126                                    ACL_EXPUNGE));
03127        if (!p)
03128               return -1;
03129 
03130        q=p;
03131        while (*aclstr)
03132        {
03133               if (*aclstr != ACL_DELETE_SPECIAL[0])
03134                      *q++ = *aclstr;
03135               ++aclstr;
03136        }
03137 
03138        strcpy(q, ACL_DELETEFOLDER ACL_DELETEMSGS ACL_EXPUNGE);
03139 
03140        rc=maildir_aclt_list_add(aclt_list, identifier, p, NULL);
03141        free(p);
03142        return rc;
03143 }
03144 
03145 void aclminimum(const char *identifier)
03146 {
03147        if (strcmp(identifier, "administrators") == 0 ||
03148            strcmp(identifier, "group=administrators") == 0)
03149        {
03150               writes(ACL_ALL);
03151               return;
03152        }
03153 
03154        if (strcmp(identifier, "-administrators") == 0 ||
03155            strcmp(identifier, "-group=administrators") == 0)
03156        {
03157               writes("\"\"");
03158               return;
03159        }
03160 
03161        writes(*identifier == '-' ? "\"\"":ACL_ADMINISTER ACL_LOOKUP);
03162        writes(" " ACL_CREATE
03163               " " ACL_EXPUNGE 
03164               " " ACL_INSERT
03165               " " ACL_POST
03166               " " ACL_READ
03167               " " ACL_SEEN
03168               " " ACL_DELETEMSGS
03169               " " ACL_WRITE
03170               " " ACL_DELETEFOLDER);
03171 }
03172 
03173 static void aclfailed(const char *mailbox, const char *identifier)
03174 {
03175        if (!identifier)
03176        {
03177               writes("* ACLFAILED \"");
03178               writeqs(mailbox);
03179               writes("\" ");
03180               writes(strerror(errno));
03181               writes("\r\n");
03182               return;
03183        }
03184 
03185        writes("* RIGHTS-INFO \"");
03186        writeqs(mailbox);
03187        writes("\" \"");
03188        writeqs(identifier);
03189        writes("\" ");
03190 
03191        aclminimum(identifier);
03192        writes("\r\n");
03193 }
03194 
03195 static int acl_settable_folder(char *mailbox,
03196                             struct maildir_info *mi)
03197 {
03198        if (maildir_info_imap_find(mi, mailbox, getenv("AUTHENTICATED")) < 0)
03199        {
03200               aclfailed(mailbox, NULL);
03201               *mailbox=0;
03202               return (-1);
03203        }
03204 
03205        if (mi->homedir == NULL || mi->maildir == NULL)
03206        {
03207               writes("* ACLFAILED \"");
03208               writeqs(mailbox);
03209               writes("\" ACLs may not be modified for special mailbox\r\n");
03210               maildir_info_destroy(mi);
03211               *mailbox=0;
03212               return -1;
03213        }
03214        return 0;
03215 }
03216 
03217 int acl_lock(const char *homedir,
03218             int (*func)(void *),
03219             void *void_arg)
03220 {
03221        struct imapscaninfo ii;
03222        int rc;
03223 
03224        imapscan_init(&ii);
03225        rc=imapmaildirlock(&ii, homedir, func, void_arg);
03226        imapscan_free(&ii);
03227        return rc;
03228 }
03229 
03230 static int do_acl_mod_0(void *);
03231 
03232 struct do_acl_info {
03233        maildir_aclt_list *aclt_list;
03234        struct maildir_info *mi;
03235        const char *identifier;
03236        const char *newrights;
03237        const char **acl_error;
03238 };
03239 
03240 
03241 static int do_acl_mod(maildir_aclt_list *aclt_list,
03242                     struct maildir_info *mi,
03243                     const char *identifier,
03244                     const char *newrights,
03245                     const char **acl_error)
03246 {
03247        struct do_acl_info dai;
03248 
03249        *acl_error=NULL;
03250 
03251        dai.aclt_list=aclt_list;
03252        dai.mi=mi;
03253        dai.identifier=identifier;
03254        dai.newrights=newrights;
03255        dai.acl_error=acl_error;
03256        return acl_lock(mi->homedir, do_acl_mod_0, &dai);
03257 }
03258 
03259 static int do_acl_mod_0(void *void_arg)
03260 {
03261        struct do_acl_info *dai=
03262               (struct do_acl_info *)void_arg;
03263        maildir_aclt_list *aclt_list=dai->aclt_list;
03264        struct maildir_info *mi=dai->mi;
03265        const char *identifier=dai->identifier;
03266        const char *newrights=dai->newrights;
03267        const char **acl_error=dai->acl_error;
03268 
03269        if (newrights[0] == '+')
03270        {
03271               maildir_aclt newacl;
03272               const maildir_aclt *oldacl;
03273 
03274               if (fix_acl_delete(&maildir_aclt_init,
03275                                &newacl, newrights+1) < 0
03276                   || ((oldacl=maildir_aclt_list_find(aclt_list, identifier))
03277                      != NULL && maildir_aclt_add(&newacl, NULL, oldacl) < 0)
03278                   || maildir_aclt_list_add(aclt_list, identifier, NULL,
03279                                         &newacl) < 0 ||
03280                   acl_write_folder(aclt_list, mi->homedir,
03281                                         mi->maildir,
03282                                         mi->owner, acl_error) < 0)
03283 
03284                      {
03285                             maildir_aclt_destroy(&newacl);
03286                             return -1;
03287                      }
03288               maildir_aclt_destroy(&newacl);
03289        }
03290        else if (newrights[0] == '-')
03291        {
03292               maildir_aclt newacl;
03293               const maildir_aclt *oldacl;
03294 
03295               oldacl=maildir_aclt_list_find(aclt_list, identifier);
03296 
03297               if (maildir_aclt_init(&newacl, oldacl == NULL ? "":NULL,
03298                                   oldacl) < 0
03299                   || fix_acl_delete(&maildir_aclt_del,
03300                                   &newacl, newrights+1) < 0
03301                   || (strlen(maildir_aclt_ascstr(&newacl)) == 0 ?
03302                      maildir_aclt_list_del(aclt_list, identifier):
03303                      maildir_aclt_list_add(aclt_list, identifier,
03304                                          NULL, &newacl)) < 0 ||
03305                   acl_write_folder(aclt_list, mi->homedir,
03306                                         mi->maildir,
03307                                         mi->owner,
03308                                         acl_error) < 0)
03309                      {
03310                             maildir_aclt_destroy(&newacl);
03311                             return -1;
03312                      }
03313                      maildir_aclt_destroy(&newacl);
03314        }
03315        else
03316        {
03317               acl_error=NULL;
03318 
03319               if ((newrights[0] == 0 ?
03320                    maildir_aclt_list_del(aclt_list, identifier):
03321                    fix_acl_delete2(aclt_list, identifier, newrights)) < 0
03322                   || acl_write_folder(aclt_list, mi->homedir,
03323                                    mi->maildir, mi->owner,
03324                                    acl_error) < 0)
03325               {
03326                      return -1;
03327               }
03328        }
03329 
03330        return 0;
03331 }
03332 
03333 static int aclstore(const char *tag, struct temp_acl_mailbox_list *mailboxes)
03334 {
03335        struct imaptoken *curtoken;
03336        char *identifier;
03337        size_t i;
03338 
03339        if ((curtoken=nexttoken_nouc())->tokentype != IT_QUOTED_STRING &&
03340            curtoken->tokentype != IT_ATOM &&
03341            curtoken->tokentype != IT_NUMBER)
03342               return -1;
03343 
03344        if ((identifier=strdup(curtoken->tokenbuf)) == NULL)
03345               write_error_exit(0);
03346 
03347        if ((curtoken=nexttoken_nouc())->tokentype != IT_QUOTED_STRING &&
03348            curtoken->tokentype != IT_ATOM &&
03349            curtoken->tokentype != IT_NUMBER)
03350        {
03351               free(identifier);
03352               return -1;
03353        }
03354 
03355        for (i=0; mailboxes && mailboxes[i].mailbox; i++)
03356        {
03357               maildir_aclt_list aclt_list;
03358               const char *acl_error;
03359               struct maildir_info mi;
03360 
03361               if (acl_settable_folder(mailboxes[i].mailbox, &mi))
03362                      continue;
03363 
03364               {
03365                      CHECK_RIGHTSM(mailboxes[i].mailbox,
03366                                   acl_rights,
03367                                   ACL_ADMINISTER);
03368                      if (acl_rights[0] == 0)
03369                      {
03370                             writes("* ACLFAILED \"");
03371                             writeqs(mailboxes[i].mailbox);
03372                             writes("\"");
03373                             accessdenied("ACL STORE",
03374                                         mailboxes[i].mailbox,
03375                                         ACL_ADMINISTER);
03376                             maildir_info_destroy(&mi);
03377                             mailboxes[i].mailbox[0]=0;
03378                             continue;
03379                      }
03380               }
03381 
03382               if (acl_read_folder(&aclt_list, mi.homedir, mi.maildir))
03383               {
03384                      aclfailed(mailboxes[i].mailbox, NULL);
03385                      maildir_info_destroy(&mi);
03386                      continue;
03387               }
03388 
03389               if (do_acl_mod(&aclt_list, &mi, identifier, curtoken->tokenbuf,
03390                             &acl_error) < 0)
03391               {
03392                      aclfailed(mailboxes[i].mailbox, acl_error);
03393 
03394                      maildir_aclt_list_destroy(&aclt_list);
03395                      maildir_info_destroy(&mi);
03396                      continue;
03397               }
03398               maildir_aclt_list_destroy(&aclt_list);
03399               maildir_info_destroy(&mi);
03400        }
03401 
03402        free(identifier);
03403        return 0;
03404 }
03405 
03406 struct aclset_info {
03407        struct maildir_info *mi;
03408        maildir_aclt_list *newlist;
03409        const char **acl_error;
03410 };
03411 
03412 static int do_aclset(void *);
03413 
03414 static int aclset(const char *tag, struct temp_acl_mailbox_list *mailboxes)
03415 {
03416        struct imaptoken *curtoken;
03417        char *identifier;
03418        maildir_aclt_list newlist;
03419        size_t i;
03420 
03421        maildir_aclt_list_init(&newlist);
03422 
03423        while ((curtoken=nexttoken_nouc())->tokentype != IT_EOL)
03424        {
03425               if (curtoken->tokentype != IT_QUOTED_STRING &&
03426                   curtoken->tokentype != IT_ATOM &&
03427                   curtoken->tokentype != IT_NUMBER)
03428                      return -1;
03429               if ((identifier=strdup(curtoken->tokenbuf)) == NULL)
03430                      write_error_exit(0);
03431 
03432               if ((curtoken=nexttoken_nouc())->tokentype
03433                   != IT_QUOTED_STRING &&
03434                   curtoken->tokentype != IT_ATOM &&
03435                   curtoken->tokentype != IT_NUMBER)
03436               {
03437                      free(identifier);
03438                      maildir_aclt_list_destroy(&newlist);
03439                      return -1;
03440               }
03441 
03442               if (fix_acl_delete2(&newlist, identifier,
03443                                 curtoken->tokenbuf) < 0)
03444               {
03445                      maildir_aclt_list_destroy(&newlist);
03446                      writes(tag);
03447                      writes(" NO ACL SET <");
03448                      writes(identifier);
03449                      writes(", ");
03450                      writes(curtoken->tokenbuf);
03451                      writes("> failed.\r\n");
03452                      free(identifier);
03453                      return 0;
03454               }
03455               free(identifier);
03456        }
03457 
03458        for (i=0; mailboxes && mailboxes[i].mailbox; i++)
03459        {
03460               const char *acl_error;
03461               struct maildir_info mi;
03462               struct aclset_info ai;
03463 
03464               if (acl_settable_folder(mailboxes[i].mailbox, &mi))
03465                      continue;
03466 
03467               {
03468                      CHECK_RIGHTSM(mailboxes[i].mailbox,
03469                                   acl_rights,
03470                                   ACL_ADMINISTER);
03471                      if (acl_rights[0] == 0)
03472                      {
03473                             maildir_info_destroy(&mi);
03474                             writes("* ACLFAILED \"");
03475                             writeqs(mailboxes[i].mailbox);
03476                             writes("\"");
03477                             accessdenied("ACL SET", mailboxes[i].mailbox,
03478                                         ACL_ADMINISTER);
03479                             mailboxes[i].mailbox[0]=0;
03480                             continue;
03481                      }
03482               }
03483 
03484               acl_error=NULL;
03485               ai.mi=&mi;
03486               ai.acl_error= &acl_error;
03487               ai.newlist= &newlist;
03488 
03489               if (acl_lock(mi.homedir, do_aclset, &ai))
03490               {
03491                      aclfailed(mailboxes[i].mailbox, acl_error);
03492                      maildir_info_destroy(&mi);
03493                      mailboxes[i].mailbox[0]=0;
03494                      continue;
03495               }
03496               maildir_info_destroy(&mi);
03497        }
03498        maildir_aclt_list_destroy(&newlist);
03499        return 0;
03500 }
03501 
03502 static int do_aclset(void *void_arg)
03503 {
03504        struct aclset_info *ai=(struct aclset_info *)void_arg;
03505 
03506        return acl_write_folder(ai->newlist, ai->mi->homedir,
03507                             ai->mi->maildir,
03508                             ai->mi->owner, ai->acl_error);
03509 }
03510 
03511 struct acldelete_info {
03512        const char *mailbox;
03513        const char *identifier;
03514        struct maildir_info *mi;
03515 };
03516 
03517 static int do_acldelete(void *);
03518 
03519 static int acldelete(const char *tag, struct temp_acl_mailbox_list *mailboxes)
03520 {
03521        struct imaptoken *curtoken;
03522        const char *identifier;
03523        size_t i;
03524 
03525        if ((curtoken=nexttoken_nouc())->tokentype != IT_QUOTED_STRING &&
03526            curtoken->tokentype != IT_ATOM &&
03527            curtoken->tokentype != IT_NUMBER)
03528               return -1;
03529 
03530        identifier=curtoken->tokenbuf;
03531 
03532        for (i=0; mailboxes && mailboxes[i].mailbox; i++)
03533        {
03534               struct maildir_info mi;
03535               struct acldelete_info ai;
03536 
03537               if (acl_settable_folder(mailboxes[i].mailbox, &mi))
03538                      continue;
03539 
03540               {
03541                      CHECK_RIGHTSM(mailboxes[i].mailbox,
03542                                   acl_rights,
03543                                   ACL_ADMINISTER);
03544                      if (acl_rights[0] == 0)
03545                      {
03546                             writes("* ACLFAILED \"");
03547                             writeqs(mailboxes[i].mailbox);
03548                             writes("\"");
03549                             accessdenied("ACL DELETE",
03550                                         mailboxes[i].mailbox,
03551                                         ACL_ADMINISTER);
03552                             maildir_info_destroy(&mi);
03553                             mailboxes[i].mailbox[0]=0;
03554                             continue;
03555                      }
03556               }
03557 
03558               ai.mailbox=mailboxes[i].mailbox;
03559               ai.mi= &mi;
03560               ai.identifier=identifier;
03561               if (acl_lock(mi.homedir, do_acldelete, &ai))
03562                      mailboxes[i].mailbox[0]=0;
03563               maildir_info_destroy(&mi);
03564        }
03565        return 0;
03566 }
03567 
03568 static int do_acldelete(void *void_arg)
03569 {
03570        struct acldelete_info *ai=
03571               (struct acldelete_info *)void_arg;
03572        const char *mailbox=ai->mailbox;
03573        struct maildir_info *mi=ai->mi;
03574 
03575        maildir_aclt_list aclt_list;
03576        const char *acl_error;
03577 
03578        if (acl_read_folder(&aclt_list, mi->homedir, mi->maildir) < 0)
03579        {
03580               writes("* NO Error reading ACLs for ");
03581               writes(mailbox);
03582               writes(": ");
03583               writes(strerror(errno));
03584               writes("\r\n");
03585               return -1;
03586        }
03587 
03588        acl_error=NULL;
03589 
03590        if (maildir_aclt_list_del(&aclt_list, ai->identifier) < 0 ||
03591            acl_write_folder(&aclt_list, mi->homedir, mi->maildir,
03592                           mi->owner, &acl_error) < 0)
03593        {
03594               aclfailed(mailbox, acl_error);
03595               maildir_aclt_list_destroy(&aclt_list);
03596               return -1;
03597        }
03598        maildir_aclt_list_destroy(&aclt_list);
03599        return 0;
03600 }
03601 
03602 
03603 static void accessdenied(const char *cmd, const char *folder,
03604                       const char *acl_required)
03605 {
03606        writes(" NO Access denied for ");
03607        writes(cmd);
03608        writes(" on ");
03609        writes(folder);
03610        writes(" (ACL \"");
03611        writes(acl_required);
03612        writes("\" required)\r\n");
03613 }
03614 
03615 /* Even if the folder does not exist, if there are subfolders it exists
03616 ** virtually.
03617 */
03618 
03619 static int folder_exists_cb(const char *hiersep,
03620                          const char *mailbox,
03621                          int flags,
03622                          void *void_arg)
03623 {
03624        *(int *)void_arg=1;
03625        return 0;
03626 }
03627 
03628 static int folder_exists(const char *folder)
03629 {
03630        int flag=0;
03631 
03632        if (mailbox_scan("", folder, LIST_CHECK1FOLDER,
03633                       folder_exists_cb, &flag))
03634               return 0;
03635        return flag;
03636 }
03637 
03638 int do_folder_delete(char *mailbox_name)
03639 {
03640        maildir_aclt_list l;
03641        const char *acl_error;
03642        struct maildir_info mi;
03643 
03644        if (maildir_info_imap_find(&mi, mailbox_name, getenv("AUTHENTICATED"))
03645            < 0)
03646               return -1;
03647 
03648        if (mi.homedir == NULL || mi.maildir == NULL)
03649        {
03650               maildir_info_destroy(&mi);
03651               return -1;
03652        }
03653 
03654        if (acl_read_folder(&l, mi.homedir, mi.maildir) < 0)
03655               return -1;
03656 
03657        if (strcmp(mi.maildir, INBOX))
03658        {
03659               char *p=maildir_name2dir(mi.homedir, mi.maildir);
03660 
03661               if (p && is_reserved(p) == 0 && mddelete(p) == 0)
03662               {
03663                      if (folder_exists(mailbox_name))
03664                      {
03665                             acl_write_folder(&l, mi.homedir,
03666                                            mi.maildir, NULL,
03667                                            &acl_error);
03668                      }
03669                      maildir_aclt_list_destroy(&l);
03670                      maildir_quota_recalculate(mi.homedir);
03671                      free(p);
03672                      maildir_info_destroy(&mi);
03673                      return 0;
03674               }
03675 
03676               if (p)
03677                      free(p);
03678        }
03679        maildir_aclt_list_destroy(&l);
03680        return -1;
03681 }
03682 
03683 int acl_flags_adjust(const char *access_rights,
03684                    struct imapflags *flags)
03685 {
03686        if (strchr(access_rights, ACL_DELETEMSGS[0]) == NULL)
03687               flags->deleted=0;
03688        if (strchr(access_rights, ACL_SEEN[0]) == NULL)
03689               flags->seen=0;
03690        if (strchr(access_rights, ACL_WRITE[0]) == NULL)
03691        {
03692               flags->answered=flags->flagged=flags->drafts=0;
03693               return 1;
03694        }
03695        return 0;
03696 }
03697 
03698 static int append(const char *tag, const char *mailbox, const char *path)
03699 {
03700 
03701        struct imapflags flags;
03702        struct libmail_kwMessage *keywords;
03703        time_t timestamp=0;
03704        unsigned long new_uidv, new_uid;
03705        char access_rights[8];
03706        struct imaptoken *curtoken;
03707 
03708        if (access(path, 0))
03709        {
03710               writes(tag);
03711               writes(" NO [TRYCREATE] Must create mailbox before append\r\n");
03712               return (0);
03713        }
03714 
03715        {
03716               CHECK_RIGHTSM(mailbox,
03717                            append_rights,
03718                            ACL_INSERT ACL_DELETEMSGS
03719                            ACL_SEEN ACL_WRITE);
03720 
03721               if (strchr(append_rights, ACL_INSERT[0]) == NULL)
03722               {
03723                      writes(tag);
03724                      accessdenied("APPEND",
03725                                  mailbox,
03726                                  ACL_INSERT);
03727                      return 0;
03728               }
03729 
03730               strcpy(access_rights, append_rights);
03731        }
03732 
03733        if (current_mailbox &&
03734            strcmp(path, current_mailbox) == 0 && current_mailbox_ro)
03735        {
03736               writes(tag);
03737               writes(" NO Current box is selected READ-ONLY.\r\n");
03738               return (0);
03739        }
03740 
03741        curtoken=nexttoken_noparseliteral();
03742        memset(&flags, 0, sizeof(flags));
03743        if ((keywords=libmail_kwmCreate()) == NULL)
03744               write_error_exit(0);
03745 
03746        if (curtoken->tokentype == IT_LPAREN)
03747        {
03748               if (get_flagsAndKeywords(&flags, &keywords))
03749               {
03750                      libmail_kwmDestroy(keywords);
03751                      return (-1);
03752               }
03753               curtoken=nexttoken_noparseliteral();
03754        }
03755        else if (curtoken->tokentype == IT_ATOM)
03756        {
03757               if (get_flagname(curtoken->tokenbuf, &flags))
03758               {
03759                      if (!valid_keyword(curtoken->tokenbuf))
03760                      {
03761                             libmail_kwmDestroy(keywords);
03762                             return -1;
03763                      }
03764 
03765                      libmail_kwmSetName(current_maildir_info.keywordList,
03766                                       keywords,
03767                                       curtoken->tokenbuf);
03768               }
03769               curtoken=nexttoken_noparseliteral();
03770        }
03771        else if (curtoken->tokentype == IT_NIL)
03772               curtoken=nexttoken_noparseliteral();
03773 
03774        if (curtoken->tokentype == IT_QUOTED_STRING)
03775        {
03776               timestamp=decode_date_time(curtoken->tokenbuf);
03777               if (timestamp == 0)
03778               {
03779                      libmail_kwmDestroy(keywords);
03780                      return (-1);
03781               }
03782               curtoken=nexttoken_noparseliteral();
03783        }
03784        else if (curtoken->tokentype == IT_NIL)
03785               curtoken=nexttoken_noparseliteral();
03786 
03787        if (curtoken->tokentype != IT_LITERAL_STRING_START)
03788        {
03789               libmail_kwmDestroy(keywords);
03790               return (-1);
03791        }
03792 
03793        acl_flags_adjust(access_rights, &flags);
03794 
03795        if (store_mailbox(tag, path, &flags,
03796                        acl_flags_adjust(access_rights, &flags)
03797                        ? NULL:keywords,
03798                        timestamp,
03799                        curtoken->tokennum, &new_uidv, &new_uid))
03800        {
03801               libmail_kwmDestroy(keywords);
03802               unread('\n');
03803               return (0);
03804        }
03805        libmail_kwmDestroy(keywords);
03806 
03807        if (nexttoken()->tokentype != IT_EOL)
03808        {
03809               return (-1);
03810        }
03811 
03812        dirsync(path);
03813        writes(tag);
03814        writes(" OK [APPENDUID ");
03815        writen(new_uidv);
03816        writes(" ");
03817        writen(new_uid);
03818        writes("] APPEND Ok.\r\n");
03819        return (0);
03820 }
03821 
03822 
03823 /* Check for 'c' rights on the parent directory. */
03824 
03825 static int check_parent_create(const char *tag,
03826                             const char *cmd, char *folder)
03827 {
03828        char *parentPtr;
03829 
03830        parentPtr=strrchr(folder, HIERCH);
03831 
03832        if (parentPtr)
03833        {
03834               *parentPtr=0;
03835 
03836               {
03837                      CHECK_RIGHTSM(folder,
03838                                   create_rights,
03839                                   ACL_CREATE);
03840 
03841                      if (create_rights[0])
03842                      {
03843                             if (parentPtr)
03844                                    *parentPtr=HIERCH;
03845                             return 0;
03846                      }
03847               }
03848        }
03849 
03850        writes(tag);
03851        accessdenied(cmd, folder, ACL_CREATE);
03852        if (parentPtr)
03853               *parentPtr=HIERCH;
03854        return -1;
03855 }
03856 
03857 /* Convert ACL1 identifiers to ACL2 */
03858 
03859 static char *acl2_identifier(const char *tag,
03860                           const char *identifier)
03861 {
03862        const char *ident_orig=identifier;
03863 
03864        char *p;
03865        int isneg=0;
03866 
03867        if (*identifier == '-')
03868        {
03869               isneg=1;
03870               ++identifier;
03871        }
03872 
03873        if (strcmp(identifier, "anyone") == 0 ||
03874            strcmp(identifier, "anonymous") == 0 ||
03875            strcmp(identifier, "authuser") == 0 ||
03876            strcmp(identifier, "owner") == 0 ||
03877            strcmp(identifier, "administrators") == 0)
03878               return my_strdup(ident_orig);
03879 
03880        if (strchr(identifier, '='))
03881        {
03882               writes(tag);
03883               writes(" NO Invalid ACL identifier.\r\n");
03884               return NULL;
03885        }
03886 
03887        p=malloc(sizeof("-user=")+strlen(identifier));
03888 
03889        if (!p)
03890               write_error_exit(0);
03891        return strcat(strcat(strcpy(p, isneg ? "-":""), "user="), identifier);
03892 }
03893 
03894 
03895 int folder_rename(struct maildir_info *mi1,
03896                 struct maildir_info *mi2,
03897                 const char **errmsg)
03898 {
03899        char *old_mailbox, *new_mailbox;
03900 
03901        if (mi1->homedir == NULL || mi1->maildir == NULL)
03902        {
03903               *errmsg="Invalid mailbox name";
03904               return -1;
03905        }
03906 
03907        if (mi2->homedir == NULL || mi2->maildir == NULL)
03908        {
03909               *errmsg="Invalid new mailbox name";
03910               return -1;
03911        }
03912 
03913        if (current_mailbox)
03914        {
03915               char *mailbox=maildir_name2dir(mi1->homedir,
03916                                           mi1->maildir);
03917               size_t l;
03918 
03919               if (!mailbox)
03920               {
03921                      *errmsg="Invalid mailbox name";
03922                      return -1;
03923               }
03924 
03925               l=strlen(mailbox);
03926 
03927               if (strncmp(mailbox, current_mailbox, l) == 0 &&
03928                   (current_mailbox[l] == 0 ||
03929                    current_mailbox[l] == HIERCH))
03930               {
03931                      free(mailbox);
03932                      *errmsg="Can't RENAME the currently-open folder";
03933                      return -1;
03934               }
03935               free(mailbox);
03936        }
03937 
03938        if (strcmp(mi1->homedir, mi2->homedir))
03939        {
03940               *errmsg="Cannot move a folder to a different account.";
03941               return -1;
03942        }
03943 
03944        if (strcmp(mi1->maildir, INBOX) == 0 ||
03945            strcmp(mi2->maildir, INBOX) == 0)
03946        {
03947               *errmsg="INBOX rename not implemented.";
03948               return -1;
03949        }
03950 
03951        if (is_reserved_name(mi1->maildir) ||
03952            is_reserved_name(mi2->maildir))
03953        {
03954               *errmsg="Reserved folder name - cannot rename.";
03955               return -1;
03956        }
03957 
03958        /* Depend on maildir_name2dir returning ./.folder, see
03959        ** maildir_rename() call. */
03960 
03961        if ((old_mailbox=maildir_name2dir(".", mi1->maildir)) == NULL ||
03962            strncmp(old_mailbox, "./", 2))
03963        {
03964               if (old_mailbox)
03965                      free(old_mailbox);
03966               *errmsg="Internal error in RENAME: maildir_name2dir failed"
03967                      " for the old folder rename.";
03968               return -1;
03969        }
03970 
03971        if ((new_mailbox=maildir_name2dir(".", mi2->maildir)) == NULL ||
03972            strncmp(new_mailbox, "./", 2))
03973        {
03974               free(old_mailbox);
03975               if (new_mailbox)
03976                      free(new_mailbox);
03977               *errmsg="Internal error in RENAME: maildir_name2dir failed"
03978                      " for the new folder rename.";
03979               return -1;
03980        }
03981 
03982        fetch_free_cache();
03983 
03984        if (maildir_rename(mi1->homedir,
03985                         old_mailbox+2, new_mailbox+2,
03986                         MAILDIR_RENAME_FOLDER |
03987                         MAILDIR_RENAME_SUBFOLDERS,
03988                         &rename_callback))
03989        {
03990               free(old_mailbox);
03991               free(new_mailbox);
03992 
03993               *errmsg="@RENAME failed: ";
03994               return -1;
03995        }
03996 
03997        maildir_quota_recalculate(mi1->homedir);
03998        free(old_mailbox);
03999        free(new_mailbox);
04000        return 0;
04001 }
04002 
04003 static int validate_charset(const char *tag, char **charset)
04004 {
04005        libmail_u_convert_handle_t conv;
04006        unicode_char *ucptr;
04007        size_t ucsize;
04008 
04009        if (*charset == NULL)
04010               *charset=my_strdup("ISO-8859-1");
04011 
04012        conv=libmail_u_convert_tou_init(*charset, &ucptr, &ucsize, 1);
04013 
04014        if (!conv)
04015        {
04016               writes(tag);
04017               writes(" NO [BADCHARSET] The requested character set is not supported.\r\n");
04018               return (-1);
04019        }
04020        if (libmail_u_convert_deinit(conv, NULL) == 0)
04021               free(ucptr);
04022        return (0);
04023 }
04024 
04025 int do_imap_command(const char *tag)
04026 {
04027 struct imaptoken *curtoken=nexttoken();
04028 int    uid=0;
04029 
04030        if (curtoken->tokentype != IT_ATOM)       return (-1);
04031 
04032        /* Commands that work in authenticated state */
04033 
04034        if (strcmp(curtoken->tokenbuf, "CAPABILITY") == 0)
04035        {
04036               if (nexttoken()->tokentype != IT_EOL)     return (-1);
04037               writes("* CAPABILITY ");
04038               imapcapability();
04039               writes("\r\n");
04040               writes(tag);
04041               writes(" OK CAPABILITY completed\r\n");
04042               return (0);
04043        }
04044        if (strcmp(curtoken->tokenbuf, "NOOP") == 0)
04045        {
04046               if (nexttoken()->tokentype != IT_EOL)     return (-1);
04047               if (current_mailbox)
04048                      doNoop(1);
04049               writes(tag);
04050               writes(" OK NOOP completed\r\n");
04051               return (0);
04052        }
04053        if (strcmp(curtoken->tokenbuf, "IDLE") == 0)
04054        {
04055               const char *p;
04056 
04057                if (nexttoken()->tokentype != IT_EOL)   return (-1);
04058 
04059               read_eol();
04060 
04061               if ((p=getenv("IMAP_ENHANCEDIDLE")) == NULL
04062                  || !atoi(p)
04063                  || imapenhancedidle())
04064                      imapidle();
04065               curtoken=nexttoken();
04066               if (strcmp(curtoken->tokenbuf, "DONE") == 0)
04067               {
04068                      if (current_mailbox)
04069                             doNoop(0);
04070                      writes(tag);
04071                      writes(" OK IDLE completed\r\n");
04072                      return (0);
04073                }
04074                return (-1);
04075        }
04076        if (strcmp(curtoken->tokenbuf, "LOGOUT") == 0)
04077        {
04078               if (nexttoken()->tokentype != IT_EOL)     return (-1);
04079               fetch_free_cache();
04080               writes("* BYE Courier-IMAP server shutting down\r\n");
04081               writes(tag);
04082               writes(" OK LOGOUT completed\r\n");
04083               writeflush();
04084               emptytrash();
04085               logoutmsg();
04086               bye();
04087        }
04088 
04089        if (strcmp(curtoken->tokenbuf, "LIST") == 0
04090               || strcmp(curtoken->tokenbuf, "LSUB") == 0)
04091        {
04092               char   *reference, *name;
04093               int    rc;
04094               char   cmdbuf[5];
04095               int    list_flags=0;
04096 
04097               strcpy(cmdbuf, curtoken->tokenbuf);
04098 
04099               curtoken=nexttoken_nouc();
04100               if (curtoken->tokentype == IT_LPAREN)
04101               {
04102                      while ((curtoken=nexttoken())->tokentype != IT_RPAREN)
04103                      {
04104                             if (curtoken->tokentype != IT_QUOTED_STRING &&
04105                                 curtoken->tokentype != IT_ATOM &&
04106                                 curtoken->tokentype != IT_NUMBER)
04107                                    return (-1);
04108 
04109                             if (strcmp(curtoken->tokenbuf, "ACL") == 0)
04110                                    list_flags |= LIST_ACL;
04111                             if (strcmp(curtoken->tokenbuf, "MYRIGHTS")==0)
04112                                    list_flags |= LIST_MYRIGHTS;
04113                             if (strcmp(curtoken->tokenbuf,
04114                                       "POSTADDRESS")==0)
04115                                    list_flags |= LIST_POSTADDRESS;
04116                      }
04117 
04118                      curtoken=nexttoken_nouc();
04119               }
04120 
04121 
04122               if (curtoken->tokentype == IT_NIL)
04123                      reference=my_strdup("");
04124               else
04125               {
04126                      if (curtoken->tokentype != IT_QUOTED_STRING &&
04127                             curtoken->tokentype != IT_ATOM &&
04128                             curtoken->tokentype != IT_NUMBER)
04129                             return (-1);
04130                      reference=my_strdup(curtoken->tokenbuf);
04131               }
04132               curtoken=nexttoken_nouc();
04133 
04134               if (curtoken->tokentype == IT_NIL)
04135                      name=my_strdup("");
04136               else
04137               {
04138                      if (curtoken->tokentype != IT_QUOTED_STRING &&
04139                             curtoken->tokentype != IT_ATOM &&
04140                             curtoken->tokentype != IT_NUMBER)
04141                             return (-1);
04142                      name=my_strdup(curtoken->tokenbuf);
04143               }
04144               if (nexttoken()->tokentype != IT_EOL)     return (-1);
04145 
04146               if (strcmp(cmdbuf, "LIST"))
04147                      list_flags |= LIST_SUBSCRIBED;
04148 
04149               rc=mailbox_scan(reference, name,
04150                             list_flags,
04151                             list_callback, cmdbuf);
04152  
04153               free(reference);
04154               free(name);
04155               if (rc == 0)
04156               {
04157                      writes(tag);
04158                      writes(" OK ");
04159                      writes(cmdbuf);
04160                      writes(" completed\r\n");
04161               }
04162               else
04163               {
04164                      writes(tag);
04165                      writes(" NO ");
04166                      writes(strerror(errno));
04167                      writes("\r\n");
04168                      rc=0;
04169               }
04170               writeflush();
04171               return (rc);
04172        }
04173 
04174        if (strcmp(curtoken->tokenbuf, "APPEND") == 0)
04175        {
04176               struct imaptoken *tok=nexttoken_nouc();
04177               struct maildir_info mi;
04178 
04179               if (tok->tokentype != IT_NUMBER &&
04180                      tok->tokentype != IT_ATOM &&
04181                      tok->tokentype != IT_QUOTED_STRING)
04182                      return (-1);
04183 
04184               if (maildir_info_imap_find(&mi, tok->tokenbuf,
04185                                       getenv("AUTHENTICATED")) < 0)
04186               {
04187                      writes(tag);
04188                      writes(" NO Invalid mailbox name.\r\n");
04189                      return (0);
04190               }
04191 
04192               if (mi.homedir && mi.maildir)
04193               {
04194                      char *p=maildir_name2dir(mi.homedir, mi.maildir);
04195                      int rc;
04196 
04197                      if (!p)
04198                      {
04199                             maildir_info_destroy(&mi);
04200                             writes(tag);
04201                             accessdenied("APPEND",
04202                                         tok->tokenbuf,
04203                                         ACL_INSERT);
04204                             return 0;
04205                      }
04206                      
04207                      rc=append(tag, tok->tokenbuf, p);
04208                      free(p);
04209                      maildir_info_destroy(&mi);
04210                      return (rc);
04211               }
04212               else if (mi.mailbox_type == MAILBOXTYPE_OLDSHARED)
04213               {
04214                      char *p=strchr(tok->tokenbuf, '.');
04215 
04216                      if (p && (p=maildir_shareddir(".", p+1)) != NULL)
04217                      {
04218                             int rc;
04219                             char   *q=malloc(strlen(p)+sizeof("/shared"));
04220 
04221                             if (!q)       write_error_exit(0);
04222 
04223                             strcat(strcpy(q, p), "/shared");
04224                             free(p);
04225                             rc=append(tag, tok->tokenbuf, q);
04226                             free(q);
04227                             maildir_info_destroy(&mi);
04228                             return rc;
04229                      }
04230               }
04231 
04232               writes(tag);
04233               accessdenied("APPEND", "folder", ACL_INSERT);
04234               return (0);
04235        }
04236 
04237        if (strcmp(curtoken->tokenbuf, "GETQUOTAROOT") == 0)
04238        {
04239               char   qroot[20];
04240               struct maildir_info minfo;
04241 
04242               curtoken=nexttoken_nouc();
04243 
04244               if (curtoken->tokentype != IT_NUMBER &&
04245                      curtoken->tokentype != IT_ATOM &&
04246                      curtoken->tokentype != IT_QUOTED_STRING)
04247                      return (-1);
04248 
04249               if (maildir_info_imap_find(&minfo, curtoken->tokenbuf,
04250                                       getenv("AUTHENTICATED")))
04251               {
04252                      writes(tag);
04253                      writes(" NO Invalid mailbox name.\r\n");
04254                      return (0);
04255               }
04256 
04257               switch (minfo.mailbox_type) {
04258               case MAILBOXTYPE_INBOX:
04259                      strcpy(qroot, "ROOT");
04260                      break;
04261               case MAILBOXTYPE_OLDSHARED:
04262                      strcpy(qroot, "SHARED");
04263                      break;
04264               case MAILBOXTYPE_NEWSHARED:
04265                      strcpy(qroot, "PUBLIC");
04266                      break;
04267               }
04268               maildir_info_destroy(&minfo);
04269 
04270               writes("*");
04271               writes(" QUOTAROOT \"");
04272               writeqs(curtoken->tokenbuf);
04273               writes("\" \"");
04274               writes(qroot);
04275               writes("\"\r\n");
04276               quotainfo_out(qroot);
04277               writes(tag);
04278               writes(" OK GETQUOTAROOT Ok.\r\n");
04279               return(0);
04280        }
04281 
04282 
04283        if (strcmp(curtoken->tokenbuf, "SETQUOTA") == 0)
04284        {
04285               writes(tag);
04286               writes(" NO SETQUOTA No permission.\r\n");
04287               return(0);
04288        }
04289 
04290        if (strcmp(curtoken->tokenbuf, "GETQUOTA") == 0)
04291        {
04292               curtoken=nexttoken_nouc();
04293 
04294               if (curtoken->tokentype != IT_NUMBER &&
04295                      curtoken->tokentype != IT_ATOM &&
04296                      curtoken->tokentype != IT_QUOTED_STRING)
04297                      return (-1);
04298 
04299               quotainfo_out(curtoken->tokenbuf);
04300               writes(tag);
04301               writes(" OK GETQUOTA Ok.\r\n");
04302               return(0);
04303        }
04304 
04305        if (strcmp(curtoken->tokenbuf, "STATUS") == 0)
04306        {
04307               char   *mailbox;
04308               int    get_messages=0,
04309                      get_recent=0,
04310                      get_uidnext=0,
04311                      get_uidvalidity=0,
04312                      get_unseen=0;
04313 
04314               struct imapscaninfo other_info, *loaded_infoptr,
04315                      *infoptr;
04316               const char *p;
04317               char   *orig_mailbox;
04318               int    oneonly;
04319 
04320               curtoken=nexttoken_nouc();
04321               mailbox=parse_mailbox_error(tag, curtoken, 0, 0);
04322               if ( mailbox == 0)
04323                      return (0);
04324 
04325               orig_mailbox=my_strdup(curtoken->tokenbuf);
04326               curtoken=nexttoken();
04327 
04328               oneonly=0;
04329               if (curtoken->tokentype != IT_LPAREN)
04330               {
04331                      if (curtoken->tokentype != IT_ATOM)
04332                      {
04333                             free(mailbox);
04334                             free(orig_mailbox);
04335                             return (-1);
04336                      }
04337                      oneonly=1;
04338               }
04339               else   nexttoken();
04340 
04341               while ((curtoken=currenttoken())->tokentype == IT_ATOM)
04342               {
04343                      if (strcmp(curtoken->tokenbuf, "MESSAGES") == 0)
04344                             get_messages=1;
04345                      if (strcmp(curtoken->tokenbuf, "RECENT") == 0)
04346                             get_recent=1;
04347                      if (strcmp(curtoken->tokenbuf, "UIDNEXT") == 0)
04348                             get_uidnext=1;
04349                      if (strcmp(curtoken->tokenbuf, "UIDVALIDITY") == 0)
04350                             get_uidvalidity=1;
04351                      if (strcmp(curtoken->tokenbuf, "UNSEEN") == 0)
04352                             get_unseen=1;
04353                      nexttoken();
04354                      if (oneonly)  break;
04355               }
04356 
04357               if ((!oneonly && curtoken->tokentype != IT_RPAREN) ||
04358                      nexttoken()->tokentype != IT_EOL)
04359               {
04360                      free(mailbox);
04361                      free(orig_mailbox);
04362                      return (-1);
04363               }
04364 
04365               {
04366                      CHECK_RIGHTSM(orig_mailbox, status_rights, ACL_READ);
04367 
04368                      if (!status_rights[0])
04369                      {
04370                             writes(tag);
04371                             accessdenied("STATUS", orig_mailbox,
04372                                         ACL_READ);
04373                             free(mailbox);
04374                             free(orig_mailbox);
04375                             return 0;
04376                      }
04377               }
04378 
04379 
04380               if (current_mailbox && strcmp(current_mailbox, mailbox) == 0)
04381               {
04382                      loaded_infoptr=0;
04383                      infoptr= &current_maildir_info;
04384               }
04385               else
04386               {
04387                      loaded_infoptr= &other_info;
04388                      infoptr=loaded_infoptr;
04389 
04390                      imapscan_init(loaded_infoptr);
04391 
04392                      if (imapscan_maildir(infoptr, mailbox, 1, 1, NULL))
04393                      {
04394                             writes(tag);
04395                             writes(" NO [ALERT] STATUS failed\r\n");
04396                             free(mailbox);
04397                             free(orig_mailbox);
04398                             return (0);
04399                      }
04400               }
04401 
04402               writes("*");
04403               writes(" STATUS \"");
04404               writeqs(orig_mailbox);
04405               writes("\" (");
04406               p="";
04407               if (get_messages)
04408               {
04409                      writes("MESSAGES ");
04410                      writen(infoptr->nmessages+infoptr->left_unseen);
04411                      p=" ";
04412               }
04413               if (get_recent)
04414               {
04415               unsigned long n=infoptr->left_unseen;
04416               unsigned long i;
04417 
04418                      for (i=0; i<infoptr->nmessages; i++)
04419                             if (infoptr->msgs[i].recentflag)
04420                                    ++n;
04421                      writes(p);
04422                      writes("RECENT ");
04423                      writen(n);
04424                      p=" ";
04425               }
04426 
04427               if (get_uidnext)
04428               {
04429                      writes(p);
04430                      writes("UIDNEXT ");
04431                      writen(infoptr->nextuid);
04432                      p=" ";
04433               }
04434 
04435               if (get_uidvalidity)
04436               {
04437                      writes(p);
04438                      writes("UIDVALIDITY ");
04439                      writen(infoptr->uidv);
04440                      p=" ";
04441               }
04442 
04443               if (get_unseen)
04444               {
04445               unsigned long n=infoptr->left_unseen, i;
04446 
04447                      for (i=0; i<infoptr->nmessages; i++)
04448                      {
04449                      const char *p=infoptr->msgs[i].filename;
04450 
04451                             p=strrchr(p, MDIRSEP[0]);
04452                             if (p && strncmp(p, MDIRSEP "2,", 3) == 0 &&
04453                                    strchr(p, 'S'))      continue;
04454                             ++n;
04455                      }
04456                      writes(p);
04457                      writes("UNSEEN ");
04458                      writen(n);
04459               }
04460               writes(")\r\n");
04461               if (loaded_infoptr)
04462                      imapscan_free(loaded_infoptr);
04463               free(mailbox);
04464               free(orig_mailbox);
04465               writes(tag);
04466               writes(" OK STATUS Completed.\r\n");
04467               return (0);
04468        }
04469 
04470        if (strcmp(curtoken->tokenbuf, "CREATE") == 0)
04471        {
04472               char   *mailbox, *orig_mailbox, *p;
04473               int    isdummy;
04474               struct maildir_info mi;
04475               struct imapscaninfo minfo;
04476 
04477               curtoken=nexttoken_nouc();
04478 
04479               if (curtoken->tokentype != IT_NUMBER &&
04480                      curtoken->tokentype != IT_ATOM &&
04481                      curtoken->tokentype != IT_QUOTED_STRING)
04482                      return (-1);
04483 
04484               isdummy=0;
04485 
04486               p=strrchr(curtoken->tokenbuf, HIERCH);
04487               if (p && p[1] == '\0')
04488               {
04489                      *p=0;
04490                      isdummy=1;    /* Ignore hierarchy creation */
04491               }
04492 
04493               if (maildir_info_imap_find(&mi, curtoken->tokenbuf,
04494                                       getenv("AUTHENTICATED")))
04495               {
04496                      writes(tag);
04497                      writes(" NO Invalid mailbox name.\r\n");
04498                      return (0);
04499               }
04500 
04501               if (!mi.homedir || !mi.maildir)
04502               {
04503                      maildir_info_destroy(&mi);
04504                      writes(tag);
04505                      accessdenied("CREATE",
04506                                  curtoken->tokenbuf,
04507                                  ACL_CREATE);
04508                      maildir_info_destroy(&mi);
04509                      return (0);
04510               }
04511 
04512               mailbox=maildir_name2dir(mi.homedir, mi.maildir);
04513               if (!mailbox)
04514               {
04515                      writes(tag);
04516                      writes(" NO Invalid mailbox name\r\n");
04517                      maildir_info_destroy(&mi);
04518                      return (0);
04519               }
04520 
04521               if (strcmp(mailbox, ".") == 0)
04522               {
04523                      writes(tag);
04524                      writes(" NO INBOX already exists!\r\n");
04525                      free(mailbox);
04526                      maildir_info_destroy(&mi);
04527                      return (0);
04528               }
04529 
04530               if (check_parent_create(tag, "CREATE", curtoken->tokenbuf))
04531               {
04532                      free(mailbox);
04533                      maildir_info_destroy(&mi);
04534                      return (0);
04535               }
04536 
04537               if (isdummy)  *p=HIERCH;
04538               orig_mailbox=my_strdup(curtoken->tokenbuf);
04539 
04540               if (nexttoken()->tokentype != IT_EOL)
04541               {
04542                      free(mailbox);
04543                      free(orig_mailbox);
04544                      maildir_info_destroy(&mi);
04545                      return (-1);
04546               }
04547 
04548               if (!isdummy)
04549               {
04550                      int did_exist;
04551                      maildir_aclt_list l;
04552 
04553                      if ((did_exist=folder_exists(orig_mailbox)) != 0)
04554                      {
04555                             if (acl_read_folder(&l,
04556                                               mi.homedir,
04557                                               mi.maildir) < 0)
04558                             {
04559                                    free(mailbox);
04560                                    free(orig_mailbox);
04561                                    writes(tag);
04562                                    writes(" NO Cannot create this folder"
04563                                           ".\r\n");
04564                                    maildir_info_destroy(&mi);
04565                                    return (0);
04566                             }
04567                             maildir_acl_delete(mi.homedir, mi.maildir);
04568                             /* Clear out fluff */
04569                      }
04570 
04571                      if (mdcreate(mailbox))
04572                      {
04573                             if (did_exist)
04574                                    maildir_aclt_list_destroy(&l);
04575                             free(mailbox);
04576                             free(orig_mailbox);
04577                             writes(tag);
04578                             writes(" NO Cannot create this folder.\r\n");
04579                             maildir_info_destroy(&mi);
04580                             return (0);
04581                      }
04582                      if (did_exist)
04583                      {
04584                             const char *acl_error;
04585 
04586                             acl_write_folder(&l, mi.homedir,
04587                                            mi.maildir, NULL,
04588                                            &acl_error);
04589                             maildir_aclt_list_destroy(&l);
04590                      }
04591               }
04592               writes(tag);
04593               writes(" OK \"");
04594               writeqs(orig_mailbox);
04595               writes("\" created.\r\n");
04596 
04597               /*
04598               ** This is a dummy call to acl_read_folder that initialized
04599               ** the default ACLs for this folder to its parent.
04600               */
04601 
04602               {
04603                      CHECK_RIGHTSM(curtoken->tokenbuf, create_rights,
04604                                   ACL_CREATE);
04605               }
04606 
04607               imapscan_init(&minfo);
04608               imapscan_maildir(&minfo, mailbox, 0,0, NULL);
04609               imapscan_free(&minfo);
04610 
04611               free(mailbox);
04612               free(orig_mailbox);
04613               maildir_info_destroy(&mi);
04614               return (0);
04615        }
04616 
04617        if (strcmp(curtoken->tokenbuf, "DELETE") == 0)
04618        {
04619        char   *mailbox;
04620        char   *p;
04621        char   *mailbox_name;
04622 
04623               curtoken=nexttoken_nouc();
04624 
04625               if (curtoken->tokentype != IT_NUMBER &&
04626                      curtoken->tokentype != IT_ATOM &&
04627                      curtoken->tokentype != IT_QUOTED_STRING)
04628                      return (-1);
04629 
04630               p=strrchr(curtoken->tokenbuf, HIERCH);
04631               if (p && p[1] == '\0')             /* Ignore hierarchy DELETE */
04632               {
04633                      if (nexttoken()->tokentype != IT_EOL)
04634                             return (-1);
04635                      writes(tag);
04636                      writes(" OK Folder directory delete punted.\r\n");
04637                      return (0);
04638               }
04639 
04640               mailbox_name=my_strdup(curtoken->tokenbuf);
04641               mailbox=parse_mailbox_error(tag, curtoken, 1, 0);
04642               if ( mailbox == 0)
04643               {
04644                      free(mailbox_name);
04645                      return (0);
04646               }
04647 
04648               if (nexttoken()->tokentype != IT_EOL)
04649               {
04650                      free(mailbox_name);
04651                      free(mailbox);
04652                      return (-1);
04653               }
04654 
04655               if (current_mailbox && strcmp(mailbox, current_mailbox) == 0)
04656               {
04657                      free(mailbox_name);
04658                      free(mailbox);
04659                      writes(tag);
04660                      writes(" NO Cannot delete currently-open folder.\r\n");
04661                      return (0);
04662               }
04663 
04664               if (strncmp(curtoken->tokenbuf, SHARED HIERCHS,
04665                      sizeof(SHARED HIERCHS)-1) == 0)
04666               {
04667                      maildir_shared_unsubscribe(0, curtoken->tokenbuf+
04668                                              sizeof(SHARED HIERCHS)-1);
04669                      free(mailbox_name);
04670                      free(mailbox);
04671                      writes(tag);
04672                      writes(" OK UNSUBSCRIBEd a shared folder.\r\n");
04673                      return (0);
04674               }
04675 
04676               {
04677                      CHECK_RIGHTSM(curtoken->tokenbuf,
04678                                   delete_rights,
04679                                   ACL_DELETEFOLDER);
04680                      if (delete_rights[0] == 0)
04681                      {
04682                             free(mailbox_name);
04683                             free(mailbox);
04684                             writes(tag);
04685                             accessdenied("DELETE",
04686                                         curtoken->tokenbuf,
04687                                         ACL_DELETEFOLDER);
04688                             return 0;
04689                      }
04690               }
04691 
04692               if (!broken_uidvs())
04693                      sleep(2); /* Make sure we never recycle them*/
04694 
04695               fetch_free_cache();
04696 
04697 
04698               if (do_folder_delete(mailbox_name))
04699               {
04700                      writes(tag);
04701                      writes(" NO Cannot delete this folder.\r\n");
04702               }
04703               else
04704               {
04705                      writes(tag);
04706                      writes(" OK Folder deleted.\r\n");
04707               }
04708 
04709               free(mailbox_name);
04710               free(mailbox);
04711               return (0);
04712        }
04713 
04714        if (strcmp(curtoken->tokenbuf, "RENAME") == 0)
04715        {
04716               char *p;
04717               struct maildir_info mi1, mi2;
04718               const char *errmsg;
04719 
04720               curtoken=nexttoken_nouc();
04721 
04722               if (curtoken->tokentype != IT_NUMBER &&
04723                   curtoken->tokentype != IT_ATOM &&
04724                   curtoken->tokentype != IT_QUOTED_STRING)
04725               {
04726                      writes(tag);
04727                      writes(" NO Invalid mailbox\r\n");
04728                      return (0);
04729               }
04730 
04731               if ((p=strrchr(curtoken->tokenbuf, HIERCH))  && p[1] == 0)
04732                      *p=0;
04733 
04734               if (maildir_info_imap_find(&mi1, curtoken->tokenbuf,
04735                                       getenv("AUTHENTICATED")) < 0)
04736               {
04737                      writes(tag);
04738                      writes(" NO Invalid mailbox name.\r\n");
04739                      return (0);
04740               }
04741 
04742               if (mi1.homedir == NULL || mi1.maildir == NULL)
04743               {
04744                      maildir_info_destroy(&mi1);
04745                      writes(tag);
04746                      writes(" NO Invalid mailbox\r\n");
04747                      return (0);
04748               }
04749 
04750               {
04751                      CHECK_RIGHTSM(curtoken->tokenbuf,
04752                                   rename_rights, ACL_DELETEFOLDER);
04753 
04754                      if (rename_rights[0] == 0)
04755                      {
04756                             maildir_info_destroy(&mi1);
04757                             writes(tag);
04758                             accessdenied("RENAME", curtoken->tokenbuf,
04759                                         ACL_DELETEFOLDER);
04760                             return (0);
04761                      }
04762               }
04763 
04764 
04765               curtoken=nexttoken_nouc();
04766               if (curtoken->tokentype != IT_NUMBER &&
04767                      curtoken->tokentype != IT_ATOM &&
04768                      curtoken->tokentype != IT_QUOTED_STRING)
04769               {
04770                      maildir_info_destroy(&mi1);
04771                      return (-1);
04772               }
04773 
04774               if ((p=strrchr(curtoken->tokenbuf, HIERCH)) && p[1] == 0)
04775               {
04776                      *p=0;
04777               }
04778 
04779 
04780               if (maildir_info_imap_find(&mi2, curtoken->tokenbuf,
04781                                       getenv("AUTHENTICATED")) < 0)
04782               {
04783                      maildir_info_destroy(&mi1);
04784                      writes(tag);
04785                      writes(" NO Invalid mailbox name.\r\n");
04786                      return (0);
04787               }
04788 
04789               if (check_parent_create(tag, "RENAME", curtoken->tokenbuf))
04790               {
04791                      maildir_info_destroy(&mi1);
04792                      maildir_info_destroy(&mi2);
04793                      return 0;
04794               }
04795 
04796               if (nexttoken()->tokentype != IT_EOL)
04797               {
04798                      maildir_info_destroy(&mi1);
04799                      maildir_info_destroy(&mi2);
04800                      return (-1);
04801               }
04802 
04803               if (!broken_uidvs())
04804                      sleep(2);
04805               /* Make sure IMAP uidvs are not recycled */
04806 
04807               if (folder_rename(&mi1, &mi2, &errmsg))
04808               {
04809                      writes(tag);
04810                      writes(" NO ");
04811                      writes(*errmsg == '@' ? errmsg+1:errmsg);
04812                      if (*errmsg == '@')
04813                             writes(strerror(errno));
04814                      writes("\r\n");
04815               }
04816               else
04817               {
04818                      writes(tag);
04819                      writes(" OK Folder renamed.\r\n");
04820               }
04821 
04822               maildir_info_destroy(&mi1);
04823               maildir_info_destroy(&mi2);
04824               return (0);
04825        }
04826 
04827        if (strcmp(curtoken->tokenbuf, "SELECT") == 0 ||
04828               strcmp(curtoken->tokenbuf, "EXAMINE") == 0)
04829        {
04830        char   *mailbox;
04831        int    ro=curtoken->tokenbuf[0] == 'E';
04832        const char *p;
04833 
04834 
04835               curtoken=nexttoken_nouc();
04836 
04837               if (current_mailbox)
04838               {
04839                      free(current_mailbox);
04840                      imapscan_free(&current_maildir_info);
04841                      imapscan_init(&current_maildir_info);
04842                      current_mailbox=0;
04843               }
04844 
04845               if (current_mailbox_acl)
04846                      free(current_mailbox_acl);
04847               current_mailbox_acl=0;
04848 
04849               mailbox=parse_mailbox_error(tag, curtoken, 0, 1);
04850               if ( mailbox == 0)
04851                      return (0);
04852 
04853               current_mailbox_acl=get_myrightson(curtoken->tokenbuf);
04854               if (current_mailbox_acl == NULL)
04855               {
04856                      free(mailbox);
04857                      writes(tag);
04858                      writes(" NO Unable to read ACLs for ");
04859                      writes(curtoken->tokenbuf);
04860                      writes(": ");
04861                      writes(strerror(errno));
04862                      writes("\r\n");
04863                      return 0;
04864               }
04865 
04866               if (strchr(current_mailbox_acl, ACL_READ[0]) == NULL)
04867               {
04868                      free(mailbox);
04869                      free(current_mailbox_acl);
04870                      current_mailbox_acl=NULL;
04871                      writes(tag);
04872                      accessdenied("SELECT/EXAMINE", curtoken->tokenbuf,
04873                                  ACL_READ);
04874                      return 0;
04875               }
04876 
04877               if (nexttoken()->tokentype != IT_EOL)
04878               {
04879                      free(mailbox);
04880                      return (-1);
04881               }
04882 
04883               if (imapscan_maildir(&current_maildir_info, mailbox, 0, ro,
04884                                  NULL))
04885               {
04886                      free(mailbox);
04887                      writes(tag);
04888                      writes(" NO Unable to open this mailbox.\r\n");
04889                      return (0);
04890               }
04891               current_mailbox=mailbox;
04892 
04893               /* check if this is a shared read-only folder */
04894 
04895               if (is_sharedsubdir(mailbox) &&
04896                      maildir_sharedisro(mailbox))
04897                      ro=1;
04898 
04899               current_mailbox_ro=ro;
04900 
04901               mailboxflags(ro);
04902               mailboxmetrics();
04903               writes("* OK [UIDVALIDITY ");
04904               writen(current_maildir_info.uidv);
04905               writes("] Ok\r\n");
04906               myrights();
04907               writes(tag);
04908 
04909               for (p=current_mailbox_acl; *p; p++)
04910                      if (strchr(ACL_INSERT ACL_EXPUNGE
04911                                ACL_SEEN ACL_WRITE ACL_DELETEMSGS,
04912                                *p))
04913                             break;
04914 
04915               if (*p == 0)
04916                      ro=1;
04917 
04918               writes(ro ? " OK [READ-ONLY] Ok\r\n":" OK [READ-WRITE] Ok\r\n");
04919               return (0);
04920        }
04921 
04922        if (strcmp(curtoken->tokenbuf, "SUBSCRIBE") == 0)
04923        {
04924        char   *mailbox;
04925        char   *p;
04926        struct maildir_info mi;
04927 
04928               curtoken=nexttoken_nouc();
04929               if (curtoken->tokentype != IT_NUMBER &&
04930                      curtoken->tokentype != IT_ATOM &&
04931                      curtoken->tokentype != IT_QUOTED_STRING)
04932                      return (-1);
04933 
04934               p=strrchr(curtoken->tokenbuf, HIERCH);
04935               if (p && p[1] == '\0')             /* Ignore hierarchy DELETE */
04936               {
04937                      if (nexttoken()->tokentype != IT_EOL)
04938                             return (-1);
04939                      writes(tag);
04940                      writes(" OK Folder directory subscribe punted.\r\n");
04941                      return (0);
04942               }
04943 
04944               mailbox=my_strdup(curtoken->tokenbuf);
04945               if (nexttoken()->tokentype != IT_EOL)
04946                      return (-1);
04947 
04948               if (maildir_info_imap_find(&mi, mailbox,
04949                                       getenv("AUTHENTICATED")) < 0)
04950               {
04951                      free(mailbox);
04952                      writes(tag);
04953                      writes(" NO Invalid mailbox name.\r\n");
04954                      return (0);
04955               }
04956 
04957               if (mi.mailbox_type != MAILBOXTYPE_OLDSHARED)
04958               {
04959                      maildir_info_destroy(&mi);
04960                      subscribe(mailbox);
04961                      free(mailbox);
04962                      writes(tag);
04963                      writes(" OK Folder subscribed.\r\n");
04964                      return (0);
04965               }
04966               maildir_info_destroy(&mi);
04967 
04968               p=strchr(mailbox, '.');
04969 
04970               p=p ? maildir_shareddir(".", p+1):NULL;
04971 
04972               if (p == NULL || access(p, 0) == 0)
04973               {
04974                      if (p)
04975                             free(p);
04976                      free(mailbox);
04977                      writes(tag);
04978                      writes(" OK Already subscribed.\r\n");
04979                      return (0);
04980               }
04981 
04982               if (!p || maildir_shared_subscribe(0, strchr(mailbox, '.')+1))
04983               {
04984                      if (p)
04985                             free(p);
04986                      free(mailbox);
04987                      writes(tag);
04988                      writes(" NO Cannot subscribe to this folder.\r\n");
04989                      return (0);
04990               }
04991               if (p)
04992                      free(p);
04993               free(mailbox);
04994               writes(tag);
04995               writes(" OK SUBSCRIBE completed.\r\n");
04996               return (0);
04997        }
04998 
04999        if (strcmp(curtoken->tokenbuf, "UNSUBSCRIBE") == 0)
05000        {
05001        char   *mailbox;
05002        char   *p;
05003        struct maildir_info mi;
05004 
05005               curtoken=nexttoken_nouc();
05006               if (curtoken->tokentype != IT_NUMBER &&
05007                      curtoken->tokentype != IT_ATOM &&
05008                      curtoken->tokentype != IT_QUOTED_STRING)
05009                      return (-1);
05010 
05011               p=strrchr(curtoken->tokenbuf, HIERCH);
05012               if (p && p[1] == '\0')             /* Ignore hierarchy DELETE */
05013               {
05014                      if (nexttoken()->tokentype != IT_EOL)
05015                             return (-1);
05016                      writes(tag);
05017                      writes(" OK Folder directory unsubscribe punted.\r\n");
05018                      return (0);
05019               }
05020 
05021               mailbox=my_strdup(curtoken->tokenbuf);
05022               if (nexttoken()->tokentype != IT_EOL)
05023                      return (-1);
05024 
05025               if (maildir_info_imap_find(&mi, mailbox,
05026                                       getenv("AUTHENTICATED")) < 0)
05027               {
05028                      free(mailbox);
05029                      writes(tag);
05030                      writes(" NO Invalid mailbox name.\r\n");
05031                      return (0);
05032               }
05033 
05034               if (mi.mailbox_type != MAILBOXTYPE_OLDSHARED)
05035               {
05036                      maildir_info_destroy(&mi);
05037                      unsubscribe(mailbox);
05038                      free(mailbox);
05039                      writes(tag);
05040                      writes(" OK Folder unsubscribed.\r\n");
05041                      return (0);
05042               }
05043               maildir_info_destroy(&mi);
05044 
05045               p=strchr(mailbox, '.');
05046 
05047               p=p ? maildir_shareddir(".", p+1):NULL;
05048 
05049 
05050               if (p == NULL || access(p, 0))
05051               {
05052                      if (p)
05053                             free(p);
05054                      free(mailbox);
05055                      writes(tag);
05056                      writes(" OK Already unsubscribed.\r\n");
05057                      return (0);
05058               }
05059 
05060               fetch_free_cache();
05061 
05062               if (!p || maildir_shared_unsubscribe(0,
05063                                                strchr(mailbox, '.')+1))
05064               {
05065                      if (p)
05066                             free(p);
05067                      free(mailbox);
05068                      writes(tag);
05069                      writes(" NO Cannot subscribe to this folder.\r\n");
05070                      return (0);
05071               }
05072               if (p)
05073                      free(p);
05074               free(mailbox);
05075               writes(tag);
05076               writes(" OK UNSUBSCRIBE completed.\r\n");
05077               return (0);
05078        }
05079 
05080        if (strcmp(curtoken->tokenbuf, "NAMESPACE") == 0)
05081        {
05082               if (nexttoken()->tokentype != IT_EOL)
05083                      return (-1);
05084               writes("* NAMESPACE ((\"INBOX.\" \".\")) NIL "
05085                      "((\"#shared.\" \".\")(\""
05086                      SHARED ".\" \".\"))\r\n");
05087               writes(tag);
05088               writes(" OK NAMESPACE completed.\r\n");
05089               return (0);
05090        }
05091 
05092        if (strcmp(curtoken->tokenbuf, "ACL") == 0)
05093        {
05094               if (aclcmd(tag))
05095               {
05096                      writes(tag);
05097                      writes(" ACL FAILED: ");
05098                      writes(strerror(errno));
05099                      writes("\r\n");
05100               }
05101               return 0;
05102        }
05103 
05104        /* RFC 2086 */
05105 
05106        if (strcmp(curtoken->tokenbuf, "SETACL") == 0 ||
05107            strcmp(curtoken->tokenbuf, "DELETEACL") == 0)
05108        {
05109               char *mailbox;
05110               char *identifier;
05111               struct maildir_info mi;
05112               maildir_aclt_list aclt_list;
05113               const char *acl_error;
05114               int doset=curtoken->tokenbuf[0] == 'S';
05115               const char *origcmd=doset ? "SETACL":"DELETEACL";
05116 
05117               curtoken=nexttoken_nouc();
05118 
05119               mailbox=parse_mailbox_error(tag, curtoken, 0, 0);
05120               if (!mailbox)
05121                      return 0;
05122               free(mailbox);
05123 
05124               mailbox=my_strdup(curtoken->tokenbuf);
05125 
05126               if (maildir_info_imap_find(&mi, mailbox,
05127                                       getenv("AUTHENTICATED")) < 0)
05128               {
05129                      writes(tag);
05130                      writes(" NO Invalid mailbox.\r\n");
05131                      free(mailbox);
05132                      return 0;
05133               }
05134 
05135               if (mi.homedir == NULL || mi.maildir == NULL)
05136               {
05137                      maildir_info_destroy(&mi);
05138                      writes(tag);
05139                      writes(" NO Cannot set ACLs for this mailbox\r\n");
05140                      free(mailbox);
05141                      return 0;
05142               }
05143 
05144               switch ((curtoken=nexttoken_nouc())->tokentype) {
05145               case IT_QUOTED_STRING:
05146               case IT_ATOM:
05147               case IT_NUMBER:
05148                      break;
05149               default:
05150                      maildir_info_destroy(&mi);
05151                      free(mailbox);
05152                      return -1;
05153               }
05154 
05155               identifier=acl2_identifier(tag, curtoken->tokenbuf);
05156 
05157               if (identifier == NULL)
05158               {
05159                      maildir_info_destroy(&mi);
05160                      free(mailbox);
05161                      return 0;
05162               }
05163 
05164               if (doset)
05165               {
05166                      switch ((curtoken=nexttoken_nouc())->tokentype) {
05167                      case IT_QUOTED_STRING:
05168                      case IT_ATOM:
05169                      case IT_NUMBER:
05170                             break;
05171                      default:
05172                             free(identifier);
05173                             maildir_info_destroy(&mi);
05174                             free(mailbox);
05175                             return -1;
05176                      }
05177               }
05178 
05179               {
05180                      CHECK_RIGHTSM(mailbox,
05181                                   acl_rights,
05182                                   ACL_ADMINISTER);
05183                      if (acl_rights[0] == 0)
05184                      {
05185                             writes(tag);
05186                             accessdenied(origcmd, mailbox,
05187                                         ACL_ADMINISTER);
05188                             free(identifier);
05189                             maildir_info_destroy(&mi);
05190                             free(mailbox);
05191                             return 0;
05192                      }
05193               }
05194 
05195               if (acl_read_folder(&aclt_list, mi.homedir, mi.maildir))
05196               {
05197                      writes(tag);
05198                      writes(" NO Cannot read existing ACLs.\r\n");
05199                      free(identifier);
05200                      maildir_info_destroy(&mi);
05201                      free(mailbox);
05202                      return 0;
05203               }
05204 
05205               if (do_acl_mod(&aclt_list, &mi, identifier,
05206                             doset ? curtoken->tokenbuf:"",
05207                             &acl_error) < 0)
05208               {
05209                      writes(tag);
05210                      writes(acl_error ?
05211                             " NO Cannot modify ACLs as requested.\r\n" :
05212                             " NO Cannot modify ACLs on this mailbox.\r\n");
05213               }
05214               else
05215               {
05216                      char *p=get_myrightson(mailbox);
05217 
05218                      if (p)
05219                             free(p);
05220                      /* Side effect - change current folder's ACL */
05221 
05222                      writes(tag);
05223                      writes(" OK ACLs updated.\r\n");
05224               }
05225 
05226               maildir_aclt_list_destroy(&aclt_list);
05227               maildir_info_destroy(&mi);
05228               free(identifier);
05229               free(mailbox);
05230               return 0;
05231        }
05232 
05233        if (strcmp(curtoken->tokenbuf, "GETACL") == 0)
05234        {
05235               maildir_aclt_list l;
05236               char *mailbox_owner;
05237               char *mb;
05238 
05239               curtoken=nexttoken_nouc();
05240 
05241               mb=parse_mailbox_error(tag, curtoken, 0, 0);
05242               if (!mb)
05243                      return 0;
05244               free(mb);
05245 
05246               {
05247                      CHECK_RIGHTSM(curtoken->tokenbuf,
05248                                   acl_rights,
05249                                   ACL_ADMINISTER);
05250                      if (acl_rights[0] == 0)
05251                      {
05252                             writes(tag);
05253                             accessdenied("GETACL", curtoken->tokenbuf,
05254                                         ACL_ADMINISTER);
05255                             return 0;
05256                      }
05257               }
05258 
05259               if (get_acllist(&l, curtoken->tokenbuf,
05260                             &mailbox_owner) < 0)
05261               {
05262                      writes(tag);
05263                      writes(" NO Cannot retrieve ACLs for mailbox.\r\n");
05264                      return 0;
05265               }
05266               free(mailbox_owner);
05267 
05268               writes("* ACL \"");
05269               writeqs(curtoken->tokenbuf);
05270               writes("\"");
05271               maildir_aclt_list_enum(&l, getacl_cb, NULL);
05272               writes("\r\n");
05273               writes(tag);
05274               writes(" OK GETACL completed.\r\n");
05275               maildir_aclt_list_destroy(&l);
05276               return 0;
05277        }
05278 
05279        if (strcmp(curtoken->tokenbuf, "LISTRIGHTS") == 0)
05280        {
05281               maildir_aclt_list l;
05282               char *mailbox_owner;
05283               char *mb;
05284 
05285               curtoken=nexttoken_nouc();
05286 
05287               mb=parse_mailbox_error(tag, curtoken, 0, 0);
05288               if (!mb)
05289                      return 0;
05290               free(mb);
05291 
05292               {
05293                      char *myrights=get_myrightson(curtoken->tokenbuf);
05294 
05295                      if (!strchr(myrights, ACL_LOOKUP[0]) &&
05296                          !strchr(myrights, ACL_READ[0]) &&
05297                          !strchr(myrights, ACL_INSERT[0]) &&
05298                          !strchr(myrights, ACL_CREATE[0]) &&
05299                          !strchr(myrights, ACL_DELETEFOLDER[0]) &&
05300                          !strchr(myrights, ACL_EXPUNGE[0]) &&
05301                          !strchr(myrights, ACL_ADMINISTER[0]))
05302                      {
05303                             free(myrights);
05304                             writes(tag);
05305                             accessdenied("GETACL", curtoken->tokenbuf,
05306                                         ACL_ADMINISTER);
05307                             return 0;
05308                      }
05309                      free(myrights);
05310               }
05311 
05312               if (get_acllist(&l, curtoken->tokenbuf,
05313                             &mailbox_owner) < 0)
05314               {
05315                      writes(tag);
05316                      writes(" NO Cannot retrieve ACLs for mailbox.\r\n");
05317                      return 0;
05318               }
05319 
05320               mb=my_strdup(curtoken->tokenbuf);
05321 
05322               switch ((curtoken=nexttoken_nouc())->tokentype) {
05323               case IT_QUOTED_STRING:
05324               case IT_ATOM:
05325               case IT_NUMBER:
05326                      break;
05327               default:
05328                      free(mb);
05329                      free(mailbox_owner);
05330                      maildir_aclt_list_destroy(&l);
05331                      return -1;
05332               }
05333 
05334               writes("* LISTRIGHTS \"");
05335               writeqs(mb);
05336               writes("\" \"");
05337               writeqs(curtoken->tokenbuf);
05338               writes("\"");
05339               free(mb);
05340 
05341 
05342               if (curtoken->tokenbuf[0] == '-' &&
05343                   (MAILDIR_ACL_ANYONE(curtoken->tokenbuf+1) ||
05344                    (strncmp(mailbox_owner, "user=", 5) == 0 &&
05345                     strcmp(curtoken->tokenbuf+1, mailbox_owner+5) == 0)))
05346               {
05347                      writes(" \"\" "
05348                             ACL_CREATE " "
05349                             ACL_DELETE_SPECIAL " "
05350                             ACL_INSERT " "
05351                             ACL_POST " "
05352                             ACL_READ " "
05353                             ACL_SEEN " "
05354                             ACL_WRITE "\r\n");
05355               }
05356               else if (strncmp(mailbox_owner, "user=", 5) == 0 &&
05357                       strcmp(curtoken->tokenbuf, mailbox_owner+5) == 0)
05358               {
05359                      writes(" \""
05360                             ACL_ADMINISTER
05361                             ACL_LOOKUP "\" "
05362                             ACL_CREATE " "
05363                             ACL_DELETE_SPECIAL " "
05364                             ACL_INSERT " "
05365                             ACL_POST " "
05366                             ACL_READ " "
05367                             ACL_SEEN " "
05368                             ACL_WRITE "\r\n");
05369               }
05370               else
05371               {
05372                      writes(" \"\" "
05373                             ACL_ADMINISTER " "
05374                             ACL_CREATE " "
05375                             ACL_DELETE_SPECIAL " "
05376                             ACL_INSERT " "
05377                             ACL_LOOKUP " "
05378                             ACL_POST " "
05379                             ACL_READ " "
05380                             ACL_SEEN " "
05381                             ACL_WRITE "\r\n");
05382               }
05383               writes(tag);
05384               writes(" OK LISTRIGHTS completed.\r\n");
05385               free(mailbox_owner);
05386               maildir_aclt_list_destroy(&l);
05387               return 0;
05388        }
05389 
05390        if (strcmp(curtoken->tokenbuf, "MYRIGHTS") == 0)
05391        {
05392               char *mb;
05393 
05394               curtoken=nexttoken_nouc();
05395 
05396               mb=parse_mailbox_error(tag, curtoken, 0, 0);
05397               if (!mb)
05398                      return 0;
05399               free(mb);
05400 
05401               {
05402                      char *myrights=get_myrightson(curtoken->tokenbuf);
05403 
05404                      if (!strchr(myrights, ACL_LOOKUP[0]) &&
05405                          !strchr(myrights, ACL_READ[0]) &&
05406                          !strchr(myrights, ACL_INSERT[0]) &&
05407                          !strchr(myrights, ACL_CREATE[0]) &&
05408                          !strchr(myrights, ACL_DELETEFOLDER[0]) &&
05409                          !strchr(myrights, ACL_EXPUNGE[0]) &&
05410                          !strchr(myrights, ACL_ADMINISTER[0]))
05411                      {
05412                             free(myrights);
05413                             writes(tag);
05414                             accessdenied("GETACL", curtoken->tokenbuf,
05415                                         ACL_ADMINISTER);
05416                             return 0;
05417                      }
05418                      free(myrights);
05419               }
05420 
05421               mb=get_myrightson(curtoken->tokenbuf);
05422 
05423               if (!mb)
05424               {
05425                      writes(tag);
05426                      writes(" NO Cannot retrieve ACLs for mailbox.\r\n");
05427                      return 0;
05428               }
05429 
05430               writes("* MYRIGHTS \"");
05431               writeqs(curtoken->tokenbuf);
05432               writes("\" \"");
05433 
05434               writeacl1(mb);
05435               free(mb);
05436               writes("\"\r\n");
05437               writes(tag);
05438               writes(" OK MYRIGHTS completed.\r\n");
05439               return 0;
05440        }
05441 
05442        /* mailbox commands */
05443 
05444        if (current_mailbox == 0)   return (-1);
05445 
05446        if (strcmp(curtoken->tokenbuf, "UID") == 0)
05447        {
05448               uid=1;
05449               if ((curtoken=nexttoken())->tokentype != IT_ATOM)
05450                      return (-1);
05451               if (strcmp(curtoken->tokenbuf, "COPY") &&
05452                   strcmp(curtoken->tokenbuf, "FETCH") &&
05453                   strcmp(curtoken->tokenbuf, "SEARCH") &&
05454                   strcmp(curtoken->tokenbuf, "THREAD") &&
05455                   strcmp(curtoken->tokenbuf, "SORT") &&
05456                   strcmp(curtoken->tokenbuf, "STORE") &&
05457                   strcmp(curtoken->tokenbuf, "EXPUNGE"))
05458                      return (-1);
05459        }
05460 
05461        if (strcmp(curtoken->tokenbuf, "CLOSE") == 0)
05462        {
05463               if (nexttoken()->tokentype != IT_EOL)
05464                      return (-1);
05465 
05466               if (!current_mailbox_ro
05467                   && strchr(current_mailbox_acl, ACL_EXPUNGE[0]))
05468                      expunge();
05469               free(current_mailbox);
05470               imapscan_free(&current_maildir_info);
05471               imapscan_init(&current_maildir_info);
05472               current_mailbox=0;
05473               writes(tag);
05474               writes(" OK mailbox closed.\r\n");
05475               return (0);
05476        }
05477 
05478        if (strcmp(curtoken->tokenbuf, "FETCH") == 0)
05479        {
05480        struct fetchinfo *fi;
05481        char   *msgset;
05482 
05483               curtoken=nexttoken();
05484               if (!ismsgset(curtoken))    return (-1);
05485               msgset=my_strdup(curtoken->tokenbuf);
05486 
05487               if ((curtoken=nexttoken())->tokentype != IT_LPAREN)
05488               {
05489                      if (curtoken->tokentype != IT_ATOM)
05490                      {
05491                             free(msgset);
05492                             return (-1);
05493                      }
05494                      fi=fetchinfo_alloc(1);
05495               }
05496               else
05497               {
05498                      (void)nexttoken();
05499                      fi=fetchinfo_alloc(0);
05500                      if (fi && currenttoken()->tokentype != IT_RPAREN)
05501                      {
05502                             fetchinfo_free(fi);
05503                             fi=0;
05504                      }
05505                      nexttoken();
05506               }
05507 
05508               if (fi == 0 || currenttoken()->tokentype != IT_EOL)
05509               {
05510                      free(msgset);
05511                      if (fi)       fetchinfo_free(fi);
05512                      return (-1);
05513               }
05514 
05515               do_msgset(msgset, &do_fetch, fi, uid);
05516               fetchinfo_free(fi);
05517               free(msgset);
05518               writes(tag);
05519               writes(" OK FETCH completed.\r\n");
05520               return (0);
05521        }
05522 
05523        if (strcmp(curtoken->tokenbuf, "STORE") == 0)
05524        {
05525        char   *msgset;
05526        struct storeinfo storeinfo_s;
05527 
05528               curtoken=nexttoken();
05529               if (!ismsgset(curtoken))    return (-1);
05530               msgset=my_strdup(curtoken->tokenbuf);
05531 
05532               (void)nexttoken();
05533               current_maildir_info.keywordList->keywordAddedRemoved=0;
05534 
05535               if (storeinfo_init(&storeinfo_s) ||
05536                      currenttoken()->tokentype != IT_EOL)
05537               {
05538                      if (storeinfo_s.keywords)
05539                             libmail_kwmDestroy(storeinfo_s.keywords);
05540                      free(msgset);
05541                      return (-1);
05542               }
05543 
05544               /* Do not change \Deleted if this is a readonly mailbox */
05545 
05546               if (current_mailbox_ro && storeinfo_s.flags.deleted)
05547               {
05548                      if (storeinfo_s.keywords)
05549                             libmail_kwmDestroy(storeinfo_s.keywords);
05550                      free(msgset);
05551                      writes(tag);
05552                      writes(" NO Current box is selected READ-ONLY.\r\n");
05553                      return (0);
05554               }
05555 
05556               fetch_free_cache();
05557 
05558               if (current_maildir_info.keywordList->keywordAddedRemoved)
05559                      mailboxflags(current_mailbox_ro);
05560 
05561               current_maildir_info.keywordList->keywordAddedRemoved=0;
05562 
05563               if (do_msgset(msgset, &do_store, &storeinfo_s, uid))
05564               {
05565                      if (storeinfo_s.keywords)
05566                             libmail_kwmDestroy(storeinfo_s.keywords);
05567                      free(msgset);
05568                      if (current_maildir_info.keywordList
05569                          ->keywordAddedRemoved)
05570                             mailboxflags(current_mailbox_ro);
05571 
05572                      writes(tag);
05573                      writes(" NO [ALERT] You exceeded your mail quota.\r\n");
05574                      return (0);
05575               }
05576               if (storeinfo_s.keywords)
05577               {
05578                      struct imap_addRemoveKeywordInfo imapInfo;
05579 
05580                      switch (storeinfo_s.plusminus) {
05581                      case '+':
05582                      case '-':
05583 
05584                             imapInfo.msgset=msgset;
05585                             imapInfo.uid=uid;
05586 
05587                             if (!fastkeywords() &&
05588                                 addRemoveKeywords(&imap_addRemoveKeywords,
05589                                                 &imapInfo, &storeinfo_s))
05590                             {
05591                                    libmail_kwmDestroy(storeinfo_s.keywords);
05592                                    free(msgset);
05593                                    writes(tag);
05594                                    writes(" NO An error occured while"
05595                                           " updating keywords: ");
05596                                    writes(strerror(errno));
05597                                    writes(".\r\n");
05598                                    return 0;
05599                             }
05600                             break;
05601                      }
05602 
05603                      libmail_kwmDestroy(storeinfo_s.keywords);
05604               }
05605               free(msgset);
05606               if (current_maildir_info.keywordList->keywordAddedRemoved)
05607                      mailboxflags(current_mailbox_ro);
05608               writes(tag);
05609               writes(" OK STORE completed.\r\n");
05610               return (0);
05611        }
05612 
05613        if (strcmp(curtoken->tokenbuf, "SEARCH") == 0)
05614        {
05615               char *charset=0;
05616               struct searchinfo *si, *sihead;
05617               unsigned long i;
05618 
05619               curtoken=nexttoken_okbracket();
05620               if (curtoken->tokentype == IT_ATOM &&
05621                      strcmp(curtoken->tokenbuf, "CHARSET") == 0)
05622               {
05623                      curtoken=nexttoken();
05624                      if (curtoken->tokentype != IT_ATOM &&
05625                             curtoken->tokentype != IT_QUOTED_STRING)
05626                             return (-1);
05627 
05628                      charset=my_strdup(curtoken->tokenbuf);
05629                      curtoken=nexttoken();
05630               }
05631 
05632               if (validate_charset(tag, &charset))
05633               {
05634                      if (charset)
05635                             free(charset);
05636                      return (0);
05637               }
05638 
05639               if ((si=alloc_parsesearch(&sihead)) == 0)
05640               {
05641                      free(charset);
05642                      return (-1);
05643               }
05644               if (currenttoken()->tokentype != IT_EOL)
05645               {
05646                      free(charset);
05647                      free_search(sihead);
05648                      return (-1);
05649               }
05650 
05651 #if 0
05652               writes("* OK ");
05653               debug_search(si);
05654               writes("\r\n");
05655 #endif
05656               writes("* SEARCH");
05657               dosearch(si, sihead, charset, uid);
05658               writes("\r\n");
05659               free(charset);
05660 
05661               for (i=0; i<current_maildir_info.nmessages; i++)
05662                      if (current_maildir_info.msgs[i].changedflags)
05663                             fetchflags(i);
05664               writes(tag);
05665               writes(" OK SEARCH done.\r\n");
05666               free_search(sihead);
05667               return (0);
05668        }
05669 
05670        if (strcmp(curtoken->tokenbuf, "THREAD") == 0)
05671        {
05672        char *charset=0;
05673        struct searchinfo *si, *sihead;
05674        unsigned long i;
05675 
05676               /* The following jazz is mainly for future extensions */
05677 
05678        void (*thread_func)(struct searchinfo *, struct searchinfo *,
05679                          const char *, int);
05680        search_type thread_type;
05681 
05682               {
05683               const char *p=getenv("IMAP_DISABLETHREADSORT");
05684               int n= p ? atoi(p):0;
05685 
05686                      if (n > 0)
05687                      {
05688                             writes(tag);
05689                             writes(" NO This command is disabled by the system administrator.\r\n");
05690                             return (0);
05691                      }
05692               }
05693 
05694               curtoken=nexttoken();
05695               if (curtoken->tokentype != IT_ATOM &&
05696                      curtoken->tokentype != IT_QUOTED_STRING)
05697                      return (-1);
05698 
05699               if (strcmp(curtoken->tokenbuf, "ORDEREDSUBJECT") == 0)
05700               {
05701                      thread_func=dothreadorderedsubj;
05702                      thread_type=search_orderedsubj;
05703               }
05704               else if (strcmp(curtoken->tokenbuf, "REFERENCES") == 0)
05705               {
05706                      thread_func=dothreadreferences;
05707                      thread_type=search_references1;
05708               }
05709               else
05710               {
05711                      return (-1);
05712               }
05713 
05714               curtoken=nexttoken();
05715               if (curtoken->tokentype != IT_ATOM &&
05716                      curtoken->tokentype != IT_QUOTED_STRING)
05717                      return (-1);
05718 
05719               charset=my_strdup(curtoken->tokenbuf);
05720               curtoken=nexttoken();
05721 
05722               if ((si=alloc_parsesearch(&sihead)) == 0)
05723               {
05724                      if (charset)  free(charset);
05725                      return (-1);
05726               }
05727 
05728               si=alloc_searchextra(si, &sihead, thread_type);
05729 
05730               if (currenttoken()->tokentype != IT_EOL)
05731               {
05732                      if (charset)  free(charset);
05733                      free_search(sihead);
05734                      return (-1);
05735               }
05736 
05737               if (validate_charset(tag, &charset))
05738               {
05739                      if (charset)
05740                             free(charset);
05741                      return (0);
05742               }
05743 
05744               writes("* THREAD ");
05745               (*thread_func)(si, sihead, charset, uid);
05746               writes("\r\n");
05747               free(charset);
05748 
05749               for (i=0; i<current_maildir_info.nmessages; i++)
05750                      if (current_maildir_info.msgs[i].changedflags)
05751                             fetchflags(i);
05752               writes(tag);
05753               writes(" OK THREAD done.\r\n");
05754               free_search(sihead);
05755               return (0);
05756        }
05757 
05758        if (strcmp(curtoken->tokenbuf, "SORT") == 0)
05759        {
05760        char *charset=0;
05761        struct searchinfo *si, *sihead;
05762        unsigned long i;
05763        struct temp_sort_stack *ts=0;
05764 
05765               {
05766               const char *p=getenv("IMAP_DISABLETHREADSORT");
05767               int n= p ? atoi(p):0;
05768 
05769                      if (n > 0)
05770                      {
05771                             writes(tag);
05772                             writes(" NO This command is disabled by the system administrator.\r\n");
05773                             return (0);
05774                      }
05775               }
05776 
05777               curtoken=nexttoken();
05778               if (curtoken->tokentype != IT_LPAREN)     return (-1);
05779               while ((curtoken=nexttoken())->tokentype != IT_RPAREN)
05780               {
05781               search_type st;
05782               struct temp_sort_stack *newts;
05783 
05784                      if (curtoken->tokentype != IT_ATOM &&
05785                             curtoken->tokentype != IT_QUOTED_STRING)
05786                      {
05787                             free_temp_sort_stack(ts);
05788                             return (-1);
05789                      }
05790 
05791                      if (strcmp(curtoken->tokenbuf, "SUBJECT") == 0)
05792                      {
05793                             st=search_orderedsubj;
05794                      }
05795                      else if (strcmp(curtoken->tokenbuf, "ARRIVAL") == 0)
05796                      {
05797                             st=search_arrival;
05798                      }
05799                      else if (strcmp(curtoken->tokenbuf, "CC") == 0)
05800                      {
05801                             st=search_cc;
05802                      }
05803                      else if (strcmp(curtoken->tokenbuf, "DATE") == 0)
05804                      {
05805                             st=search_date;
05806                      }
05807                      else if (strcmp(curtoken->tokenbuf, "FROM") == 0)
05808                      {
05809                             st=search_from;
05810                      }
05811                      else if (strcmp(curtoken->tokenbuf, "REVERSE") == 0)
05812                      {
05813                             st=search_reverse;
05814                      }
05815                      else if (strcmp(curtoken->tokenbuf, "SIZE") == 0)
05816                      {
05817                             st=search_size;
05818                      }
05819                      else if (strcmp(curtoken->tokenbuf, "TO") == 0)
05820                      {
05821                             st=search_to;
05822                      }
05823                      else
05824                      {
05825                             free_temp_sort_stack(ts);
05826                             return (-1);
05827                      }
05828 
05829                      newts=(struct temp_sort_stack *)malloc(
05830                             sizeof(struct temp_sort_stack));
05831                      if (!newts)   write_error_exit(0);
05832                      newts->next=ts;
05833                      newts->type=st;
05834                      ts=newts;
05835               }
05836 
05837               if (ts == 0   /* No criteria */
05838                      || ts->type == search_reverse)
05839                             /* Can't end with the REVERSE keyword */
05840               {
05841                      free_temp_sort_stack(ts);
05842                      return (-1);
05843               }
05844 
05845               curtoken=nexttoken();
05846               if (curtoken->tokentype != IT_ATOM &&
05847                      curtoken->tokentype != IT_QUOTED_STRING)
05848               {
05849                      free_temp_sort_stack(ts);
05850                      return (-1);
05851               }
05852 
05853               charset=my_strdup(curtoken->tokenbuf);
05854               curtoken=nexttoken();
05855 
05856               if ((si=alloc_parsesearch(&sihead)) == 0)
05857               {
05858                      if (charset)  free(charset);
05859                      free_temp_sort_stack(ts);
05860                      return (-1);
05861               }
05862 
05863               while (ts)
05864               {
05865               struct temp_sort_stack *cts=ts;
05866 
05867                      ts=cts->next;
05868                      si=alloc_searchextra(si, &sihead, cts->type);
05869                      free(cts);
05870               }
05871 
05872               if (currenttoken()->tokentype != IT_EOL)
05873               {
05874                      if (charset)  free(charset);
05875                      free_search(sihead);
05876                      return (-1);
05877               }
05878 
05879               if (validate_charset(tag, &charset))
05880               {
05881                      if (charset) free(charset);
05882                      free_search(sihead);
05883                      return (0);
05884               }
05885 
05886               writes("* SORT");
05887               dosortmsgs(si, sihead, charset, uid);
05888               writes("\r\n");
05889               free(charset);
05890 
05891               for (i=0; i<current_maildir_info.nmessages; i++)
05892                      if (current_maildir_info.msgs[i].changedflags)
05893                             fetchflags(i);
05894               writes(tag);
05895               writes(" OK SORT done.\r\n");
05896               free_search(sihead);
05897               return (0);
05898        }
05899 
05900        if (strcmp(curtoken->tokenbuf, "CHECK") == 0)
05901        {
05902               if (nexttoken()->tokentype != IT_EOL)     return (-1);
05903               doNoop(0);
05904               writes(tag);
05905               writes(" OK CHECK completed\r\n");
05906               return (0);
05907        }
05908 
05909        if (strcmp(curtoken->tokenbuf, "EXPUNGE") == 0)
05910        {
05911               if (strchr(current_mailbox_acl, ACL_EXPUNGE[0]) == NULL)
05912               {
05913                      writes(tag);
05914                      accessdenied("EXPUNGE", "current mailbox",
05915                                  ACL_EXPUNGE);
05916                      return 0;
05917               }
05918 
05919               if (current_mailbox_ro)
05920               {
05921                      writes(tag);
05922                      writes(" NO Cannot expunge read-only mailbox.\r\n");
05923                      return 0;
05924               }
05925 
05926               if (uid)
05927               {
05928                      char *msgset;
05929 
05930                      curtoken=nexttoken();
05931                      if (!ismsgset(curtoken))    return (-1);
05932                      msgset=my_strdup(curtoken->tokenbuf);
05933                      if (nexttoken()->tokentype != IT_EOL)     return (-1);
05934 
05935                      do_msgset(msgset, &uid_expunge, NULL, 1);
05936                      free(msgset);
05937               }
05938               else
05939               {
05940                      if (nexttoken()->tokentype != IT_EOL)     return (-1);
05941                      expunge();
05942               }
05943               doNoop(0);
05944               writes(tag);
05945               writes(" OK EXPUNGE completed\r\n");
05946               return (0);
05947        }
05948 
05949        if (strcmp(curtoken->tokenbuf, "COPY") == 0)
05950        {
05951        struct maildirsize quotainfo;
05952        char   *mailbox;
05953        char   *msgset;
05954        struct copyquotainfo cqinfo;
05955        int    has_quota;
05956        int    isshared;
05957        struct do_copy_info copy_info;
05958        unsigned long copy_uidv;
05959        char access_rights[8];
05960 
05961               curtoken=nexttoken();
05962               if (!ismsgset(curtoken))    return (-1);
05963               msgset=my_strdup(curtoken->tokenbuf);
05964 
05965               curtoken=nexttoken_nouc();
05966 
05967               if (curtoken->tokentype != IT_NUMBER &&
05968                      curtoken->tokentype != IT_ATOM &&
05969                      curtoken->tokentype != IT_QUOTED_STRING)
05970               {
05971                      free(msgset);
05972                      return (-1);
05973               }
05974 
05975               mailbox=decode_valid_mailbox(curtoken->tokenbuf, 1);
05976 
05977               if (!mailbox)
05978               {
05979                      struct maildir_info mi;
05980 
05981                      free(msgset);
05982 
05983                      if (maildir_info_imap_find(&mi, curtoken->tokenbuf,
05984                                              getenv("AUTHENTICATED"))
05985                          == 0)
05986                      {
05987                             if (nexttoken()->tokentype == IT_EOL)
05988                             {
05989                                    maildir_info_destroy(&mi);
05990                                    writes(tag);
05991                                    writes(" NO [TRYCREATE] Mailbox does not exist.\r\n");
05992                                    return (0);
05993                             }
05994                             maildir_info_destroy(&mi);
05995                      }
05996                      return (-1);
05997               }
05998 
05999               {
06000                      CHECK_RIGHTSM(curtoken->tokenbuf,
06001                                   append_rights,
06002                                   ACL_INSERT ACL_DELETEMSGS
06003                                   ACL_SEEN ACL_WRITE);
06004                      
06005                      if (strchr(append_rights, ACL_INSERT[0]) == NULL)
06006                      {
06007                             writes(tag);
06008                             accessdenied("COPY",
06009                                         curtoken->tokenbuf,
06010                                         ACL_INSERT);
06011                             return 0;
06012                      }
06013 
06014                      strcpy(access_rights, append_rights);
06015               }
06016 
06017               if (nexttoken()->tokentype != IT_EOL)
06018               {
06019                      free(msgset);
06020                      return (-1);
06021               }
06022 
06023               if (access(mailbox, 0))
06024               {
06025                      writes(tag);
06026                      writes(" NO [TRYCREATE] Mailbox does not exist.\r\n");
06027                      free(msgset);
06028                      free(mailbox);
06029                      return (0);
06030               }
06031 
06032               fetch_free_cache();
06033               cqinfo.destmailbox=mailbox;
06034               cqinfo.acls=access_rights;
06035 
06036               /*
06037               ** If the destination is a shared folder, copy it into the
06038               ** real shared folder.
06039               */
06040 
06041               isshared=0;
06042               if (is_sharedsubdir(cqinfo.destmailbox))
06043               {
06044               char   *p=malloc(strlen(cqinfo.destmailbox)+sizeof("/shared"));
06045 
06046                      if (!p)       write_error_exit(0);
06047                      strcat(strcpy(p, cqinfo.destmailbox), "/shared");
06048 
06049                      free(mailbox);
06050                      mailbox=cqinfo.destmailbox=p;
06051                      isshared=1;
06052               }
06053 
06054               cqinfo.nbytes=0;
06055               cqinfo.nfiles=0;
06056 
06057               has_quota=0;
06058               if (!isshared && maildirquota_countfolder(cqinfo.destmailbox))
06059               {
06060                      if (maildir_openquotafile(&quotainfo,
06061                                             ".") == 0)
06062                      {
06063                             if (quotainfo.fd >= 0)
06064                                    has_quota=1;
06065                             maildir_closequotafile(&quotainfo);
06066                      }
06067 
06068                      if (has_quota > 0 &&
06069                          do_msgset(msgset, &do_copy_quota_calc, &cqinfo,
06070                                   uid))
06071                             has_quota= -1;
06072               }
06073 
06074               if (has_quota > 0 && cqinfo.nfiles > 0)
06075               {
06076 
06077                      if (maildir_quota_add_start(".", &quotainfo,
06078                                               cqinfo.nbytes,
06079                                               cqinfo.nfiles,
06080                                               getenv("MAILDIRQUOTA")))
06081                      {
06082                             writes(tag);
06083                             writes(
06084                      " NO [ALERT] You exceeded your mail quota.\r\n");
06085                             free(msgset);
06086                             free(mailbox);
06087                             return (0);
06088                      }
06089 
06090                      maildir_quota_add_end(&quotainfo,
06091                                          cqinfo.nbytes,
06092                                          cqinfo.nfiles);
06093               }
06094 
06095               if (is_outbox(mailbox))
06096               {
06097                      int counter=0;
06098 
06099                      if (do_msgset(msgset, &do_count, &counter, uid) ||
06100                          counter > 1)
06101                      {
06102                             writes(tag);
06103                             writes(" NO [ALERT] Only one message may be sent at a time.\r\n");
06104                             free(msgset);
06105                             free(mailbox);
06106                             return (0);
06107                      }
06108               }
06109 
06110               copy_info.mailbox=mailbox;
06111               copy_info.uidplus_list=NULL;
06112               copy_info.uidplus_tail= &copy_info.uidplus_list;
06113               copy_info.acls=access_rights;
06114 
06115               if (has_quota < 0 ||
06116                   do_msgset(msgset, &do_copy_message, &copy_info, uid) ||
06117                   uidplus_fill(copy_info.mailbox, copy_info.uidplus_list,
06118                              &copy_uidv))
06119               {
06120                      uidplus_abort(copy_info.uidplus_list);
06121                      writes(tag);
06122                      writes(" NO [ALERT] COPY failed - no write permission or out of disk space.\r\n");
06123                      free(msgset);
06124                      free(mailbox);
06125                      return (0);
06126               }
06127 
06128               dirsync(mailbox);
06129 
06130               writes(tag);
06131               writes(" OK");
06132 
06133               if (copy_info.uidplus_list != NULL)
06134               {
06135                      writes(" [COPYUID ");
06136                      writen(copy_uidv);
06137                      uidplus_writemsgset(copy_info.uidplus_list, 0);
06138                      uidplus_writemsgset(copy_info.uidplus_list, 1);
06139                      writes("]");
06140               }
06141 
06142               writes(" COPY completed.\r\n");
06143               uidplus_free(copy_info.uidplus_list);
06144 
06145               free(msgset);
06146               free(mailbox);
06147               return (0);
06148        }
06149        return (-1);
06150 }
06151 
06152 static void dogethostname()
06153 {
06154 char   buf[2048];
06155 char   *p;
06156 
06157        if (gethostname(buf, sizeof(buf)) < 0)
06158               strcpy(buf, "courier-imap");
06159        p=malloc(strlen(buf)+sizeof("HOSTNAME="));
06160        if (!p)
06161               write_error_exit(0);
06162        strcat(strcpy(p, "HOSTNAME="), buf);
06163        putenv(p);
06164 }
06165 
06166 #if 0
06167 static char *getcurdir()
06168 {
06169 char   *pathbuf=0;
06170 size_t pathlen=256;
06171 
06172        for (;;)
06173        {
06174               if ((pathbuf=pathbuf ? realloc(pathbuf, pathlen):
06175                      malloc(pathlen)) == 0)
06176                      write_error_exit(0);
06177               if (getcwd(pathbuf, pathlen-1))
06178                      return (pathbuf);
06179               if (errno != ERANGE)
06180               {
06181                      free(pathbuf);
06182                      return (0);
06183               }
06184               pathlen += 256;
06185        }
06186 }
06187 
06188 static char *getimapscanpath(const char *argv0)
06189 {
06190 char   *p, *q;
06191 
06192        if (*argv0 != '/')
06193        {
06194               p=getcurdir();
06195               if (!p)
06196               {
06197                      perror("getcwd");
06198                      exit(1);
06199               }
06200               q=malloc(strlen(p)+strlen(argv0)+sizeof("//imapscan"));
06201               if (!q)       write_error_exit(0);
06202               strcat(strcat(strcpy(q, p), "/"), argv0);
06203        }
06204        else
06205        {
06206               q=malloc(strlen(argv0)+sizeof("imapscan"));
06207               if (!q)       write_error_exit(0);
06208               strcpy(q, argv0);
06209        }
06210        p=strrchr(q, '/');
06211        if (p && p[1] == 0)
06212        {
06213               *p=0;
06214               p=strrchr(q, '/');
06215        }
06216        
06217        if (p)
06218               p[1]=0;
06219        else   *q=0;
06220 
06221        strcat(q, "imapscan");
06222        return (q);
06223 }
06224 #endif
06225 
06226 static void chkdisabled(const char *ip, const char *port)
06227 {
06228        const char *p;
06229        if (auth_getoptionenvint("disableimap"))
06230        {
06231               writes("* BYE IMAP access disabled for this account.\r\n");
06232               writeflush();
06233               exit(0);
06234        }
06235 
06236        if (    auth_getoptionenvint("disableinsecureimap")
06237            && ((p=getenv("IMAP_TLS")) == NULL || !atoi(p)))
06238        {
06239               writes("* BYE IMAP access disabled via insecure connection.\r\n");
06240               writeflush();
06241               exit(0);
06242        }
06243 
06244        fprintf(stderr, "INFO: LOGIN, user=%s, ip=[%s], port=[%s], protocol=%s\n",
06245               getenv("AUTHENTICATED"), ip, port,
06246               protocol);
06247 }
06248 
06249 static int chk_clock_skew()
06250 {
06251        static const char fn[]="tmp/courier-imap.clockskew.chk";
06252        struct stat stat_buf;
06253        int fd;
06254        time_t t;
06255 
06256        unlink(fn);
06257        fd=open(fn, O_RDWR|O_TRUNC|O_CREAT, 0666);
06258        time(&t);
06259 
06260        if (fd < 0)
06261               return 0; /* Something else is wrong */
06262 
06263        if (fstat(fd, &stat_buf) < 0)
06264        {
06265               close(fd);
06266               return -1; /* Something else is wrong */
06267        }
06268        close(fd);
06269        unlink(fn);
06270 
06271        if (stat_buf.st_mtime < t - 30 || stat_buf.st_mtime > t+30)
06272               return -1;
06273        return 0;
06274 }
06275 
06276 #if SMAP
06277 
06278 static int is_smap()
06279 {
06280        const char *p;
06281 
06282        p=getenv("PROTOCOL");
06283 
06284        if (p && strcmp(p, "SMAP1") == 0)
06285               return 1;
06286        return 0;
06287 }
06288 
06289 #else
06290 
06291 #define is_smap() 0
06292 
06293 #endif
06294 
06295 
06296 int main(int argc, char **argv)
06297 {
06298        const char *ip;
06299        const char *p;
06300        const char *tag;
06301        const char *port;
06302        mode_t oldumask;
06303 
06304 #ifdef HAVE_SETVBUF_IOLBF
06305        setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
06306 #endif
06307        time(&start_time);
06308        if (argc > 1 && strcmp(argv[1], "--version") == 0)
06309        {
06310               printf("%s\n", PROGRAMVERSION);
06311               exit(0);
06312        }
06313 
06314        if ((tag=getenv("IMAPLOGINTAG")) != 0)
06315        {
06316               if (getenv("AUTHENTICATED") == NULL)
06317               {
06318                      printf("* BYE AUTHENTICATED environment variable not set.\r\n");
06319                      fflush(stdout);
06320                      exit(0);
06321               }
06322        }
06323        else
06324        {
06325               const char *p;
06326 
06327               putenv("TCPREMOTEIP=127.0.0.1");
06328               putenv("TCPREMOTEPORT=0");
06329 
06330               p=getenv("AUTHENTICATED");
06331               if (!p || !*p)
06332               {
06333                      struct passwd *pw=getpwuid(getuid());
06334                      char *me;
06335 
06336                      if (!pw)
06337                      {
06338                             fprintf(stderr,
06339                                    "ERR: uid %lu not found in passwd file\n",
06340                                    (unsigned long)getuid());
06341                             exit(1);
06342                      }
06343 
06344                      me=malloc(sizeof("AUTHENTICATED=")+strlen(pw->pw_name));
06345                      if (!me)
06346                             write_error_exit(0);
06347 
06348                      strcat(strcpy(me, "AUTHENTICATED="), pw->pw_name);
06349                      putenv(me);
06350               }
06351        }
06352 
06353 #if HAVE_SETLOCALE
06354        setlocale(LC_CTYPE, "C");
06355 #endif
06356 
06357        ip=getenv("TCPREMOTEIP");
06358        if (!ip || !*ip)     exit(0);
06359 
06360        port=getenv("TCPREMOTEPORT");
06361        if (!port || !*port) exit(0);
06362 
06363        protocol=getenv("PROTOCOL");
06364 
06365        if (!protocol || !*protocol)
06366               protocol="IMAP";
06367 
06368        putenv("IMAP_STARTTLS=NO"); /* No longer grok STARTTLS */
06369 
06370        /* We use select() with a timeout, so use non-blocking filedescs */
06371 
06372        if (fcntl(0, F_SETFL, O_NONBLOCK) ||
06373            fcntl(1, F_SETFL, O_NONBLOCK))
06374        {
06375               perror("fcntl");
06376               exit(1);
06377        }
06378 
06379        {
06380        struct stat   buf;
06381 
06382               if ( stat(".", &buf) < 0 || buf.st_mode & S_ISVTX)
06383               {
06384                      fprintf(stderr, "INFO: LOCKED, user=%s, ip=[%s], port=[%s]\n",
06385                             getenv("AUTHENTICATED"), ip, port);
06386 
06387                      if (is_smap())
06388                             writes("-ERR ");
06389                      else
06390                             writes("* BYE ");
06391 
06392                      writes("Your account is temporarily unavailable (+t bit set on home directory).\r\n");
06393                      writeflush();
06394                      exit(0);
06395               }
06396        }
06397 
06398        if (argc > 1)
06399               p=argv[1];
06400        else
06401               p=getenv("MAILDIR");
06402 
06403        if (!p)
06404               p="./Maildir";
06405 #if 0
06406        imapscanpath=getimapscanpath(argv[0]);
06407 #endif
06408        if (chdir(p))
06409        {
06410               fprintf(stderr, "chdir %s: %s\n", p, strerror(errno));
06411               write_error_exit(strerror(errno));
06412        }
06413        maildir_loginexec();
06414 
06415        if (auth_getoptionenvint("disableshared"))
06416        {
06417               maildir_acl_disabled=1;
06418               maildir_newshared_disabled=1;
06419        }
06420 
06421        /* Remember my device/inode */
06422 
06423        {
06424               struct stat   buf;
06425 
06426               if ( stat(".", &buf) < 0)
06427                      write_error_exit("Cannot stat current directory");
06428 
06429               homedir_dev=buf.st_dev;
06430               homedir_ino=buf.st_ino;
06431 
06432               errno=0;
06433 
06434               p=getenv("IMAP_MAILBOX_SANITY_CHECK");
06435 
06436               if (!p || !*p) p="1";
06437 
06438               if (atoi(p))
06439               {
06440                      if ( buf.st_uid != geteuid() ||
06441                           buf.st_gid != getegid())
06442                             write_error_exit("Account's mailbox directory is not owned by the correct uid or gid");
06443               }
06444        }
06445 
06446        p=getenv("HOSTNAME");
06447        if (!p)
06448               dogethostname();
06449 
06450        if ((p=getenv("IMAP_TRASHFOLDERNAME")) != 0 && *p) 
06451        {
06452               trash = strdup(p);
06453               dot_trash = malloc(strlen(trash) + 2);
06454               dot_trash[0] = '.';
06455               strcpy(&dot_trash[1], trash); 
06456        }
06457        
06458 #if 0
06459        mdcreate("." DRAFTS);
06460 #endif
06461 
06462        if ((p=getenv("IMAPDEBUGFILE")) != 0 && *p &&
06463            access(p, 0) == 0)
06464        {
06465               oldumask = umask(027);
06466               debugfile=fopen(p, "a");
06467               umask(oldumask);
06468               if (debugfile==NULL)
06469                      write_error_exit(0);
06470        }
06471        initcapability();
06472 
06473        emptytrash();
06474        signal(SIGPIPE, SIG_IGN);
06475 
06476        libmail_kwVerbotten=KEYWORD_IMAPVERBOTTEN;
06477        libmail_kwCaseSensitive=0;
06478 
06479        if (!keywords())
06480               libmail_kwEnabled=0;
06481 
06482        maildir_info_munge_complex((p=getenv("IMAP_SHAREDMUNGENAMES")) &&
06483                                atoi(p));
06484 
06485 #if SMAP
06486        if (is_smap())
06487        {
06488               if (chk_clock_skew())
06489               {
06490                      writes("-ERR Clock skew detected. Check the clock on the file server\r\n");
06491                      writeflush();
06492                      exit(0);
06493               }
06494 
06495               writes("+OK SMAP1 LOGIN Ok.\n");
06496 
06497               smapflag=1;
06498 
06499               libmail_kwVerbotten=KEYWORD_SMAPVERBOTTEN;
06500               libmail_kwCaseSensitive=1;
06501 
06502               chkdisabled(ip, port);
06503               smap();
06504               logoutmsg();
06505               emptytrash();
06506               return (0);
06507        }
06508 #endif
06509 
06510        if (chk_clock_skew())
06511        {
06512               writes("* BYE Clock skew detected. Check the clock on the file server\r\n");
06513               writeflush();
06514               exit(0);
06515        }
06516 
06517        {
06518               struct maildirwatch *w;
06519 
06520               if ((w=maildirwatch_alloc(".")) == NULL)
06521               {
06522                      writes("* OK [ALERT] Filesystem notification initialization error -- contact your mail administrator (check for configuration errors with the FAM/Gamin library)\r\n");
06523               }
06524               else
06525               {
06526                      maildirwatch_free(w);
06527               }
06528        }
06529 
06530        if ((tag=getenv("IMAPLOGINTAG")) != 0)
06531        {
06532               writes(tag);
06533               writes(" OK LOGIN Ok.\r\n");
06534        }
06535        else
06536               writes("* PREAUTH Ready.\r\n");
06537        writeflush();
06538        chkdisabled(ip, port);
06539        imapscan_init(&current_maildir_info);
06540        mainloop();
06541        fetch_free_cached();
06542        bye();
06543        return (0);
06544 }