/* ** Copyright 2003-2011 Double Precision, Inc. ** See COPYING for distribution information. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_UTIME_H #include #endif #if TIME_WITH_SYS_TIME #include #include #else #if HAVE_SYS_TIME_H #include #else #include #endif #endif #if HAVE_LOCALE_H #include #endif #include #include #include "mysignal.h" #include "imapd.h" #include "fetchinfo.h" #include "searchinfo.h" #include "storeinfo.h" #include "mailboxlist.h" #include "thread.h" #include "outbox.h" #include "imapwrite.h" #include "imaptoken.h" #include "imapscanclient.h" #include "searchinfo.h" #include "maildir/config.h" #include "maildir/maildircreate.h" #include "maildir/maildirrequota.h" #include "maildir/maildirgetquota.h" #include "maildir/maildirquota.h" #include "maildir/maildirmisc.h" #include "maildir/maildirwatch.h" #include "maildir/maildiraclt.h" #include "maildir/maildirnewshared.h" #include "maildir/maildirinfo.h" #include #include "rfc2045/rfc2045.h" #include "rfc822/rfc822.h" #define SMAP_BUFSIZ 8192 #define SHARED "shared" #define LIST_FOLDER 1 #define LIST_DIRECTORY 2 #define FETCH_UID 1 #define FETCH_SIZE 2 #define FETCH_FLAGS 4 #define FETCH_KEYWORDS 8 #define FETCH_INTERNALDATE 16 extern dev_t homedir_dev; extern ino_t homedir_ino; int mdcreate(const char *mailbox); int mddelete(const char *s); extern int folder_rename(struct maildir_info *mi1, struct maildir_info *mi2, const char **errmsg); extern int current_temp_fd; extern const char *current_temp_fn; extern int snapshot_init(const char *, const char *); extern int keywords(); extern unsigned long header_count, body_count; extern char *compute_myrights(maildir_aclt_list *l, const char *l_owner); extern int addRemoveKeywords(int (*callback_func)(void *, void *), void *callback_func_arg, struct storeinfo *storeinfo_s); extern int doAddRemoveKeywords(unsigned long n, int uid, void *vp); extern void snapshot_select(int); extern void doflags(FILE *fp, struct fetchinfo *fi, struct imapscaninfo *i, unsigned long msgnum, struct rfc2045 *mimep); extern void set_time(const char *tmpname, time_t timestamp); extern int imapenhancedidle(void); extern void imapidle(void); extern void expunge(); extern void doNoop(int); extern int do_store(unsigned long, int, void *); extern int reflag_filename(struct imapscanmessageinfo *mi, struct imapflags *flags, int fd); extern void do_expunge(unsigned long expunge_start, unsigned long expunge_end, int force); extern char *current_mailbox, *current_mailbox_acl; static int current_mailbox_shared; extern struct imapscaninfo current_maildir_info; extern void get_message_flags(struct imapscanmessageinfo *, char *, struct imapflags *); extern void fetchflags(unsigned long); extern int acl_lock(const char *homedir, int (*func)(void *), void *void_arg); extern void aclminimum(const char *); struct rfc2045 *fetch_alloc_rfc2045(unsigned long, FILE *); FILE *open_cached_fp(unsigned long); void fetch_free_cache(); extern FILE *maildir_mkfilename(const char *mailbox, struct imapflags *flags, unsigned long s, char **tmpname, char **newname); /* ** Parse a word from the current SMAP command. */ static char *getword(char **ptr) { char *p= *ptr, *q, *r; while (*p && isspace((int)(unsigned char)*p)) p++; if (*p != '"') { for (q=p; *q; q++) { if (isspace((int)(unsigned char)*q)) { *q++=0; break; } } *ptr=q; return p; } ++p; r=q=p; while (*r) { if (*r == '"') { if (r[1] == '"') { r += 2; *q++='"'; continue; } ++r; break; } *q++ = *r++; } *q=0; *ptr=r; return p; } #define UC(c) if ( (c) >= 'a' && (c) <= 'z') (c) += 'A' - 'a' static void up(char *p) { while (*p) { UC(*p); p++; } } /* ** Write a WORD reply. */ static void smapword_s(const char *w); void smapword(const char *w) { writes("\""); smapword_s(w); writes("\""); } static void smapword_s(const char *w) { while (w && *w) { size_t i; for (i=0; w[i]; i++) if (w[i] == '"') break; if (i) writemem(w, i); w += i; if (*w) { writes("\"\""); ++w; } } } struct fn_word_list { struct fn_word_list *next; char *w; }; /* ** Create a folder word array by reading words from the SMAP command. */ static char **fn_fromwords(char **ptr) { struct fn_word_list *h=NULL, *n, **t=&h; size_t cnt=0; char *p; char **fn; while (*(p=getword(ptr))) { n=malloc(sizeof(struct fn_word_list)); if (!n || !(n->w=strdup(p))) { if (n) free(n); while ((n=h) != NULL) { h=n->next; free(n->w); free(n); } return NULL; } n->next=NULL; *t=n; t= &n->next; cnt++; } if (!h) { errno=EINVAL; return NULL; } fn=malloc((cnt+1)*sizeof(char *)); cnt=0; while ((n=h) != NULL) { h=n->next; if (fn) fn[cnt]=n->w; else free(n->w); free(n); cnt++; } if (fn) fn[cnt]=0; return fn; } /* ** LIST-related functions. */ struct list_hier { struct list_hier *next; char *hier; int flags; }; struct list_callback_info { struct list_hier *hier; /* Hierarchy being listed */ struct list_hier *found; }; static void list(char *folder, const char *descr, int type) { writes("* LIST "); smapword(folder); writes(" "); smapword(descr); if (type & LIST_FOLDER) writes(" FOLDER"); if (type & LIST_DIRECTORY) writes(" DIRECTORY"); writes("\n"); } /* ** Callback from maildir_list. f="INBOX.folder.name" ** */ struct list_callback_utf8 { void (*callback_func)(const char *, char **, void *); void *callback_arg; const char *homedir; const char *owner; }; static void list_callback(const char *f, void *vp) { struct list_callback_utf8 *utf8=(struct list_callback_utf8 *)vp; maildir_aclt_list l; char **fn=maildir_smapfn_fromutf8(f); if (!fn) { perror(f); return; } if (maildir_acl_read(&l, utf8->homedir, strchr(f, '.')) == 0) { char *myrights; char *owner=malloc(sizeof("user=")+strlen(utf8->owner)); if (!owner) write_error_exit(0); strcat(strcpy(owner, "user="), utf8->owner); myrights=compute_myrights(&l, owner); free(owner); if (myrights && strchr(myrights, ACL_LOOKUP[0]) != NULL) (*utf8->callback_func)(f, fn, utf8->callback_arg); if (myrights) free(myrights); maildir_aclt_list_destroy(&l); } maildir_smapfn_free(fn); } /* ** list_callback callback that accumulates existing folders beneath a ** certain hierarchy. */ static void list_utf8_callback(const char *n, char **f, void *vp) { struct list_callback_info *lci=(struct list_callback_info *)vp; struct list_hier *h=lci->hier; for (;;) { if (!*f) return; if (h) { if (strcmp(h->hier, *f)) break; h=h->next; f++; continue; } for (h=lci->found; h; h=h->next) { if (strcmp(h->hier, *f) == 0) break; } if (!h) { if ((h=malloc(sizeof(struct list_hier))) == NULL || (h->hier=strdup(*f)) == NULL) { if (h) free(h); perror("malloc"); break; } h->next=lci->found; lci->found=h; h->flags=0; } if (f[1]) h->flags |= LIST_DIRECTORY; else h->flags |= LIST_FOLDER; break; } } /* ** SMAP1 list command goes here. Dirty hack: build the hierarchy list on ** the stack. */ static void do_listcmd(struct list_hier **head, struct list_hier **tail, char **ptr); static void listcmd(struct list_hier **head, struct list_hier **tail, char **ptr) { char *p; if (*(p=getword(ptr))) { struct list_hier node; node.next=NULL; node.hier=p; *tail= &node; listcmd(head, &node.next, ptr); return; } do_listcmd(head, tail, ptr); } struct smap_find_info { char *homedir; char *maildir; }; static int smap_find_cb(struct maildir_newshared_enum_cb *cb); static int smap_list_cb(struct maildir_newshared_enum_cb *cb); static int read_acls(maildir_aclt_list *aclt_list, struct maildir_info *minfo); static void do_listcmd(struct list_hier **head, struct list_hier **tail, char **ptr) { struct list_hier *p; size_t cnt; char **vecs; int hierlist=0; if (!*head) /* No arguments to LIST */ { list(INBOX, "New Mail", LIST_FOLDER); list(INBOX, "Folders", LIST_DIRECTORY); list(PUBLIC, "Public Folders", LIST_DIRECTORY); } else { struct list_callback_info lci; struct list_callback_utf8 list_utf8_info; list_utf8_info.callback_func= &list_utf8_callback; list_utf8_info.callback_arg= &lci; lci.hier= *head; lci.found=NULL; if (strcmp(lci.hier->hier, PUBLIC) == 0) { struct maildir_shindex_cache *curcache; struct list_hier *p=lci.hier->next; struct smap_find_info sfi; int eof; char *d; curcache=maildir_shared_cache_read(NULL, NULL, NULL); while (curcache && p) { size_t i; int rc; struct list_hier inbox; for (i=0; inrecords; i++) if (strcmp(curcache->records[i].name, p->hier) == 0) break; if (i >= curcache->nrecords) { curcache=NULL; break; } sfi.homedir=NULL; sfi.maildir=NULL; curcache->indexfile.startingpos= curcache->records[i].offset; rc=maildir_newshared_nextAt(&curcache ->indexfile, &eof, smap_find_cb, &sfi); if (rc || eof) { fprintf(stderr, "ERR: Internal error -" " maildir_newshared_nextAt: %s\n", strerror(errno)); curcache=NULL; break; } if (!sfi.homedir) { curcache= maildir_shared_cache_read(curcache, sfi.maildir, p->hier); p=p->next; free(sfi.maildir); continue; } inbox.next=p->next; inbox.hier=INBOX; d=maildir_location(sfi.homedir, sfi.maildir); free(sfi.homedir); free(sfi.maildir); lci.hier= &inbox; list_utf8_info.homedir=d; list_utf8_info.owner=p->hier; maildir_list(d, &list_callback, &list_utf8_info); free(d); curcache=NULL; break; } if (curcache) /* List a shared hierarchy */ { int rc; curcache->indexfile.startingpos=0; eof=0; do { rc=(curcache->indexfile.startingpos ? maildir_newshared_next: maildir_newshared_nextAt) (&curcache->indexfile, &eof, &smap_list_cb, &list_utf8_info); if (rc) fprintf(stderr, "ERR: Internal error -" " maildir_newshared_next: %s\n", strerror(errno)); } while (rc == 0 && !eof); hierlist=1; } } else { list_utf8_info.homedir="."; list_utf8_info.owner=getenv("AUTHENTICATED"); maildir_list(".", &list_callback, &list_utf8_info); } for (cnt=0, p= *head; p; p=p->next) ++cnt; vecs=malloc(sizeof(char *)*(cnt+2)); if (!vecs) { while (lci.found) { struct list_hier *h=lci.found; lci.found=h->next; free(h->hier); free(h); } write_error_exit(0); } for (cnt=0, p= *head; p; p=p->next) { vecs[cnt]=p->hier; ++cnt; } while (lci.found) { struct list_hier *h=lci.found; struct maildir_info minfo; maildir_aclt_list aclt_list; lci.found=h->next; vecs[cnt]=h->hier; vecs[cnt+1]=0; if (maildir_info_smap_find(&minfo, vecs, getenv("AUTHENTICATED")) == 0) { if (read_acls(&aclt_list, &minfo) == 0) { char *acl; acl=compute_myrights(&aclt_list, minfo.owner); if (acl) { if (strchr(acl, ACL_LOOKUP[0]) == NULL) { h->flags=LIST_DIRECTORY; if (hierlist) list(h->hier, h->hier, h->flags); } else list(h->hier, h->hier, h->flags); free(acl); } else { fprintf(stderr, "ERR: Cannot compute" " my access rights" " for %s: %s\n", h->hier, strerror(errno)); } maildir_aclt_list_destroy(&aclt_list); } else { fprintf(stderr, "ERR: Cannot read ACLs" " for %s(%s): %s\n", minfo.homedir ? minfo.homedir : ".", minfo.maildir ? minfo.maildir : "unknown", strerror(errno)); } } else { fprintf(stderr, "ERR: Internal error in list():" " cannot find folder %s: %s\n", h->hier, strerror(errno)); } free(h->hier); free(h); } free(vecs); } writes("+OK LIST completed\n"); } static int smap_find_cb(struct maildir_newshared_enum_cb *cb) { struct smap_find_info *ifs=(struct smap_find_info *)cb->cb_arg; if (cb->homedir) ifs->homedir=my_strdup(cb->homedir); if (cb->maildir) ifs->maildir=my_strdup(cb->maildir); return 0; } static int smap_list_cb(struct maildir_newshared_enum_cb *cb) { struct list_callback_utf8 *list_utf8_info= (struct list_callback_utf8 *)cb->cb_arg; struct list_callback_info *lci= (struct list_callback_info *)list_utf8_info->callback_arg; char *d; struct list_hier *h; struct stat stat_buf; if (cb->homedir == NULL) { if ((h=malloc(sizeof(struct list_hier))) == NULL || (h->hier =strdup(cb->name)) == NULL) { if (h) free(h); perror("ERR: malloc"); return 0; } h->next= lci->found; lci->found=h; h->flags = LIST_DIRECTORY; return 0; } d=maildir_location(cb->homedir, cb->maildir); if (!d) { perror("ERR: get_topmaildir"); return 0; } if (stat(d, &stat_buf) < 0 || (stat_buf.st_dev == homedir_dev && stat_buf.st_ino == homedir_ino)) { free(d); return 0; } list_utf8_info->homedir=d; list_utf8_info->owner=cb->name; lci->hier=NULL; h=lci->found; lci->found=NULL; maildir_list(d, &list_callback, list_utf8_info); free(d); if (!lci->found) lci->found=h; else { char *p; while (lci->found->next) /* SHOULDN'T HAPPEN!!! */ { struct list_hier *p=lci->found->next; lci->found->next=p->next; free(p->hier); free(p); fprintf(stderr, "ERR: Unexpected folder list" " in smap_list_cb()\n"); } lci->found->next=h; p=my_strdup(cb->name); free(lci->found->hier); lci->found->hier=p; } return (0); } /* ** Read the name of a new folder. Returns the pathname to the folder, suitable ** for immediate creation. */ static char *getCreateFolder_int(char **ptr, char *need_perms) { char **fn; char *n; struct maildir_info minfo; size_t i; char *save; maildir_aclt_list aclt_list; fn=fn_fromwords(ptr); if (!fn) return NULL; if (need_perms) { for (i=0; fn[i]; i++) ; if (i == 0) { *need_perms=0; maildir_smapfn_free(fn); errno=EINVAL; return NULL; } save=fn[--i]; fn[i]=NULL; if (maildir_info_smap_find(&minfo, fn, getenv("AUTHENTICATED")) < 0) { fn[i]=save; maildir_smapfn_free(fn); return NULL; } fn[i]=save; if (read_acls(&aclt_list, &minfo)) { maildir_smapfn_free(fn); maildir_info_destroy(&minfo); return NULL; } save=compute_myrights(&aclt_list, minfo.owner); maildir_aclt_list_destroy(&aclt_list); for (i=0; need_perms[i]; i++) if (save == NULL || strchr(save, need_perms[i])==NULL) { if (save) free(save); maildir_smapfn_free(fn); maildir_info_destroy(&minfo); *need_perms=0; errno=EPERM; return NULL; } if (save) free(save); maildir_info_destroy(&minfo); } if (maildir_info_smap_find(&minfo, fn, getenv("AUTHENTICATED")) < 0) { maildir_smapfn_free(fn); return NULL; } maildir_smapfn_free(fn); if (minfo.homedir == NULL || minfo.maildir == NULL) { maildir_info_destroy(&minfo); errno=ENOENT; return NULL; } n=maildir_name2dir(minfo.homedir, minfo.maildir); if (need_perms && strchr(need_perms, ACL_CREATE[0])) { /* Initialize the ACL structures */ if (read_acls(&aclt_list, &minfo) == 0) maildir_aclt_list_destroy(&aclt_list); } maildir_info_destroy(&minfo); return n; } static char *getCreateFolder(char **ptr, char *perms) { char *p=getCreateFolder_int(ptr, perms); if (p && strncmp(p, "./", 2) == 0) { char *q=p+2; while ((q[-2]=*q) != 0) q++; } return p; } static int read_acls(maildir_aclt_list *aclt_list, struct maildir_info *minfo) { char *q; int rc; if (minfo->homedir == NULL || minfo->maildir == NULL) { if (minfo->mailbox_type == MAILBOXTYPE_NEWSHARED) { /* Intermediate node in public hier */ maildir_aclt_list_init(aclt_list); if (maildir_aclt_list_add(aclt_list, "anyone", ACL_LOOKUP, NULL) < 0) { maildir_aclt_list_destroy(aclt_list); return -1; } return 0; } return -1; } q=maildir_name2dir(".", minfo->maildir); if (!q) { fprintf(stderr, "ERR: Internal error" " in read_acls(%s)\n", minfo->maildir); return -1; } rc=maildir_acl_read(aclt_list, minfo->homedir, q[0] == '.' && q[1] == '/' ? q+2:q); free(q); if (current_mailbox) { q=maildir_name2dir(minfo->homedir, minfo->maildir); if (q) { if (strcmp(q, current_mailbox) == 0) { char *r=compute_myrights(aclt_list, minfo->owner); if (r && strcmp(current_mailbox_acl, r)) { free(current_mailbox_acl); current_mailbox_acl=r; r=NULL; } if (r) free(r); } free(q); } } return rc; } static char *getExistingFolder_int(char **ptr, char *rightsWanted) { char **fn; char *n; struct maildir_info minfo; fn=fn_fromwords(ptr); if (!fn) return NULL; if (maildir_info_smap_find(&minfo, fn, getenv("AUTHENTICATED")) < 0) { maildir_smapfn_free(fn); return NULL; } maildir_smapfn_free(fn); if (minfo.homedir == NULL || minfo.maildir == NULL) { maildir_info_destroy(&minfo); errno=ENOENT; return NULL; } n=maildir_name2dir(minfo.homedir, minfo.maildir); if (n && rightsWanted) { maildir_aclt_list aclt_list; char *q, *r, *s; if (read_acls(&aclt_list, &minfo) < 0) { free(n); maildir_info_destroy(&minfo); return NULL; } q=compute_myrights(&aclt_list, minfo.owner); maildir_aclt_list_destroy(&aclt_list); if (q == NULL) { free(n); maildir_info_destroy(&minfo); return NULL; } for (r=s=rightsWanted; *r; r++) if (strchr(q, *r)) *s++ = *r; *s=0; free(q); } maildir_info_destroy(&minfo); return n; } static char *getAccessToFolder(char **ptr, char *rightsWanted) { char *p=getExistingFolder_int(ptr, rightsWanted); if (p && strncmp(p, "./", 2) == 0) { char *q=p+2; while ((q[-2]=*q) != 0) q++; } return p; } static void smap1_noop(int real_noop) { if (current_mailbox) doNoop(real_noop); writes("+OK Folder updated\n"); } /* Parse a message set. Return the next word following the message set */ struct smapmsgset { struct smapmsgset *next; unsigned nranges; unsigned long range[2][2]; }; static struct smapmsgset msgset; static const char digit[]="0123456789"; static char *markmsgset(char **ptr, int *hasmsgset) { unsigned long n; char *w; struct smapmsgset *msgsetp; while ((msgsetp=msgset.next) != NULL) { msgset.next=msgsetp->next; free(msgsetp); } msgsetp= &msgset; msgsetp->nranges=0; *hasmsgset=0; n=0; while (*(w=getword(ptr))) { unsigned long a=0, b=0; const char *d; if (!*w || (d=strchr(digit, *w)) == NULL) break; *hasmsgset=1; while ( *w && (d=strchr(digit, *w)) != NULL) { a=a * 10 + d-digit; w++; } b=a; if (*w == '-') { ++w; b=0; while ( *w && (d=strchr(digit, *w)) != NULL) { b=b * 10 + d-digit; w++; } } if (a <= n || b < a) { errno=EINVAL; return NULL; } n=b; if (msgsetp->nranges >= sizeof(msgsetp->range)/sizeof(msgsetp->range[0])) { if ((msgsetp->next=malloc(sizeof(struct smapmsgset))) == NULL) { write_error_exit(0); } msgsetp=msgsetp->next; msgsetp->next=NULL; msgsetp->nranges=0; } msgsetp->range[msgsetp->nranges][0]=a; msgsetp->range[msgsetp->nranges][1]=b; ++msgsetp->nranges; } return w; } static void parseflags(char *q, struct imapflags *flags) { char *p; if ((q=strchr(q, '=')) == NULL) return; ++q; while (*q) { p=q; while (*q) { if (*q == ',') { *q++=0; break; } q++; } if (strcmp(p, "SEEN") == 0) flags->seen=1; else if (strcmp(p, "REPLIED") == 0) flags->answered=1; else if (strcmp(p, "DRAFT") == 0) flags->drafts=1; else if (strcmp(p, "DELETED") == 0) flags->deleted=1; else if (strcmp(p, "MARKED") == 0) flags->flagged=1; } } extern int get_keyword(struct libmail_kwMessage **kwPtr, const char *kw); extern int valid_keyword(const char *kw); static void parsekeywords(char *q, struct libmail_kwMessage **msgp) { char *p; if ((q=strchr(q, '=')) == NULL) return; ++q; while (*q) { p=q; while (*q) { if (*q == ',') { *q++=0; break; } q++; } get_keyword(msgp, p); } } static int applymsgset( int (*callback_func)(unsigned long, void *), void *callback_arg) { struct smapmsgset *msgsetp= &msgset; unsigned long n; int rc; while (msgsetp) { unsigned i; for (i=0; inranges; i++) { for (n=msgsetp->range[i][0]; n <= msgsetp->range[i][1]; n++) { if (current_mailbox == NULL || n > current_maildir_info.nmessages) break; rc=(*callback_func)(n-1, callback_arg); if (rc) return rc; } } msgsetp=msgsetp->next; } return 0; } static int do_attrfetch(unsigned long n, void *vp); static int applyflags(unsigned long n, void *vp) { struct storeinfo *si=(struct storeinfo *)vp; int attrs; struct libmail_kwMessage *newKw; if (n >= current_maildir_info.nmessages) return 0; attrs= si->keywords ? FETCH_KEYWORDS:FETCH_FLAGS; if (!si->plusminus) { if (si->keywords == NULL) /* STORE FLAGS= */ si->keywords=current_maildir_info.msgs[n].keywordMsg; else /* STORE KEYWORDS= */ get_message_flags(current_maildir_info.msgs+n, 0, &si->flags); } /* do_store may clobber si->keywords. Punt */ newKw=si->keywords; if (do_store(n+1, 0, si)) { si->keywords=newKw; return -1; } si->keywords=newKw; do_attrfetch(n, &attrs); return 0; } struct smapAddRemoveKeywordInfo { struct storeinfo *si; void *storeVoidArg; }; static int addRemoveSmapKeywordsCallback(void *myVoidArg, void *storeVoidArg); static int addRemoveSmapKeywords(struct storeinfo *si) { struct smapAddRemoveKeywordInfo ar; ar.si=si; return addRemoveKeywords(addRemoveSmapKeywordsCallback, &ar, si); } static int doAddRemoveSmapKeywords(unsigned long n, void *voidArg); static int addRemoveSmapKeywordsCallback(void *myVoidArg, void *storeVoidArg) { struct smapAddRemoveKeywordInfo *info= (struct smapAddRemoveKeywordInfo *)myVoidArg; info->storeVoidArg=storeVoidArg; return applymsgset(doAddRemoveSmapKeywords, info); } static int doAddRemoveSmapKeywords(unsigned long n, void *voidArg) { struct smapAddRemoveKeywordInfo *info= (struct smapAddRemoveKeywordInfo *)voidArg; return doAddRemoveKeywords(n+1, 0, info->storeVoidArg); } static int setdate(unsigned long n, void *vp) { time_t datestamp=*(time_t *)vp; char *filename=maildir_filename(current_mailbox, 0, current_maildir_info.msgs[n] .filename); if (filename) { set_time(filename, datestamp); free(filename); } return 0; } static int msg_expunge(unsigned long n, void *vp) { do_expunge(n, n+1, 1); return 0; } struct smapfetchinfo { int peek; char *entity; char *hdrs; char *mimeid; }; static int hashdr(const char *hdrList, const char *hdr) { if (!hdrList || !*hdrList) return 1; while (*hdrList) { size_t n; int is_envelope=0; int is_mime=0; if (*hdrList == ',') { ++hdrList; continue; } if (strncmp(hdrList, ":ENVELOPE", 9) == 0) { switch (hdrList[9]) { case 0: case ',': is_envelope=1; break; } } if (strncmp(hdrList, ":MIME", 5) == 0) { switch (hdrList[5]) { case 0: case ',': is_mime=1; break; } } if (is_envelope || is_mime) { char hbuf[30]; hbuf[0]=0; strncat(hbuf, hdr, 29); up(hbuf); if (strcmp(hbuf, "DATE") == 0) return 1; if (strcmp(hbuf, "SUBJECT") == 0) return 1; if (strcmp(hbuf, "FROM") == 0) return 1; if (strcmp(hbuf, "SENDER") == 0) return 1; if (strcmp(hbuf, "REPLY-TO") == 0) return 1; if (strcmp(hbuf, "TO") == 0) return 1; if (strcmp(hbuf, "CC") == 0) return 1; if (strcmp(hbuf, "BCC") == 0) return 1; if (strcmp(hbuf, "IN-REPLY-TO") == 0) return 1; if (strcmp(hbuf, "MESSAGE-ID") == 0) return 1; if (strcmp(hbuf, "REFERENCES") == 0) return 1; if (is_mime) { if (strcmp(hbuf, "MIME-VERSION") == 0) return 1; if (strncmp(hbuf, "CONTENT-", 8) == 0) return 1; } } for (n=0; hdrList[n] && hdrList[n] != ',' && hdr[n]; n++) { char a=hdrList[n]; char b=hdr[n]; UC(b); if (a != b) break; } if ((hdrList[n] == 0 || hdrList[n] == ',') && hdr[n] == 0) return 1; hdrList += n; while (*hdrList && *hdrList != ',') ++hdrList; } return 0; } static void writemimeid(struct rfc2045 *rfcp) { if (rfcp->parent) { writemimeid(rfcp->parent); writes("."); } writen(rfcp->pindex); } static int dump_hdrs(int fd, unsigned long n, struct rfc2045 *rfcp, const char *hdrs, const char *type) { struct rfc2045src *src; struct rfc2045headerinfo *h; char *header; char *value; int rc; off_t start_pos, end_pos, dummy, start_body; off_t nbodylines; int get_flags=RFC2045H_NOLC; rc=0; if (type && strcmp(type, "RAWHEADERS") == 0) get_flags |= RFC2045H_KEEPNL; if (!rfcp) { struct stat stat_buf; if (fstat(fd, &stat_buf)) end_pos=8000; /* Heh */ else end_pos=stat_buf.st_size; start_pos=0; start_body=0; } else rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &dummy, &nbodylines); writes("{."); writen(start_body - start_pos); writes("} FETCH "); writen(n+1); if (type) { writes(" "); writes(type); writes("\n"); } else /* MIME */ { writes(" LINES="); writen(nbodylines); writes(" SIZE="); writen(end_pos-start_body); writes(" \"MIME.ID="); if (rfcp->parent) { writemimeid(rfcp); writes("\" \"MIME.PARENT="); if (rfcp->parent->parent) writemimeid(rfcp->parent); } writes("\"\n"); } src=rfc2045src_init_fd(fd); h=src ? rfc2045header_start(src, rfcp):NULL; while (h && (rc=rfc2045header_get(h, &header, &value, get_flags)) == 0 && header) { if (hashdr(hdrs, header)) { if (*header == '.') writes("."); writes(header); writes(": "); writes(value); writes("\n"); header_count += strlen(header)+strlen(value)+3; } } writes(".\n"); if (h) rfc2045header_end(h); else rc= -1; if (src) rfc2045src_deinit(src); return rc; } static int dump_body(FILE *fp, unsigned long msgNum, struct rfc2045 *rfcp, int dump_all) { char buffer[SMAP_BUFSIZ]; off_t start_pos, end_pos, dummy, start_body; int i; int first; if (!rfcp) { struct stat stat_buf; if (fstat(fileno(fp), &stat_buf) < 0) return -1; if (dump_all) { start_pos=start_body=0; } else { if (fseek(fp, 0L, SEEK_SET) < 0) return -1; if (!(rfcp=rfc2045_alloc())) return -1; do { i=fread(buffer, 1, sizeof(buffer), fp); if (i < 0) { rfc2045_free(rfcp); return -1; } if (i == 0) break; rfc2045_parse(rfcp, buffer, i); } while (rfcp->workinheader); rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &dummy, &dummy); rfc2045_free(rfcp); start_pos=0; } end_pos=stat_buf.st_size; } else rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &dummy, &dummy); if (dump_all) start_body=start_pos; if (fseek(fp, start_body, SEEK_SET) < 0) return -1; first=1; do { int n=sizeof(buffer); if (n > end_pos - start_body) n=end_pos - start_body; for (i=0; iworkinheader); myrfcp->endpos=stat_buf.st_size; return myrfcp; } static int do_dump_decoded(const char *chunk, size_t chunkSize, void *vp) { struct decodeinfo *myDecodeInfo=(struct decodeinfo *) vp; while (chunkSize) { size_t n; if (myDecodeInfo->bufptr >= sizeof(myDecodeInfo->buffer)) do_dump_decoded_flush(myDecodeInfo); n=sizeof(myDecodeInfo->buffer)-myDecodeInfo->bufptr; if (n > chunkSize) n=chunkSize; memcpy(myDecodeInfo->buffer + myDecodeInfo->bufptr, chunk, n); myDecodeInfo->bufptr += n; chunk += n; chunkSize -= n; } return 0; } static void do_dump_decoded_flush(struct decodeinfo *myDecodeInfo) { size_t chunkSize= myDecodeInfo->bufptr; myDecodeInfo->bufptr=0; if (chunkSize == 0) return; if (myDecodeInfo->first) { myDecodeInfo->first=0; writes("{"); writen(chunkSize); writes("/"); writen(myDecodeInfo->estSize); writes("} FETCH "); writen(myDecodeInfo->msgNum+1); writes(" CONTENTS\n"); } else { writen(chunkSize); writes("\n"); } writemem(myDecodeInfo->buffer, chunkSize); body_count += chunkSize; } static int mime(int fd, unsigned long n, struct rfc2045 *rfcp, const char *hdrs) { int rc=dump_hdrs(fd, n, rfcp, hdrs, NULL); if (rc) return rc; for (rfcp=rfcp->firstpart; rfcp; rfcp=rfcp->next) if (!rfcp->isdummy) { rc=mime(fd, n, rfcp, hdrs); if (rc) return rc; } return 0; } /* ** Find the specified MIME id. */ static struct rfc2045 *findmimeid(struct rfc2045 *rfcp, const char *mimeid) { unsigned long n; while (mimeid && *mimeid) { const char *d; n=0; if (strchr(digit, *mimeid) == NULL) return NULL; while (*mimeid && (d=strchr(digit, *mimeid)) != NULL) { n=n * 10 + d-digit; mimeid++; } while (rfcp) { if (!rfcp->isdummy && rfcp->pindex == n) break; rfcp=rfcp->next; } if (!rfcp) return NULL; if (*mimeid == '.') { ++mimeid; rfcp=rfcp->firstpart; } } return rfcp; } static int do_fetch(unsigned long n, void *vp) { struct smapfetchinfo *fi=(struct smapfetchinfo *)vp; FILE *fp=open_cached_fp(n); int rc=0; if (!fp) return -1; if (strcmp(fi->entity, "MIME") == 0) { struct rfc2045 *rfcp=fetch_alloc_rfc2045(n, fp); int fd; if (!rfcp) return -1; fd=dup(fileno(fp)); if (fd < 0) return -1; rc=mime(fd, n, rfcp, fi->hdrs); close(fd); } else if (strcmp(fi->entity, "HEADERS") == 0 || strcmp(fi->entity, "RAWHEADERS") == 0) { int fd; struct rfc2045 *rfcp; fd=dup(fileno(fp)); if (fd < 0) return -1; if (!fi->mimeid || !*fi->mimeid) rfcp=NULL; else { rfcp=fetch_alloc_rfc2045(n, fp); rfcp=findmimeid(rfcp, fi->mimeid); if (!rfcp) { close(fd); errno=EINVAL; return -1; } } rc=dump_hdrs(fd, n, rfcp, fi->hdrs, fi->entity); close(fd); } else if (strcmp(fi->entity, "BODY") == 0 || strcmp(fi->entity, "ALL") == 0) { struct rfc2045 *rfcp; if (!fi->mimeid || !*fi->mimeid) rfcp=NULL; else { rfcp=fetch_alloc_rfc2045(n, fp); rfcp=findmimeid(rfcp, fi->mimeid); if (!rfcp) { errno=EINVAL; return -1; } } rc=dump_body(fp, n, rfcp, fi->entity[0] == 'A'); } else if (strcmp(fi->entity, "BODY.DECODED") == 0) { struct rfc2045 *rfcp; if (!fi->mimeid || !*fi->mimeid) rfcp=NULL; else { rfcp=fetch_alloc_rfc2045(n, fp); rfcp=findmimeid(rfcp, fi->mimeid); if (!rfcp) { errno=EINVAL; return -1; } } rc=dump_decoded(fp, n, rfcp); } else { rc=0; } if (rc == 0 && !fi->peek) { struct imapflags flags; get_message_flags(current_maildir_info.msgs+n, 0, &flags); if (!flags.seen) { flags.seen=1; reflag_filename(¤t_maildir_info.msgs[n], &flags, fileno(fp)); current_maildir_info.msgs[n].changedflags=1; } } if (current_maildir_info.msgs[n].changedflags) fetchflags(n); return rc; } void smap_fetchflags(unsigned long n) { int items=FETCH_FLAGS | FETCH_KEYWORDS; do_attrfetch(n, &items); } static int do_attrfetch(unsigned long n, void *vp) { int items=*(int *)vp; if (n >= current_maildir_info.nmessages) return 0; writes("* FETCH "); writen(n+1); if (items & FETCH_FLAGS) { char buf[256]; get_message_flags(current_maildir_info.msgs+n, buf, 0); writes(" FLAGS="); writes(buf); current_maildir_info.msgs[n].changedflags=0; } if ((items & FETCH_KEYWORDS) && keywords()) { struct libmail_kwMessageEntry *kme; writes(" \"KEYWORDS="); if (current_maildir_info.msgs[n].keywordMsg && current_maildir_info.msgs[n].keywordMsg->firstEntry) { const char *p=""; for (kme=current_maildir_info.msgs[n] .keywordMsg->firstEntry; kme; kme=kme->next) { writes(p); p=","; writes(keywordName(kme->libmail_keywordEntryPtr)); } } writes("\""); } if (items & FETCH_UID) { char *p, *q; writes(" \"UID="); p=current_maildir_info.msgs[n].filename; q=strrchr(p, MDIRSEP[0]); if (q) *q=0; smapword_s(p); if (q) *q=MDIRSEP[0]; writes("\""); } if (items & FETCH_SIZE) { char *p=current_maildir_info.msgs[n].filename; unsigned long cnt; if (maildir_parsequota(p, &cnt)) { FILE *fp=open_cached_fp(n); struct stat stat_buf; if (fp && fstat(fileno(fp), &stat_buf) == 0) cnt=stat_buf.st_size; else cnt=0; } writes(" SIZE="); writen(cnt); } if (items & FETCH_INTERNALDATE) { struct stat stat_buf; FILE *fp=open_cached_fp(n); if (fp && fstat(fileno(fp), &stat_buf) == 0) { char buf[256]; rfc822_mkdate_buf(stat_buf.st_mtime, buf); writes(" \"INTERNALDATE="); smapword_s(buf); writes("\""); } } writes("\n"); return 0; } struct add_rcptlist { struct add_rcptlist *next; char *rcptto; }; static unsigned long add_msg(FILE *fp, const char *format, char *buffer, size_t bufsize) { unsigned long n=0; writes("> Go ahead\n"); writeflush(); if (*format == '.') { int last_eol=1; int dot_stuffed=0; int counter=-1; for (;;) { char c; if ( ((counter=counter + 1 ) % 8192) == 0) read_timeout(60); c=READ(); if (c == '\r') continue; if (dot_stuffed && c == '\n') break; dot_stuffed=0; if (c == '.') { if (last_eol) { dot_stuffed=1; continue; } } last_eol= c == '\n'; putc( (int)(unsigned char)c, fp); n++; } if (!last_eol) { putc('\n', fp); n++; } } else { unsigned long chunkSize; char last_char='\n'; while (sscanf(format, "%lu", &chunkSize) == 1) { while (chunkSize) { size_t nn=bufsize; size_t i; if (nn > chunkSize) nn=(size_t)chunkSize; read_timeout(60); nn=doread(buffer, nn); chunkSize -= nn; n += nn; for (i=0; ifirstEntry) return 0; if (maildir_kwSave(destmailbox, newname, msg, &tmpkname, &newkname, 0)) { perror("maildir_kwSave"); return -1; } rename(tmpkname, newkname); free(tmpkname); free(newkname); return 0; } static void fixnewfilename(char *p) { char *q; /* Nice hack: */ q=strrchr(strrchr(p, '/'), MDIRSEP[0]); if (strcmp(q, MDIRSEP "2,") == 0) { *q=0; memcpy(strrchr(p, '/')-3, "new", 3); } } static int do_copymsg(unsigned long n, void *voidptr) { char buf[SMAP_BUFSIZ]; struct copyquotainfo *cqinfo=(struct copyquotainfo *)voidptr; struct imapflags new_flags; int fd; struct stat stat_buf; FILE *fp; char *tmpname, *newname; fd=imapscan_openfile(current_mailbox, ¤t_maildir_info, n); if (fd < 0) return (0); if (fstat(fd, &stat_buf) < 0) { close(fd); return (0); } get_message_flags(current_maildir_info.msgs+n, 0, &new_flags); fp=maildir_mkfilename(cqinfo->destmailbox, &new_flags, stat_buf.st_size, &tmpname, &newname); fixnewfilename(newname); if (!fp) { close(fd); return (-1); } while (stat_buf.st_size) { int n=sizeof(buf); if (n > stat_buf.st_size) n=stat_buf.st_size; n=read(fd, buf, n); if (n <= 0 || fwrite(buf, 1, n, fp) != n) { close(fd); fclose(fp); unlink(tmpname); free(tmpname); free(newname); fprintf(stderr, "ERR: error copying a message, user=%s, errno=%d\n", getenv("AUTHENTICATED"), errno); return (-1); } stat_buf.st_size -= n; } close(fd); if (fflush(fp) || ferror(fp)) { fclose(fp); unlink(tmpname); free(tmpname); free(newname); fprintf(stderr, "ERR: error copying a message, user=%s, errno=%d\n", getenv("AUTHENTICATED"), errno); return (-1); } fclose(fp); if (do_copyKeywords(current_maildir_info.msgs[n].keywordMsg, cqinfo->destmailbox, strrchr(newname, '/')+1)) { unlink(tmpname); free(tmpname); free(newname); fprintf(stderr, "ERR: error copying keywords, " "user=%s, errno=%d\n", getenv("AUTHENTICATED"), errno); return (-1); } current_maildir_info.msgs[n].copiedflag=1; maildir_movetmpnew(tmpname, newname); set_time(newname, stat_buf.st_mtime); free(tmpname); copieduid(n+1, newname); free(newname); return 0; } static int do_movemsg(unsigned long n, void *voidptr) { char *filename; struct copyquotainfo *cqinfo=(struct copyquotainfo *)voidptr; char *newfilename; if (n >= current_maildir_info.nmessages) return 0; filename=maildir_filename(current_mailbox, 0, current_maildir_info.msgs[n].filename); if (!filename) return 0; newfilename=malloc(strlen(cqinfo->destmailbox) + sizeof("/cur") + strlen(strrchr(filename, '/'))); if (!newfilename) { free(filename); write_error_exit(0); } strcat(strcat(strcpy(newfilename, cqinfo->destmailbox), "/cur"), strrchr(filename, '/')); if (do_copyKeywords(current_maildir_info.msgs[n].keywordMsg, cqinfo->destmailbox, strrchr(newfilename, '/')+1)) { fprintf(stderr, "ERR: error copying keywords, " "user=%s, errno=%d\n", getenv("AUTHENTICATED"), errno); free(filename); free(newfilename); return -1; } if (maildir_movetmpnew(filename, newfilename) == 0) { copieduid(n+1, newfilename); free(filename); free(newfilename); return 0; } if (do_copymsg(n, voidptr)) return -1; unlink(filename); free(filename); free(newfilename); return 0; } static struct searchinfo *createSearch2(char *w, struct searchinfo **head, char **ptr); static struct searchinfo *createSearch(struct searchinfo **head, char **ptr) { char *w=getword(ptr); struct searchinfo *siAnd, *n; up(w); if (strcmp(w, "MARKED") == 0) { w=getword(ptr); up(w); n=createSearch2(w, head, ptr); if (!n) return NULL; siAnd=alloc_search(head); siAnd->type=search_and; siAnd->b=n; siAnd->a=n=alloc_search(head); n->type=search_msgflag; if (!(n->as=strdup("\\FLAGGED"))) write_error_exit(0); return siAnd; } if (strcmp(w, "UNMARKED") == 0) { w=getword(ptr); up(w); n=createSearch2(w, head, ptr); if (!n) return NULL; siAnd=alloc_search(head); siAnd->type=search_and; siAnd->b=n; siAnd->a=n=alloc_search(head); n->type=search_not; n=n->a=alloc_search(head); n->type=search_msgflag; if (!(n->as=strdup("\\FLAGGED"))) write_error_exit(0); return siAnd; } if (strcmp(w, "ALL") == 0) { w=getword(ptr); up(w); return createSearch2(w, head, ptr); } { char *ww=getword(ptr); up(ww); n=createSearch2(ww, head, ptr); if (!n) return NULL; siAnd=alloc_search(head); siAnd->type=search_and; siAnd->b=n; siAnd->a=n=alloc_search(head); n->type=search_messageset; if (!(n->as=strdup(w))) write_error_exit(0); for (ww=n->as; *ww; ww++) if (*ww == '-') *ww=':'; if (!ismsgset_str(n->as)) { errno=EINVAL; return NULL; } } return siAnd; } static struct searchinfo *createSearch2(char *w, struct searchinfo **head, char **ptr) { int notflag=0; struct searchinfo *n; if (strcmp(w, "NOT") == 0) { notflag=1; w=getword(ptr); up(w); } if (strcmp(w, "REPLIED") == 0) { n=alloc_search(head); n->type=search_msgflag; if (!(n->as=strdup("\\ANSWERED"))) write_error_exit(0); } else if (strcmp(w, "DELETED") == 0) { n=alloc_search(head); n->type=search_msgflag; if (!(n->as=strdup("\\DELETED"))) write_error_exit(0); } else if (strcmp(w, "DRAFT") == 0) { n=alloc_search(head); n->type=search_msgflag; if (!(n->as=strdup("\\DRAFT"))) write_error_exit(0); } else if (strcmp(w, "SEEN") == 0) { n=alloc_search(head); n->type=search_msgflag; if (!(n->as=strdup("\\SEEN"))) write_error_exit(0); } else if (strcmp(w, "KEYWORD") == 0) { n=alloc_search(head); n->type=search_msgkeyword; if (!(n->as=strdup(getword(ptr)))) write_error_exit(0); } else if (strcmp(w, "FROM") == 0 || strcmp(w, "TO") == 0 || strcmp(w, "CC") == 0 || strcmp(w, "BCC") == 0 || strcmp(w, "SUBJECT") == 0) { n=alloc_search(head); n->type=search_header; if (!(n->cs=strdup(w))) write_error_exit(0); n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "HEADER") == 0) { n=alloc_search(head); n->type=search_header; if (!(n->cs=strdup(getword(ptr)))) write_error_exit(0); up(n->cs); n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "BODY") == 0) { n=alloc_search(head); n->type=search_body; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "TEXT") == 0) { n=alloc_search(head); n->type=search_text; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "BEFORE") == 0) { n=alloc_search(head); n->type=search_before; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "ON") == 0) { n=alloc_search(head); n->type=search_on; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "SINCE") == 0) { n=alloc_search(head); n->type=search_since; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "SENTBEFORE") == 0) { n=alloc_search(head); n->type=search_sentbefore; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "SENTON") == 0) { n=alloc_search(head); n->type=search_senton; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "SINCE") == 0) { n=alloc_search(head); n->type=search_sentsince; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "SMALLER") == 0) { n=alloc_search(head); n->type=search_smaller; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else if (strcmp(w, "LARGER") == 0) { n=alloc_search(head); n->type=search_larger; n->as=strdup(getword(ptr)); if (!n->as) write_error_exit(0); } else { errno=EINVAL; return NULL; } if (notflag) { struct searchinfo *p=alloc_search(head); p->type=search_not; p->a=n; n=p; } return n; } static int do_copyto(char *toFolder, int (*do_func)(unsigned long, void *), const char *acls) { int has_quota=0; struct copyquotainfo cqinfo; struct maildirsize quotainfo; cqinfo.destmailbox=toFolder; cqinfo.nbytes=0; cqinfo.nfiles=0; cqinfo.acls=acls; if (maildirquota_countfolder(toFolder)) { if (maildir_openquotafile("ainfo, ".") == 0) { if (quotainfo.fd >= 0) has_quota=1; maildir_closequotafile("ainfo); } if (has_quota > 0 && applymsgset( &calc_quota, &cqinfo )) has_quota= -1; } if (has_quota > 0 && cqinfo.nfiles > 0) { if (maildir_quota_add_start(".", "ainfo, cqinfo.nbytes, cqinfo.nfiles, getenv("MAILDIRQUOTA"))) { errno=ENOSPC; return (-1); } maildir_quota_add_end("ainfo, cqinfo.nbytes, cqinfo.nfiles); } return applymsgset(do_func, &cqinfo); } static int copyto(char *toFolder, int do_move, const char *acls) { if (!do_move) return do_copyto(toFolder, &do_copymsg, acls); if (!current_mailbox_shared && maildirquota_countfolder(current_mailbox) == maildirquota_countfolder(toFolder)) { if (do_copyto(toFolder, do_movemsg, acls)) return -1; doNoop(0); return(0); } if (do_copyto(toFolder, &do_copymsg, acls)) return -1; applymsgset(&msg_expunge, NULL); doNoop(0); return 0; } struct smap1_search_results { unsigned prev_runs; unsigned long prev_search_hit; unsigned long prev_search_hit_start; }; static void smap1_search_cb_range(struct smap1_search_results *searchResults) { if (searchResults->prev_runs > 100) { writes("\n"); searchResults->prev_runs=0; } if (searchResults->prev_runs == 0) writes("* SEARCH"); writes(" "); writen(searchResults->prev_search_hit_start); if (searchResults->prev_search_hit_start != searchResults->prev_search_hit) { writes("-"); writen(searchResults->prev_search_hit); } ++searchResults->prev_runs; } static void smap1_search_cb(struct searchinfo *si, struct searchinfo *sihead, int isuid, unsigned long i, void *dummy) { struct smap1_search_results *searchResults= (struct smap1_search_results *)dummy; ++i; if (searchResults->prev_search_hit == 0) { searchResults->prev_search_hit= searchResults->prev_search_hit_start=i; return; } if (i != searchResults->prev_search_hit+1) { smap1_search_cb_range(searchResults); searchResults->prev_search_hit_start=i; } searchResults->prev_search_hit=i; } static void accessdenied(const char *acl_required) { writes("-ERR Access denied: ACL \""); writes(acl_required); writes("\" is required\n"); } static int getacl(const char *ident, const maildir_aclt *acl, void *cb_arg) { int *n=(int *)cb_arg; if (*n > 5) { writes("\n"); *n=0; } if (*n == 0) writes("* GETACL"); writes(" "); smapword(ident); writes(" "); smapword(maildir_aclt_ascstr(acl)); ++*n; return 0; } struct setacl_info { struct maildir_info minfo; char **ptr; }; static int dosetdeleteacl(void *cb_arg, int); static int setacl(void *cb_arg) { return dosetdeleteacl(cb_arg, 0); } static int deleteacl(void *cb_arg) { return dosetdeleteacl(cb_arg, 1); } static int dosetdeleteacl(void *cb_arg, int dodelete) { struct setacl_info *sainfo=(struct setacl_info *)cb_arg; char *q; int cnt; const char *identifier; const char *action; const char *err_failedrights; char *path; maildir_aclt_list aclt_list; if (read_acls(&aclt_list, &sainfo->minfo) < 0) { writes("-ERR Unable to read existing ACLS: "); writes(strerror(errno)); writes("\n"); return 0; } q=compute_myrights(&aclt_list, sainfo->minfo.owner); if (!q || !strchr(q, ACL_ADMINISTER[0])) { if (q) free(q); maildir_aclt_list_destroy(&aclt_list); accessdenied(ACL_ADMINISTER); return 0; } free(q); while (*(identifier=getword(sainfo->ptr))) { if (dodelete) { if (maildir_aclt_list_del(&aclt_list, identifier) < 0) { maildir_aclt_list_destroy(&aclt_list); writes("-ERR Error: "); writes(strerror(errno)); writes("\n"); return 0; } continue; } action=getword(sainfo->ptr); if (*action == '+') { maildir_aclt newacl; const maildir_aclt *oldacl; if (maildir_aclt_init(&newacl, action+1, NULL) < 0) { maildir_aclt_list_destroy(&aclt_list); writes("-ERR Error: "); writes(strerror(errno)); writes("\n"); return 0; } oldacl=maildir_aclt_list_find(&aclt_list, identifier ); if (oldacl) { if (maildir_aclt_add(&newacl, NULL, oldacl) < 0) { maildir_aclt_destroy(&newacl); maildir_aclt_list_destroy(&aclt_list); writes("-ERR Error: "); writes(strerror(errno)); writes("\n"); return 0; } } if (maildir_aclt_list_add(&aclt_list, identifier, NULL, &newacl) < 0) { maildir_aclt_destroy(&newacl); maildir_aclt_list_destroy(&aclt_list); writes("-ERR Error: "); writes(strerror(errno)); writes("\n"); return 0; } maildir_aclt_destroy(&newacl); continue; } if (*action == '-') { maildir_aclt newacl; const maildir_aclt *oldacl; oldacl=maildir_aclt_list_find(&aclt_list, identifier ); if (!oldacl) continue; if (maildir_aclt_init(&newacl, NULL, oldacl) < 0) { maildir_aclt_list_destroy(&aclt_list); writes("-ERR Error: "); writes(strerror(errno)); writes("\n"); return 0; } if (maildir_aclt_del(&newacl, action+1, NULL) < 0) { maildir_aclt_destroy(&newacl); maildir_aclt_list_destroy(&aclt_list); writes("-ERR Error: "); writes(strerror(errno)); writes("\n"); return 0; } if (strlen(maildir_aclt_ascstr(&newacl)) == 0 ? maildir_aclt_list_del(&aclt_list, identifier) :maildir_aclt_list_add(&aclt_list, identifier, NULL, &newacl) < 0) { maildir_aclt_destroy(&newacl); maildir_aclt_list_destroy(&aclt_list); writes("-ERR Error: "); writes(strerror(errno)); writes("\n"); return 0; } maildir_aclt_destroy(&newacl); continue; } if (strlen(action) == 0 ? maildir_aclt_list_del(&aclt_list, identifier): maildir_aclt_list_add(&aclt_list, identifier, action, NULL) < 0) { maildir_aclt_list_destroy(&aclt_list); writes("-ERR Error: "); writes(strerror(errno)); writes("\n"); return 0; } } path=maildir_name2dir(".", sainfo->minfo.maildir); err_failedrights=NULL; if (!path || maildir_acl_write(&aclt_list, sainfo->minfo.homedir, path[0] == '.' && path[1] == '/' ? path+2:path, sainfo->minfo.owner, &err_failedrights)) { if (path) free(path); if (err_failedrights) { writes("* ACLMINIMUM "); writes(err_failedrights); writes(" "); aclminimum(err_failedrights); writes("\n"); } writes("-ERR ACL update failed\n"); maildir_aclt_list_destroy(&aclt_list); return 0; } cnt=0; maildir_aclt_list_enum(&aclt_list, getacl, &cnt); if (cnt) writes("\n"); maildir_aclt_list_destroy(&aclt_list); /* Reread ACLs if the current mailbox's ACLs have changed */ if (read_acls(&aclt_list, &sainfo->minfo) < 0) { writes("-ERR Unable to re-read ACLS: "); writes(strerror(errno)); writes("\n"); return 0; } maildir_aclt_list_destroy(&aclt_list); writes("+OK Updated ACLs\n"); return 0; } static int checkacl(char **folder, struct maildir_info *minfo, const char *acls) { char *q; maildir_aclt_list aclt_list; if (maildir_info_smap_find(minfo, folder, getenv("AUTHENTICATED")) < 0) return -1; if (read_acls(&aclt_list, minfo) < 0) { maildir_info_destroy(minfo); return -1; } q=compute_myrights(&aclt_list, minfo->owner); maildir_aclt_list_destroy(&aclt_list); while (*acls) { if (q == NULL || strchr(q, *acls) == NULL) { if (q) free(q); maildir_info_destroy(minfo); return -1; } ++acls; } if (q) free(q); return 0; } void smap() { char buffer[8192]; char *ptr; struct imapflags add_flags; int in_add=0; char *add_from=NULL; char *add_folder=NULL; time_t add_internaldate=0; char *add_notify=NULL; unsigned add_rcpt_count=0; struct libmail_kwMessage *addKeywords=NULL; struct add_rcptlist *add_rcpt_list=NULL; char rights_buf[40]; enabled_utf8=1; imapscan_init(¤t_maildir_info); memset(&add_flags, 0, sizeof(add_flags)); #define GETFOLDER(acl) ( strcpy(rights_buf, (acl)), \ getAccessToFolder(&ptr, rights_buf)) for (;;) { char *p; writeflush(); read_timeout(30 * 60); smap_readline(buffer, sizeof(buffer)); ptr=buffer; p=getword(&ptr); up(p); if (strcmp(p, "ADD") == 0) { char **argvec; const char *okmsg="So far, so good..."; int err_sent=0; in_add=1; while (*(p=getword(&ptr))) { char *q=strchr(p, '='); if (q) *q++=0; up(p); if (strcmp(p, "FOLDER") == 0) { if (add_folder) free(add_folder); add_folder= GETFOLDER(ACL_INSERT ACL_DELETEMSGS ACL_SEEN ACL_WRITE); if (!add_folder) { writes("-ERR Invalid folder: "); writes(strerror(errno)); writes("\n"); break; } if (strchr(rights_buf, ACL_INSERT[0]) == NULL) { accessdenied(ACL_INSERT); free(add_folder); add_folder=NULL; break; } okmsg="Will add to this folder"; } if (strcmp(p, "MAILFROM") == 0 && q) { if (add_from) free(add_from); if ((add_from=strdup(q)) == NULL) { writes("-ERR "); writes(strerror(errno)); writes("\n"); break; } okmsg="MAIL FROM set"; } if (strcmp(p, "NOTIFY") == 0 && q) { if (add_notify) free(add_notify); if ((add_notify=strdup(q)) == NULL) { writes("-ERR "); writes(strerror(errno)); writes("\n"); break; } okmsg="NOTIFY set"; } if (strcmp(p, "RCPTTO") == 0 && q) { struct add_rcptlist *rcpt= malloc(sizeof(struct add_rcptlist)); if (rcpt == NULL || (rcpt->rcptto=strdup(q)) == NULL) { if (rcpt) free(rcpt); writes("-ERR "); writes(strerror(errno)); writes("\n"); break; } rcpt->next=add_rcpt_list; add_rcpt_list=rcpt; ++add_rcpt_count; okmsg="RCPT TO set"; } if (strcmp(p, "FLAGS") == 0 && q) { memset(&add_flags, 0, sizeof(add_flags)); *--q='='; parseflags(q, &add_flags); if (strchr(rights_buf, ACL_SEEN[0]) == NULL) add_flags.seen=0; if (strchr(rights_buf, ACL_DELETEMSGS[0]) == NULL) add_flags.deleted=0; if (strchr(rights_buf, ACL_WRITE[0]) == NULL) add_flags.answered= add_flags.flagged= add_flags.drafts=0; okmsg="FLAGS set"; } if (strcmp(p, "KEYWORDS") == 0 && q && keywords() && strchr(rights_buf, ACL_WRITE[0])) { if (addKeywords) libmail_kwmDestroy(addKeywords); addKeywords=libmail_kwmCreate(); if (addKeywords == NULL) { write_error_exit(0); } *--q='='; parsekeywords(q, &addKeywords); okmsg="KEYWORDS set"; } if (strcmp(p, "INTERNALDATE") == 0 && q) { if (rfc822_parsedate_chk(q, &add_internaldate) == 0) okmsg="INTERNALDATE set"; } if (p[0] == '{') { char *tmpname, *newname; char *s; char *tmpKeywords=NULL; char *newKeywords=NULL; FILE *fp; unsigned long n; fp=maildir_mkfilename(add_folder ?add_folder :".", &add_flags, 0, &tmpname, &newname); if (!fp) { writes("-ERR "); writes(strerror(errno)); writes("\n"); break; } fixnewfilename(newname); current_temp_fd=fileno(fp); current_temp_fn=tmpname; n=add_msg(fp, p+1, buffer, sizeof(buffer)); if (n) { s=maildir_requota(newname, n); if (!s) n=0; else { free(newname); newname=s; } } current_temp_fd= -1; current_temp_fn= NULL; if (n > 0 && add_folder && maildirquota_countfolder(add_folder) && maildirquota_countfile(newname)) { struct maildirsize quotainfo; if (maildir_quota_add_start(add_folder, "ainfo, n, 1, getenv("MAILDIRQUOTA"))) { errno=ENOSPC; n=0; } else maildir_quota_add_end("ainfo, n, 1); } fclose(fp); chmod(tmpname, 0600); if (add_folder && n && addKeywords) { if (maildir_kwSave(add_folder, strrchr(newname, '/')+1, addKeywords, &tmpKeywords, &newKeywords, 0)) { tmpKeywords=NULL; newKeywords=NULL; n=0; perror("maildir_kwSave"); } } argvec=NULL; if (add_rcpt_count > 0 && n) { argvec=malloc(sizeof(char *) * (add_rcpt_count +10)); if (!argvec) n=0; } if (argvec) { int i=1; struct add_rcptlist *l; argvec[i++]="-oi"; argvec[i++]="-f"; argvec[i++]=add_from ? add_from: (char *) defaultSendFrom(); if (add_notify) { argvec[i++]="-N"; argvec[i++]=add_notify; } for (l=add_rcpt_list; l; l=l->next) { argvec[i++]=l->rcptto; } argvec[i]=0; i=imapd_sendmsg(tmpname, argvec, &senderr); free(argvec); if (i) { n=0; err_sent=1; } } if (tmpKeywords) { rename(tmpKeywords, newKeywords); free(tmpKeywords); free(newKeywords); } if (add_folder && n) { if (maildir_movetmpnew(tmpname, newname) ) n=0; else { if (add_internaldate) set_time(newname, add_internaldate); adduid(newname); } } if (n == 0) { unlink(tmpname); free(tmpname); free(newname); if (!err_sent) { writes("-ERR "); writes(strerror(errno)); writes("\n"); } break; } unlink(tmpname); free(tmpname); free(newname); okmsg="Message saved"; p=NULL; break; } } if (p && *p) continue; /* Error inside the loop */ writes("+OK "); writes(okmsg); writes("\n"); if (p) continue; } if (in_add) { struct add_rcptlist *l; while ((l=add_rcpt_list) != NULL) { add_rcpt_list=l->next; free(l->rcptto); free(l); } memset(&add_flags, 0, sizeof(add_flags)); if (add_from) free(add_from); if (add_folder) free(add_folder); if (add_notify) free(add_notify); if (addKeywords) libmail_kwmDestroy(addKeywords); in_add=0; add_from=NULL; add_folder=NULL; add_internaldate=0; add_notify=NULL; addKeywords=NULL; add_rcpt_count=0; if (!p) continue; /* Just added a message */ } if (strcmp(p, "LOGOUT") == 0) break; if (strcmp(p, "RSET") == 0) { writes("+OK Reset\n"); continue; } if (strcmp(p, "GETACL") == 0 || strcmp(p, "ACL") == 0) { char **fn=fn_fromwords(&ptr); struct maildir_info minfo; maildir_aclt_list aclt_list; char *q; int cnt; if (!fn) { writes("-ERR Invalid folder: "); writes(strerror(errno)); writes("\n"); continue; } if (maildir_info_smap_find(&minfo, fn, getenv("AUTHENTICATED")) < 0) { maildir_smapfn_free(fn); writes("-ERR Invalid folder: "); writes(strerror(errno)); writes("\n"); continue; } if (read_acls(&aclt_list, &minfo) < 0) { maildir_info_destroy(&minfo); maildir_smapfn_free(fn); writes("-ERR Unable to read" " existing ACLS: "); writes(strerror(errno)); writes("\n"); continue; } q=compute_myrights(&aclt_list, minfo.owner); if (!q || strcmp(p, "ACL") ? !strchr(q, ACL_ADMINISTER[0]) : !maildir_acl_canlistrights(q) ) { if (q) free(q); maildir_aclt_list_destroy(&aclt_list); maildir_info_destroy(&minfo); maildir_smapfn_free(fn); accessdenied(ACL_ADMINISTER); continue; } if (strcmp(p, "ACL") == 0) { writes("* ACL "); smapword(q); writes("\n"); free(q); } else { free(q); cnt=0; maildir_aclt_list_enum(&aclt_list, getacl, &cnt); if (cnt) writes("\n"); } maildir_aclt_list_destroy(&aclt_list); maildir_info_destroy(&minfo); maildir_smapfn_free(fn); writes("+OK ACLs retrieved\n"); continue; } if (strcmp(p, "SETACL") == 0 || strcmp(p, "DELETEACL") == 0) { char **fn=fn_fromwords(&ptr); struct setacl_info sainfo; if (!fn) { writes("-ERR Invalid folder: "); writes(strerror(errno)); writes("\n"); continue; } if (maildir_info_smap_find(&sainfo.minfo, fn, getenv("AUTHENTICATED")) < 0) { maildir_smapfn_free(fn); writes("-ERR Invalid folder: "); writes(strerror(errno)); writes("\n"); continue; } sainfo.ptr= &ptr; acl_lock(sainfo.minfo.homedir, *p == 'S' ? setacl:deleteacl, &sainfo); maildir_smapfn_free(fn); maildir_info_destroy(&sainfo.minfo); continue; } if (strcmp(p, "LIST") == 0) { struct list_hier *hier=NULL; listcmd(&hier, &hier, &ptr); continue; } if (strcmp(p, "STATUS") == 0) { char *t; struct imapscaninfo other_info, *loaded_infoptr, *infoptr; unsigned long n, i; getword(&ptr); t=GETFOLDER(ACL_LOOKUP); if (!t) { writes("-ERR Cannot read folder status: "); writes(strerror(errno)); writes("\n"); continue; } if (strchr(rights_buf, ACL_LOOKUP[0]) == NULL) { accessdenied(ACL_LOOKUP); continue; } if (current_mailbox && strcmp(current_mailbox, t) == 0) { loaded_infoptr=0; infoptr= ¤t_maildir_info; } else { loaded_infoptr= &other_info; infoptr=loaded_infoptr; imapscan_init(loaded_infoptr); if (imapscan_maildir(infoptr, t, 1, 1, NULL)) { writes("-ERR Cannot read" " folder status: "); writes(strerror(errno)); writes("\n"); continue; } } writes("* STATUS EXISTS="); writen(infoptr->nmessages+infoptr->left_unseen); n=infoptr->left_unseen; for (i=0; inmessages; i++) { const char *p=infoptr->msgs[i].filename; p=strrchr(p, MDIRSEP[0]); if (p && strncmp(p, MDIRSEP "2,", 3) == 0 && strchr(p, 'S')) continue; ++n; } writes(" UNSEEN="); writen(n); writes("\n+OK Folder status retrieved\n"); if (loaded_infoptr) imapscan_free(loaded_infoptr); continue; } if (strcmp(p, "CREATE") == 0) { char *t; strcpy(rights_buf, ACL_CREATE); t=getCreateFolder(&ptr, rights_buf); if (t) { if (mdcreate(t)) { writes("-ERR Cannot create folder: "); writes(strerror(errno)); writes("\n"); } else { writes("+OK Folder created\n"); } free(t); } else { if (rights_buf[0] == 0) accessdenied(ACL_CREATE); else { writes("-ERR Cannot create folder: "); writes(strerror(errno)); writes("\n"); } } continue; } if (strcmp(p, "MKDIR") == 0) { char *t; strcpy(rights_buf, ACL_CREATE); t=getCreateFolder(&ptr, rights_buf); if (t) { writes("+OK Folder created\n"); free(t); } else if (rights_buf[0] == 0) accessdenied(ACL_CREATE); else { writes("-ERR Cannot create folder: "); writes(strerror(errno)); writes("\n"); } continue; } if (strcmp(p, "RMDIR") == 0) { char *t; strcpy(rights_buf, ACL_DELETEFOLDER); t=getCreateFolder(&ptr, rights_buf); if (t) { writes("+OK Folder deleted\n"); free(t); } else if (rights_buf[0] == 0) accessdenied(ACL_DELETEFOLDER); else { writes("-ERR Cannot create folder: "); writes(strerror(errno)); writes("\n"); } continue; } if (strcmp(p, "DELETE") == 0) { char **fn; char *t=NULL; fn=fn_fromwords(&ptr); if (fn) { struct maildir_info minfo; if (maildir_info_smap_find(&minfo, fn, getenv("AUTHENTICATED")) == 0) { if (minfo.homedir && minfo.maildir) { maildir_aclt_list list; char *q; if (strcmp(minfo.maildir, INBOX) == 0) { writes("-ERR INBOX may" " not be deleted\n"); maildir_info_destroy(&minfo); continue; } if (read_acls(&list, &minfo) < 0) { maildir_info_destroy(&minfo); accessdenied(ACL_DELETEFOLDER); continue; } q=compute_myrights(&list, minfo.owner ); if (!q || strchr(q, ACL_DELETEFOLDER[0]) == NULL) { if (q) free(q); maildir_info_destroy(&minfo); accessdenied(ACL_DELETEFOLDER); continue; } free(q); maildir_aclt_list_destroy(&list); t=maildir_name2dir(minfo.homedir, minfo.maildir); } maildir_info_destroy(&minfo); } } if (t && current_mailbox && strcmp(t, current_mailbox) == 0) { writes("-ERR Cannot DELETE currently open folder.\n"); free(t); continue; } if (t) { if (mddelete(t) == 0) { maildir_quota_recalculate("."); writes("+OK Folder deleted"); } else { writes("-ERR Cannot delete folder: "); writes(strerror(errno)); } writes("\n"); free(t); } else { if (t) { free(t); errno=EINVAL; } writes("-ERR Unable to delete folder: "); writes(strerror(errno)); writes("\n"); } continue; } if (strcmp(p, "RENAME") == 0) { struct maildir_info msrc, mdst; char **fnsrc, **fndst; size_t i; char *save; const char *errmsg; if ((fnsrc=fn_fromwords(&ptr)) == NULL) { writes("-ERR "); writes(strerror(errno)); writes("\n"); continue; } if ((fndst=fn_fromwords(&ptr)) == NULL) { maildir_smapfn_free(fnsrc); writes("-ERR "); writes(strerror(errno)); writes("\n"); continue; } for (i=0; fndst[i]; i++) ; if (i == 0) { maildir_smapfn_free(fnsrc); maildir_smapfn_free(fndst); writes("-ERR Invalid destination folder name\n"); continue; } if (checkacl(fnsrc, &msrc, ACL_DELETEFOLDER)) { maildir_smapfn_free(fnsrc); maildir_smapfn_free(fndst); accessdenied(ACL_DELETEFOLDER); continue; } save=fndst[--i]; fndst[i]=NULL; if (checkacl(fndst, &mdst, ACL_CREATE)) { fndst[i]=save; maildir_smapfn_free(fnsrc); maildir_smapfn_free(fndst); maildir_info_destroy(&msrc); accessdenied(ACL_CREATE); continue; } fndst[i]=save; maildir_info_destroy(&mdst); if (maildir_info_smap_find(&mdst, fndst, getenv("AUTHENTICATED")) < 0) { maildir_smapfn_free(fnsrc); maildir_smapfn_free(fndst); maildir_info_destroy(&msrc); writes("-ERR Internal error in RENAME: "); writes(strerror(errno)); writes("\n"); continue; } if (folder_rename(&msrc, &mdst, &errmsg)) { writes("-ERR "); writes(*errmsg == '@' ? errmsg+1:errmsg); if (*errmsg == '@') writes(strerror(errno)); writes("\n"); } else { writes("+OK Folder renamed.\n"); } maildir_info_destroy(&msrc); maildir_info_destroy(&mdst); continue; } if (strcmp(p, "OPEN") == 0 || strcmp(p, "SOPEN") == 0) { char **fn; char *q; const char *snapshot=0; struct maildir_info minfo; maildir_aclt_list aclt_list; if (current_mailbox) { free(current_mailbox); imapscan_free(¤t_maildir_info); imapscan_init(¤t_maildir_info); current_mailbox=0; } if (current_mailbox_acl) free(current_mailbox_acl); current_mailbox_acl=NULL; current_mailbox_shared=0; fetch_free_cache(); if (p[0] == 'S') snapshot=getword(&ptr); fn=fn_fromwords(&ptr); if (!fn) { writes("-ERR Invalid folder: "); writes(strerror(errno)); writes("\n"); continue; } if (maildir_info_smap_find(&minfo, fn, getenv("AUTHENTICATED")) < 0) { maildir_smapfn_free(fn); writes("-ERR Invalid folder: "); writes(strerror(errno)); writes("\n"); continue; } if (read_acls(&aclt_list, &minfo) < 0) { maildir_info_destroy(&minfo); maildir_smapfn_free(fn); writes("-ERR Unable to read" " existing ACLS: "); writes(strerror(errno)); writes("\n"); continue; } q=compute_myrights(&aclt_list, minfo.owner); maildir_aclt_list_destroy(&aclt_list); maildir_smapfn_free(fn); if (!q || strchr(q, ACL_READ[0]) == NULL) { if (q) free(q); maildir_info_destroy(&minfo); accessdenied(ACL_READ); maildir_info_destroy(&minfo); continue; } current_mailbox_acl=q; current_mailbox=maildir_name2dir(minfo.homedir, minfo.maildir); if (current_mailbox == NULL) { fprintf(stderr, "ERR: Internal error" " in maildir_name2dir(%s,%s)\n", minfo.homedir, minfo.maildir); maildir_info_destroy(&minfo); continue; } maildir_info_destroy(&minfo); snapshot_select(snapshot != NULL); if (snapshot_init(current_mailbox, snapshot)) { writes("* SNAPSHOTEXISTS "); smapword(snapshot); writes("\n"); smap1_noop(0); continue; } if (imapscan_maildir(¤t_maildir_info, current_mailbox, 0, 0, NULL) == 0) { snapshot_init(current_mailbox, NULL); writes("* EXISTS "); writen(current_maildir_info.nmessages); writes("\n+OK Folder opened\n"); continue; } writes("-ERR Cannot open the folder: "); writes(strerror(errno)); writes("\n"); free(current_mailbox); current_mailbox=NULL; continue; } if (strcmp(p, "CLOSE") == 0) { if (current_mailbox) { free(current_mailbox); imapscan_free(¤t_maildir_info); imapscan_init(¤t_maildir_info); current_mailbox=0; } writes("+OK Folder closed\n"); continue; } if (strcmp(p, "NOOP") == 0) { smap1_noop(1); continue; } if (strcmp(p, "IDLE") == 0) { if (imapenhancedidle()) imapidle(); read_timeout(60); smap_readline(buffer, sizeof(buffer)); ptr=buffer; p=getword(&ptr); up(p); if (strcmp(p, "RESUME")) { writes("-ERR RESUME is required to follow IDLE\n"); } else writes("+OK Resumed...\n"); continue; } if (!current_mailbox) p=""; /* FALLTHROUGH */ if (strcmp(p, "EXPUNGE") == 0) { int hasSet; p=markmsgset(&ptr, &hasSet); if (p) { if (strchr(current_mailbox_acl, ACL_EXPUNGE[0]) == NULL) { accessdenied(ACL_EXPUNGE); continue; } if (hasSet) { if (strchr(current_mailbox_acl, ACL_DELETEMSGS[0]) == NULL) { accessdenied(ACL_DELETEMSGS); continue; } applymsgset( &msg_expunge, NULL); } else expunge(); smap1_noop(0); continue; } } if (strcmp(p, "STORE") == 0) { struct storeinfo si; int dummy; p=markmsgset(&ptr, &dummy); dummy=0; if (!p) dummy=1; while (p && *p) { char *q=strchr(p, '='); if (q) *q=0; up(p); /* Uppercase only the keyword, for now */ if (q) *q='='; if (strncmp(p, "FLAGS=", 6) == 0) { memset(&si, 0, sizeof(si)); up(p); parseflags(p, &si.flags); if ((dummy=applymsgset(&applyflags, &si)) != 0) break; } else if (strncmp(p, "+FLAGS=", 7) == 0 || strncmp(p, "-FLAGS=", 7) == 0) { memset(&si, 0, sizeof(si)); up(p); si.plusminus=p[0]; parseflags(p, &si.flags); if ((dummy=applymsgset(&applyflags, &si)) != 0) break; } else if (strncmp(p, "KEYWORDS=", 9) == 0 && keywords()) { struct libmail_kwMessage *kwm; memset(&si, 0, sizeof(si)); kwm=si.keywords=libmail_kwmCreate(); if (!kwm) write_error_exit(0); parsekeywords(p, &si.keywords); dummy=applymsgset(&applyflags, &si); libmail_kwmDestroy(kwm); if (dummy != 0) break; } else if ((strncmp(p, "+KEYWORDS=", 10) == 0 || strncmp(p, "-KEYWORDS=", 10) == 0) && keywords()) { memset(&si, 0, sizeof(si)); si.keywords=libmail_kwmCreate(); if (!si.keywords) write_error_exit(0); si.plusminus=p[0]; parsekeywords(p, &si.keywords); dummy=applymsgset(&applyflags, &si); if (dummy == 0) dummy=addRemoveSmapKeywords(&si); libmail_kwmDestroy(si.keywords); if (dummy != 0) break; } else if (strncmp(p, "INTERNALDATE=", 13) == 0) { time_t t; up(p); if (rfc822_parsedate_chk(p+13, &t) == 0 && (dummy=applymsgset(&setdate, &t)) != 0) break; } p=getword(&ptr); } if (dummy) { writes("-ERR Cannot update folder status: "); writes(strerror(errno)); writes("\n"); } else writes("+OK Folder status updated\n"); continue; } if (strcmp(p, "FETCH") == 0) { int dummy; struct smapfetchinfo fi; int fetch_items=0; for (p=markmsgset(&ptr, &dummy); p && *p; p=getword(&ptr)) { if ((fi.entity=strchr(p, '=')) == NULL) { up(p); if (strcmp(p, "UID") == 0) fetch_items |= FETCH_UID; if (strcmp(p, "SIZE") == 0) fetch_items |= FETCH_SIZE; if (strcmp(p, "FLAGS") == 0) fetch_items |= FETCH_FLAGS; if (strcmp(p, "KEYWORDS") == 0) fetch_items |= FETCH_KEYWORDS; if (strcmp(p, "INTERNALDATE") == 0) fetch_items |= FETCH_INTERNALDATE; continue; } *fi.entity++=0; fi.hdrs=strrchr(fi.entity, '('); if (fi.hdrs) { char *q; *fi.hdrs++=0; q=strrchr(fi.hdrs, ')'); if (q) *q=0; up(fi.hdrs); } fi.mimeid=strrchr(fi.entity, '['); if (fi.mimeid) { char *q; *fi.mimeid++=0; q=strrchr(fi.mimeid, ']'); if (q) *q=0; } up(p); if (strcmp(p, "CONTENTS") == 0 || strcmp(p, "CONTENTS.PEEK") == 0) { fi.peek=strchr(p, '.') != NULL; if (applymsgset(&do_fetch, &fi) == 0) { continue; } } else { continue; } writes("-ERR Cannot retrieve message: "); writes(strerror(errno)); writes("\n"); break; } if (!p || !*p) { if (fetch_items && applymsgset(&do_attrfetch, &fetch_items)) { writes("-ERR Cannot retrieve message: "); writes(strerror(errno)); writes("\n"); } else writes("+OK Message retrieved.\n"); } continue; } if (strcmp(p, "COPY") == 0 || strcmp(p, "MOVE") == 0) { int dummy; int domove= *p == 'M'; p=markmsgset(&ptr, &dummy); if (dummy && *p == 0) { p=GETFOLDER(ACL_INSERT ACL_DELETEMSGS ACL_SEEN ACL_WRITE); if (p) { if (strchr(rights_buf, ACL_INSERT[0]) == NULL) { free(p); accessdenied(ACL_INSERT); continue; } if (copyto(p, domove, rights_buf) == 0) { free(p); writes("+OK Messages copied.\n" ); continue; } free(p); } writes("-ERR Cannot copy messages: "); writes(strerror(errno)); writes("\n"); continue; } writes("-ERR Syntax error.\n"); continue; } if (strcmp(p, "SEARCH") == 0) { struct searchinfo *searchInfo=NULL; struct searchinfo *si; struct smap1_search_results searchResults; if ((si=createSearch(&searchInfo, &ptr)) == NULL) { writes("-ERR SEARCH failed: "); writes(strerror(errno)); writes("\n"); free_search(searchInfo); continue; } searchResults.prev_runs=0; searchResults.prev_search_hit=0; searchResults.prev_search_hit_start=0; search_internal(si, searchInfo, "utf-8", 0, smap1_search_cb, &searchResults); if (searchResults.prev_search_hit) smap1_search_cb_range(&searchResults); if (searchResults.prev_runs) writes("\n"); writes("+OK Search completed.\n"); free_search(searchInfo); continue; } writes("-ERR Syntax error.\n"); } writes("* BYE Courier-SMAP server shutting down\n" "+OK LOGOUT completed\n"); writeflush(); }