ShellBanner
System:Linux MiraNet 3.0.0-14-generic-pae #23-Ubuntu SMP Mon Nov 21 22:07:10 UTC 2011 i686
Software:Apache. PHP/5.3.6-13ubuntu3.10
ID:uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
Safe Mode:OFF
Open_Basedir:OFF
Freespace:27.38 GB of 70.42 GB (38.88%)
MySQL: ON MSSQL: OFF Oracle: OFF PostgreSQL: OFF Curl: OFF Sockets: ON Fetch: OFF Wget: ON Perl: ON
Disabled Functions: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,

/ usr/ src/ courier-0.66.1/ imap/ - drwxrwxrwx

Directory:
Viewing file:     smap.c (78.67 KB)      -rw-rw-rw-
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
** Copyright 2003-2011 Double Precision, Inc.
** See COPYING for distribution information.
*/

#if    HAVE_CONFIG_H
#include    "config.h"
#endif
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <errno.h>
#include    <ctype.h>
#include    <signal.h>
#include    <fcntl.h>
#if    HAVE_UNISTD_H
#include    <unistd.h>
#endif
#if    HAVE_UTIME_H
#include    <utime.h>
#endif
#if TIME_WITH_SYS_TIME
#include    <sys/time.h>
#include    <time.h>
#else
#if HAVE_SYS_TIME_H
#include    <sys/time.h>
#else
#include    <time.h>
#endif
#endif
#if HAVE_LOCALE_H
#include    <locale.h>
#endif

#include    <sys/types.h>
#include    <sys/stat.h>

#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    "unicode/unicode.h"

#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_fromutf7(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; i<curcache->nrecords; 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; i<msgsetp->nranges; 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; i<n; i++)
        {
            int ch=getc(fp);

            if (ch == EOF)
            {
                errno=EIO;
                return -1;
            }
            buffer[i]=ch;
        }

        if (first)
        {
            if (start_body == end_pos)
            {
                writes("{.0} FETCH ");
                writen(msgNum+1);
                writes(" CONTENTS\n.");
            }
            else
            {
                writes("{");
                writen(i);
                writes("/");
                writen(end_pos - start_body);
                writes("} FETCH ");
                writen(msgNum+1);
                writes(" CONTENTS\n");
            }
        }
        else
        {
            writen(i);
            writes("\n");
        }

        first=0;
        writemem(buffer, i);

        start_body += i;
        body_count += i;
    } while (start_body < end_pos);
    writes("\n");
    return 0;
}

struct decodeinfo {
    char buffer[SMAP_BUFSIZ];
    size_t bufptr;

    int first;
    unsigned long msgNum;
    off_t estSize;
};

static void do_dump_decoded_flush(struct decodeinfo *);

static struct rfc2045 *decodeCreateRfc(FILE *fp);
static int do_dump_decoded(const char *, size_t, void *);

static int dump_decoded(FILE *fp, unsigned long msgNum,
            struct rfc2045 *rfcp)
{
    struct decodeinfo myDecodeInfo;
    const char *content_type;
    const char *content_transfer_encoding;
    const char *charset;
        off_t start_pos, end_pos, dummy, start_body;

    struct rfc2045src *src;
    struct rfc2045 *myrfcp=NULL;
    int fd;
    int i;

    if (!rfcp)
    {
        rfcp=myrfcp=decodeCreateRfc(fp);
        if (!rfcp)
            return -1;
    }

    if ((fd=dup(fileno(fp))) < 0)
    {
        if (myrfcp)
            rfc2045_free(myrfcp);
        return -1;
    }

    myDecodeInfo.first=1;
    myDecodeInfo.msgNum=msgNum;
    myDecodeInfo.bufptr=0;

    rfc2045_mimeinfo(rfcp, &content_type, &content_transfer_encoding,
             &charset);
    rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &dummy,
            &dummy);
    myDecodeInfo.estSize=end_pos - start_body;

    if (content_transfer_encoding
        && strlen(content_transfer_encoding) == 6)
    {
        char buf[7];

        strcpy(buf, content_transfer_encoding);
        up(buf);

        if (strcmp(buf, "BASE64") == 0)
            myDecodeInfo.estSize = myDecodeInfo.estSize / 4 * 3;

        /* Better estimate of base64 content */
    }

    src=rfc2045src_init_fd(fd);

    i=src ? rfc2045_decodemimesection(src, rfcp, &do_dump_decoded,
                      &myDecodeInfo):-1;

    do_dump_decoded_flush(&myDecodeInfo);

    if (src)
        rfc2045src_deinit(src);

    close(fd);

    if (i == 0 && myDecodeInfo.first) /* Empty body, punt */
    {
        writes("{.0} FETCH ");
        writen(msgNum+1);
        writes(" CONTENTS\n.");
    }
    writes("\n");
    if (myrfcp)
        rfc2045_free(myrfcp);
    return i;
}

/* Dummy up a rfc2045 structure for retrieving the entire msg body */

static struct rfc2045 *decodeCreateRfc(FILE *fp)
{
    char buffer[SMAP_BUFSIZ];
    struct stat stat_buf;
    int i;
    struct rfc2045 *myrfcp;

    if (fstat(fileno(fp), &stat_buf) < 0)
        return NULL;

    if (fseek(fp, 0L, SEEK_SET) < 0)
        return NULL;

    if (!(myrfcp=rfc2045_alloc()))
        return NULL;

    do
    {
        i=fread(buffer, 1, sizeof(buffer), fp);

        if (i < 0)
        {
            rfc2045_free(myrfcp);
            return NULL;
            }

        if (i == 0)
            break;
        rfc2045_parse(myrfcp, buffer, i);
    } while (myrfcp->workinheader);

    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;
        int rc;

        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(&current_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; i<nn; i++)
                {
                    last_char=buffer[i];

                    if (last_char == '\r')
                        continue;
                    putc((int)(unsigned char)last_char,
                         fp);
                }
            }

            read_timeout(60);
            smap_readline(buffer, bufsize);
            format=buffer;
        }

        if (last_char != '\n')
        {
            putc('\n', fp);
            n++;
        }
    }

    if (n == 0)
    {
        ++n;
        putc('\n', fp);
    }

    if (fflush(fp) < 0 || ferror(fp))
        return 0;
    return n;
}

static void adduid(char *n)
{
    char *q;

    q=strrchr(n, '/');
    if (q)
        n=q+1;

    q=strrchr(n, MDIRSEP[0]);
    if (q)
        *q=0;
    writes("* ADD \"UID=");
    smapword_s(n);
    writes("\"\n");
    if (q)
        *q=MDIRSEP[0];
}

static void senderr(char *errmsg)
{
    writes("-ERR ");
    writes(errmsg);
    writes("\n");
}

static int calc_quota(unsigned long n, void *voidptr)
{
    return do_copy_quota_calc(n+1, 0, voidptr);
}

/* Copy msg to another folder */

static void copieduid(unsigned long n, char *newname)
{
    char *p, *q;

    writes("* COPY ");
    writen(n);
    writes(" \"NEWUID=");

    p=strrchr(newname, '/')+1;

    if ((q=strrchr(p, MDIRSEP[0])) != NULL)
        *q=0;
    
    smapword_s(p);
    writes("\"\n");
}

static int do_copyKeywords(struct libmail_kwMessage *msg,
               const char *destmailbox,
               const char *newname)
{
    char *tmpkname, *newkname;

    if (!msg || !msg->firstEntry)
        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, &current_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(&quotainfo, ".") == 0)
        {
            if (quotainfo.fd >= 0)
                has_quota=1;
            maildir_closequotafile(&quotainfo);
        }

        if (has_quota > 0 && applymsgset( &calc_quota, &cqinfo ))
            has_quota= -1;
    }

    if (has_quota > 0 && cqinfo.nfiles > 0)
    {
        if (maildir_quota_add_start(".", &quotainfo,
                        cqinfo.nbytes,
                        cqinfo.nfiles,
                        getenv("MAILDIRQUOTA")))
        {
            errno=ENOSPC;
            return (-1);
        }

        maildir_quota_add_end(&quotainfo,
                      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];

    imapscan_init(&current_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)
                {
                    add_internaldate=rfc822_parsedt(q);

                    if (add_internaldate)
                        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, &quotainfo, n, 1,
                                        getenv("MAILDIRQUOTA")))
                        {
                            errno=ENOSPC;
                            n=0;
                        }
                        else
                            maildir_quota_add_end(&quotainfo, 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= &current_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, i;

            for (i=0; i<infoptr->nmessages; 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(&current_maildir_info);
                imapscan_init(&current_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(&current_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(&current_maildir_info);
                imapscan_init(&current_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 ((p=getenv("IMAP_ENHANCEDIDLE")) == NULL
                || !atoi(p)
                || 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);

                    t=rfc822_parsedt(p+13);

                    if (t &&
                        (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();
}
Command:
Quick Commands:
Upload:
[OK] Max size: 100MB
PHP Filesystem: <@ Ú
Search File:
regexp
Create File:
Overwrite [OK]
View File:
Mass Defacement:
[+] Main Directory: [+] Defacement Url:
LmfaoX Shell - Private Build [BETA] - v0.1 -; Generated: 0.2225 seconds