diff -r 000000000000 -r 9e2cb1ed20b1 maildir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/maildir.c Thu Mar 27 09:53:52 2014 +0500 @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2014 Sunil Nimmagadda + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pop3d.h" + +static int init(struct mdrop *, size_t *, size_t *); +static int retr(struct mdrop *, unsigned int, size_t *, size_t *); +static int update(struct mdrop *); +static int new_to_cur(struct mdrop *); +static int msgcmp(struct msg *, struct msg *); +RB_PROTOTYPE(msgtree, msg, e.t_entry, msgcmp); + +struct m_backend m_backend_maildir = { + init, + retr, + update +}; + +/* + * No resource management on error path as the process is + * killed if an error occurs. + */ +static int +init(struct mdrop *m, size_t *nmsgs, size_t *sz) +{ + SHA1_CTX ctx; + struct stat sb; + char buf[MAXBSIZE]; + DIR *dirp; + struct dirent *dp; + struct msg *msg; + u_char *C; + size_t i; + ssize_t len; + int cur_fd, msg_fd; + + *nmsgs = 0; + *sz = 0; + if (new_to_cur(m) == -1) { + logit(LOG_WARNING, "maildir: move msgs from new to cur failed"); + return (-1); + } + + if ((cur_fd = openat(m->fd, "cur", O_RDONLY)) == -1) { + logit(LOG_CRIT, "maildir: unable to open \"cur\" dir"); + return (-1); + } + + if ((dirp = fdopendir(cur_fd)) == NULL) + return (-1); + + while ((dp = readdir(dirp))) { + if (dp->d_type != DT_REG) + continue; + + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) + continue; + + msg = xcalloc(1, sizeof(*msg), "init"); + if ((msg->u.fname = strdup(dp->d_name)) == NULL) + fatalx("init: strdup"); + + if (fstatat(cur_fd, dp->d_name, &sb, 0) == -1) { + logit(LOG_CRIT, "%s fstatat failed", dp->d_name); + return (-1); + } + + msg->sz = sb.st_size; + if ((msg_fd = openat(cur_fd, dp->d_name, O_RDONLY)) == -1) { + logit(LOG_CRIT, "%s openat failed", dp->d_name); + return (-1); + } + + SHA1Init(&ctx); + while (( len = read(msg_fd, buf, sizeof(buf))) > 0) { + SHA1Update(&ctx, (u_int8_t *)buf, len); + for (C = buf;len--; ++C) + if (*C == '\n') + msg->nlines += 1; + } + + SHA1End(&ctx, msg->hash); + close(msg_fd); + RB_INSERT(msgtree, &m->e.t_msgs, msg); + m->nmsgs += 1; + } + + /* allocate space for nmsgs of struct msg pointers */ + m->msgs_index = xcalloc(m->nmsgs, sizeof(msg), "init"); + *nmsgs = m->nmsgs; + i = 0; + *sz = 0; + RB_FOREACH(msg, msgtree, &m->e.t_msgs) { + m->msgs_index[i++] = msg; + /* calculate maildir size by counting newline as 2 (CRLF) */ + *sz += msg->sz + msg->nlines; + } + + closedir(dirp); + close(cur_fd); + return (0); +} + +static int +new_to_cur(struct mdrop *m) +{ + DIR *dirp; + struct dirent *dp; + int cur_fd, new_fd; + + + if ((cur_fd = openat(m->fd, "cur", O_RDONLY)) == -1) { + logit(LOG_CRIT, "maildir: unable to open \"cur\" dir"); + return (-1); + } + + if ((new_fd = openat(m->fd, "new", O_RDONLY)) == -1) { + logit(LOG_CRIT, "maildir: unable to open \"new\" dir"); + return (-1); + } + + if ((dirp = fdopendir(new_fd)) == NULL) + return (-1); + + while ((dp = readdir(dirp))) { + if (dp->d_type != DT_REG) + continue; + + if (strcmp(dp->d_name, ".") == 0 || + strcmp(dp->d_name, "..") == 0) + continue; + + if(renameat(new_fd, dp->d_name, cur_fd, dp->d_name) == -1) { + logit(LOG_CRIT, "maildir: renameat failed"); + return (-1); + } + } + + closedir(dirp); + close(cur_fd); + close(new_fd); + return (0); +} + +static int +retr(struct mdrop *m, unsigned int idx, size_t *nlines, size_t *offset) +{ + char buf[MAXPATHLEN]; + int fd, r; + + *offset = 0; + r = snprintf(buf, sizeof(buf), "cur/%s", m->msgs_index[idx]->u.fname); + if ((u_int)r >= sizeof(buf)) { + logit(LOG_WARNING, "path too long"); + return (-1); + } + + fd = openat(m->fd, buf, O_RDONLY); + return (fd); +} + +static int +update(struct mdrop *m) +{ + char buf[MAXPATHLEN]; + size_t i, j = 0; + int r; + + for (i = 0; i < m->nmsgs; i++) + if (m->msgs_index[i]->flags & F_DELE) + j += 1; + + if (j == 0) /* nothing to update */ + return (0); + + for (i = 0; i < m->nmsgs; i++) { + if (!(m->msgs_index[i]->flags & F_DELE)) + continue; + + r = snprintf(buf, sizeof(buf), "cur/%s", + m->msgs_index[i]->u.fname); + if ((u_int)r >= sizeof(buf)) { + logit(LOG_WARNING, "path too long"); + return (1); + } + + if (unlinkat(m->fd, buf, 0) == -1) { + logit(LOG_CRIT, "%s unlink failed", buf); + return (1); + } + } + + return (0); +} + +static int +msgcmp(struct msg *m1, struct msg *m2) +{ + return strcmp(m1->u.fname, m2->u.fname); +} + +RB_GENERATE(msgtree, msg, e.t_entry, msgcmp);