/* elmo - ELectronic Mail Operator Copyright (C) 2003, 2004 rzyjontko 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; version 2. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ---------------------------------------------------------------------- This module is a wrapper for multiple maildrops formats. There are 2 kinds of functions in this module. Some of them may check the box type in wrapbox_marray. The others need to behave well, no matter what kind of box do they get (or even no box at all). If you want to register a new format or extend an exisiting one, then follow these steps: 0. suppose your format name is foo 1. include foo.h 2. register BOX_FOO in mail.h 3. add functions 4. there is no strict prototype of every function, please feed yours whith any data you find relevant 5. add your name to copyright notice above and to the list below Please look at the functions below. There is an explanation, of how to change them. People responsible for box types: maildir - rzyjontko mbox - rzyjontko */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #include #include #include #include #include #include "mail.h" #include "wrapbox.h" #include "maildir.h" #include "mbox.h" #include "mime.h" #include "rmime.h" #include "error.h" #include "mybox.h" #include "xmalloc.h" #include "folder.h" #include "file.h" #include "pop.h" #include "hash.h" #include "ask.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #define PREAMBLE do { if (wrapbox_marray == NULL) return; } while (0) /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ struct size_pair { int total; int unread; }; /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ /* This is used to cache information about box sizes. This table holds size_pair structures mapped to box names. */ static htable_t *box_cache = NULL; /* This tells us if user whishes to count messages in mbox files. */ static int count_mbox_mails = 0; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /** * this is where mails' info is stored */ mail_array_t *wrapbox_marray = NULL; /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static int size_cache_lookup (const char *box, int *unread) { entry_t *entry = htable_lookup (box_cache, box); struct size_pair *data; if (entry == NULL) return -1; data = (struct size_pair *) entry->content; if (data->total == -1) return -1; if (unread) *unread = data->unread; return data->total; } static void size_cache_insert (const char *box, int total, int unread) { entry_t *entry; struct size_pair *data = xmalloc (sizeof (struct size_pair)); data->total = total; data->unread = unread; entry = htable_insert (box_cache, box, data); if (entry->content != data){ xfree (entry->content); entry->content = data; } } static void size_cache_remove (const char *box) { const char *seek; entry_t *entry; struct size_pair *data; seek = strrchr (box, '/'); if (seek == NULL) seek = box; else seek++; entry = htable_lookup (box_cache, seek); if (entry == NULL) return; data = (struct size_pair *) entry->content; data->total = -1; data->unread = -1; } static void size_cache_remove_file (const char *file) { char *seek; char *tmp; seek = strrchr (file, '/'); if (seek == NULL){ size_cache_remove (file); return; } *seek = '\0'; tmp = strrchr (file, '/'); if (tmp == NULL){ size_cache_remove (file); *seek = '/'; return; } *tmp = '\0'; size_cache_remove (file); *seek = '/'; *tmp = '/'; } static void free_temp_resources (void) { /* maildir */ maildir_free_resources (); /* mbox */ mbox_free_resources (); if (wrapbox_marray){ size_cache_remove (wrapbox_marray->path); mail_array_destroy (wrapbox_marray); wrapbox_marray = NULL; } } /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ void wrapbox_init (void) { char *answer = ask_for_default ("count_mbox", 0); box_cache = htable_create (4); if (answer && strcasecmp (answer, "yes") == 0) count_mbox_mails = 1; } /** * foo_free_resources should only free it's local resources (if any) * do not flush any data - just free unnecessary memory */ void wrapbox_free_resources (void) { free_temp_resources (); if (box_cache) htable_destroy (box_cache, xfree); box_cache = NULL; } /** * this function must be able to recognize box format depending only on its * name, so a sequence is very important */ mail_array_t * wrapbox_open_box (const char *name) { mail_array_t *new_marray = NULL; /* maildir */ new_marray = maildir_read_dir (name); if (new_marray) return new_marray; /* mbox */ if (mbox_may_be_valid (name)){ new_marray = mbox_read_file (name); } return new_marray; } int wrapbox_read_box (const char *name) { mail_array_t *new_marray; new_marray = wrapbox_open_box (name); if (new_marray){ free_temp_resources (); wrapbox_marray = new_marray; return 0; } return 1; } /** * foo_mail_header should put header into *place */ int wrapbox_mail_header (mail_t *mail, char **place) { int ret = 1; if (wrapbox_marray == NULL || mail == NULL) return 1; switch (wrapbox_marray->type){ case BOX_MBOX: ret = mbox_mail_header (mail, place, wrapbox_marray->path); break; case BOX_MAILDIR: ret = maildir_mail_header (mail, place); break; default: return 1; } return ret; } /** * use the chosen mime * foo_mail_body should put body in *place, but not decode it * return 1 if something went wrong */ str_t * wrapbox_mail_body (mail_t *mail, mime_t *mime, int no_decrypt) { int ret = 1; char *place; char *file; if (wrapbox_marray == NULL || mail == NULL) return NULL; errno = 0; if (mime == NULL){ if (! no_decrypt){ mime_decrypt (mail->mime); } mime = mime_first_text (mail->mime); if (mime == NULL){ return NULL; } } switch (wrapbox_marray->type){ case BOX_MBOX: file = (mail->mime->d_file) ? mail->mime->d_file : wrapbox_marray->path; ret = mbox_mail_body (mail, & place, mime, file); break; case BOX_MAILDIR: file = (mail->mime->d_file) ? mail->mime->d_file : mail->place.file_name; ret = maildir_mail_body (mail, & place, mime, file); break; default: return NULL; } return mime_decode (mime, place, 1); } /** * foo_fetch_single must save this mail into a file and return its name */ char * wrapbox_fetch_single (mail_t *mail) { switch (mail->type){ case BOX_MAILDIR: return maildir_fetch_single (mail); case BOX_MBOX: if (wrapbox_marray == NULL) return NULL; return mbox_fetch_single (wrapbox_marray->path, mail); default: return NULL; } return NULL; } /** * foo_fetch_where must return a valid file name, where elmo can * fetch new message, it will be then used in wrapbox_deliver_to */ char * wrapbox_fetch_where (int new) { if (wrapbox_marray == NULL) return NULL; switch (wrapbox_marray->type){ case BOX_MAILDIR: return maildir_fetch_where (wrapbox_marray->path, new); case BOX_MBOX: return NULL; default: return NULL; } return NULL; } /** * foo_deliver_to must deliver a message stored in file to a box * return codes: * 0 - delivered and destroyed file * 1 - couldn't deliver (maybe bad box type) * 2 - delivered and left the file unchanged */ int wrapbox_deliver_to (const char *file, const char *box) { int ret; size_cache_remove (box); size_cache_remove_file (file); /* maildir */ ret = maildir_deliver_to (file, box); if (ret == 0 || ret == 2) return ret; return 1; } /** * foo_remove must permanently remove a message */ void wrapbox_remove (mail_t *mail) { PREAMBLE; if (mail == NULL) return; switch (mail->type){ case BOX_INVALID: return; case BOX_MAILDIR: size_cache_remove_file (mail->place.file_name); maildir_remove (mail); return; default: return; } } /** * this function must not depend on box type, do not change it */ int wrapbox_move_mail_to (mail_t *mail, const char *box) { int ret; char *file; if (mail == NULL || box == NULL) return 1; mail->flags &= ~ FLAG_FLAGGED; wrapbox_apply_flag (mail); file = wrapbox_fetch_single (mail); if (file == NULL) return 1; if (box){ ret = wrapbox_deliver_to (file, box); if (ret == 0 || ret == 1){ xfree (file); return ret; } } wrapbox_remove (mail); xfree (file); return 0; } /** * return mail size in bytes */ int wrapbox_mail_size (mail_t *mail) { switch (mail->type){ case BOX_MBOX: return mbox_mail_size (mail); case BOX_MAILDIR: return maildir_mail_size (mail); case BOX_POP3: return pop_mail_size (mail->place.num); default: return 0; } } /** * foo_mail_count should check if unread != NULL and return -1 if it is * not a box of type foo, */ int wrapbox_box_mail_count (const char *box, int *unread) { int ret; char *tmp = NULL; const char *the_box; ret = size_cache_lookup (box, unread); if (ret != -1) return ret; if (*box != '/'){ tmp = file_with_dir (mybox_dir, box); the_box = tmp; } else { the_box = box; } /* maildir */ ret = maildir_box_mail_count (the_box, unread); if (ret != -1){ if (tmp) xfree (tmp); size_cache_insert (box, ret, *unread); return ret; } /* mbox */ if (count_mbox_mails) ret = mbox_box_mail_count (the_box, unread); if (ret != -1){ if (tmp) xfree (tmp); size_cache_insert (box, ret, *unread); return ret; } /* fallback */ if (unread) *unread = 0; if (tmp) xfree (tmp); return 0; } /** * foo_refresh could read box once more but try to avoid it if it was * unnecessary, wrapbox_marray must be left unchanged on any failure */ void wrapbox_refresh (void) { mail_array_t *new_marray = NULL; PREAMBLE; switch (wrapbox_marray->type){ case BOX_INVALID: break; case BOX_MAILDIR: new_marray = maildir_refresh (wrapbox_marray); if (new_marray) mail_array_substitute (wrapbox_marray, new_marray); new_marray = NULL; break; default: break; } } void wrapbox_apply_flag (mail_t *mail) { PREAMBLE; switch (wrapbox_marray->type){ case BOX_MAILDIR: maildir_apply_flag (mail); break; default: break; } } /** * foo_apply_flags must compare flags of every message in a box with * those available on filesystem and update them */ void wrapbox_apply_flags (void) { PREAMBLE; switch (wrapbox_marray->type){ case BOX_MAILDIR: maildir_apply_flags (wrapbox_marray); break; default: break; } } /** * this returns number of messages in wrapbox_marray or -1 if none */ int wrapbox_mail_count (void) { if (wrapbox_marray) return wrapbox_marray->count; return -1; } /** * foo_dump_place should dump 'place' member of 'mail' structure to * the given memchunk using its functions * * foo_dump_file_name should return an allocated string with name of * the cache file */ void wrapbox_dump_box (void) { int fd; FILE *fp; char *fname; PREAMBLE; if (mybox_no_cache (wrapbox_marray->path)) return; switch (wrapbox_marray->type){ case BOX_MAILDIR: fname = maildir_dump_file_name (wrapbox_marray); break; default: return; } fd = open (fname, O_WRONLY | O_CREAT | O_TRUNC, 0600); fp = fdopen (fd, "w"); if (fp == NULL){ error_ (errno, "%s", fname); return; } xfree (fname); switch (wrapbox_marray->type){ case BOX_MAILDIR: mail_array_dump (wrapbox_marray, fp, maildir_dump_place); break; default: break; } fclose (fp); } void wrapbox_empty_trash (void) { char *trash = mybox_trash (); PREAMBLE; switch (wrapbox_marray->type){ case BOX_MAILDIR: maildir_empty_box (trash); folder_update(); break; default: break; } xfree (trash); } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE wrapbox.c * ****************************************************************************/