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