1 /*----------------------------------------------------------------------
2 
3          T H E   C A R M E L   M A I L   D R I V E R
4 
5   Author(s):   Laurence Lundblade
6                Baha'i World Centre
7                Data Processing
8                Haifa, Israel
9  	       Internet: lgl@cac.washington.edu or laurence@bwc.org
10                September 1992
11 
12   Last Edited: Aug 31, 1994
13 
14 The "Carmel" mail format is custom mail file format that has messages
15 saved in individual files and an index file that references them. It
16 has also been called the "pod" or "vmail" format. This is probably not
17 of interest to anyone away from it's origin, though the Carmel2 format
18 might be.
19 
20 This driver reads and writes the format in conjunction with the carmel2
21 driver. The carmel2 file driver does most of the work as most of the
22 operations are done on the created auxiliary carmel2 index and the
23 carmel index is updated when the mail file is closed.
24 
25   ----------------------------------------------------------------------*/
26 
27 
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <pwd.h>
31 #include <netdb.h>
32 #include <errno.h>
33 extern int errno;		/* just in case */
34 #include "mail.h"
35 #include "osdep.h"
36 #include <sys/dir.h>
37 #include <sys/file.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
40 #include "carmel2.h"
41 #include "carmel.h"
42 #include "rfc822.h"
43 #include "misc.h"
44 
45 
46 /* Driver dispatch used by MAIL. The carmel 2driver shares most of
47    its data structures and functions with the carmel driver
48  */
49 
50 DRIVER carmeldriver = {
51   "carmel",
52   (DRIVER *) NIL,		/* next driver */
53   carmel_valid,			/* mailbox is valid for us */
54   carmel_parameters,            /* Set parameters */
55   carmel_find,			/* find mailboxes */
56   carmel_find_bboards,		/* find bboards */
57   carmel_find_all,              /* find all mailboxes */
58   carmel_find_bboards,          /* find all the bboards ; just a noop here  */
59   carmel_subscribe,	        /* subscribe to mailbox */
60   carmel_unsubscribe,	        /* unsubscribe from mailbox */
61   carmel_subscribe_bboard,	/* subscribe to bboard */
62   carmel_unsubscribe_bboard,    /* unsubscribe from bboard */
63   carmel_create,		/* create mailbox */
64   carmel_delete,		/* delete mailbox */
65   carmel_rename,		/* rename mailbox */
66   carmel_open,			/* open mailbox */
67   carmel_close,			/* close mailbox */
68   carmel2_fetchfast,		/* fetch message "fast" attributes */
69   carmel2_fetchflags,		/* fetch message flags */
70   carmel2_fetchstructure,	/* fetch message envelopes */
71   carmel2_fetchheader,		/* fetch message header only */
72   carmel2_fetchtext,		/* fetch message body only */
73   carmel2_fetchbody,		/* fetch message body section */
74   carmel2_setflag,		/* set message flag */
75   carmel2_clearflag,		/* clear message flag */
76   carmel2_search,		/* search for message based on criteria */
77   carmel2_ping,			/* ping mailbox to see if still alive */
78   carmel_check,	                /* check for new messages */
79   carmel_expunge,		/* expunge deleted messages */
80   carmel2_copy,			/* copy messages to another mailbox */
81   carmel2_move,			/* move messages to another mailbox */
82   carmel_append,                /* Append message to a mailbox */
83   carmel2_gc,			/* garbage collect stream */
84 };
85 
86 MAILSTREAM carmelproto ={&carmeldriver}; /* HACK for default_driver in pine.c*/
87 
88 #ifdef ANSI
89 static int   carmel_sift_files(struct direct *);
90 static void  carmel_recreate_index(MAILSTREAM *);
91 static char  *carmel_calc_paths(int, char *, int);
92 static int   vmail2carmel(char *, char *, MAILSTREAM *, char *, char *);
93 static int   write_carmel_index(FILE *, MESSAGECACHE *, ENVELOPE *);
94 static int   carmel_reset_index_list();
95 static void  carmel_kill_locks(char *);
96 #else
97 static int   carmel_sift_files();
98 static void  carmel_recreate_index();
99 static char  *carmel_calc_paths();
100 static int   vmail2carmel();
101 static int   write_carmel_index();
102 static int   carmel_reset_index_list();
103 static void  carmel_kill_locks();
104 #endif
105 
106 
107 
108 extern char carmel_20k_buf[20000], carmel_path_buf[], carmel_error_buf[];
109 
110 static int   disk_setup_checked = 0;
111 
112 /*----------------------------------------------------------------------
113   Carmel mail validate mailbox
114 
115 Args: name -- name to check
116 
117 Returns: our driver if name is valid, otherwise calls valid in next driver
118  ---*/
119 
carmel_valid(name)120 DRIVER *carmel_valid (name)
121 	char *name;
122 {
123     return(carmel_isvalid(name) ? &carmeldriver : NIL);
124 }
125 
126 
127 
128 /*----------------------------------------------------------------------
129     Check and see if a named mailbox is a valid carmel mail index
130 
131 Args: name -- name or path of mail file. It is a FQN, e.g. #carmel#sent-mail
132 
133 Returns:  0 if it is not a valid mailbox
134           1 if it is.
135 
136    A carmel index must be a regular file, readable, and have the second word in
137 the file be "index", upper or lower case.
138   ----*/
139 int
carmel_isvalid(name)140 carmel_isvalid (name)
141 	char *name;
142 {
143     struct stat     sbuf;
144     int             fd;
145     char           *p, *carmel_index_file;
146     struct carmel_mb_name *parsed_name;
147 
148     if(!disk_setup_checked){
149        disk_setup_checked = 1;
150        carmel_init(NULL);
151     }
152 
153     /* Don't accept plain "inbox".  If we do then the carmel driver
154        takes over all other drivers linked after it.  This way the
155        inbox-path in Pine can be set to #carmel#MAIL to make the
156        inbox the carmel one
157       */
158 
159     parsed_name = carmel_parse_mb_name(name, '\0');
160     if(parsed_name == NULL)
161       return(0);
162 
163     carmel_free_mb_name(parsed_name);
164 
165     carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, name, 0);
166 
167     if(carmel_index_file == NULL)
168       return(0); /* Will get two error messages here, one from dummy driver */
169 
170     if(stat(carmel_index_file, &sbuf) < 0)
171       return(1); /* If the name matches and it doesn't exist, it's OK */
172 
173     if(!(sbuf.st_mode & S_IFREG))
174       return(0);
175 
176     fd = open(carmel_index_file, O_RDONLY);
177     if(fd < 0)
178       return(0);
179 
180     if(read(fd, carmel_20k_buf, 200) <= 0)
181       return(0);
182     carmel_20k_buf[199] = '\0';
183 
184     close(fd);
185 
186     for(p = carmel_20k_buf; *p && !isspace(*p); p++); /* first word */
187     for(; *p && isspace(*p); p++);  /* space */
188     lcase(p);                       /* lower case the "index" */
189     if(!*p || strncmp(p, "index", 5))
190       return(0);
191 
192     return(1);
193 }
194 
195 
196 
197 
198 /*----------------------------------------------------------------------
199 
200   ----*/
201 void *
carmel_parameters(function,value)202 carmel_parameters(function, value)
203      long  function;
204      char *value;
205 {}
206 
207 
208 
209 /*----------------------------------------------------------------------
210    This is used by scandir to determine which files in the directory
211  are treated as mail files
212   ----*/
213 static char *sift_pattern = NULL;
214 static int
carmel_sift_files(dir)215 carmel_sift_files(dir)
216      struct direct *dir;
217 {
218     if(dir->d_name[0] == '.')
219       return(0);
220     else if(strcmp(dir->d_name, "MAIL") == 0)
221      /* Never return the inbox. "INBOX" is always included by default */
222       return(0);
223     else if(pmatch(dir->d_name, sift_pattern))
224       return(1);
225     else
226       return(0);
227 }
228 
229 int alphasort();
230 
231 /* ----------------------------------------------------------------------
232   Carmel find list of mail boxes
233   Args: mail stream
234         pattern to search
235 
236   This scans the ".vmail/index" directory for a list of folders
237   (indexes in vmail terms).
238 
239 BUG -- doesn't really match patterns (yet)
240  ----*/
241 void
carmel_find(stream,pat)242 carmel_find(stream, pat)
243 	MAILSTREAM *stream;
244 	char *pat;
245 {
246     char            tmp[MAILTMPLEN];
247     struct direct **namelist, **n;
248     int             num;
249     struct carmel_mb_name *parsed_name;
250     struct passwd         *pw;
251 
252     parsed_name = carmel_parse_mb_name(pat, '\0');
253 
254     if(parsed_name == NULL)
255       return;
256 
257     if(parsed_name->user == NULL) {
258         sprintf(tmp, "%s/%s", myhomedir(), CARMEL_INDEX_DIR);
259     } else {
260         pw = getpwnam(parsed_name->user);
261         if(pw == NULL) {
262             sprintf(carmel_error_buf,
263                   "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
264                     pat, parsed_name->user);
265             mm_log(carmel_error_buf, ERROR);
266             return;
267         }
268         sprintf(tmp, "%s/%s", pw->pw_dir, CARMEL_INDEX_DIR);
269     }
270 
271     sift_pattern = parsed_name->mailbox;
272 
273     num = scandir(tmp, &namelist, carmel_sift_files, alphasort);
274 
275     if(num <= 0) {
276 /*        sprintf(carmel_error_buf, "Error finding mailboxes \"%s\": %s",
277                 pat, strerror(errno));
278         mm_log(carmel_error_buf, ERROR);*/
279         return;
280     }
281 
282     for(n = namelist; num > 0; num--, n++) {
283         if(parsed_name->user == NULL) {
284             sprintf(tmp, "%s%s%c%s", CARMEL_NAME_PREFIX, parsed_name->version,
285                     CARMEL_NAME_CHAR, (*n)->d_name);
286         } else {
287             sprintf(tmp, "%s%s%c%s%c%s", CARMEL_NAME_PREFIX,
288                     parsed_name->version, CARMEL_NAME_CHAR,
289                     parsed_name->user, CARMEL_NAME_CHAR,
290                     (*n)->d_name);
291         }
292 	mm_mailbox(tmp);
293 	free(*n);
294     }
295     free(namelist);
296     carmel_free_mb_name(parsed_name);
297 }
298 
299 
300 /*----------------------------------------------------------------------
301      Find_all is the same for find in the carmel driver; there is no
302 difference between subscribed and unsubscribed mailboxes
303   ----*/
304 void
carmel_find_all(stream,pat)305 carmel_find_all (stream, pat)
306 	MAILSTREAM *stream;
307 	char *pat;
308 {
309    carmel_find(stream, pat);
310 }
311 
312 
313 /*----------------------------------------------------------------------
314    Find bulliten boards is a no-op for carmel, there are none (yet)
315  */
316 void
carmel_find_bboards(stream,pat)317 carmel_find_bboards (stream,pat)
318      MAILSTREAM *stream;
319      char *pat;
320 {
321   /* Always a no-op */
322 }
323 
324 
325 /*----------------------------------------------------------------------
326    No subscription management for the carmel format. Probably should work
327  with the .mailbox list format, but that's probably implemented in mail.c
328  ----*/
carmel_subscribe()329 long carmel_subscribe() {}
carmel_unsubscribe()330 long carmel_unsubscribe() {}
carmel_subscribe_bboard()331 long carmel_subscribe_bboard() {}
carmel_unsubscribe_bboard()332 long carmel_unsubscribe_bboard() {}
333 
334 
335 
336 /*----------------------------------------------------------------------
337     Create a carmel folder
338 
339 Args:  stream --  Unused here
340        mailbox -- the FQN of mailbox to create
341 
342 Returns:  T on success, NIL on failure.
343 
344 An error message is logged on failure.
345   ----*/
346 long
carmel_create(stream,mailbox)347 carmel_create(stream, mailbox)
348      MAILSTREAM *stream;
349      char       *mailbox;
350 {
351     char       *carmel_index_file, *carmel2_index_file;
352     struct stat sb;
353     FILE       *f;
354 
355     carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0);
356 
357     if(carmel_index_file == NULL)
358       return(NIL);
359 
360     /*--- Make sure it doesn't already exist ---*/
361     if(stat(carmel_index_file, &sb) >= 0)
362       return(NIL);
363 
364     /*--- The carmel index ----*/
365     f = fopen(carmel_index_file, "w");
366     if(f == NULL)
367       goto bomb;
368 
369     if(fprintf(f, "%-13.13s index.....\n",carmel_pretty_mailbox(mailbox))==EOF)
370       goto bomb;
371 
372     if(fclose(f) == EOF)
373       goto bomb;
374 
375     /*--- The carmel2 index ----*/
376     carmel2_index_file = carmel_calc_paths(CalcPathCarmel2Index, mailbox,0);
377     f = fopen(carmel2_index_file, "w");
378     if(f == NULL)
379       goto bomb;
380 
381     if(fprintf(f, "\254\312--CARMEL-MAIL-FILE-INDEX--\n") == EOF)
382       goto bomb;
383 
384     if(fclose(f) == EOF)
385       goto bomb;
386 
387 
388     carmel_kill_locks(mailbox); /* Get rid of any left over locks */
389     carmel_reset_index_list();
390     return(T);
391 
392   bomb:
393     unlink(carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0));
394     sprintf(carmel_error_buf, "Error creating mailbox %s: %s",
395 	    carmel_pretty_mailbox(mailbox), strerror(errno));
396     mm_log(carmel_error_buf, ERROR);
397     return(NIL);
398 }
399 
400 
401 
402 /*----------------------------------------------------------------------
403     Delete a carmel mailbox, and it's carmel2 index
404 
405 Args: stream --  unused here
406       mailbox -- FQN of mailbox to delete
407 
408 Returns: NIL -- if delete fails
409          T   -- if successful
410   ----*/
411 long
carmel_delete(stream,mailbox)412 carmel_delete(stream, mailbox)
413      MAILSTREAM *stream;
414      char       *mailbox;
415 {
416     char *carmel_index_file, *carmel2_index_file;
417 
418     carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0);
419     if(carmel_index_file == NULL)
420       return(NIL);
421 
422     if(unlink(carmel_index_file) < 0) {
423 	sprintf(carmel_error_buf, "Error deleting mailbox %s: %s",
424 		carmel_pretty_mailbox(mailbox), strerror(errno));
425 	mm_log(carmel_error_buf, ERROR);
426 	return(NIL);
427     }
428 
429     /*------ Second the carmel2 index, quietly -----*/
430     carmel2_index_file = carmel_calc_paths(CalcPathCarmel2Index, mailbox, 0);
431 
432     unlink(carmel2_index_file);
433 
434     carmel_kill_locks(mailbox); /* Make sure all locks are clear */
435     carmel_reset_index_list();
436     return(T);
437 }
438 
439 
440 
441 /*----------------------------------------------------------------------
442    Rename a carmel index, and its carmel2 index
443 
444 Args:  stream -- unused
445        orig   -- FQN of original mailbox
446        new    -- FQN of new name
447 
448 Returns: NIL if rename failed, T if it succeeded.
449 
450 BUG: theoretically this could rename a folder from one user name
451 to another. Either that should be disallowed here, or code to make
452 it work should be written.
453   ----*/
454 long
carmel_rename(stream,orig,new)455 carmel_rename(stream, orig, new)
456      MAILSTREAM *stream;
457      char       *orig;
458      char       *new;
459 {
460     char path_buf[CARMEL_PATHBUF_SIZE], *new_path, *orig_index_file;
461 
462     /*---- First the Carmel index -----*/
463     orig_index_file = carmel_calc_paths(CalcPathCarmelIndex, orig, 0);
464     if(orig_index_file == NULL)
465       return(NIL);
466     strcpy(path_buf, orig_index_file);
467     new_path = carmel_calc_paths(CalcPathCarmelIndex, new, 0);
468     if(new_path == NULL)
469       return(NIL);
470 
471     if(rename(path_buf, new_path) < 0) {
472 	sprintf(carmel_error_buf, "Error renaming mailbox %s: %s",
473 		carmel_pretty_mailbox(orig), strerror(errno));
474 	mm_log(carmel_error_buf, ERROR);
475 	return(NIL);
476     }
477 
478     /*----- Next the Carmel index, quietly ------*/
479     strcpy(path_buf, carmel_calc_paths(CalcPathCarmel2Index, orig, 0));
480     new_path = carmel_calc_paths(CalcPathCarmel2Index, new, 0);
481 
482     rename(path_buf, new_path);
483 
484     carmel_reset_index_list();
485     return(T);
486 }
487 
488 
489 
490 /*----------------------------------------------------------------------
491   Carmel mail open
492 
493  Args: stream
494 
495  Returns: stream on success, NIL on failure.
496 
497 The complex set of comparison determine if we get it for read, write or not
498 at all.
499 
500 The folder will be open for write if:
501    - It is not locked
502    - The carmel index is writeable
503    - The carmel index is writeable
504    - The carmel index is openable and not corrupt
505 
506 The folder open will fail if:
507    - The carmel index does exist
508    - The carmel carmel index is missing
509         AND the folder is locked or unwritable
510 
511 The folder is opened readonly if:
512    - It is locked
513    - The carmel index is not writable
514    - The carmel index is not writable
515 
516  ----*/
517 
518 MAILSTREAM *
carmel_open(stream)519 carmel_open(stream)
520 	MAILSTREAM *stream;
521 {
522     char            carmel_index_path[CARMEL_PATHBUF_SIZE];
523     char            carmel2_index_path[CARMEL_PATHBUF_SIZE];
524     char            tmp[MAILTMPLEN], tmp2[MAILTMPLEN];
525     struct stat     carmel_stat, carmel2_stat;
526     int             carmel2_up_to_date;
527 
528     if(!stream)
529       return(&carmelproto); /* Must be a OP_PROTOTYPE call */
530 
531     mailcache = carmel2_cache;
532 
533     /* close old file if stream being recycled */
534     if (LOCAL) {
535         carmel_close (stream);		/* dump and save the changes */
536         stream->dtb = &carmeldriver;	/* reattach this driver */
537         mail_free_cache (stream);	/* clean up cache */
538     }
539 
540     /* Allocate local stream */
541     stream->local              = fs_get (sizeof (CARMEL2LOCAL));
542     LOCAL->carmel              = 1;
543     LOCAL->msg_buf             = NULL;
544     LOCAL->msg_buf_size        = 0;
545     LOCAL->buffered_file       = NULL;
546     LOCAL->msg_buf_text_start  = NULL;
547     LOCAL->msg_buf_text_offset = 0;
548     LOCAL->stdio_buf           = NULL;
549     LOCAL->dirty               = NIL;
550     stream->msgno              = -1;
551     stream->env                = NULL;
552     stream->body               = NULL;
553     stream->scache             = 1;
554     LOCAL->calc_paths          = carmel_calc_paths;
555     LOCAL->aux_copy            = carmel_copy;
556     LOCAL->index_stream        = NULL;
557     LOCAL->new_file_on_copy    = 0;
558 
559     /*------ Figure out the file paths -------*/
560     if(carmel_calc_paths(CalcPathCarmelIndex,stream->mailbox,0) == NULL)
561       return(NULL);
562     strcpy(carmel_index_path,
563 	   carmel_calc_paths(CalcPathCarmelIndex,stream->mailbox,0));
564     strcpy(carmel2_index_path,
565 	   carmel_calc_paths(CalcPathCarmel2Index,stream->mailbox,0));
566 
567     /*------ Does the CARMEL index exist? Fail if not ------*/
568     if(stat(carmel_index_path, &carmel_stat) < 0) {
569 	sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
570 	mm_log(carmel_error_buf, ERROR);
571         return(NULL);  /* FAIL, no carmel index */
572     }
573 
574     /*----- Determine if carmel index is up to date -----*/
575     if(stat(carmel2_index_path, &carmel2_stat) >= 0 &&
576        carmel2_stat.st_mtime >= carmel_stat.st_mtime)
577       carmel2_up_to_date = 1;
578     else
579       carmel2_up_to_date = 0;
580 
581     /*------ Do we have write access to the Carmel mail index? ----*/
582     if(access(carmel2_index_path, W_OK) != 0 && errno != ENOENT)
583       stream->readonly = 1; /* fail later if dir index is uncreatable */
584 
585     /*------ If CARMEL index R/O and Carmel out of date then fail -----*/
586     if(stream->readonly && !carmel2_up_to_date) {
587 	mm_log("Can't open mailbox; Unable to write carmel index", ERROR);
588         return(NULL);
589     }
590 
591     /*-------- Case for R/O stream or failed lock ---------*/
592     if(stream->readonly ||
593        carmel2_lock(stream->local, stream->mailbox, READ_LOCK)< 0){
594 	/* If carmel is out of date here that's OK, we just will see old data*/
595 	if(access(carmel_index_path, R_OK) == 0) {
596 	    stream->readonly = 1;
597   	    goto open_it;
598 	} else {
599 	    /*-- Can't lock, and there's no carmel index, best to fail -*/
600 	    mm_log("Can't open mailbox, can't lock to create carmel index",
601 		   ERROR);
602   	    return(NULL);
603         }
604     }
605     /* Index is now read locked */
606 
607     /*---- Got an up to date carmel index and write access too ----*/
608     if(carmel2_up_to_date)
609       if(access(carmel2_index_path, W_OK) == 0) {
610           goto open_it;
611       } else {
612 	  stream->readonly = 1;
613 	  goto open_it;
614       }
615 
616     /*---- If needed recreation of carmel index fails, go readonly -----*/
617     if(vmail2carmel(carmel_index_path, carmel2_index_path, stream,
618                      stream->mailbox) < 0) {
619 	carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
620         stream->readonly = 1;
621     }
622 
623   open_it:
624     /*-- carmel_open2 shouldn't fail after all this check, but just in case -*/
625     if(carmel_open2(stream, carmel2_index_path) < 0) {
626 	/* carmel_open2 will take care of the error message */
627 	carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
628 	if (LOCAL->msg_buf) fs_give ((void **) &LOCAL->msg_buf);
629 				    /* nuke the local data */
630 	fs_give ((void **) &stream->local);
631         return(NULL);
632     }
633 
634     if(stream->readonly)
635 	mm_log("Mailbox opened READONLY", WARN);
636 
637     mail_exists (stream,stream->nmsgs);
638     mail_recent (stream,stream->recent);
639 
640     return(stream);
641 }
642 
643 
644 
645 /*----------------------------------------------------------------------
646    Create a carmel2 index for a carmel index
647 
648  Args: folder        -- the full UNIX path name existing carmel folder
649        carmel_folder -- the full UNIX path name of carmel index to create
650        stream        -- MAILSTREAM to operate on
651 
652  Returns: 0 if all is OK
653          -1 if it failed
654 
655   This reads the Carmel index, and the headers of the carmel messages to
656 construct entries for a carmel index.
657  ----*/
658 static
vmail2carmel(folder,carmel2_folder,stream,mailbox)659 vmail2carmel(folder, carmel2_folder, stream, mailbox)
660      MAILSTREAM *stream;
661      char       *folder, *carmel2_folder;
662      char       *mailbox;
663 {
664     FILE        *carmel_stream;
665     FILE        *carmel2_stream;
666     ENVELOPE    *e;
667     BODY        *b;
668     MESSAGECACHE mc;
669     char         pathbuf[CARMEL_PATHBUF_SIZE], *message, *p, *save_subject;
670     struct stat  st;
671     STRING       string_struct;
672 
673     sprintf(pathbuf, "Recreating Carmel2 index for \"%s\"...",
674             carmel_pretty_mailbox(mailbox));
675     mm_log(pathbuf, WARN);
676 
677     mm_critical(stream);
678     if(carmel2_lock(stream->local, mailbox, WRITE_LOCK) < 0)
679       return(-1);
680 
681     carmel_stream = fopen(folder,"r");
682     if(carmel_stream == NULL) {
683 	carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
684         mm_nocritical(stream);
685         return(-1);
686     }
687 
688     carmel2_stream = fopen(carmel2_folder, "w");
689     if(carmel2_stream == NULL) {
690 	carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
691         mm_nocritical(stream);
692         return(-1);
693     }
694 
695     fprintf(carmel2_stream, "\254\312--CARMEL-MAIL-FILE-INDEX--\n");
696 
697     /* The first ....  line */
698     fgets(carmel_20k_buf, sizeof(carmel_20k_buf), carmel_stream);
699 
700     /*---- Loop through all entries in carmel index -----*/
701     while(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),carmel_stream) != NULL){
702 
703 	mc.data1      = 0L;
704 	mc.data2      = atol(carmel_20k_buf); /* The file name/number */
705 	mc.deleted    = 0;
706 	mc.seen       = 1;
707 	mc.flagged    = 0;
708 	mc.answered   = 0;
709 	mc.recent     = 0;
710 	mc.user_flags = 0;
711 
712 	strcpy(pathbuf,carmel_calc_paths(CalcPathCarmel2Data,
713 					 mailbox, mc.data2));
714 
715 	if(stat(pathbuf, &st) >= 0 ||
716 	   stat(strcat(pathbuf, ".wid"), &st) >= 0) {
717 	    save_subject = cpystr(carmel_20k_buf + 27);
718 	    save_subject[strlen(save_subject) - 1] = '\0';
719             mc.rfc822_size = st.st_size;
720             message = carmel2_readmsg(stream, 1, 0, mc.data2);
721             INIT(&string_struct, mail_string, (void *)"", 0);
722 	    rfc822_parse_msg(&e, &b, message, strlen(message), &string_struct,
723 			     mylocalhost(), carmel_20k_buf);
724 	    carmel2_parse_bezerk_status(&mc, message);
725 	    carmel2_rfc822_date(&mc, message);
726 	    if(e->subject == NULL ||
727 	     strncmp(save_subject,e->subject,strlen(save_subject))){
728 		if(e->subject != NULL)
729 		  fs_give((void **)(&(e->subject)));
730 		if(strlen(save_subject))
731 		  e->subject = cpystr(save_subject);
732 		else
733 		  e->subject = NULL;
734 	    }
735 	} else {
736             /* Data file is missing; copy record as best we can */
737 	    carmel_20k_buf[strlen(carmel_20k_buf) - 1] = '\0';
738             e = mail_newenvelope();
739 	    e->subject = cpystr(carmel_20k_buf+27);
740 	    e->from  = mail_newaddr();
741 	    e->from->mailbox = cpystr(carmel_20k_buf+17);
742 	    for(p = e->from->mailbox; *p && !isspace(*p); p++);
743 	    *p = '\0';
744 	    mc.hours     = 1;
745 	    mc.minutes   = 0;
746 	    mc.seconds   = 0;
747 	    mc.zoccident = 0;
748 	    mc.zhours    = 0;
749 	    mc.zminutes  = 0;
750 	    mc.day       = 1;
751 	    mc.month     = 1;
752 	    mc.year      = 1;
753 	}
754 	carmel2_write_index(e, &mc, carmel2_stream);
755 	mail_free_envelope(&e);
756     }
757 
758     fclose(carmel_stream);
759     fclose(carmel2_stream);
760 
761     carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
762     mm_nocritical(stream);
763 
764     mm_log("Recreating Carmel2 index.....Done", WARN);
765 
766     return(1);
767 }
768 
769 
770 
771 #define TESTING /* Make a copy of carmel index before rewriting */
772 
773 /*----------------------------------------------------------------------`
774    Close a carmel mail folder
775 
776 Args: stream -- Mail stream to close
777 
778 This will cause the carmel index to be recreated
779 
780   ----*/
781 void
carmel_close(stream)782 carmel_close(stream)
783 	MAILSTREAM *stream;
784 {
785     if (LOCAL && LOCAL->index_stream != NULL) {
786         /* only if a file is open */
787         if(!stream->readonly) {
788     	    carmel_check (stream);		/* dump final checkpoint */
789             carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
790         }
791 
792         fclose(LOCAL->index_stream);
793         if (LOCAL->msg_buf) fs_give ((void **) &LOCAL->msg_buf);
794           if(LOCAL->stdio_buf)
795           fs_give ((void **) &LOCAL->stdio_buf);
796 	fs_give ((void **) &stream->local);
797 
798     }
799     stream->dtb = NIL;		/* log out the DTB */
800 }
801 
802 
803 
804 /*----------------------------------------------------------------------
805   Check for new mail and write out the indexes as a checkpoint
806 
807 Args -- The stream to checkpoint
808  ----*/
809 void
carmel_check(stream)810 carmel_check(stream)
811      MAILSTREAM *stream;
812 {
813     if(carmel2_check2(stream))
814       carmel_recreate_index(stream); /* only if carmel2 index changed */
815 }
816 
817 
818 
819 
820 /*----------------------------------------------------------------------
821    Expunge the mail stream
822 
823 Args -- The stream to checkpoint
824 
825 Here we expunge the carmel2 index and then recreate the carmel index from
826 it.
827  ----*/
828 void
carmel_expunge(stream)829 carmel_expunge(stream)
830      MAILSTREAM *stream;
831 {
832     carmel2_expunge(stream);
833     carmel_recreate_index(stream);
834 }
835 
836 
837 
838 /*----------------------------------------------------------------------
839    Rewrite the carmel index from the carmel2 index
840 
841 Args: -- stream  to be recreated
842 
843 BUG: could probably use some error checking here
844   ----*/
845 static void
carmel_recreate_index(stream)846 carmel_recreate_index(stream)
847      MAILSTREAM *stream;
848 {
849 #ifdef TESTING
850     char          copy1[CARMEL_PATHBUF_SIZE];
851     char          copy2[CARMEL_PATHBUF_SIZE];
852 #define MAXBACKUPS 4
853     int nback;
854 #endif
855     long          msgno;
856     MESSAGECACHE *mc;
857     ENVELOPE     *envelope;
858     FILE         *carmel_index;
859     struct stat   sb;
860     char         *cm_index_file;
861     struct timeval tp[2];
862 
863     mm_critical(stream);
864 
865     /*---- calculate the carmel index path -----*/
866     strcpy(carmel_path_buf,
867            (*(LOCAL->calc_paths))(CalcPathCarmelIndex, stream->mailbox, 0));
868 
869     /*---- Get the first index line out of old index ----*/
870     *carmel_20k_buf = '\0';
871     carmel_index = fopen(carmel_path_buf, "r");
872     if(carmel_index != NULL) {
873         fgets(carmel_20k_buf,sizeof(carmel_20k_buf), carmel_index);
874         fclose(carmel_index);
875     }
876 
877 #ifdef TESTING
878     /*--- rotate Backup copies of the index --- crude*/
879     for (nback = MAXBACKUPS; 1 < nback; --nback) {
880       strcpy(copy2,
881            (*(LOCAL->calc_paths))(CalcPathCarmelBackup, stream->mailbox, nback));
882       strcpy(copy1,
883            (*(LOCAL->calc_paths))(CalcPathCarmelBackup, stream->mailbox, nback-1));
884       unlink(copy2);
885       link(copy1,copy2);
886       }
887     unlink(copy1);
888     link(carmel_path_buf, copy1);
889     unlink(carmel_path_buf);
890 #endif
891 
892     /*---- Reopen the carmel index for write, truncating it ------*/
893     carmel_index = fopen(carmel_path_buf, "w");
894 
895     if(carmel_index == NULL)
896 	goto bomb;
897 
898     if(fputs(carmel_20k_buf, carmel_index) == EOF)
899       goto bomb;
900 
901     /*---- Loop through all the message the stream has ----*/
902     for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
903         mc = MC(msgno);
904     	envelope = carmel2_fetchstructure(stream, msgno, NULL);
905     	if(write_carmel_index(carmel_index, mc, envelope) < 0)
906     	  goto bomb;
907     }
908     if(fclose(carmel_index) == EOF)
909       goto bomb;
910 
911     /*---- Get mod time of carmel index ---*/
912     cm_index_file = (*(LOCAL->calc_paths))(CalcPathCarmelIndex,
913 					   stream->mailbox, 0);
914     if(stat(cm_index_file, &sb) >= 0) {
915 	tp[0].tv_sec  = sb.st_atime;
916 	tp[0].tv_usec = 0;
917 	tp[1].tv_sec  = sb.st_mtime;
918 	tp[1].tv_usec = 0;
919 
920 	/*---- Set Carmel index to have same mod time ---*/
921 	utimes(stream->mailbox, tp);
922     }
923     mm_nocritical(stream);
924 
925 #ifdef CHATTY
926     sprintf(carmel_error_buf, "Rewrote Carmel index \"%s\" with %d messages",
927 	    carmel_pretty_mailbox(stream->mailbox), msgno-1);
928     mm_log(carmel_error_buf, WARN);
929 #endif
930 
931     return;
932 
933   bomb:
934     mm_nocritical(stream);
935     sprintf(carmel_error_buf, "Error recreating carmel index: %s",
936    	    strerror(errno));
937     mm_log(carmel_error_buf, ERROR);
938 }
939 
940 
941 
942 /*----------------------------------------------------------------------
943   Do all the file name generation for the carmel driver
944 
945 Args: operation -- The type of calculation to perform
946       mailbox   -- The canonical mailbox name
947       data_file_num -- For calculating the name of a file storing a message
948 
949 Returns:  string with the path name in static storage or NULL on error
950 
951 The Path passed in will be in the format #carmel#user-id#folder or
952                                          #carmel#folder
953 
954 The only that error occurs is in looking up the user-id. A error is logged
955 when that happens.
956   ----*/
957 static char *
carmel_calc_paths(operation,mailbox,data_file_num)958 carmel_calc_paths(operation, mailbox, data_file_num)
959      char *mailbox;
960      int   data_file_num, operation;
961 {
962     static char    path[CARMEL_PATHBUF_SIZE];
963     char          *home_dir, *end_user_name;
964     struct passwd *pw;
965     struct carmel_mb_name *parsed_name;
966 
967     parsed_name = carmel_parse_mb_name(mailbox, '\0');
968 
969     if(parsed_name == NULL) {
970         mm_log("Internal error (bug). Invalid Carmel folder name",ERROR);
971         return(NULL);
972     }
973 
974     if(parsed_name->user != NULL) {
975         /*---- has a user in mailbox name -----*/
976         pw = getpwnam(parsed_name->user);
977         if(pw == NULL) {
978             sprintf(carmel_error_buf,
979                   "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
980                     mailbox, parsed_name->user);
981             mm_log(carmel_error_buf, ERROR);
982             carmel_free_mb_name(parsed_name);
983             return(NULL);
984         }
985         home_dir = pw->pw_dir;
986     } else {
987         home_dir = myhomedir();
988     }
989     mailbox  = parsed_name->mailbox;
990 
991     if(strucmp2(mailbox, "inbox") == 0) /* Map "inbox" into "MAIL" */
992       mailbox = "MAIL";
993 
994 
995     switch(operation) {
996 
997       case CalcPathCarmelIndex:
998         sprintf(path, "%s/%s/%s", home_dir, CARMEL_INDEX_DIR, mailbox);
999         break;
1000 
1001       case CalcPathCarmelBackup:
1002         sprintf(path, "%s/%s/.%s.COD-FUR-%d", home_dir, CARMEL_INDEX_DIR,
1003                 mailbox, data_file_num);
1004         break;
1005 
1006       case CalcPathCarmel2Data:
1007 	sprintf(path, "%s/%s/%d", home_dir, CARMEL_MSG_DIR, data_file_num);
1008 	break;
1009 
1010       case CalcPathCarmel2Index:
1011         sprintf(path, "%s/%s/.%s.cx", home_dir, CARMEL_INDEX_DIR, mailbox);
1012         break;
1013 
1014       case CalcPathCarmel2MAXNAME:
1015         sprintf(path, "%s/%s", home_dir, CARMEL_MAXFILE);
1016         break;
1017 
1018       case CalcPathCarmel2WriteLock:
1019         sprintf(path, "%s/%s/.%s.wl", home_dir, CARMEL_INDEX_DIR, mailbox);
1020         break;
1021 
1022       case CalcPathCarmel2ReadLock:
1023         sprintf(path, "%s/%s/.%s.rl", home_dir, CARMEL_INDEX_DIR, mailbox);
1024         break;
1025     }
1026 
1027 /*    sprintf(carmel_debug, "CARMEL: calc_paths returning \"%s\"\n", path);
1028     mm_dlog(carmel_debug);   */
1029 
1030     carmel_free_mb_name(parsed_name);
1031 
1032     return(path);
1033 }
1034 
1035 
1036 
1037 /*----------------------------------------------------------------------
1038     Copy carmel messages, this is the auxiliary copy, called from carmel2_copy
1039 
1040 Args:  local -- Fake CARMELLOCAL structure
1041        mailbox -- Destination mailbox, in FQN format, e.g. #carmel#fooo
1042        e       -- envelope
1043        mc      -- MESSAGECACHE entry
1044 
1045 Returns: -1 on failure
1046   ----*/
1047 long
carmel_copy(local,mailbox,e,mc)1048 carmel_copy(local, mailbox, e, mc)
1049      CARMEL2LOCAL *local;
1050      char         *mailbox;
1051      ENVELOPE     *e;
1052      MESSAGECACHE *mc;
1053 {
1054     FILE       *carmel_index;
1055     int         new;
1056     struct stat sb;
1057     char        *carmel_index_file;
1058 
1059     carmel_index_file = (*(local->calc_paths))(CalcPathCarmelIndex, mailbox,0);
1060     if(carmel_index_file == NULL)
1061       return(-1);
1062 
1063     if(stat(carmel_index_file, &sb) < 0)
1064       new = 1;
1065     else
1066       new = 0;
1067 
1068     carmel_index = fopen(carmel_index_file, "a+");
1069     if(carmel_index == NULL)
1070       return(-1);
1071 
1072     if(new)
1073       fprintf(carmel_index, "%-13.13s index.....\n",
1074               carmel_pretty_mailbox(mailbox));
1075 
1076     if(write_carmel_index(carmel_index, mc, e) < 0) {
1077 	fclose(carmel_index);
1078 	return(-1);
1079     }
1080 
1081     if(fclose(carmel_index) == EOF)
1082       return(-1);
1083 
1084     return(0);
1085 }
1086 
1087 
1088 
1089 /*----------------------------------------------------------------------
1090     Make sure all the directories are there and writable for carmel
1091 
1092 Args: folders_dir   - suggested directory, ignored by carmel
1093 
1094 Result: returns 0 if OK, -1 if not
1095  ----*/
carmel_init(folders_dir)1096 carmel_init(folders_dir)
1097      char *folders_dir;
1098 {
1099     char  *p;
1100     FILE  *mail_file;
1101     struct stat sb;
1102 
1103     /*----- The ~/.vmail directory ----*/
1104     sprintf(carmel_path_buf, "%s/%s", myhomedir(), CARMEL_DIR);
1105     carmel2_check_dir(carmel_path_buf);
1106 
1107     /*---- The ~/.vmail/.MAXNAME file ------*/
1108     sprintf(carmel_path_buf,"%s/%s", myhomedir(), CARMEL_MAXFILE);
1109     if(stat(carmel_path_buf, &sb) < 0) {
1110 	mail_file = fopen(carmel_path_buf, "w");
1111 	if(mail_file == NULL) {
1112 	    mm_log("Error creating .MAXNAME file", ERROR);
1113 	} else {
1114 	    fprintf(mail_file, "100000");
1115 	    fclose(mail_file);
1116 	}
1117     }
1118 
1119     /*----- The ~/.vmail/index directory -----*/
1120     sprintf(carmel_path_buf,"%s/%s",myhomedir(),CARMEL_INDEX_DIR);
1121     carmel2_check_dir(carmel_path_buf);
1122 
1123     /*----- The ~/.vmail/msg directory -----/*
1124     sprintf(carmel_path_buf,"%s/%s",myhomedir(),CARMEL_MSG_DIR);
1125     carmel2_check_dir(carmel_path_buf);
1126 
1127     /*----- The ~/.vmail/MAIL file -----*/
1128     sprintf(carmel_path_buf, "%s/%s/MAIL", myhomedir(),  CARMEL_INDEX_DIR);
1129     if(stat(carmel_path_buf, &sb) < 0) {
1130         mail_file = fopen(carmel_path_buf, "w");
1131 	if(mail_file == NULL) {
1132 	    mm_log("Error creating \"MAIL\" folder", WARN);
1133 	} else {
1134  	    fprintf(mail_file, "MAIL          index.....\n");
1135 	    fclose(mail_file);
1136 	}
1137     }
1138 
1139     return(0);
1140 }
1141 
1142 
1143 
1144 /*----------------------------------------------------------------------
1145    Write an entry into a carmel index
1146 
1147 Args: file -- The open file stream
1148       mc   -- the MESSAGECACHE
1149       envelope -- The envelope to write
1150 
1151 Returns: 0 if all is OK, -1 if not
1152   ----*/
1153 static
write_carmel_index(file,mc,envelope)1154 write_carmel_index(file, mc, envelope)
1155      FILE         *file;
1156      MESSAGECACHE *mc;
1157      ENVELOPE     *envelope;
1158 {
1159     if(fprintf(file, "%6d %02d %-.3s %2d %-9.9s %-.50s\n",
1160 	       mc->data2, mc->day,
1161 	       month_abbrev2(mc->month),
1162 	       (mc->year + 1969) % 100,
1163 	       envelope != NULL && envelope->from != NULL &&
1164 	       envelope->from->mailbox != NULL ?
1165 	         envelope->from->mailbox :  "",
1166 	       envelope != NULL && envelope->subject != NULL ?
1167 	         envelope->subject : "") == EOF)
1168       return(-1);
1169     else
1170       return(0);
1171 }
1172 
1173 
1174 
1175 /*----------------------------------------------------------------------
1176     Append a message to a carmel mailbox
1177 
1178 Args: stream  --  a suggested stream, ignored here
1179       mailbox --  FQN of mailbox to append message to
1180       message --  Text string of message
1181 
1182 Returns: T on success, NIL on failure
1183   ----*/
1184 long
carmel_append(stream,mailbox,flags,date,message)1185 carmel_append(stream, mailbox, flags, date, message)
1186      MAILSTREAM *stream;
1187      char       *mailbox, *flags, *date;
1188      STRING     *message;
1189 {
1190     CARMEL2LOCAL local;
1191 
1192     /*---- A fake local data structure to pass to other functions---*/
1193     local.calc_paths = carmel_calc_paths;
1194     local.carmel     = 1;
1195     local.aux_copy   = carmel_copy;
1196 
1197 
1198     return(carmel2_append2(stream, &local, mailbox, flags, date, message));
1199 }
1200 
1201 
1202 
1203 
1204 
1205 /*----------------------------------------------------------------------
1206   This really just removes the file Carmel uses for the list of
1207  folders, because Carmel will just recreate it from the indexes themselves.
1208   ----*/
1209 static
carmel_reset_index_list()1210 carmel_reset_index_list()
1211 {
1212     sprintf(carmel_path_buf, "%s/.inda", myhomedir());
1213     unlink(carmel_path_buf);
1214 
1215     sprintf(carmel_path_buf, "%s/.indf", myhomedir());
1216     unlink(carmel_path_buf);
1217 }
1218 
1219 
1220 
1221 /*----------------------------------------------------------------------
1222     Used to make sure there are no old locks of any sort left around
1223  when a folder is deleted or when one is created.
1224   --*/
1225 static void
carmel_kill_locks(mailbox)1226 carmel_kill_locks(mailbox)
1227      char *mailbox;
1228 {
1229     unlink(carmel_calc_paths(CalcPathCarmel2WriteLock, mailbox, 0));
1230     unlink(carmel_calc_paths(CalcPathCarmel2ReadLock, mailbox, 0));
1231     unlink(carmel_calc_paths(CalcPathCarmel2Expunge, mailbox, 0));
1232 }
1233