/* $Id: fetch-maildir.c,v 1.84 2007/08/30 10:45:06 nicm Exp $ */ /* * Copyright (c) 2006 Nicholas Marriott * * 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 MIND, 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 "fdm.h" #include "fetch.h" int fetch_maildir_commit(struct account *, struct mail *); void fetch_maildir_abort(struct account *); u_int fetch_maildir_total(struct account *); void fetch_maildir_desc(struct account *, char *, size_t); void fetch_maildir_free(void *); int fetch_maildir_makepaths(struct account *); void fetch_maildir_freepaths(struct account *); int fetch_maildir_poll(struct account *); int fetch_maildir_state_init(struct account *, struct fetch_ctx *); int fetch_maildir_state_build(struct account *, struct fetch_ctx *); int fetch_maildir_state_next(struct account *, struct fetch_ctx *); int fetch_maildir_state_open(struct account *, struct fetch_ctx *); int fetch_maildir_state_mail(struct account *, struct fetch_ctx *); struct fetch fetch_maildir = { "maildir", fetch_maildir_state_init, NULL, fetch_maildir_commit, fetch_maildir_abort, fetch_maildir_total, fetch_maildir_desc }; void fetch_maildir_free(void *ptr) { struct fetch_maildir_mail *aux = ptr; xfree(aux); } /* Make an array of all the paths to visit. */ int fetch_maildir_makepaths(struct account *a) { struct fetch_maildir_data *data = a->data; char *path; u_int i, j; glob_t g; struct stat sb; data->paths = xmalloc(sizeof *data->paths); ARRAY_INIT(data->paths); for (i = 0; i < ARRAY_LENGTH(data->maildirs); i++) { path = ARRAY_ITEM(data->maildirs, i); if (glob(path, GLOB_BRACE|GLOB_NOCHECK, NULL, &g) != 0) { log_warn("%s: glob(\"%s\")", a->name, path); goto error; } if (g.gl_pathc < 1) fatalx("glob returned garbage"); for (j = 0; j < (u_int) g.gl_pathc; j++) { xasprintf(&path, "%s/cur", g.gl_pathv[j]); ARRAY_ADD(data->paths, path); if (stat(path, &sb) != 0) { log_warn("%s: %s", a->name, path); goto error; } if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; log_warn("%s: %s", a->name, path); goto error; } xasprintf(&path, "%s/new", g.gl_pathv[j]); ARRAY_ADD(data->paths, path); if (stat(path, &sb) != 0) { log_warn("%s", path); goto error; } if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; log_warn("%s", path); goto error; } } globfree(&g); } return (0); error: fetch_maildir_freepaths(a); return (-1); } /* Free the array. */ void fetch_maildir_freepaths(struct account *a) { struct fetch_maildir_data *data = a->data; u_int i; for (i = 0; i < ARRAY_LENGTH(data->paths); i++) xfree(ARRAY_ITEM(data->paths, i)); ARRAY_FREEALL(data->paths); } /* Count maildir total. */ int fetch_maildir_poll(struct account *a) { struct fetch_maildir_data *data = a->data; u_int i; char *path, entry[MAXPATHLEN]; DIR *dirp; struct dirent *dp; struct stat sb; data->total = 0; for (i = 0; i < ARRAY_LENGTH(data->paths); i++) { path = ARRAY_ITEM(data->paths, i); log_debug("%s: trying path: %s", a->name, path); if ((dirp = opendir(path)) == NULL) { log_warn("%s: %s: opendir", a->name, path); return (-1); } while ((dp = readdir(dirp)) != NULL) { if (dp->d_type == DT_REG) { data->total++; continue; } if (dp->d_type != DT_UNKNOWN) continue; if (mkpath(entry, sizeof entry, "%s/%s", path, dp->d_name) != 0) { log_warn("%s: %s: printpath", a->name, path); closedir(dirp); return (-1); } if (stat(entry, &sb) != 0) { log_warn("%s: %s: stat", a->name, entry); closedir(dirp); return (-1); } if (!S_ISREG(sb.st_mode)) continue; data->total++; } if (closedir(dirp) != 0) { log_warn("%s: %s: closedir", a->name, path); return (-1); } } return (0); } /* Commit mail. */ int fetch_maildir_commit(struct account *a, struct mail *m) { struct fetch_maildir_mail *aux; aux = m->auxdata; if (m->decision == DECISION_DROP && unlink(aux->path) != 0) { log_warn("%s: %s: unlink", a->name, aux->path); return (FETCH_ERROR); } return (FETCH_AGAIN); } /* Abort fetch. */ void fetch_maildir_abort(struct account *a) { struct fetch_maildir_data *data = a->data; if (data->dirp != NULL) closedir(data->dirp); fetch_maildir_freepaths(a); } /* Return total mails. */ u_int fetch_maildir_total(struct account *a) { struct fetch_maildir_data *data = a->data; return (data->total); } /* Initialise maildir fetch context. */ int fetch_maildir_state_init(struct account *a, struct fetch_ctx *fctx) { struct fetch_maildir_data *data = a->data; if (fetch_maildir_makepaths(a) != 0) return (FETCH_ERROR); if (ARRAY_EMPTY(data->paths)) { log_warnx("%s: no maildirs found", a->name); return (-1); } data->index = 0; data->dirp = NULL; /* Poll counts mails and exits. */ if (fctx->flags & FETCH_POLL) { if (fetch_maildir_poll(a) != 0) return (FETCH_ERROR); fetch_maildir_freepaths(a); return (FETCH_EXIT); } fctx->state = fetch_maildir_state_open; return (0); } /* Next state. Move to next path. */ int fetch_maildir_state_next(struct account *a, struct fetch_ctx *fctx) { struct fetch_maildir_data *data = a->data; if (data->index <= ARRAY_LENGTH(data->paths)) data->index++; if (data->index == ARRAY_LENGTH(data->paths)) { fetch_maildir_freepaths(a); return (FETCH_EXIT); } fctx->state = fetch_maildir_state_open; return (FETCH_AGAIN); } /* Open state. */ int fetch_maildir_state_open(struct account *a, unused struct fetch_ctx *fctx) { struct fetch_maildir_data *data = a->data; char *path; path = ARRAY_ITEM(data->paths, data->index); /* Open the directory. */ log_debug2("%s: trying path: %s", a->name, path); if ((data->dirp = opendir(path)) == NULL) { log_warn("%s: %s: opendir", a->name, path); return (FETCH_ERROR); } fctx->state = fetch_maildir_state_mail; return (FETCH_AGAIN); } /* Mail state. Find and read mail file. */ int fetch_maildir_state_mail(struct account *a, struct fetch_ctx *fctx) { struct fetch_maildir_data *data = a->data; struct mail *m = fctx->mail; struct fetch_maildir_mail *aux; struct dirent *dp; char *path, *maildir, name[MAXPATHLEN]; struct stat sb; uintmax_t size; int fd; ssize_t n; path = ARRAY_ITEM(data->paths, data->index); restart: /* Read the next dir entry. */ dp = readdir(data->dirp); if (dp == NULL) { if (closedir(data->dirp) != 0) { log_warn("%s: %s: closedir", a->name, path); return (FETCH_ERROR); } data->dirp = NULL; fctx->state = fetch_maildir_state_next; return (FETCH_AGAIN); } if (mkpath(name, sizeof name, "%s/%s", path, dp->d_name) != 0) { log_warn("%s: %s: printpath", a->name, path); return (FETCH_ERROR); } if (stat(name, &sb) != 0) { log_warn("%s: %s: stat", a->name, name); return (FETCH_ERROR); } if (!S_ISREG(sb.st_mode)) goto restart; /* Open the mail. */ if (mail_open(m, sb.st_size) != 0) { log_warn("%s: failed to create mail", a->name); return (FETCH_ERROR); } /* Got a valid entry, start reading it. */ log_debug2("%s: reading mail from: %s", a->name, name); size = sb.st_size; if (sb.st_size <= 0) { m->size = 0; return (FETCH_MAIL); } else if (size > SIZE_MAX || size > conf.max_size) { m->size = SIZE_MAX; return (FETCH_MAIL); } /* Open the file. */ if ((fd = open(name, O_RDONLY, 0)) == -1) { log_warn("%s: %s: open", a->name, name); return (FETCH_ERROR); } /* Add the tags. */ maildir = xbasename(xdirname(path)); default_tags(&m->tags, maildir); add_tag(&m->tags, "maildir", "%s", maildir); /* Add aux data. */ aux = xmalloc(sizeof *aux); strlcpy(aux->path, name, sizeof aux->path); m->auxdata = aux; m->auxfree = fetch_maildir_free; /* Read the mail. */ if ((n = read(fd, m->data, size)) == -1 || (size_t) n != size) { close(fd); log_warn("%s: %s: read", a->name, name); return (FETCH_ERROR); } close(fd); log_debug2("%s: read %ju bytes", a->name, size); m->size = size; return (FETCH_MAIL); } void fetch_maildir_desc(struct account *a, char *buf, size_t len) { struct fetch_maildir_data *data = a->data; char *maildirs; maildirs = fmt_strings("maildir ", data->maildirs); strlcpy(buf, maildirs, len); xfree(maildirs); }