static const char rcsid[]="$Id$"; /* * Copyright (C) 1996-8 Michael R. Elkins * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * This file contains routines specific to MH and ``maildir'' style mailboxes */ #include "mutt.h" #include "mx.h" #include "mailbox.h" #include "copy.h" #include "buffy.h" #include "sort.h" #include #include #include #include #include #include #include struct maildir { HEADER *h; char *canon_fname; struct maildir *next; }; static void maildir_free_entry(struct maildir **md) { if(!md || !*md) return; safe_free((void **) &(*md)->canon_fname); if((*md)->h) mutt_free_header(&(*md)->h); safe_free((void **) md); } static void maildir_free_maildir(struct maildir **md) { struct maildir *p, *q; if(!md || !*md) return; for(p = *md; p; p = q) { q = p->next; maildir_free_entry(&p); } } static void maildir_parse_flags(HEADER *h, const char *path) { char *p; h->flagged = 0; h->read = 0; h->replied = 0; if ((p = strrchr (path, ':')) != NULL && strncmp (p + 1, "2,", 2) == 0) { p += 3; while (*p) { switch (*p) { case 'F': h->flagged = 1; break; case 'S': /* seen */ h->read = 1; break; case 'R': /* replied */ h->replied = 1; break; } p++; } } } static void maildir_update_mtime(CONTEXT *ctx) { char buf[_POSIX_PATH_MAX]; struct stat st; if(ctx->magic == M_MAILDIR) { snprintf(buf, sizeof(buf), "%s/%s", ctx->path, "cur"); if(stat (buf, &st) == 0) ctx->mtime_cur = st.st_mtime; snprintf(buf, sizeof(buf), "%s/%s", ctx->path, "new"); } else strfcpy(buf, ctx->path, sizeof(buf)); if(stat(buf, &st) == 0) ctx->mtime = st.st_mtime; } static HEADER *maildir_parse_message(int magic, const char *fname, int is_old) { FILE *f; HEADER *h = NULL; struct stat st; if ((f = fopen (fname, "r")) != NULL) { h = mutt_new_header(); h->env = mutt_read_rfc822_header (f, h, 0); fstat (fileno (f), &st); fclose (f); if (!h->received) h->received = h->date_sent; if (h->content->length <= 0) h->content->length = st.st_size - h->content->offset; h->index = -1; if (magic == M_MAILDIR) { /* maildir stores its flags in the filename, so ignore the flags in * the header of the message */ h->old = is_old; maildir_parse_flags(h, fname); } } return h; } /* note that this routine will _not_ modify the context given by ctx. */ static int maildir_parse_entry(CONTEXT *ctx, struct maildir ***last, const char *subdir, const char *fname, int *count, int is_old) { struct maildir *entry; HEADER *h; char buf[_POSIX_PATH_MAX]; if(subdir) snprintf(buf, sizeof(buf), "%s/%s/%s", ctx->path, subdir, fname); else snprintf(buf, sizeof(buf), "%s/%s", ctx->path, fname); if((h = maildir_parse_message(ctx->magic, buf, is_old)) != NULL) { if(count) { (*count)++; if (!ctx->quiet && ReadInc && ((*count % ReadInc) == 0 || *count == 1)) mutt_message (_("Reading %s... %d"), ctx->path, *count); } if (subdir) { snprintf (buf, sizeof (buf), "%s/%s", subdir, fname); h->path = safe_strdup (buf); } else h->path = safe_strdup (fname); entry = safe_calloc(sizeof(struct maildir), 1); entry->h = h; **last = entry; *last = &entry->next; return 0; } return -1; } /* Ignore the garbage files. A valid MH message consists of only * digits. Deleted message get moved to a filename with a comma before * it. */ int mh_valid_message (const char *s) { for (; *s; s++) { if (!isdigit ((unsigned char) *s)) return 0; } return 1; } static int maildir_parse_dir(CONTEXT *ctx, struct maildir ***last, const char *subdir, int *count) { DIR *dirp; struct dirent *de; char buf[_POSIX_PATH_MAX]; int is_old = 0; if(subdir) { snprintf(buf, sizeof(buf), "%s/%s", ctx->path, subdir); is_old = (strcmp("cur", subdir) == 0) && option(OPTMARKOLD); } else strfcpy(buf, ctx->path, sizeof(buf)); if((dirp = opendir(buf)) == NULL) return -1; while ((de = readdir (dirp)) != NULL) { if ((ctx->magic == M_MH && !mh_valid_message(de->d_name)) || (ctx->magic == M_MAILDIR && *de->d_name == '.')) continue; /* FOO - really ignore the return value? */ maildir_parse_entry(ctx, last, subdir, de->d_name, count, is_old); } closedir(dirp); return 0; } static void maildir_add_to_context(CONTEXT *ctx, struct maildir *md) { while(md) { if(md->h) { if(ctx->msgcount == ctx->hdrmax) mx_alloc_memory(ctx); ctx->hdrs[ctx->msgcount] = md->h; ctx->hdrs[ctx->msgcount]->index = ctx->msgcount; ctx->size += md->h->content->length + md->h->content->offset - md->h->content->hdr_offset; md->h = NULL; mx_update_context(ctx); } md = md->next; } } static void maildir_move_to_context(CONTEXT *ctx, struct maildir **md) { maildir_add_to_context(ctx, *md); maildir_free_maildir(md); } /* Read a MH/maildir style mailbox. * * args: * ctx [IN/OUT] context for this mailbox * subdir [IN] NULL for MH mailboxes, otherwise the subdir of the * maildir mailbox to read from */ int mh_read_dir (CONTEXT *ctx, const char *subdir) { struct maildir *md; struct maildir **last; int count; md = NULL; last = &md; count = 0; maildir_update_mtime(ctx); if(maildir_parse_dir(ctx, &last, subdir, &count) == -1) return -1; maildir_move_to_context(ctx, &md); return 0; } /* read a maildir style mailbox */ int maildir_read_dir (CONTEXT * ctx) { /* maildir looks sort of like MH, except that there are two subdirectories * of the main folder path from which to read messages */ if (mh_read_dir (ctx, "new") == -1 || mh_read_dir (ctx, "cur") == -1) return (-1); return 0; } /* Open a new (unique) message in a maildir mailbox. In order to avoid the * need for locks, the filename is generated in such a way that it is unique, * even over NFS: