summaryrefslogtreecommitdiff
path: root/contrib/carmel/c-client/carmel.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/carmel/c-client/carmel.c')
-rw-r--r--contrib/carmel/c-client/carmel.c1232
1 files changed, 1232 insertions, 0 deletions
diff --git a/contrib/carmel/c-client/carmel.c b/contrib/carmel/c-client/carmel.c
new file mode 100644
index 00000000..5265097d
--- /dev/null
+++ b/contrib/carmel/c-client/carmel.c
@@ -0,0 +1,1232 @@
+/*----------------------------------------------------------------------
+
+ T H E C A R M E L M A I L D R I V E R
+
+ Author(s): Laurence Lundblade
+ Baha'i World Centre
+ Data Processing
+ Haifa, Israel
+ Internet: lgl@cac.washington.edu or laurence@bwc.org
+ September 1992
+
+ Last Edited: Aug 31, 1994
+
+The "Carmel" mail format is cutsom mail file format that has messages
+saved in individual files and an index file that references them. It
+has also been called the "pod" or "vmail" format. This is probably not
+of interest to anyone away from it's origin, though the Carmel2 format
+might be.
+
+This driver reads and writes the format in conjunction with the carmel2
+driver. The carmel2 file driver does most of the work as most of the
+operations are done on the created auxiliary carmel2 index and the
+carmel index is updated when the mail file is closed.
+
+ ----------------------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/dir.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "carmel2.h"
+#include "carmel.h"
+#include "rfc822.h"
+#include "misc.h"
+
+
+/* Driver dispatch used by MAIL. The carmel 2driver shares most of
+ its data structures and functions with the carmel driver
+ */
+
+DRIVER carmeldriver = {
+ "carmel",
+ (DRIVER *) NIL, /* next driver */
+ carmel_valid, /* mailbox is valid for us */
+ carmel_parameters, /* Set parameters */
+ carmel_find, /* find mailboxes */
+ carmel_find_bboards, /* find bboards */
+ carmel_find_all, /* find all mailboxes */
+ carmel_find_bboards, /* find all the bboards ; just a noop here */
+ carmel_subscribe, /* subscribe to mailbox */
+ carmel_unsubscribe, /* unsubscribe from mailbox */
+ carmel_subscribe_bboard, /* subscribe to bboard */
+ carmel_unsubscribe_bboard, /* unsubscribe from bboard */
+ carmel_create, /* create mailbox */
+ carmel_delete, /* delete mailbox */
+ carmel_rename, /* rename mailbox */
+ carmel_open, /* open mailbox */
+ carmel_close, /* close mailbox */
+ carmel2_fetchfast, /* fetch message "fast" attributes */
+ carmel2_fetchflags, /* fetch message flags */
+ carmel2_fetchstructure, /* fetch message envelopes */
+ carmel2_fetchheader, /* fetch message header only */
+ carmel2_fetchtext, /* fetch message body only */
+ carmel2_fetchbody, /* fetch message body section */
+ carmel2_setflag, /* set message flag */
+ carmel2_clearflag, /* clear message flag */
+ carmel2_search, /* search for message based on criteria */
+ carmel2_ping, /* ping mailbox to see if still alive */
+ carmel_check, /* check for new messages */
+ carmel_expunge, /* expunge deleted messages */
+ carmel2_copy, /* copy messages to another mailbox */
+ carmel2_move, /* move messages to another mailbox */
+ carmel_append, /* Append message to a mailbox */
+ carmel2_gc, /* garbage collect stream */
+};
+
+MAILSTREAM carmelproto ={&carmeldriver}; /* HACK for default_driver in pine.c*/
+
+#ifdef ANSI
+static int carmel_sift_files(struct direct *);
+static void carmel_recreate_index(MAILSTREAM *);
+static char *carmel_calc_paths(int, char *, int);
+static int vmail2carmel(char *, char *, MAILSTREAM *, char *, char *);
+static int write_carmel_index(FILE *, MESSAGECACHE *, ENVELOPE *);
+static int carmel_reset_index_list();
+static void carmel_kill_locks(char *);
+#else
+static int carmel_sift_files();
+static void carmel_recreate_index();
+static char *carmel_calc_paths();
+static int vmail2carmel();
+static int write_carmel_index();
+static int carmel_reset_index_list();
+static void carmel_kill_locks();
+#endif
+
+
+
+extern char carmel_20k_buf[20000], carmel_path_buf[], carmel_error_buf[];
+
+static int disk_setup_checked = 0;
+
+/*----------------------------------------------------------------------
+ Carmel mail validate mailbox
+
+Args: name -- name to check
+
+Returns: our driver if name is valid, otherwise calls valid in next driver
+ ---*/
+
+DRIVER *carmel_valid (name)
+ char *name;
+{
+ return(carmel_isvalid(name) ? &carmeldriver : NIL);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Check and see if a named mailbox is a valid carmel mail index
+
+Args: name -- name or path of mail file. It is a FQN, e.g. #carmel#sent-mail
+
+Returns: 0 if it is not a valid mailbox
+ 1 if it is.
+
+ A carmel index must be a regular file, readable, and have the second word in
+the file be "index", upper or lower case.
+ ----*/
+int
+carmel_isvalid (name)
+ char *name;
+{
+ struct stat sbuf;
+ int fd;
+ char *p, *carmel_index_file;
+ struct carmel_mb_name *parsed_name;
+
+ if(!disk_setup_checked){
+ disk_setup_checked = 1;
+ carmel_init(NULL);
+ }
+
+ /* Don't accept plain "inbox". If we do then the carmel driver
+ takes over all other drivers linked after it. This way the
+ inbox-path in Pine can be set to #carmel#MAIL to make the
+ inbox the carmel one
+ */
+
+ parsed_name = carmel_parse_mb_name(name, '\0');
+ if(parsed_name == NULL)
+ return(0);
+
+ carmel_free_mb_name(parsed_name);
+
+ carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, name, 0);
+
+ if(carmel_index_file == NULL)
+ return(0); /* Will get two error messages here, one from dummy driver */
+
+ if(stat(carmel_index_file, &sbuf) < 0)
+ return(1); /* If the name matches and it doesn't exist, it's OK */
+
+ if(!(sbuf.st_mode & S_IFREG))
+ return(0);
+
+ fd = open(carmel_index_file, O_RDONLY);
+ if(fd < 0)
+ return(0);
+
+ if(read(fd, carmel_20k_buf, 200) <= 0)
+ return(0);
+ carmel_20k_buf[199] = '\0';
+
+ close(fd);
+
+ for(p = carmel_20k_buf; *p && !isspace(*p); p++); /* frist word */
+ for(; *p && isspace(*p); p++); /* space */
+ lcase(p); /* lower case the "index" */
+ if(!*p || strncmp(p, "index", 5))
+ return(0);
+
+ return(1);
+}
+
+
+
+
+/*----------------------------------------------------------------------
+
+ ----*/
+void *
+carmel_parameters(function, value)
+ long function;
+ char *value;
+{}
+
+
+
+/*----------------------------------------------------------------------
+ This is used by scandir to determine which files in the directory
+ are treated as mail files
+ ----*/
+static char *sift_pattern = NULL;
+static int
+carmel_sift_files(dir)
+ struct direct *dir;
+{
+ if(dir->d_name[0] == '.')
+ return(0);
+ else if(strcmp(dir->d_name, "MAIL") == 0)
+ /* Never return the inbox. "INBOX" is always included by default */
+ return(0);
+ else if(pmatch(dir->d_name, sift_pattern))
+ return(1);
+ else
+ return(0);
+}
+
+int alphasort();
+
+/* ----------------------------------------------------------------------
+ Carmel find list of mail boxes
+ Args: mail stream
+ pattern to search
+
+ This scans the ".vmail/index" directory for a list of folders
+ (indexes in vmail terms).
+
+BUG -- doesn't really match patterns (yet)
+ ----*/
+void
+carmel_find(stream, pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ char tmp[MAILTMPLEN];
+ struct direct **namelist, **n;
+ int num;
+ struct carmel_mb_name *parsed_name;
+ struct passwd *pw;
+
+ parsed_name = carmel_parse_mb_name(pat, '\0');
+
+ if(parsed_name == NULL)
+ return;
+
+ if(parsed_name->user == NULL) {
+ sprintf(tmp, "%s/%s", myhomedir(), CARMEL_INDEX_DIR);
+ } else {
+ pw = getpwnam(parsed_name->user);
+ if(pw == NULL) {
+ sprintf(carmel_error_buf,
+ "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
+ pat, parsed_name->user);
+ mm_log(carmel_error_buf, ERROR);
+ return;
+ }
+ sprintf(tmp, "%s/%s", pw->pw_dir, CARMEL_INDEX_DIR);
+ }
+
+ sift_pattern = parsed_name->mailbox;
+
+ num = scandir(tmp, &namelist, carmel_sift_files, alphasort);
+
+ if(num <= 0) {
+/* sprintf(carmel_error_buf, "Error finding mailboxes \"%s\": %s",
+ pat, strerror(errno));
+ mm_log(carmel_error_buf, ERROR);*/
+ return;
+ }
+
+ for(n = namelist; num > 0; num--, n++) {
+ if(parsed_name->user == NULL) {
+ sprintf(tmp, "%s%s%c%s", CARMEL_NAME_PREFIX, parsed_name->version,
+ CARMEL_NAME_CHAR, (*n)->d_name);
+ } else {
+ sprintf(tmp, "%s%s%c%s%c%s", CARMEL_NAME_PREFIX,
+ parsed_name->version, CARMEL_NAME_CHAR,
+ parsed_name->user, CARMEL_NAME_CHAR,
+ (*n)->d_name);
+ }
+ mm_mailbox(tmp);
+ free(*n);
+ }
+ free(namelist);
+ carmel_free_mb_name(parsed_name);
+}
+
+
+/*----------------------------------------------------------------------
+ Find_all is the same for find in the carmel driver; there is no
+difference between subscribed and unsubscribed mailboxes
+ ----*/
+void
+carmel_find_all (stream, pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ carmel_find(stream, pat);
+}
+
+
+/*----------------------------------------------------------------------
+ Find bulliten boards is a no-op for carmel, there are none (yet)
+ */
+void
+carmel_find_bboards (stream,pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ /* Always a no-op */
+}
+
+
+/*----------------------------------------------------------------------
+ No subscription management for the carmel format. Probably should work
+ with the .mailbox list format, but that's probably implemented in mail.c
+ ----*/
+long carmel_subscribe() {}
+long carmel_unsubscribe() {}
+long carmel_subscribe_bboard() {}
+long carmel_unsubscribe_bboard() {}
+
+
+
+/*----------------------------------------------------------------------
+ Create a carmel folder
+
+Args: stream -- Unused here
+ mailbox -- the FQN of mailbox to create
+
+Returns: T on success, NIL on failure.
+
+An error message is logged on failure.
+ ----*/
+long
+carmel_create(stream, mailbox)
+ MAILSTREAM *stream;
+ char *mailbox;
+{
+ char *carmel_index_file, *carmel2_index_file;
+ struct stat sb;
+ FILE *f;
+
+ carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0);
+
+ if(carmel_index_file == NULL)
+ return(NIL);
+
+ /*--- Make sure it doesn't already exist ---*/
+ if(stat(carmel_index_file, &sb) >= 0)
+ return(NIL);
+
+ /*--- The carmel index ----*/
+ f = fopen(carmel_index_file, "w");
+ if(f == NULL)
+ goto bomb;
+
+ if(fprintf(f, "%-13.13s index.....\n",carmel_pretty_mailbox(mailbox))==EOF)
+ goto bomb;
+
+ if(fclose(f) == EOF)
+ goto bomb;
+
+ /*--- The carmel2 index ----*/
+ carmel2_index_file = carmel_calc_paths(CalcPathCarmel2Index, mailbox,0);
+ f = fopen(carmel2_index_file, "w");
+ if(f == NULL)
+ goto bomb;
+
+ if(fprintf(f, "\254\312--CARMEL-MAIL-FILE-INDEX--\n") == EOF)
+ goto bomb;
+
+ if(fclose(f) == EOF)
+ goto bomb;
+
+
+ carmel_kill_locks(mailbox); /* Get rid of any left over locks */
+ carmel_reset_index_list();
+ return(T);
+
+ bomb:
+ unlink(carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0));
+ sprintf(carmel_error_buf, "Error creating mailbox %s: %s",
+ carmel_pretty_mailbox(mailbox), strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NIL);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Delete a carmel mailbox, and it's carmel2 index
+
+Args: stream -- unsed here
+ mailbox -- FQN of mailbox to delete
+
+Returns: NIL -- if delete fails
+ T -- if successful
+ ----*/
+long
+carmel_delete(stream, mailbox)
+ MAILSTREAM *stream;
+ char *mailbox;
+{
+ char *carmel_index_file, *carmel2_index_file;
+
+ carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0);
+ if(carmel_index_file == NULL)
+ return(NIL);
+
+ if(unlink(carmel_index_file) < 0) {
+ sprintf(carmel_error_buf, "Error deleting mailbox %s: %s",
+ carmel_pretty_mailbox(mailbox), strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NIL);
+ }
+
+ /*------ Second the carmel2 index, quietly -----*/
+ carmel2_index_file = carmel_calc_paths(CalcPathCarmel2Index, mailbox, 0);
+
+ unlink(carmel2_index_file);
+
+ carmel_kill_locks(mailbox); /* Make sure all locks are clear */
+ carmel_reset_index_list();
+ return(T);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Rename a carmel index, and its carmel2 index
+
+Args: stream -- unused
+ orig -- FQN of original mailbox
+ new -- FQN of new name
+
+Returns: NIL if rename failed, T if it succeeded.
+
+BUG: theoretically this could rename a folder from one user name
+to another. Either that should be disallowed here, or code to make
+it work should be written.
+ ----*/
+long
+carmel_rename(stream, orig, new)
+ MAILSTREAM *stream;
+ char *orig;
+ char *new;
+{
+ char path_buf[CARMEL_PATHBUF_SIZE], *new_path, *orig_index_file;
+
+ /*---- First the Carmel index -----*/
+ orig_index_file = carmel_calc_paths(CalcPathCarmelIndex, orig, 0);
+ if(orig_index_file == NULL)
+ return(NIL);
+ strcpy(path_buf, orig_index_file);
+ new_path = carmel_calc_paths(CalcPathCarmelIndex, new, 0);
+ if(new_path == NULL)
+ return(NIL);
+
+ if(rename(path_buf, new_path) < 0) {
+ sprintf(carmel_error_buf, "Error renaming mailbox %s: %s",
+ carmel_pretty_mailbox(orig), strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NIL);
+ }
+
+ /*----- Next the Carmel index, quietly ------*/
+ strcpy(path_buf, carmel_calc_paths(CalcPathCarmel2Index, orig, 0));
+ new_path = carmel_calc_paths(CalcPathCarmel2Index, new, 0);
+
+ rename(path_buf, new_path);
+
+ carmel_reset_index_list();
+ return(T);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel mail open
+
+ Args: stream
+
+ Returns: stream on success, NIL on failure.
+
+The complex set of comparison determine if we get it for read, write or not
+at all.
+
+The folder will be open for write if:
+ - It is not locked
+ - The carmel index is writeable
+ - The carmel index is writeable
+ - The carmel index is openable and not corrupt
+
+The folder open will fail if:
+ - The carmel index does exist
+ - The carmel carmel index is missing
+ AND the folder is locked or unwritable
+
+The folder is opened readonly if:
+ - It is locked
+ - The carmel index is not writable
+ - The carmel index is not writable
+
+ ----*/
+
+MAILSTREAM *
+carmel_open(stream)
+ MAILSTREAM *stream;
+{
+ char carmel_index_path[CARMEL_PATHBUF_SIZE];
+ char carmel2_index_path[CARMEL_PATHBUF_SIZE];
+ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN];
+ struct stat carmel_stat, carmel2_stat;
+ int carmel2_up_to_date;
+
+ if(!stream)
+ return(&carmelproto); /* Must be a OP_PROTOTYPE call */
+
+ mailcache = carmel2_cache;
+
+ /* close old file if stream being recycled */
+ if (LOCAL) {
+ carmel_close (stream); /* dump and save the changes */
+ stream->dtb = &carmeldriver; /* reattach this driver */
+ mail_free_cache (stream); /* clean up cache */
+ }
+
+ /* Allocate local stream */
+ stream->local = fs_get (sizeof (CARMEL2LOCAL));
+ LOCAL->carmel = 1;
+ LOCAL->msg_buf = NULL;
+ LOCAL->msg_buf_size = 0;
+ LOCAL->buffered_file = NULL;
+ LOCAL->msg_buf_text_start = NULL;
+ LOCAL->msg_buf_text_offset = 0;
+ LOCAL->stdio_buf = NULL;
+ LOCAL->dirty = NIL;
+ stream->msgno = -1;
+ stream->env = NULL;
+ stream->body = NULL;
+ stream->scache = 1;
+ LOCAL->calc_paths = carmel_calc_paths;
+ LOCAL->aux_copy = carmel_copy;
+ LOCAL->index_stream = NULL;
+ LOCAL->new_file_on_copy = 0;
+
+ /*------ Figure out the file paths -------*/
+ if(carmel_calc_paths(CalcPathCarmelIndex,stream->mailbox,0) == NULL)
+ return(NULL);
+ strcpy(carmel_index_path,
+ carmel_calc_paths(CalcPathCarmelIndex,stream->mailbox,0));
+ strcpy(carmel2_index_path,
+ carmel_calc_paths(CalcPathCarmel2Index,stream->mailbox,0));
+
+ /*------ Does the CARMEL index exist? Fail if not ------*/
+ if(stat(carmel_index_path, &carmel_stat) < 0) {
+ sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NULL); /* FAIL, no carmel index */
+ }
+
+ /*----- Determine if carmel index is up to date -----*/
+ if(stat(carmel2_index_path, &carmel2_stat) >= 0 &&
+ carmel2_stat.st_mtime >= carmel_stat.st_mtime)
+ carmel2_up_to_date = 1;
+ else
+ carmel2_up_to_date = 0;
+
+ /*------ Do we have write access to the Carmel mail index? ----*/
+ if(access(carmel2_index_path, W_OK) != 0 && errno != ENOENT)
+ stream->readonly = 1; /* fail later if dir index is uncreatable */
+
+ /*------ If CARMEL index R/O and Carmel out of date then fail -----*/
+ if(stream->readonly && !carmel2_up_to_date) {
+ mm_log("Can't open mailbox; Unable to write carmel index", ERROR);
+ return(NULL);
+ }
+
+ /*-------- Case for R/O stream or failed lock ---------*/
+ if(stream->readonly ||
+ carmel2_lock(stream->local, stream->mailbox, READ_LOCK)< 0){
+ /* If carmel is out of date here that's OK, we just will see old data*/
+ if(access(carmel_index_path, R_OK) == 0) {
+ stream->readonly = 1;
+ goto open_it;
+ } else {
+ /*-- Can't lock, and there's no carmel index, best to fail -*/
+ mm_log("Can't open mailbox, can't lock to create carmel index",
+ ERROR);
+ return(NULL);
+ }
+ }
+ /* Index is now read locked */
+
+ /*---- Got an up to date carmel index and write access too ----*/
+ if(carmel2_up_to_date)
+ if(access(carmel2_index_path, W_OK) == 0) {
+ goto open_it;
+ } else {
+ stream->readonly = 1;
+ goto open_it;
+ }
+
+ /*---- If needed recreation of carmel index fails, go readonly -----*/
+ if(vmail2carmel(carmel_index_path, carmel2_index_path, stream,
+ stream->mailbox) < 0) {
+ carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
+ stream->readonly = 1;
+ }
+
+ open_it:
+ /*-- carmel_open2 shouldn't fail after all this check, but just in case -*/
+ if(carmel_open2(stream, carmel2_index_path) < 0) {
+ /* carmel_open2 will take care of the error message */
+ carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
+ if (LOCAL->msg_buf) fs_give ((void **) &LOCAL->msg_buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ return(NULL);
+ }
+
+ if(stream->readonly)
+ mm_log("Mailbox opened READONLY", WARN);
+
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+
+ return(stream);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Create a carmel2 index for a carmel index
+
+ Args: folder -- the full UNIX path name existing carmel folder
+ carmel_folder -- the full UNIX path name of carmel index to create
+ stream -- MAILSTREAM to operate on
+
+ Returns: 0 if all is OK
+ -1 if it failed
+
+ This reads the Carmel index, and the headers of the carmel messages to
+construct entries for a carmel index.
+ ----*/
+static
+vmail2carmel(folder, carmel2_folder, stream, mailbox)
+ MAILSTREAM *stream;
+ char *folder, *carmel2_folder;
+ char *mailbox;
+{
+ FILE *carmel_stream;
+ FILE *carmel2_stream;
+ ENVELOPE *e;
+ BODY *b;
+ MESSAGECACHE mc;
+ char pathbuf[CARMEL_PATHBUF_SIZE], *message, *p, *save_subject;
+ struct stat st;
+ STRING string_struct;
+
+ sprintf(pathbuf, "Recreating Carmel2 index for \"%s\"...",
+ carmel_pretty_mailbox(mailbox));
+ mm_log(pathbuf, WARN);
+
+ mm_critical(stream);
+ if(carmel2_lock(stream->local, mailbox, WRITE_LOCK) < 0)
+ return(-1);
+
+ carmel_stream = fopen(folder,"r");
+ if(carmel_stream == NULL) {
+ carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+ return(-1);
+ }
+
+ carmel2_stream = fopen(carmel2_folder, "w");
+ if(carmel2_stream == NULL) {
+ carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+ return(-1);
+ }
+
+ fprintf(carmel2_stream, "\254\312--CARMEL-MAIL-FILE-INDEX--\n");
+
+ /* The first .... line */
+ fgets(carmel_20k_buf, sizeof(carmel_20k_buf), carmel_stream);
+
+ /*---- Loop through all entries in carmel index -----*/
+ while(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),carmel_stream) != NULL){
+
+ mc.data1 = 0L;
+ mc.data2 = atol(carmel_20k_buf); /* The file name/number */
+ mc.deleted = 0;
+ mc.seen = 1;
+ mc.flagged = 0;
+ mc.answered = 0;
+ mc.recent = 0;
+ mc.user_flags = 0;
+
+ strcpy(pathbuf,carmel_calc_paths(CalcPathCarmel2Data,
+ mailbox, mc.data2));
+
+ if(stat(pathbuf, &st) >= 0 ||
+ stat(strcat(pathbuf, ".wid"), &st) >= 0) {
+ save_subject = cpystr(carmel_20k_buf + 27);
+ save_subject[strlen(save_subject) - 1] = '\0';
+ mc.rfc822_size = st.st_size;
+ message = carmel2_readmsg(stream, 1, 0, mc.data2);
+ INIT(&string_struct, mail_string, (void *)"", 0);
+ rfc822_parse_msg(&e, &b, message, strlen(message), &string_struct,
+ mylocalhost(), carmel_20k_buf);
+ carmel2_parse_bezerk_status(&mc, message);
+ carmel2_rfc822_date(&mc, message);
+ if(e->subject == NULL ||
+ strncmp(save_subject,e->subject,strlen(save_subject))){
+ if(e->subject != NULL)
+ fs_give((void **)(&(e->subject)));
+ if(strlen(save_subject))
+ e->subject = cpystr(save_subject);
+ else
+ e->subject = NULL;
+ }
+ } else {
+ /* Data file is missing; copy record as best we can */
+ carmel_20k_buf[strlen(carmel_20k_buf) - 1] = '\0';
+ e = mail_newenvelope();
+ e->subject = cpystr(carmel_20k_buf+27);
+ e->from = mail_newaddr();
+ e->from->mailbox = cpystr(carmel_20k_buf+17);
+ for(p = e->from->mailbox; *p && !isspace(*p); p++);
+ *p = '\0';
+ mc.hours = 1;
+ mc.minutes = 0;
+ mc.seconds = 0;
+ mc.zoccident = 0;
+ mc.zhours = 0;
+ mc.zminutes = 0;
+ mc.day = 1;
+ mc.month = 1;
+ mc.year = 1;
+ }
+ carmel2_write_index(e, &mc, carmel2_stream);
+ mail_free_envelope(&e);
+ }
+
+ fclose(carmel_stream);
+ fclose(carmel2_stream);
+
+ carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+
+ mm_log("Recreating Carmel2 index.....Done", WARN);
+
+ return(1);
+}
+
+
+
+#define TESTING /* Make a copy of carmel index before rewriting */
+
+/*----------------------------------------------------------------------`
+ Close a carmel mail folder
+
+Args: stream -- Mail stream to close
+
+This will cause the carmel index to be recreated
+
+ ----*/
+void
+carmel_close(stream)
+ MAILSTREAM *stream;
+{
+ if (LOCAL && LOCAL->index_stream != NULL) {
+ /* only if a file is open */
+ if(!stream->readonly) {
+ carmel_check (stream); /* dump final checkpoint */
+ carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
+ }
+
+ fclose(LOCAL->index_stream);
+ if (LOCAL->msg_buf) fs_give ((void **) &LOCAL->msg_buf);
+ if(LOCAL->stdio_buf)
+ fs_give ((void **) &LOCAL->stdio_buf);
+ fs_give ((void **) &stream->local);
+
+ }
+ stream->dtb = NIL; /* log out the DTB */
+}
+
+
+
+/*----------------------------------------------------------------------
+ Check for new mail and write out the indexes as a checkpoint
+
+Args -- The stream to checkpoint
+ ----*/
+void
+carmel_check(stream)
+ MAILSTREAM *stream;
+{
+ if(carmel2_check2(stream))
+ carmel_recreate_index(stream); /* only if carmel2 index changed */
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Expunge the mail stream
+
+Args -- The stream to checkpoint
+
+Here we expunge the carmel2 index and then recreate the carmel index from
+it.
+ ----*/
+void
+carmel_expunge(stream)
+ MAILSTREAM *stream;
+{
+ carmel2_expunge(stream);
+ carmel_recreate_index(stream);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Rewrite the carmel index from the carmel2 index
+
+Args: -- stream to be recreated
+
+BUG: could probably use some error checking here
+ ----*/
+static void
+carmel_recreate_index(stream)
+ MAILSTREAM *stream;
+{
+#ifdef TESTING
+ char copy1[CARMEL_PATHBUF_SIZE];
+ char copy2[CARMEL_PATHBUF_SIZE];
+#define MAXBACKUPS 4
+ int nback;
+#endif
+ long msgno;
+ MESSAGECACHE *mc;
+ ENVELOPE *envelope;
+ FILE *carmel_index;
+ struct stat sb;
+ char *cm_index_file;
+ struct timeval tp[2];
+
+ mm_critical(stream);
+
+ /*---- calculate the carmel index path -----*/
+ strcpy(carmel_path_buf,
+ (*(LOCAL->calc_paths))(CalcPathCarmelIndex, stream->mailbox, 0));
+
+ /*---- Get the first index line out of old index ----*/
+ *carmel_20k_buf = '\0';
+ carmel_index = fopen(carmel_path_buf, "r");
+ if(carmel_index != NULL) {
+ fgets(carmel_20k_buf,sizeof(carmel_20k_buf), carmel_index);
+ fclose(carmel_index);
+ }
+
+#ifdef TESTING
+ /*--- rotate Backup copies of the index --- crude*/
+ for (nback = MAXBACKUPS; 1 < nback; --nback) {
+ strcpy(copy2,
+ (*(LOCAL->calc_paths))(CalcPathCarmelBackup, stream->mailbox, nback));
+ strcpy(copy1,
+ (*(LOCAL->calc_paths))(CalcPathCarmelBackup, stream->mailbox, nback-1));
+ unlink(copy2);
+ link(copy1,copy2);
+ }
+ unlink(copy1);
+ link(carmel_path_buf, copy1);
+ unlink(carmel_path_buf);
+#endif
+
+ /*---- Reopen the carmel index for write, truncating it ------*/
+ carmel_index = fopen(carmel_path_buf, "w");
+
+ if(carmel_index == NULL)
+ goto bomb;
+
+ if(fputs(carmel_20k_buf, carmel_index) == EOF)
+ goto bomb;
+
+ /*---- Loop through all the message the stream has ----*/
+ for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
+ mc = MC(msgno);
+ envelope = carmel2_fetchstructure(stream, msgno, NULL);
+ if(write_carmel_index(carmel_index, mc, envelope) < 0)
+ goto bomb;
+ }
+ if(fclose(carmel_index) == EOF)
+ goto bomb;
+
+ /*---- Get mod time of carmel index ---*/
+ cm_index_file = (*(LOCAL->calc_paths))(CalcPathCarmelIndex,
+ stream->mailbox, 0);
+ if(stat(cm_index_file, &sb) >= 0) {
+ tp[0].tv_sec = sb.st_atime;
+ tp[0].tv_usec = 0;
+ tp[1].tv_sec = sb.st_mtime;
+ tp[1].tv_usec = 0;
+
+ /*---- Set Carmel index to have same mod time ---*/
+ utimes(stream->mailbox, tp);
+ }
+ mm_nocritical(stream);
+
+#ifdef CHATTY
+ sprintf(carmel_error_buf, "Rewrote Carmel index \"%s\" with %d messages",
+ carmel_pretty_mailbox(stream->mailbox), msgno-1);
+ mm_log(carmel_error_buf, WARN);
+#endif
+
+ return;
+
+ bomb:
+ mm_nocritical(stream);
+ sprintf(carmel_error_buf, "Error recreating carmel index: %s",
+ strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Do all the file name generation for the carmel driver
+
+Args: operation -- The type of calculation to perform
+ mailbox -- The canonical mailbox name
+ data_file_num -- For calculating the name of a file storing a message
+
+Returns: string with the path name in static storage or NULL on error
+
+The Path passed in will be in the format #carmel#user-id#folder or
+ #carmel#folder
+
+The only that error occurs is in looking up the user-id. A error is logged
+when that happens.
+ ----*/
+static char *
+carmel_calc_paths(operation, mailbox, data_file_num)
+ char *mailbox;
+ int data_file_num, operation;
+{
+ static char path[CARMEL_PATHBUF_SIZE];
+ char *home_dir, *end_user_name;
+ struct passwd *pw;
+ struct carmel_mb_name *parsed_name;
+
+ parsed_name = carmel_parse_mb_name(mailbox, '\0');
+
+ if(parsed_name == NULL) {
+ mm_log("Internal error (bug). Invalid Carmel folder name",ERROR);
+ return(NULL);
+ }
+
+ if(parsed_name->user != NULL) {
+ /*---- has a user in mailbox name -----*/
+ pw = getpwnam(parsed_name->user);
+ if(pw == NULL) {
+ sprintf(carmel_error_buf,
+ "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
+ mailbox, parsed_name->user);
+ mm_log(carmel_error_buf, ERROR);
+ carmel_free_mb_name(parsed_name);
+ return(NULL);
+ }
+ home_dir = pw->pw_dir;
+ } else {
+ home_dir = myhomedir();
+ }
+ mailbox = parsed_name->mailbox;
+
+ if(strucmp2(mailbox, "inbox") == 0) /* Map "inbox" into "MAIL" */
+ mailbox = "MAIL";
+
+
+ switch(operation) {
+
+ case CalcPathCarmelIndex:
+ sprintf(path, "%s/%s/%s", home_dir, CARMEL_INDEX_DIR, mailbox);
+ break;
+
+ case CalcPathCarmelBackup:
+ sprintf(path, "%s/%s/.%s.COD-FUR-%d", home_dir, CARMEL_INDEX_DIR,
+ mailbox, data_file_num);
+ break;
+
+ case CalcPathCarmel2Data:
+ sprintf(path, "%s/%s/%d", home_dir, CARMEL_MSG_DIR, data_file_num);
+ break;
+
+ case CalcPathCarmel2Index:
+ sprintf(path, "%s/%s/.%s.cx", home_dir, CARMEL_INDEX_DIR, mailbox);
+ break;
+
+ case CalcPathCarmel2MAXNAME:
+ sprintf(path, "%s/%s", home_dir, CARMEL_MAXFILE);
+ break;
+
+ case CalcPathCarmel2WriteLock:
+ sprintf(path, "%s/%s/.%s.wl", home_dir, CARMEL_INDEX_DIR, mailbox);
+ break;
+
+ case CalcPathCarmel2ReadLock:
+ sprintf(path, "%s/%s/.%s.rl", home_dir, CARMEL_INDEX_DIR, mailbox);
+ break;
+ }
+
+/* sprintf(carmel_debug, "CARMEL: calc_paths returning \"%s\"\n", path);
+ mm_dlog(carmel_debug); */
+
+ carmel_free_mb_name(parsed_name);
+
+ return(path);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Copy carmel messages, this is the auxiliary copy, called from carmel2_copy
+
+Args: local -- Fake CARMELLOCAL structure
+ mailbox -- Destination mailbox, in FQN format, e.g. #carmel#fooo
+ e -- envelope
+ mc -- MESSAGECACHE entry
+
+Retuns: -1 on failure
+ ----*/
+long
+carmel_copy(local, mailbox, e, mc)
+ CARMEL2LOCAL *local;
+ char *mailbox;
+ ENVELOPE *e;
+ MESSAGECACHE *mc;
+{
+ FILE *carmel_index;
+ int new;
+ struct stat sb;
+ char *carmel_index_file;
+
+ carmel_index_file = (*(local->calc_paths))(CalcPathCarmelIndex, mailbox,0);
+ if(carmel_index_file == NULL)
+ return(-1);
+
+ if(stat(carmel_index_file, &sb) < 0)
+ new = 1;
+ else
+ new = 0;
+
+ carmel_index = fopen(carmel_index_file, "a+");
+ if(carmel_index == NULL)
+ return(-1);
+
+ if(new)
+ fprintf(carmel_index, "%-13.13s index.....\n",
+ carmel_pretty_mailbox(mailbox));
+
+ if(write_carmel_index(carmel_index, mc, e) < 0) {
+ fclose(carmel_index);
+ return(-1);
+ }
+
+ if(fclose(carmel_index) == EOF)
+ return(-1);
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Make sure all the directories are there and writable for carmel
+
+Args: folders_dir - suggested directory, ignored by carmel
+
+Result: returns 0 if OK, -1 if not
+ ----*/
+carmel_init(folders_dir)
+ char *folders_dir;
+{
+ char *p;
+ FILE *mail_file;
+ struct stat sb;
+
+ /*----- The ~/.vmail directory ----*/
+ sprintf(carmel_path_buf, "%s/%s", myhomedir(), CARMEL_DIR);
+ carmel2_check_dir(carmel_path_buf);
+
+ /*---- The ~/.vmail/.MAXNAME file ------*/
+ sprintf(carmel_path_buf,"%s/%s", myhomedir(), CARMEL_MAXFILE);
+ if(stat(carmel_path_buf, &sb) < 0) {
+ mail_file = fopen(carmel_path_buf, "w");
+ if(mail_file == NULL) {
+ mm_log("Error creating .MAXNAME file", ERROR);
+ } else {
+ fprintf(mail_file, "100000");
+ fclose(mail_file);
+ }
+ }
+
+ /*----- The ~/.vmail/index directory -----*/
+ sprintf(carmel_path_buf,"%s/%s",myhomedir(),CARMEL_INDEX_DIR);
+ carmel2_check_dir(carmel_path_buf);
+
+ /*----- The ~/.vmail/msg directory -----/*
+ sprintf(carmel_path_buf,"%s/%s",myhomedir(),CARMEL_MSG_DIR);
+ carmel2_check_dir(carmel_path_buf);
+
+ /*----- The ~/.vmail/MAIL file -----*/
+ sprintf(carmel_path_buf, "%s/%s/MAIL", myhomedir(), CARMEL_INDEX_DIR);
+ if(stat(carmel_path_buf, &sb) < 0) {
+ mail_file = fopen(carmel_path_buf, "w");
+ if(mail_file == NULL) {
+ mm_log("Error creating \"MAIL\" folder", WARN);
+ } else {
+ fprintf(mail_file, "MAIL index.....\n");
+ fclose(mail_file);
+ }
+ }
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Write an entry into a carmel index
+
+Args: file -- The open file stream
+ mc -- the MESSAGECACHE
+ envelope -- The envelope to write
+
+Returns: 0 if all is OK, -1 if not
+ ----*/
+static
+write_carmel_index(file, mc, envelope)
+ FILE *file;
+ MESSAGECACHE *mc;
+ ENVELOPE *envelope;
+{
+ if(fprintf(file, "%6d %02d %-.3s %2d %-9.9s %-.50s\n",
+ mc->data2, mc->day,
+ month_abbrev2(mc->month),
+ (mc->year + 1969) % 100,
+ envelope != NULL && envelope->from != NULL &&
+ envelope->from->mailbox != NULL ?
+ envelope->from->mailbox : "",
+ envelope != NULL && envelope->subject != NULL ?
+ envelope->subject : "") == EOF)
+ return(-1);
+ else
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Append a message to a carmel mailbox
+
+Args: stream -- a suggested stream, ignored here
+ mailbox -- FQN of mailbox to append message to
+ message -- Text string of message
+
+Returns: T on success, NIL on failure
+ ----*/
+long
+carmel_append(stream, mailbox, flags, date, message)
+ MAILSTREAM *stream;
+ char *mailbox, *flags, *date;
+ STRING *message;
+{
+ CARMEL2LOCAL local;
+
+ /*---- A fake local data structure to pass to other functions---*/
+ local.calc_paths = carmel_calc_paths;
+ local.carmel = 1;
+ local.aux_copy = carmel_copy;
+
+
+ return(carmel2_append2(stream, &local, mailbox, flags, date, message));
+}
+
+
+
+
+
+/*----------------------------------------------------------------------
+ This really just removes the file Carmel uses for the list of
+ folders, because Carmel will just recreate it from the indexes themselves.
+ ----*/
+static
+carmel_reset_index_list()
+{
+ sprintf(carmel_path_buf, "%s/.inda", myhomedir());
+ unlink(carmel_path_buf);
+
+ sprintf(carmel_path_buf, "%s/.indf", myhomedir());
+ unlink(carmel_path_buf);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Used to make sure there are no old locks of any sort left around
+ when a folder is deleted or when one is created.
+ --*/
+static void
+carmel_kill_locks(mailbox)
+ char *mailbox;
+{
+ unlink(carmel_calc_paths(CalcPathCarmel2WriteLock, mailbox, 0));
+ unlink(carmel_calc_paths(CalcPathCarmel2ReadLock, mailbox, 0));
+ unlink(carmel_calc_paths(CalcPathCarmel2Expunge, mailbox, 0));
+}