1 #define TESTING
2 /*----------------------------------------------------------------------
3
4 T H E C A R M E L M A I L F I L E D R I V E R
5
6 Author(s): Laurence Lundblade
7 Baha'i World Centre
8 Data Processing
9 Haifa, Israel
10 Internet: lgl@cac.washington.edu or laurence@bwc.org
11 September 1992
12
13 Last edited: Aug 31, 1994
14
15 The Carmel2 mail file stores messages in individual files and
16 implements folders or mailboxes with index files that contain
17 references to the files a nd a full c-client envelope in an easily
18 parsed form. It was written as a needed part of the pod mail file
19 driver with hopes that it might be useful otherwise some day. It has
20 only been run with the carmel driver.
21
22 Advantages over Berkeley format and driver:
23 + Opening mail folder is very fast
24 + Expunge is fast
25 + Check point is very fast
26 + Memory usage is much lower
27 + Search of message headers is fast
28 Disadvantages:
29 - Fetching a large message is slow
30 - Searching the message bodies is slow
31 - Sorting the mailbox is slow
32
33 Index File Format
34 -----------------
35
36 The first line of the file is always:
37 "\254\312--CARMEL2-MAIL-FILE-INDEX--\n"
38
39 It is followed by index entries which are of the format:
40 ---START-OF-MESSAGE---\007\001nnnnnnnnnn
41 Ufrads______________________________
42 Zxxxxxx
43 D..... <Many fields here, labeled by the first letter in the line>
44 <Fields may be repeated>
45
46
47 The index is almost an ASCII file. With the first version of this
48 driver it is not advisable to edit this file, lest the byte counts get
49 disrupted. In the future it will be editable. The file starts with
50 two binary bytes so the file(1) utility will report it as "data" to
51 discourage would be hackers from messing with it. The ^G is also in
52 each index entry for the same reason. If you are reading this source you
53 probably know enough to edit the index file without destroying it.
54 Other parts of the format are designed for the easiest possible
55 parsing. The idea was to have a file format that was reasonable to
56 fiddle for debugging, to discourage inexperienced folks for fiddling
57 it and to be efficient for use by the program.
58
59
60 Routines and data structures
61 ----------------------------
62
63 C-CLIENT INTERFACE FUNCTIONS
64 carmel2_valid - check to see if a mailbox is valid for carmel2 mail files
65 carmel2_isvalid - actual work of checking
66 carmel2_find - generate list of carmel2 mailboxes
67 carmel2_sift_files - select mailboxes out of list, used with scandir
68 carmel2_find_bboars - dummy routine, doesn't do anything
69
70 carmel2_open - initial phase of opening a mailbox
71 carmel2_open2 - real work of opening mailbox, shared with carmel driver
72 carmel2_close - close a mail stream
73
74 carmel2_fetchfast - fetch "fast" message info, noop for this driver
75 carmel2_fetchflags - fetch the flags, a noop for this driver
76 carmel2_fetchstructure - fetch and envelope and possibly body
77
78 carmel2_fetchheader - fetch the text header of the message
79 carmel2_fetchtext - fetch the text of the message (no header included)
80 carmel2_fetchbody - fetch the text of a body part of a message
81
82 carmel2_setflag - Set a flag for a message sequence
83 carmel2_clearflag - Clear a flag for a message sequence
84
85 carmel2_search - Invoke the search facilities
86
87 carmel2_ping - Check for new mail and see if still alive
88 carmel2_check - Checkpoint the message statuses to the disk
89 carmel2_expunge - Delete all the messages marked for delete
90
91 carmel2_copy - Copy a message to another mailbox
92 carmel2_move - Move a message to another mailbox
93
94 carmel_gc - Garbage collection, a noop for this driver
95 carmel_cache - The required c-client cache handler, doesn't do much
96
97 carmel2_append - Append a message to a mail folder
98
99 SUPPORT FUNCTIONS
100 carmel_bodystruct - Fetch the body structure for a carmel message
101 carmel_parse_address - Parse the address out of a carmel index
102 carmel_parse_addr_field - Parse individual address field out of carmel index
103 carmel2_write_index - Write an entry into a carmel index
104 carmel2_index_address - Write an address into a carmel index
105
106 carmel_readmsg - Read a message file into memory, header text or both
107 carmel_spool_mail - Get new mail out of spoold mail file
108 carmel_copy_msg_file - Make copy of message file when copying (unused)
109
110 carmel2_lock - Lock a carmel index for read or write
111 carmel2_unlock - Unlock a carmel index
112 carmel2_update_lock - Touch lock mod time, marking it as active
113 carmel2_bezerk_lock - Lock the spool mail file Berkeley style
114 carmel2_bezerk_unlock - Unlock the spool mail file
115
116 carmel2_check_dir - Check that directory exists and is writeable
117 carmel2_new_data_file - Get file number for new message file
118 carmel2_calc_paths - Calculate path names for carmel driver
119
120 carmel_parse_bezerk_status - Parse the "Status: OR" field in mail headers
121 carmel2_getflags - Turn the named flags into a bit mask
122 carmel_new_mc - Get pointer to new MESSAGECACHE, allocating if needed
123
124 carmel_append2 - The real work of append a message to a mailbox
125
126 carmel2_search-* - A bunch of search support routines
127
128 month_abbrev2 - Returns three letter month abbreviation given a name
129 carmel2_rfc822_date - Parse a date string, into MESSAGECACHE structure
130 carmel2_date2string - Generate a date string from MESSAGECACHE
131 carmel2_parse_date - Parse date out of a carmel index
132 next_num - Called to parse dates
133
134 strucmp2 - Case insensitive strcmp()
135 struncmp2 - Case insensitive strncmp()
136
137 ----------------------------------------------------------------------*/
138
139 #include <stdio.h>
140 #include <ctype.h>
141 #include <pwd.h>
142 #include <netdb.h>
143 #include <errno.h>
144 extern int errno; /* just in case */
145 #include "mail.h"
146 #include "osdep.h"
147 #include <sys/dir.h>
148 #include <sys/file.h>
149 #include <sys/stat.h>
150 #include <sys/time.h>
151 #include "carmel2.h"
152 #include "rfc822.h"
153 #include "misc.h"
154
155
156 char *strindex2(), *strchr();
157
158 #ifdef ANSI
159 static int carmel2_sift_files(struct direct *);
160 static BODY *carmel2_bodystruct(MAILSTREAM *, MESSAGECACHE *);
161 static ADDRESS *carmel2_parse_address(char *, ADDRESS *, char *);
162 static char *carmel2_parse_addr_field(char **);
163 static int carmel2_index_address(ADDRESS *, int , FILE *);
164 static int carmel2_parse_mail(MAILSTREAM *, long);
165 void carmel2_spool_mail(MAILSTREAM *, char *, char *, int);
166 static int carmel2_copy_msg_file(MAILSTREAM *, int, char *);
167 static int carmel2_bezerk_lock(char *, char *);
168 static void carmel2_bezerk_unlock(int, char *);
169 static int carmel2_new_data_file(CARMEL2LOCAL *, char *);
170 static char *carmel2_calc_paths(int, char *, int);
171 static short carmel2_getflags(MAILSTREAM *, char *);
172 static MESSAGECACHE *carmel2_new_mc(MAILSTREAM *, int);
173 static char carmel2_search_all(MAILSTREAM *, long, char *, long);
174 static char carmel2_search_answered(MAILSTREAM *, long, char *, long);
175 static char carmel2_search_deleted(MAILSTREAM *, long, char *, long);
176 static char carmel2_search_flagged(MAILSTREAM *, long, char *, long);
177 static char carmel2_search_keyword(MAILSTREAM *, long, char *, long);
178 static char carmel2_search_new(MAILSTREAM *, long, char *, long);
179 static char carmel2_search_old(MAILSTREAM *, long, char *, long);
180 static char carmel2_search_recent(MAILSTREAM *, long, char *, long);
181 static char carmel2_search_seen(MAILSTREAM *, long, char *, long);
182 static char carmel2_search_unanswered(MAILSTREAM *, long, char *, long);
183 static char carmel2_search_undeleted(MAILSTREAM *, long, char *, long);
184 static char carmel2_search_unflagged(MAILSTREAM *, long, char *, long);
185 static char carmel2_search_unkeyword(MAILSTREAM *, long, char *, long);
186 static char carmel2_search_unseen(MAILSTREAM *, long, char *, long);
187 static char carmel2_search_before(MAILSTREAM *, long, char *, long);
188 static char carmel2_search_on(MAILSTREAM *, long, char *, long);
189 static char carmel2_search_since(MAILSTREAM *, long, char *, long);
190 static unsigned long carmel2_msgdate(MAILSTREAM *, long);
191 static char carmel2_search_body(MAILSTREAM *, long, char *, long);
192 static char carmel2_search_subject(MAILSTREAM *, long, char *, long);
193 static char carmel2_search_text(MAILSTREAM *, long, char *, long);
194 static char carmel2_search_bcc(MAILSTREAM *, long, char *, long);
195 static char carmel2_search_cc(MAILSTREAM *, long, char *, long);
196 static char carmel2_search_from(MAILSTREAM *, long, char *, long);
197 static char carmel2_search_to(MAILSTREAM *, long, char *, long);
198 typedef char (*search_t) ();
199 static search_t carmel2_search_date(search_t, long *);
200 static search_t carmel2_search_flag(search_t, char **);
201 static search_t carmel2_search_string(search_t, char **, long *);
202 static void carmel2_date2string(char *, MESSAGECACHE *);
203 static void carmel2_parse_date(MESSAGECACHE *, char *);
204 static int next_num(char **);
205 #else
206 static int carmel2_sift_files();
207 static BODY *carmel2_bodystruct();
208 static ADDRESS *carmel2_parse_address();
209 static char *carmel2_parse_addr_field();
210 static int carmel2_index_address();
211 static int carmel2_parse_mail();
212 void carmel2_spool_mail();
213 static int carmel2_copy_msg_file();
214 static int carmel2_bezerk_lock();
215 static void carmel2_bezerk_unlock();
216 static int carmel2_new_data_file();
217 static char *carmel2_calc_paths();
218 static short carmel2_getflags();
219 static MESSAGECACHE *carmel2_new_mc();
220 static char carmel2_search_all();
221 static char carmel2_search_answered();
222 static char carmel2_search_deleted();
223 static char carmel2_search_flagged();
224 static char carmel2_search_keyword();
225 static char carmel2_search_new();
226 static char carmel2_search_old();
227 static char carmel2_search_recent();
228 static char carmel2_search_seen();
229 static char carmel2_search_unanswered();
230 static char carmel2_search_undeleted();
231 static char carmel2_search_unflagged();
232 static char carmel2_search_unkeyword();
233 static char carmel2_search_unseen();
234 static char carmel2_search_before();
235 static char carmel2_search_on();
236 static char carmel2_search_since();
237 static unsigned long carmel2_msgdate();
238 static char carmel2_search_body();
239 static char carmel2_search_subject();
240 static char carmel2_search_text();
241 static char carmel2_search_bcc();
242 static char carmel2_search_cc();
243 static char carmel2_search_from();
244 static char carmel2_search_to();
245 typedef char (*search_t) ();
246 static search_t carmel2_search_date();
247 static search_t carmel2_search_flag();
248 static search_t carmel2_search_string();
249 static void carmel2_date2string();
250 static void carmel2_parse_date();
251 static int next_num();
252 #endif
253
254
255 /*------ Driver dispatch used by MAIL -------*/
256
257 DRIVER carmel2driver = {
258 "carmel2",
259 (DRIVER *) NIL, /* next driver */
260 carmel2_valid, /* mailbox is valid for us */
261 carmel2_parameters, /* manipulate parameters */
262 carmel2_find, /* find mailboxes */
263 carmel2_find_bboards, /* find bboards */
264 carmel2_find_all, /* find all mailboxes */
265 carmel2_find_bboards, /* find all bboards ; just a noop here */
266 carmel2_subscribe, /* subscribe to mailbox */
267 carmel2_unsubscribe, /* unsubscribe from mailbox */
268 carmel2_subscribe_bboard, /* subscribe to bboard */
269 carmel2_unsubscribe_bboard, /* unsubscribe from bboard */
270 carmel2_create,
271 carmel2_delete,
272 carmel2_rename,
273 carmel2_open, /* open mailbox */
274 carmel2_close, /* close mailbox */
275 carmel2_fetchfast, /* fetch message "fast" attributes */
276 carmel2_fetchflags, /* fetch message flags */
277 carmel2_fetchstructure, /* fetch message envelopes */
278 carmel2_fetchheader, /* fetch message header only */
279 carmel2_fetchtext, /* fetch message body only */
280 carmel2_fetchbody, /* fetch message body section */
281 carmel2_setflag, /* set message flag */
282 carmel2_clearflag, /* clear message flag */
283 carmel2_search, /* search for message based on criteria */
284 carmel2_ping, /* ping mailbox to see if still alive */
285 carmel2_check, /* check for new messages */
286 carmel2_expunge, /* expunge deleted messages */
287 carmel2_copy, /* copy messages to another mailbox */
288 carmel2_move, /* move messages to another mailbox */
289 carmel2_append, /* Append message to a mailbox */
290 carmel2_gc, /* garbage collect stream */
291 };
292
293 MAILSTREAM carmel2proto ={&carmel2driver};/*HACK for default_driver in pine.c*/
294
295 /*-- Some string constants used in carmel2 indexes --*/
296 char *carmel2_s_o_m = "---START-OF-MESSAGE---";
297 int carmel2_s_o_m_len = 22;
298 char *carmel2_s_o_f = "\254\312--CARMEL2-MAIL-FILE-INDEX--\n";
299
300
301
302 /*-- Buffers used for various reasons, also used by carmel driver --*/
303 char carmel_20k_buf[20000], carmel_path_buf[CARMEL_PATHBUF_SIZE],
304 carmel_error_buf[200];
305
306
307 /*----------------------------------------------------------------------
308 Carmel mail validate mailbox
309
310 Args: - mailbox name
311
312 Returns: our driver if name is valid, otherwise calls valid in next driver
313 ---*/
314
carmel2_valid(name)315 DRIVER *carmel2_valid (name)
316 char *name;
317 {
318 return (carmel2_isvalid (name) ? &carmel2driver : NIL);
319 }
320
321
322
323 /*----------------------------------------------------------------------
324 Open the mailbox and see if it's the correct format
325
326 Args: name -- name of the mailbox, not fully qualified path
327
328 Returns: 0 if is is not valid, 1 if it is valid
329
330 The file must be a regular file and start with the string in the
331 variable carmel2_s_o_f. It has a magic number of sorts
332 ----*/
333 int
carmel2_isvalid(name)334 carmel2_isvalid (name)
335 char *name;
336 {
337 char *carmel_index_file;
338 struct stat sbuf;
339 int fd;
340 struct carmel_mb_name *parsed_name;
341
342 /*---- Must match FQN name of carmel mailboxes ----*/
343 parsed_name = carmel_parse_mb_name(name, '2');
344 if(parsed_name == NULL)
345 return;
346 carmel_free_mb_name(parsed_name);
347
348 carmel_index_file = carmel2_calc_paths(CalcPathCarmel2Index, name, 0);
349 if(carmel_index_file == NULL)
350 return(0); /* Will get two error messages here, one from dummy driver */
351
352 if(stat(carmel_index_file, &sbuf) < 0)
353 return(1); /* If the names match and it doesn't exist, it's valid */
354
355 if(!(sbuf.st_mode & S_IFREG))
356 return(0);
357
358 fd = open(carmel_index_file, O_RDONLY);
359 if(fd < 0)
360 return(0);
361
362 if(read(fd, carmel_20k_buf, 200) <= 0)
363 return(0);
364
365 close(fd);
366
367 if(strncmp(carmel_20k_buf, carmel2_s_o_f, strlen(carmel2_s_o_f)))
368 return(0);
369
370 return(1);
371 }
372
373
374
375 /*----------------------------------------------------------------------
376 Set parameters for the carmel driver.
377
378 Currently does nothing
379 ----*/
380 void *
carmel2_parameters()381 carmel2_parameters()
382 {
383 }
384
385
386
387
388 /*----------------------------------------------------------------------
389 Carmel mail find list of mailboxes
390
391 Args: stream -- mail stream to find mailboxes for
392 pat -- wildcard pattern to match (currently unused)
393
394 Returns nothing, the results are passed back by calls to mm_log
395
396 This needs testing
397 ----*/
398
399 void
carmel2_find(stream,pat)400 carmel2_find (stream, pat)
401 MAILSTREAM *stream;
402 char *pat;
403 {
404 char tmp[MAILTMPLEN];
405 struct direct **namelist, **n;
406 int num;
407 extern int alphasort();
408 struct carmel_mb_name *parsed_name;
409 struct passwd *pw;
410
411 parsed_name = carmel_parse_mb_name(pat, '2');
412 if(parsed_name == NULL)
413 return;
414
415 if(parsed_name->user == NULL) {
416 sprintf(tmp, "%s/%s", myhomedir(), CARMEL2_INDEX_DIR);
417 } else {
418 pw = getpwnam(parsed_name->user);
419 if(pw == NULL) {
420 sprintf(carmel_error_buf,
421 "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
422 pat, parsed_name->user);
423 mm_log(carmel_error_buf, ERROR);
424 return;
425 }
426 sprintf(tmp, "%s/%s", pw->pw_dir, CARMEL2_INDEX_DIR);
427 }
428
429 sprintf(tmp, "%s/%s", myhomedir(), CARMEL2_INDEX_DIR);
430
431 num = scandir(tmp, &namelist, carmel2_sift_files, alphasort);
432
433 if(num <= 0) {
434 /* Seems this error message should not be logged
435 sprintf(carmel_error_buf, "Error finding mailboxes \"%s\": %s",
436 pat, strerror(errno));
437 mm_log(carmel_error_buf, ERROR); */
438 return;
439 }
440
441 for(n = namelist; num > 0; num--, n++) {
442 if(parsed_name->user == NULL) {
443 sprintf(tmp, "%s%s%c%s", CARMEL_NAME_PREFIX, parsed_name->version,
444 CARMEL_NAME_CHAR, (*n)->d_name);
445 } else {
446 sprintf(tmp, "%s%s%c%s%c%s", CARMEL_NAME_PREFIX,
447 parsed_name->version, CARMEL_NAME_CHAR,
448 parsed_name->user, CARMEL_NAME_CHAR,
449 (*n)->d_name);
450 }
451 mm_mailbox(tmp);
452 free(*n);
453 }
454 free(namelist);
455 carmel_free_mb_name(parsed_name);
456 }
457
458
459
460 /*----------------------------------------------------------------------
461 Find_all is the same as find for the carmel2 format -- no notion
462 of subscribed and unsubscribed
463 ---*/
464 void
carmel2_find_all(stream,pat)465 carmel2_find_all (stream, pat)
466 MAILSTREAM *stream;
467 char *pat;
468 {
469 carmel2_find(stream, pat);
470 }
471
472
473
474 /*----------------------------------------------------------------------
475 Carmel2 mail find list of bboards; always NULL, no bboards
476 ----*/
carmel2_find_bboards(stream,pat)477 void carmel2_find_bboards (stream,pat)
478 MAILSTREAM *stream;
479 char *pat;
480 {
481 /* Always a no-op, Carmel2 file format doesn't do news */
482 }
483
484
485
486 /*----------------------------------------------------------------------
487 Carmel2 mail find list of bboards; always NULL, no bboards
488 ----*/
carmel2_find_all_bboards(stream,pat)489 void carmel2_find_all_bboards (stream,pat)
490 MAILSTREAM *stream;
491 char *pat;
492 {
493 /* Always a no-op, Carmel2 file format doesn't do news */
494 }
495
496
497
498 /*----------------------------------------------------------------------
499 This is used by scandir to determine which files in the directory
500 are treated as mail files
501 ----*/
502 static int
carmel2_sift_files(dir)503 carmel2_sift_files(dir)
504 struct direct *dir;
505 {
506 if(dir->d_name[0] == '.')
507 return(0);
508 else
509 return(1);
510 }
511
512
513
514 /*----------------------------------------------------------------------
515 Dummy subscribe/unsubscribe routines.
516 Carmel driver does support this. Maybe it should support the UNIX
517 sm sometime
518 ----*/
carmel2_subscribe()519 long carmel2_subscribe() {}
carmel2_unsubscribe()520 long carmel2_unsubscribe() {}
carmel2_subscribe_bboard()521 long carmel2_subscribe_bboard() {}
carmel2_unsubscribe_bboard()522 long carmel2_unsubscribe_bboard() {}
523
524
525
526 /*----------------------------------------------------------------------
527 ----*/
528 long
carmel2_create(stream,mailbox)529 carmel2_create(stream, mailbox)
530 MAILSTREAM *stream;
531 char *mailbox;
532 {
533 }
534
535
536
537 /*----------------------------------------------------------------------
538 ----*/
539 long
carmel2_delete(stream,mailbox)540 carmel2_delete(stream, mailbox)
541 MAILSTREAM *stream;
542 char *mailbox;
543 {
544 }
545
546
547
548 /*----------------------------------------------------------------------
549 ----*/
550 long
carmel2_rename(stream,orig_mailbox,new_mailbox)551 carmel2_rename(stream, orig_mailbox, new_mailbox)
552 MAILSTREAM *stream;
553 char *orig_mailbox, *new_mailbox;
554 {
555 }
556
557
558 /*----------------------------------------------------------------------
559 Open a carmel2 mail folder/stream
560
561 Args: stream -- stream to by fully opened
562
563 Returns: it's argument or NULL of the open fails
564
565 This needs testing and more code, see pod_open for examples
566 ----*/
567
568 MAILSTREAM *
carmel2_open(stream)569 carmel2_open (stream)
570 MAILSTREAM *stream;
571 {
572 char tmp[MAILTMPLEN];
573 struct hostent *host_name;
574
575 if(!stream) return &carmel2proto; /* Must be a OP_PROTOTYPE call */
576
577 /* close old file if stream being recycled */
578 if (LOCAL) {
579 carmel2_close (stream); /* dump and save the changes */
580 stream->dtb = &carmel2driver; /* reattach this driver */
581 mail_free_cache (stream); /* clean up cache */
582 }
583
584 mailcache = carmel2_cache;
585
586 /* Allocate local stream */
587 stream->local = fs_get (sizeof (CARMEL2LOCAL));
588
589 stream->readonly = 1; /* Read-only till more work is done */
590
591 LOCAL->msg_buf = NULL;
592 LOCAL->msg_buf_size = 0;
593 LOCAL->buffered_file = NULL;
594 LOCAL->msg_buf_text_start = NULL;
595 LOCAL->msg_buf_text_offset = 0;
596 LOCAL->stdio_buf = NULL;
597 stream->msgno = -1;
598 stream->env = NULL;
599 stream->body = NULL;
600 stream->scache = 1;
601 LOCAL->calc_paths = carmel2_calc_paths;
602 LOCAL->aux_copy = NULL;
603 LOCAL->new_file_on_copy = 1;
604
605 if(carmel_open2(stream,
606 (*(LOCAL->calc_paths))(CalcPathCarmel2Index, stream->mailbox,0)) < 0)
607 return(NULL);
608
609 mail_exists (stream,stream->nmsgs);
610 mail_recent (stream,stream->recent);
611
612 return(stream);
613 }
614
615
616
617 /*----------------------------------------------------------------------
618 Do the real work of opening a Carmel folder.
619
620 Args: stream -- The mail stream being opened
621 file -- Path name of the actual Carmel index file
622
623 Returns: -1 if the open fails
624 0 if it succeeds
625
626 This is shared between the Carmel driver and the Pod driver.
627
628 Here, the status, size and date (fast info) of a message entry
629 is read out of the index file into the MESSAGECACHE structures.
630 To make the reading efficient these items are at the beginning
631 of each entry and there is a byte offset to the next entry.
632 If the byte offset is wrong (as detected by looking for the
633 start of message string) then the index is read line by line
634 until it synchs up. This allows manual editing of the index.
635 However, the first two lines of an index entry cannot be
636 edited because mail_check() writes them in place. If these
637 lines have been edited it is detected here and the folder is
638 deemed corrupt.
639 ---*/
carmel_open2(stream,file)640 carmel_open2(stream, file)
641 MAILSTREAM *stream;
642 char *file;
643 {
644 long file_pos, recent;
645 MESSAGECACHE *mc;
646 struct stat sb;
647
648 if(stat(file, &sb) < 0) {
649 sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
650 mm_log(carmel_error_buf, ERROR);
651 return(-1);
652 }
653
654 LOCAL->index_stream = fopen(file, stream->readonly ? "r" : "r+");
655 LOCAL->stdio_buf = fs_get(CARMEL2_INDEX_BUF_SIZE);
656 setbuffer(LOCAL->index_stream, LOCAL->stdio_buf, CARMEL2_INDEX_BUF_SIZE);
657 if(LOCAL->index_stream == NULL) {
658 sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
659 mm_log(carmel_error_buf, ERROR);
660 return(-1);
661 }
662
663 recent = 0;
664 stream->nmsgs = 0;
665 LOCAL->cache_size = 0;
666 LOCAL->mc_blocks = NULL;
667 LOCAL->index_size = sb.st_size;
668
669 /*---- Read line with magic number, which we already checked ------*/
670 if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),LOCAL->index_stream)==NULL){
671 fclose(LOCAL->index_stream);
672 mm_log("Mailbox is corrupt. Open failed", ERROR);
673 return(-1);
674 }
675
676 file_pos = ftell(LOCAL->index_stream);
677 if(carmel2_parse_mail(stream, file_pos) < 0) {
678 fclose(LOCAL->index_stream);
679 mm_log("Mailbox is corrupt. Open failed", ERROR);
680 return(-1);
681 }
682
683 /* Bug, this doesn't really do the recent correctly */
684
685 stream->recent = recent;
686 return(0);
687 }
688
689
690
691 /*----------------------------------------------------------------------
692 Carmel mail close
693
694 Args: stream -- stream to close
695
696 ----*/
697
698 void
carmel2_close(stream)699 carmel2_close (stream)
700 MAILSTREAM *stream;
701 {
702 if (LOCAL) { /* only if a file is open */
703 carmel2_check (stream); /* dump final checkpoint */
704 if(LOCAL->msg_buf)
705 fs_give ((void **) &LOCAL->msg_buf);
706 if(LOCAL->stdio_buf)
707 fs_give ((void **) &LOCAL->stdio_buf);
708 /* nuke the local data */
709 fs_give ((void **) &stream->local);
710 stream->dtb = NIL; /* log out the DTB */
711 }
712 }
713
714
715
716 /*----------------------------------------------------------------------
717 Carmel mail fetch fast information.
718
719 This is a no-op because the data is always available as it is read in to
720 the message cache blocks when the folder is opened
721 ----*/
722 void
carmel2_fetchfast(stream,sequence)723 carmel2_fetchfast (stream, sequence)
724 MAILSTREAM *stream;
725 char *sequence;
726 {
727 return;
728 }
729
730
731
732 /*----------------------------------------------------------------------
733 Carmel2 mail fetch flags.
734
735 This is a no-op because the data is always available as it is read in to
736 the message cache blocks when the folder is opened
737 ----*/
738 void
carmel2_fetchflags(stream,sequence)739 carmel2_fetchflags(stream, sequence)
740 MAILSTREAM *stream;
741 char *sequence;
742 {
743 return; /* no-op for local mail */
744 }
745
746
747
748 /*----------------------------------------------------------------------
749 Carmel mail fetch message structure
750 Args: stream -- stream to get structure for
751 msgno -- Message number to fetch data for
752 body -- Pointer to place to return body structure, may be NULL
753
754 If the request is the for the same msgno as last time, the saved copy
755 of the envelope and/or body structure is returned.
756
757 To get the envelope the Carmel index file itself must be read and parsed,
758 but this is fast because it is easy to parse (by design) and the amount of
759 data is small.
760
761 To get the body, the whole message is read into memory and then parsed
762 by routines called from here. Doing this for a large message can be slow,
763 so it is best not to request the body if it is not needed. (body == NULL)
764 ----*/
765
766 ENVELOPE *
carmel2_fetchstructure(stream,msgno,body)767 carmel2_fetchstructure (stream, msgno, body)
768 MAILSTREAM *stream;
769 long msgno;
770 BODY **body;
771 {
772 MESSAGECACHE *mc;
773 ENVELOPE **env;
774 BODY **b;
775
776 env = &stream->env;
777 b = &stream->body;
778
779 if (msgno != stream->msgno){
780 /* flush old poop if a different message */
781 mail_free_envelope (env);
782 mail_free_body (b);
783 }
784 stream->msgno = msgno;
785
786 mc = MC(msgno);
787
788 if(*env == NULL) {
789 *env = mail_newenvelope();
790
791 fseek(LOCAL->index_stream, mc->data1, 0);
792 fgets(carmel_20k_buf, sizeof(carmel_20k_buf), LOCAL->index_stream);
793 if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len))
794 return(NULL); /* Oh ooo */
795
796 carmel2_date2string(carmel_20k_buf, mc);
797 (*env)->date = cpystr(carmel_20k_buf);
798
799 while(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
800 LOCAL->index_stream) != NULL &&
801 strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len)) {
802 carmel_20k_buf[strlen(carmel_20k_buf) - 1] = '\0';
803 switch(*carmel_20k_buf) {
804 case 'F':
805 (*env)->from = carmel2_parse_address(carmel_20k_buf,
806 (*env)->from,
807 mylocalhost());
808 break;
809 case 'T':
810 (*env)->to = carmel2_parse_address(carmel_20k_buf,
811 (*env)->to,
812 mylocalhost());
813 break;
814 case 'C':
815 (*env)->cc = carmel2_parse_address(carmel_20k_buf,
816 (*env)->cc,
817 mylocalhost());
818 break;
819 case 'B':
820 (*env)->bcc = carmel2_parse_address(carmel_20k_buf,
821 (*env)->bcc,
822 mylocalhost());
823 break;
824 case 'E':
825 (*env)->sender = carmel2_parse_address(carmel_20k_buf,
826 (*env)->sender,
827 mylocalhost());
828 break;
829 case 'R':
830 (*env)->reply_to = carmel2_parse_address(carmel_20k_buf,
831 (*env)->reply_to,
832 mylocalhost());
833 break;
834 case 'H':
835 (*env)->return_path =carmel2_parse_address(carmel_20k_buf,
836 (*env)->return_path,
837 mylocalhost());
838 break;
839 case 'J':
840 (*env)->subject = cpystr(carmel_20k_buf+1);
841 break;
842 case 'I':
843 (*env)->message_id = cpystr(carmel_20k_buf+1);
844 break;
845 case 'L':
846 (*env)->in_reply_to = cpystr(carmel_20k_buf+1);
847 break;
848 case 'N':
849 (*env)->newsgroups = cpystr(carmel_20k_buf+1);
850 break;
851 case 'r':
852 (*env)->remail = cpystr(carmel_20k_buf+1);
853 break;
854 default:
855 break;
856 }
857 }
858 }
859
860 if(body != NULL) {
861 if(*b == NULL) {
862 /* Have to fetch the body structure too */
863 *b = carmel2_bodystruct(stream, mc);
864 }
865 *body = *b;
866 }
867
868 return(*env); /* return the envelope */
869 }
870
871
872
873
874 /*----------------------------------------------------------------------
875 Carmel mail fetch message header
876
877 Args: stream --
878 msgno
879
880 Returns: pointer to text of mail header. Returned string is null terminated
881 and consists of internal storage. It will be valid till the next
882 call to mail_fetchheader() or mail_fetchtext(). An empty
883 string is returned if the fetch fails.
884 ----*/
885
886 char *
carmel2_fetchheader(stream,msgno)887 carmel2_fetchheader (stream, msgno)
888 MAILSTREAM *stream;
889 long msgno;
890 {
891 char *header;
892 MESSAGECACHE *mc;
893
894 mc = MC(msgno);
895 header = carmel2_readmsg(stream, 1, 0, mc->data2);
896
897 return(header == NULL ? "" : header);
898 }
899
900
901
902 /*----------------------------------------------------------------------
903 Carmel mail fetch message text (only)
904
905 Args: stream --
906 msgno
907
908 Returns: pointer to text of mail message. Returned string is null terminated
909 and consists of internal storage. It will be valid till the next
910 call to mail_fetchheader() or mail_fetchtext(). An empty
911 string is returned if the fetch fails.
912 ----*/
913
914 char *
carmel2_fetchtext(stream,msgno)915 carmel2_fetchtext(stream, msgno)
916 MAILSTREAM *stream;
917 long msgno;
918 {
919 MESSAGECACHE *mc;
920 char *m;
921
922 mc = MC(msgno);
923
924 if (!mc->seen) { /* if message not seen before */
925 mc->seen = T; /* mark as seen */
926 LOCAL->dirty = T; /* and that stream is now dirty */
927 }
928
929 m = carmel2_readmsg(stream, 0, mc->rfc822_size, mc->data2);
930
931 return(m);
932 }
933
934
935
936 /*----------------------------------------------------------------------
937 Fetch MIME body parts
938
939 Args: stream
940 msgno
941 section -- string section number spec. i.e. "1.3.4"
942 len -- pointer to return len in
943
944 Returns: The contents of the body part, or NULL
945 ---*/
946
947 char *
carmel2_fetchbody(stream,msgno,section,len)948 carmel2_fetchbody (stream, msgno, section, len)
949 MAILSTREAM *stream;
950 long msgno;
951 char *section;
952 unsigned long *len;
953 {
954 char *base;
955 BODY *b;
956 PART *part;
957 int part_num;
958 long offset;
959 MESSAGECACHE *mc;
960
961 if (carmel2_fetchstructure(stream, msgno, &b) == NULL || b == NULL)
962 return(NULL);
963
964 if(section == NULL || *section == '\0')
965 return(NULL);
966
967 part_num = strtol(section, §ion, 10);
968 if(part_num <= 0)
969 return(NULL);
970
971 base = carmel2_fetchtext(stream, msgno);
972 if(base == NULL)
973 base = "";
974 offset = 0;
975
976 do { /* until find desired body part */
977 if (b->type == TYPEMULTIPART) {
978 part = b->contents.part; /* yes, find desired part */
979 while (--part_num && (part = part->next));
980 if (!part)
981 return (NIL); /* bad specifier */
982
983 /* Found part, get ready to go further */
984 b = &part->body;
985 if(b->type == TYPEMULTIPART && *section == '\0')
986 return(NULL); /* Ran out of section numbers, needed more */
987
988 offset = part->offset; /* and its offset */
989
990 } else if (part_num != 1) {
991 return NIL; /* Not Multipart, better be part 1 */
992 }
993
994 /* need to go down further? */
995
996 if(*section == 0) {
997 break;
998 } else {
999 switch (b->type) {
1000 case TYPEMESSAGE: /* embedded message, calculate new base */
1001 offset = b->contents.msg.offset;
1002 b = b->contents.msg.body;
1003 /* got its body, drop into multipart case*/
1004
1005 case TYPEMULTIPART: /* multipart, get next section */
1006 if ((*section++ == '.') &&
1007 (part_num = strtol (section, §ion,10)) > 0)
1008 break; /* Found the part */
1009
1010 default: /* bogus subpart specification */
1011 return(NULL);
1012 }
1013 }
1014 } while (part_num);
1015
1016 mc = MC(msgno);
1017 /* lose if body bogus */
1018 if ((!b) || b->type == TYPEMULTIPART)
1019 return(NULL);
1020
1021 if (!mc->seen) { /* if message not seen before */
1022 mc->seen = T; /* mark as seen */
1023 LOCAL->dirty = T; /* and that stream is now dirty */
1024 }
1025
1026 *len = b->size.ibytes;
1027 return(base + offset);
1028 }
1029
1030
1031
1032 /*----------------------------------------------------------------------
1033 * Carmel mail set flag
1034 * Accepts: MAIL stream
1035 * sequence
1036 * flag(s)
1037 */
1038
1039 void
carmel2_setflag(stream,sequence,flag)1040 carmel2_setflag (stream,sequence,flag)
1041 MAILSTREAM *stream;
1042 char *sequence;
1043 char *flag;
1044 {
1045 MESSAGECACHE *elt;
1046 long i;
1047 short f = carmel2_getflags (stream,flag);
1048 if (!f) return; /* no-op if no flags to modify */
1049 /* get sequence and loop on it */
1050 if (mail_sequence (stream,sequence))
1051 for (i = 1; i <= stream->nmsgs; i++){
1052 elt = MC(i);
1053 if (elt->sequence) {
1054 /* set all requested flags */
1055 if (f&fSEEN) elt->seen = T;
1056 if (f&fDELETED) elt->deleted = T;
1057 if (f&fFLAGGED) elt->flagged = T;
1058 if (f&fANSWERED) elt->answered = T;
1059 /* Could be more creative about keeping track to see if something
1060 really changed */
1061 LOCAL->dirty = T;
1062 }
1063 }
1064 }
1065
1066
1067
1068 /*----------------------------------------------------------------------
1069 * Carmel mail clear flag
1070 * Accepts: MAIL stream
1071 * sequence
1072 * flag(s)
1073 */
1074
1075 void
carmel2_clearflag(stream,sequence,flag)1076 carmel2_clearflag (stream,sequence,flag)
1077 MAILSTREAM *stream;
1078 char *sequence;
1079 char *flag;
1080 {
1081 MESSAGECACHE *elt;
1082 long i = stream->nmsgs;
1083 short f = carmel2_getflags (stream,flag);
1084 if (!f) return; /* no-op if no flags to modify */
1085 /* get sequence and loop on it */
1086 if (mail_sequence (stream,sequence))
1087 for(i = 1; i <= stream->nmsgs; i++) {
1088 elt = MC(i);
1089 if (elt->sequence) {
1090 /* clear all requested flags */
1091 if (f&fSEEN) elt->seen = NIL;
1092 if (f&fDELETED) elt->deleted = NIL;
1093 if (f&fFLAGGED) elt->flagged = NIL;
1094 if (f&fANSWERED) elt->answered = NIL;
1095 /* clearing either seen or deleted does this */
1096 /* Could be more creative about keeping track to see if something
1097 really changed */
1098 LOCAL->dirty = T; /* mark stream as dirty */
1099 }
1100 }
1101 }
1102
1103
1104
1105 /* Carmel mail search for messages
1106 * Accepts: MAIL stream
1107 * search criteria
1108 */
1109
1110 void
carmel2_search(stream,criteria)1111 carmel2_search (stream,criteria)
1112 MAILSTREAM *stream;
1113 char *criteria;
1114 {
1115 long i,n;
1116 char *d;
1117 search_t f;
1118
1119 /* initially all searched */
1120 for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
1121 /* get first criterion */
1122 if (criteria && (criteria = strtok (criteria," "))) {
1123 /* for each criterion */
1124 for (; criteria; (criteria = strtok (NIL," "))) {
1125 f = NIL; d = NIL; n = 0; /* init then scan the criterion */
1126 switch (*ucase (criteria)) {
1127 case 'A': /* possible ALL, ANSWERED */
1128 if (!strcmp (criteria+1,"LL")) f = carmel2_search_all;
1129 else if (!strcmp (criteria+1,"NSWERED")) f = carmel2_search_answered;
1130 break;
1131 case 'B': /* possible BCC, BEFORE, BODY */
1132 if (!strcmp (criteria+1,"CC"))
1133 f = carmel2_search_string (carmel2_search_bcc,&d,&n);
1134 else if (!strcmp (criteria+1,"EFORE"))
1135 f = carmel2_search_date (carmel2_search_before,&n);
1136 else if (!strcmp (criteria+1,"ODY"))
1137 f = carmel2_search_string (carmel2_search_body,&d,&n);
1138 break;
1139 case 'C': /* possible CC */
1140 if (!strcmp (criteria+1,"C"))
1141 f = carmel2_search_string (carmel2_search_cc,&d,&n);
1142 break;
1143 case 'D': /* possible DELETED */
1144 if (!strcmp (criteria+1,"ELETED")) f = carmel2_search_deleted;
1145 break;
1146 case 'F': /* possible FLAGGED, FROM */
1147 if (!strcmp (criteria+1,"LAGGED")) f = carmel2_search_flagged;
1148 else if (!strcmp (criteria+1,"ROM"))
1149 f = carmel2_search_string (carmel2_search_from,&d,&n);
1150 break;
1151 case 'K': /* possible KEYWORD */
1152 if (!strcmp (criteria+1,"EYWORD"))
1153 f = carmel2_search_flag (carmel2_search_keyword,&d);
1154 break;
1155 case 'N': /* possible NEW */
1156 if (!strcmp (criteria+1,"EW")) f = carmel2_search_new;
1157 break;
1158
1159 case 'O': /* possible OLD, ON */
1160 if (!strcmp (criteria+1,"LD")) f = carmel2_search_old;
1161 else if (!strcmp (criteria+1,"N"))
1162 f = carmel2_search_date (carmel2_search_on,&n);
1163 break;
1164 case 'R': /* possible RECENT */
1165 if (!strcmp (criteria+1,"ECENT")) f = carmel2_search_recent;
1166 break;
1167 case 'S': /* possible SEEN, SINCE, SUBJECT */
1168 if (!strcmp (criteria+1,"EEN")) f = carmel2_search_seen;
1169 else if (!strcmp (criteria+1,"INCE"))
1170 f = carmel2_search_date (carmel2_search_since,&n);
1171 else if (!strcmp (criteria+1,"UBJECT"))
1172 f = carmel2_search_string (carmel2_search_subject,&d,&n);
1173 break;
1174 case 'T': /* possible TEXT, TO */
1175 if (!strcmp (criteria+1,"EXT"))
1176 f = carmel2_search_string (carmel2_search_text,&d,&n);
1177 else if (!strcmp (criteria+1,"O"))
1178 f = carmel2_search_string (carmel2_search_to,&d,&n);
1179 break;
1180 case 'U': /* possible UN* */
1181 if (criteria[1] == 'N') {
1182 if (!strcmp (criteria+2,"ANSWERED")) f = carmel2_search_unanswered;
1183 else if (!strcmp (criteria+2,"DELETED")) f = carmel2_search_undeleted;
1184 else if (!strcmp (criteria+2,"FLAGGED")) f = carmel2_search_unflagged;
1185 else if (!strcmp (criteria+2,"KEYWORD"))
1186 f = carmel2_search_flag (carmel2_search_unkeyword,&d);
1187 else if (!strcmp (criteria+2,"SEEN")) f = carmel2_search_unseen;
1188 }
1189 break;
1190 default: /* we will barf below */
1191 break;
1192 }
1193 if (!f) { /* if can't determine any criteria */
1194 sprintf(carmel_error_buf,"Unknown search criterion: %s",criteria);
1195 mm_log (carmel_error_buf,ERROR);
1196 return;
1197 }
1198 /* run the search criterion */
1199 for (i = 1; i <= stream->nmsgs; ++i)
1200 if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
1201 mail_elt (stream,i)->searched = NIL;
1202 }
1203 /* report search results to main program */
1204 for (i = 1; i <= stream->nmsgs; ++i)
1205 if (mail_elt (stream,i)->searched) mail_searched (stream,i);
1206 }
1207 }
1208
1209
1210
1211
1212 /*----------------------------------------------------------------------
1213 * Carmel mail ping mailbox
1214 * Accepts: MAIL stream
1215 * Returns: T if stream alive, else NIL
1216 */
1217
1218 long
carmel2_ping(stream)1219 carmel2_ping (stream)
1220 MAILSTREAM *stream;
1221 {
1222 struct stat sb;
1223 char path[1000], *mailfile;
1224 #ifdef TESTING
1225 char debug_buf[1000];
1226 #endif
1227
1228 if(!stream->readonly &&
1229 carmel2_update_lock(stream->local, stream->mailbox, READ_LOCK) < 0) {
1230 /* Yuck! Someone messed up our lock file, this stream is hosed */
1231 mm_log("Mailbox updated unexpectedly! Mailbox closed", ERROR);
1232 stream->readonly = 1;
1233 return NIL;
1234 }
1235
1236 /*--- First check to see if the Carmel index grew ----*/
1237 /* BUG, will this really work on NFS since the file is held open? */
1238 stat((*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox, 0), &sb);
1239 if(sb.st_size > LOCAL->index_size) {
1240 #ifdef TESTING
1241 mm_log("!!!!! Carmel index grew", NIL);
1242 #endif
1243 /* Pull in the new mail.... */
1244 #ifdef MAY_NEED_FOR_NFS
1245 /*---- First close and reopen the mail file -----*/
1246 fclose(LOCAL->index_stream);
1247 LOCAL->index_stream = fopen((*LOCAL->calc_paths)(CalcPathCarmel2Index,
1248 stream->mailbox, 0),
1249 stream->readonly ? "r" : "r+");
1250 if(LOCAL->index_stream == NULL) {
1251 mm_log("Mailbox stolen. Mailbox closed", ERROR);
1252 stream->readonly = 1;
1253 return NIL;
1254 }
1255 #endif
1256 if(carmel2_parse_mail(stream, LOCAL->index_size) < 0) {
1257 mm_log("Mailbox corrupted. Mailbox closed", ERROR);
1258 stream->readonly = 1;
1259 return NIL;
1260 }
1261 LOCAL->index_size = sb.st_size;
1262 }
1263
1264 if(sb.st_size < LOCAL->index_size) {
1265 /* Something bad happened, mail box shrunk on us, shutdown */
1266 stream->readonly = 1;
1267 mm_log("Mailbox shrank unexpectedly! Mailbox closed", ERROR);
1268 return NIL;
1269 }
1270
1271 #ifdef TESTING
1272 sprintf(debug_buf, "!!!!! Mailbox:\"%s\" pretty_mailbox:\"%s\"",
1273 stream->mailbox, carmel_pretty_mailbox(stream->mailbox));
1274 mm_log(debug_buf, NIL);
1275 sprintf(debug_buf, "!!!! Readonly:%d, Carmel:%d", stream->readonly,
1276 LOCAL->carmel);
1277 mm_log(debug_buf, NIL);
1278 #endif
1279
1280 if(!stream->readonly &&
1281 ((LOCAL->carmel &&
1282 strcmp(carmel_pretty_mailbox(stream->mailbox), "MAIL") == 0) ||
1283 strucmp2(carmel_pretty_mailbox(stream->mailbox), "inbox") == 0)) {
1284 /* If it's the inbox we've got to check the spool file */
1285 mailfile = getenv("MAIL") == NULL ? MAILFILE : getenv("MAIL");
1286 sprintf(path, mailfile, myhomedir());
1287 #ifdef TESTING
1288 sprintf(debug_buf, "!!!!! Checking spool mail\"%s\"", path);
1289 mm_log(debug_buf, NIL);
1290 #endif
1291 if(stat(path, &sb) < 0)
1292 return(T); /* No new mail...*/
1293 if(sb.st_size > 0) {
1294 mm_critical(stream);
1295 if(carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK) < 0) {
1296 mm_nocritical(stream);
1297 return(T);
1298 }
1299 mm_log("!!!! Inbox locked, sucking in mail", NIL);
1300 carmel2_spool_mail(stream, path, stream->mailbox, 1); /*go get it*/
1301 carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
1302 mm_nocritical(stream);
1303 stat((*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox,0),
1304 &sb);
1305 LOCAL->index_size = sb.st_size;
1306 }
1307 }
1308
1309 return T;
1310 }
1311
1312
1313
1314
1315
1316 /*----------------------------------------------------------------------
1317 This checks for new mail and checkpoints the mail file
1318
1319 Args -- The stream to check point
1320
1321 ----*/
1322 void
carmel2_check(stream)1323 carmel2_check(stream)
1324 MAILSTREAM *stream;
1325 {
1326 (void)carmel2_check2(stream);
1327 }
1328
1329
1330
1331 /*----------------------------------------------------------------------
1332 Do actual work of a check on carmel2 index, returning status
1333
1334 Returns: 0 if no checkpoint was done, 1 if it was done.
1335 ----*/
carmel2_check2(stream)1336 carmel2_check2(stream)
1337 MAILSTREAM *stream;
1338 {
1339 int msgno;
1340 MESSAGECACHE *mc;
1341
1342 if(stream->readonly || LOCAL == NULL)
1343 return(0); /* Nothing to do in readonly or closed case */
1344
1345 carmel2_ping(stream); /* check for new mail (Ping always checks) */
1346
1347 if(!LOCAL->dirty)
1348 return(0); /* Nothing to do */
1349
1350 mm_critical(stream);
1351 if(carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK) < 0) {
1352 mm_nocritical(stream);
1353 return(0); /* Couldn't get a write lock, nothing we can do */
1354 }
1355
1356 for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
1357 mc = MC(msgno);
1358 fseek(LOCAL->index_stream, mc->data1 + carmel2_s_o_m_len + 13, 0);
1359 if(fprintf(LOCAL->index_stream,
1360 "U%c%c%c%c%c____________________________\n",
1361 mc->flagged ? 'F' : 'f',
1362 mc->recent ? 'R' : 'r',
1363 mc->answered ? 'A' : 'a',
1364 mc->deleted ? 'D' : 'd',
1365 mc->seen ? 'S' : 's') == EOF) {
1366 /* BUG .. Need some error check here */
1367 }
1368 }
1369 fflush(LOCAL->index_stream);
1370
1371 carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
1372 mm_nocritical(stream);
1373 mm_log("Check completed", NIL);
1374 return(1);
1375 }
1376
1377
1378
1379
1380
1381 /*----------------------------------------------------------------------
1382 Carmel2 mail expunge mailbox
1383
1384 Args: stream -- mail stream to expunge
1385
1386 ----*/
1387
carmel2_expunge(stream)1388 void carmel2_expunge (stream)
1389 MAILSTREAM *stream;
1390 {
1391 char *index_file;
1392 long msgno, new_msgno;
1393 FILE *new_index;
1394 MESSAGECACHE *mc, *new_mc;
1395 ENVELOPE *envelope;
1396 int save_e;
1397 struct stat sb;
1398
1399 if (stream->readonly) {
1400 if(!stream->silent)
1401 mm_log ("Expunge ignored on readonly mailbox",NIL);
1402 return;
1403 }
1404
1405 mm_critical(stream);
1406 carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK);
1407
1408 strcpy(carmel_path_buf,
1409 (*LOCAL->calc_paths)(CalcPathCarmel2Expunge, stream->mailbox, 0));
1410
1411 new_index = fopen(carmel_path_buf, "w");
1412 if(new_index == NULL) {
1413 goto fail;
1414 }
1415 if(fprintf(new_index, carmel2_s_o_f) == EOF)
1416 goto fail;
1417
1418 new_msgno = 1;
1419 for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
1420 mc = MC(msgno);
1421 if(mc->deleted) {
1422 mm_expunged(stream, new_msgno);
1423 continue;
1424 }
1425
1426 if(msgno != new_msgno) {
1427 new_mc = MC(new_msgno);
1428 *new_mc = *mc;
1429 new_mc->msgno = new_msgno;
1430 mc = new_mc;
1431 }
1432
1433 envelope = carmel2_fetchstructure(stream, msgno, NULL);
1434 if(envelope == NULL)
1435 goto fail;
1436
1437 /* get this after envelope to offset is still valid in old file */
1438 mc->data1 = ftell(new_index);
1439
1440 if(carmel2_write_index(envelope, mc, new_index) < 0)
1441 goto fail;
1442 new_msgno++;
1443 }
1444
1445 index_file = (*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox, 0);
1446
1447 /*--- Close it to make sure bits are really on disk across NFS. ---*/
1448 if(fclose(new_index) != EOF) {
1449 /*--- copy of index was a success, rename it ---*/
1450 unlink(index_file);
1451 link(carmel_path_buf, index_file);
1452
1453 /*--- Save the mail index size ----*/
1454 stat(index_file, &sb);
1455 LOCAL->index_size = sb.st_size;
1456
1457 stream->nmsgs = new_msgno - 1;
1458 mm_exists(stream, stream->nmsgs);
1459
1460 fclose(LOCAL->index_stream);
1461 LOCAL->index_stream = fopen(index_file, "r+");
1462 }
1463 unlink(carmel_path_buf);
1464 carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
1465 mm_nocritical(stream);
1466 return;
1467
1468 fail:
1469 save_e = errno;
1470 if(new_index != NULL)
1471 fclose(new_index);
1472 unlink(carmel_path_buf);
1473 sprintf(carmel_error_buf, "Expunge failed: %s", strerror(save_e));
1474 mm_log(carmel_error_buf, WARN);
1475 carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
1476 mm_nocritical(stream);
1477 return;
1478 }
1479
1480
1481
1482 /*----------------------------------------------------------------------
1483 Carmel2 mail copy message(s)
1484
1485 Args: stream - mail stream
1486 sequence - message sequence
1487 mailbox - destination mailbox, FQN
1488
1489 Returns: T if copy successful, else NIL
1490 ----*/
1491 long
carmel2_copy(stream,sequence,mailbox)1492 carmel2_copy(stream, sequence, mailbox)
1493 MAILSTREAM *stream;
1494 char *sequence;
1495 char *mailbox;
1496 {
1497 ENVELOPE *e;
1498 MESSAGECACHE *mc, new_mc;
1499 long msgno, file_no, file_pos;
1500 int fail;
1501 char *file_name, *line;
1502 FILE *dest_stream;
1503 struct stat sb;
1504
1505 if (!mail_sequence (stream,sequence)) /* get sequence to do */
1506 return(NIL);
1507
1508 /*--- Get the file open (creating if needed) first ----*/
1509 file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Index, mailbox, 0, 0);
1510 if(file_name == NULL)
1511 return(NIL);
1512
1513 if(stat(file_name, &sb) < 0) {
1514 mm_notify (stream,"[TRYCREATE] Must create mailbox before copy", NIL);
1515 return(NIL);
1516 }
1517
1518 dest_stream = fopen(file_name, "a+");
1519 if(dest_stream == NULL) {
1520 sprintf(carmel_error_buf, "Can't copy message to %s: %s",
1521 mailbox, strerror(errno));
1522 mm_log(carmel_error_buf, ERROR);
1523 return(NIL);
1524 }
1525
1526 mm_critical(stream);
1527
1528 if(carmel2_lock(stream->local, mailbox, WRITE_LOCK) < 0) {
1529 mm_nocritical(stream);
1530 sprintf(carmel_error_buf,
1531 "Mailbox %s locked, can't copy messages to it",
1532 mailbox);
1533 mm_log(carmel_error_buf, ERROR);
1534 fclose(dest_stream);
1535 return(NIL);
1536 }
1537
1538
1539 /*--- Get the destination Carmel index open ----*/
1540 file_pos = ftell(dest_stream);
1541 fail = 0;
1542
1543
1544 for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
1545 mc = MC(msgno);
1546 if(!mc->sequence)
1547 continue;
1548
1549 new_mc = *mc;
1550
1551 if(LOCAL->new_file_on_copy) {
1552 new_mc.data2 = carmel2_copy_msg_file(stream, mc->data2, mailbox);
1553 if((long)new_mc.data2 < 0) {
1554 fail = 1;
1555 break;
1556 }
1557 }
1558
1559 e = carmel2_fetchstructure(stream, msgno, NULL);
1560 if(carmel2_write_index(e, &new_mc, dest_stream) < 0) {
1561 fail = 1;
1562 break;
1563 }
1564
1565 if(LOCAL->carmel && LOCAL->aux_copy != NULL) {
1566 if((*LOCAL->aux_copy)(stream->local, mailbox, e, &new_mc)) {
1567 fail = 1;
1568 break;
1569 }
1570 }
1571 }
1572
1573 if(fail) {
1574 ftruncate(fileno(dest_stream), file_pos);
1575 }
1576
1577 fclose(dest_stream);
1578
1579 carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
1580
1581 mm_nocritical(stream);
1582
1583 return(fail ? NIL : T);
1584 }
1585
1586
1587
1588 /*----------------------------------------------------------------------
1589 Carmel2 mail move message(s)
1590
1591
1592 Returns: T if move successful, else NIL
1593 ----*/
1594
1595 long
carmel2_move(stream,sequence,mailbox)1596 carmel2_move (stream,sequence,mailbox)
1597 MAILSTREAM *stream;
1598 char *sequence;
1599 char *mailbox;
1600 {
1601 long i;
1602 MESSAGECACHE *elt;
1603
1604 if (!(mail_sequence (stream,sequence) &&
1605 carmel2_copy (stream,sequence,mailbox))) return NIL;
1606 /* delete all requested messages */
1607 for (i = 1; i <= stream->nmsgs; i++)
1608 elt = MC(i);
1609 if (elt->sequence) {
1610 elt->deleted = T; /* mark message deleted */
1611 LOCAL->dirty = T; /* mark mailbox as dirty */
1612 }
1613 return T;
1614 }
1615
1616
1617
1618 /*----------------------------------------------------------------------
1619 * Carmel2 garbage collect stream
1620 * Accepts: Mail stream
1621 * garbage collection flags
1622 */
1623
carmel2_gc(stream,gcflags)1624 void carmel2_gc (stream, gcflags)
1625 MAILSTREAM *stream;
1626 long gcflags;
1627 {
1628 /* No garbage collection in Carmel2 driver, not much to collect */
1629 }
1630
1631
1632
1633 /*----------------------------------------------------------------------
1634 Handle MessageCache for carmel2 mail driver
1635
1636 Args: stream --
1637 msgno -- message number
1638 op -- operation code
1639
1640 The carmel2 format keeps MESSAGECACHE entries in core for all messages
1641 in the open mail folder so there isn't any cache flushing and rereading
1642 that has to go on.
1643 ----*/
1644 void *
carmel2_cache(stream,msgno,op)1645 carmel2_cache(stream, msgno, op)
1646 MAILSTREAM *stream;
1647 long msgno;
1648 long op;
1649 {
1650 /* It's a carmel driver if first 6 letters of name are carmel */
1651 if(stream->dtb == NULL)
1652 return(0);
1653
1654 if(struncmp2(stream->dtb->name, "carmel", 6) == 0) {
1655 if(op == CH_MAKEELT)
1656 return(MC(msgno));
1657 else
1658 return(0);
1659 }
1660
1661 /* Not a carmel2 or carmel driver, call the standard function. This works
1662 as long as there is only one other driver since we know it must be
1663 mm_cache().
1664 */
1665 return(mm_cache(stream, msgno, op));
1666 }
1667
1668
1669
1670 /*----------------------------------------------------------------------
1671 Append a message to a mailbox
1672
1673 Args: mailbox -- The message to append the mail folder too
1674 message -- Message header and text in rfc822 \r\n format to append
1675
1676 Returns: T if all went OK, NIL if not
1677 ----*/
1678 long
carmel2_append(stream,mailbox,flags,date,message)1679 carmel2_append(stream, mailbox, flags, date, message)
1680 MAILSTREAM *stream;
1681 char *mailbox, *flags, *date;
1682 STRING *message;
1683 {
1684 CARMEL2LOCAL local;
1685
1686 /*---- A fake local data structure to pass to other functions---*/
1687 local.calc_paths = carmel2_calc_paths;
1688 local.carmel = 0;
1689 local.aux_copy = NULL;
1690
1691 return(carmel2_append2(stream, &local, mailbox, flags, date, message));
1692 }
1693
1694
1695
1696
1697 /*----------------------------------------------------------------------
1698 Fetch the body structure for a camel message
1699
1700 Args: stream -- MAILSTREAM
1701 mc -- The incore message cache entry for the message
1702
1703 Returns: body structure
1704
1705 Note, the envelope that results from the parse here is ignored. Only
1706 the envelope from the index is actually used in the Carmel2 format.
1707 ----*/
1708 static BODY *
carmel2_bodystruct(stream,mc)1709 carmel2_bodystruct(stream, mc)
1710 MESSAGECACHE *mc;
1711 MAILSTREAM *stream;
1712 {
1713 char *header, *text, *file, *p;
1714 ENVELOPE *e_place_holder;
1715 BODY *b;
1716 STRING string_struct;
1717
1718 header = carmel2_readmsg(stream, 1, mc->rfc822_size, mc->data2);
1719 if(header == NULL)
1720 return(NULL);
1721
1722 text = carmel2_readmsg(stream, 0, mc->rfc822_size, mc->data2);
1723 if(text == NULL)
1724 return(NULL);
1725
1726 INIT(&string_struct, mail_string, (void *)text, strlen(text));
1727 rfc822_parse_msg(&e_place_holder, &b, header, strlen(header),
1728 &string_struct, mylocalhost(), carmel_20k_buf);
1729
1730 mail_free_envelope(&e_place_holder);
1731
1732 #ifdef BWC
1733 /* If there's no content type in the header and it's the carmel
1734 driver at the BWC set the type X-BWC-Glyph
1735 */
1736 for(p = header; *p; p++)
1737 if(*p=='\n' && (*(p+1)=='C' || *(p+1)=='c') &&
1738 struncmp2(p+1,"content-type:", 13) == 0)
1739 break;
1740
1741 if(!*p && LOCAL->carmel && /* Carmel, not Carmel2 */
1742 b->type == TYPETEXT && /* Type text */
1743 (b->subtype == NULL || strcmp(b->subtype,"PLAIN") == 0) &&
1744 ((int)mc->year) + 1969 < 1994) {
1745 /* Most mail in a pod mail store is in the BWC-Glyph character
1746 set, but there is no tag in the data on disk, so we fake it here.
1747 We expect after 1994 all mail generated in BWC-Glyph format
1748 will have proper tags!
1749 */
1750 b->subtype = cpystr("X-BWC-Glyph");
1751 }
1752 #endif
1753
1754 return(b);
1755 }
1756
1757
1758
1759 /*----------------------------------------------------------------------
1760 Parse an address in a Carmel2 format mail index
1761
1762 Args: line -- The line from the index to parse
1763 addr -- The address list to add to (if there is one)
1764
1765 Returns: address list
1766 ----*/
1767 static ADDRESS *
carmel2_parse_address(line,addr,localhost)1768 carmel2_parse_address(line, addr, localhost)
1769 char *line, *localhost;
1770 ADDRESS *addr;
1771 {
1772 ADDRESS *a;
1773
1774 if(addr == NULL) {
1775 addr = mail_newaddr();
1776 a = addr;
1777 } else {
1778 for(a = addr; a!= NULL && a->next != NULL; a = a->next);
1779 a->next = mail_newaddr();
1780 a = a->next;
1781 }
1782
1783 line++; /* Skip the tag chacater */
1784 a->personal = carmel2_parse_addr_field(&line);
1785 a->mailbox = carmel2_parse_addr_field(&line);
1786 a->host = carmel2_parse_addr_field(&line);
1787 a->adl = carmel2_parse_addr_field(&line);
1788 /* if(a->host == NULL)
1789 a->host = cpystr(localhost); */ /* host can't be NULL */
1790 /* Yes it can for Internet group syntax */
1791 return(addr);
1792 }
1793
1794
1795
1796 /*----------------------------------------------------------------------
1797 Parse the next address field out of a carmel address index entry
1798
1799 Args: string -- pointer to pointer to string
1800
1801 Returns: field in malloced string or NULL
1802
1803 Input string is a bunch of substrings separated by ^A. This function scans
1804 for the next ^A or end of string, cuts it out and returns it. The original
1805 strings passed in is mangled
1806 ----*/
1807 static char *
carmel2_parse_addr_field(string)1808 carmel2_parse_addr_field(string)
1809 char **string;
1810 {
1811 char *p, end, *start;
1812
1813 start = p = *string;
1814 while(*p > '\001') /* Find end of sub string or string */
1815 p++;
1816 if((p - *string) == 0) {
1817 if(*p) p++;
1818 *string = p;
1819 return(NULL); /* If nothing found return nothing */
1820 }
1821 end = *p; /* Save terminating character (^A or \0) */
1822 *p = '\0';
1823 if(end) /* If not end of string, get ready for next call */
1824 p++;
1825 *string = p; /* Return pointer to next substring */
1826 return(cpystr(start));
1827 }
1828
1829
1830
1831
1832 /*----------------------------------------------------------------------
1833 Write an entry into a carmel2 index
1834
1835 Args: e -- Envelope
1836 mc -- Message Cache element
1837 stream -- File stream to write to
1838
1839 Returns: 0 if OK, -1 if failed
1840 ----*/
carmel2_write_index(e,mc,stream)1841 carmel2_write_index(e, mc, stream)
1842 ENVELOPE *e;
1843 MESSAGECACHE *mc;
1844 FILE *stream;
1845 {
1846 long f_start, f_end;
1847
1848 f_start = ftell(stream);
1849
1850 if(fprintf(stream, "%s\007\001xxxxxxxxxx\n", carmel2_s_o_m) == EOF)
1851 goto blah;
1852 if(fprintf(stream, "U%c%c%c%c%c____________________________\n",
1853 mc->flagged ? 'F' : 'f',
1854 mc->recent ? 'R' : 'r',
1855 mc->answered ? 'A' : 'a',
1856 mc->deleted ? 'D' : 'd',
1857 mc->seen ? 'S' : 's') == EOF)
1858 goto blah;
1859 if(fprintf(stream, "Z%d\n", mc->rfc822_size) == EOF)
1860 goto blah;
1861 if(fprintf(stream, "D%d\001%d\001%d\001%d\001%d\001%d\001%d\001%d\n",
1862 mc->year+1969, mc->month, mc->day, mc->hours, mc->minutes,
1863 mc->seconds, mc->zhours * (mc->zoccident ? 1 : -1),
1864 mc->zminutes) == EOF)
1865 goto blah;
1866 if(fprintf(stream, "Svmail\n") == EOF)
1867 goto blah;
1868 if(fprintf(stream, "P%d\n",mc->data2) == EOF)
1869 goto blah;
1870 if(carmel2_index_address(e->to, 'T', stream) < 0)
1871 goto blah;
1872 if(carmel2_index_address(e->from, 'F', stream) < 0)
1873 goto blah;
1874 if(carmel2_index_address(e->cc, 'C', stream) < 0)
1875 goto blah;
1876 if(carmel2_index_address(e->bcc, 'B', stream) < 0)
1877 goto blah;
1878 #ifdef HAVE_RESENT
1879 if(carmel2_index_address(e->resent_to, 't', stream) < 0)
1880 goto blah;
1881 if(carmel2_index_address(e->resent_from, 'f', stream) < 0)
1882 goto blah;
1883 if(carmel2_index_address(e->resent_cc, 'c', stream) < 0)
1884 goto blah;
1885 if(carmel2_index_address(e->resent_bcc, 'b', stream) < 0)
1886 goto blah;
1887 #endif
1888 if(carmel2_index_address(e->return_path, 'H', stream) < 0)
1889 goto blah;
1890 if(carmel2_index_address(e->sender, 'E', stream) < 0)
1891 goto blah;
1892 if(carmel2_index_address(e->reply_to, 'R', stream) < 0)
1893 goto blah;
1894 if(e->in_reply_to != NULL)
1895 if(fprintf(stream, "L%s\n", e->in_reply_to) == EOF)
1896 goto blah;
1897 if(e->remail != NULL)
1898 if(fprintf(stream, "r%s\n", e->remail) == EOF)
1899 goto blah;
1900 if(e->message_id != NULL)
1901 if(fprintf(stream, "I%s\n", e->message_id) == EOF)
1902 goto blah;
1903 if(e->newsgroups != NULL)
1904 if(fprintf(stream, "N%s\n", e->newsgroups) == EOF)
1905 goto blah;
1906 if(e->subject != NULL)
1907 if(fprintf(stream, "J%s\n", e->subject) == EOF)
1908 goto blah;
1909
1910 /*--- figure out and write the offset ---*/
1911 f_end = ftell(stream);
1912 if(fseek(stream, f_start, 0) < 0)
1913 goto blah;
1914 if(fprintf(stream, "%s\007\001%10d\n", carmel2_s_o_m, f_end - f_start)==EOF)
1915 goto blah;
1916 if(fseek(stream, f_end, 0) < 0)
1917 goto blah;
1918
1919 return(0);
1920
1921 blah:
1922 /* Index entry is a bit of a mess now. Maybe we should try to truncate */
1923 return(-1);
1924 }
1925
1926
1927
1928 /*----------------------------------------------------------------------
1929 Write an address entry into a carmel2 index
1930
1931 Args: addr -- addresslist
1932 field -- Field character specifier
1933 stream -- File stream to write to
1934
1935 Returns 0 if OK, -1 on error writing
1936 ---*/
1937 static
carmel2_index_address(addr,field,stream)1938 carmel2_index_address(addr, field, stream)
1939 ADDRESS *addr;
1940 int field;
1941 FILE *stream;
1942 {
1943 ADDRESS *a;
1944
1945 for(a = addr; a != NULL; a = a->next) {
1946 if(fprintf(stream, "%c%s\001%s\001%s\001%s\n",
1947 field,
1948 a->personal == NULL ? "" : a->personal,
1949 a->mailbox == NULL ? "" : a->mailbox,
1950 a->host == NULL ? "" : a->host,
1951 a->adl == NULL ? "" : a->adl) == EOF)
1952 return(-1);
1953 }
1954 return(0);
1955 }
1956
1957
1958
1959 /*----------------------------------------------------------------------
1960 Real work of reading mail data files
1961
1962 Args: stream
1963 header_only -- return header if set, text if not
1964 file_size -- The file size if known (saves a stat)
1965 file -- name of the file to read
1966
1967 Returns buffer with text stored in internal buffer. The Carmel2 format
1968 buffers the text of the current message and header in an internal
1969 buffer. The buffer never shrinks and is expanded as needed, up to a
1970 maximum. The text in the buffer is in CRLF format and is read in line
1971 by line using stdio. It is believed this will be efficient on whatever
1972 machine it is running on and will not use too much memory. (There's
1973 some extra memory used for buffering in stdio.) If a request is made
1974 first for only the header, then only the header will be read in. This
1975 is a big efficiency win when the file is large and only the header is
1976 needed. (In the Carmel2 format the header is genera lly not used, and
1977 when it is it is with the body to do a MIME parse, but the pod format
1978 does read the headers in to create the Carmel2 index.) An estimate is
1979 made of the size needed to expand the file to convert the line ends
1980 from LF to CRLF. The estimate alloca tes 10% extra space. If it
1981 doesn't fit, the whole buffer will be expanded by 50% and the whole
1982 read done over. When the header is read in a 30K buffer is allocated
1983 initially (if the buffer is smaller than that initially). The 50%
1984 increase is applied to the buffer when reading only the header.
1985 ----*/
1986
1987 char *
carmel2_readmsg(stream,header_only,file_size,file_num)1988 carmel2_readmsg(stream, header_only, file_size, file_num)
1989 MAILSTREAM *stream;
1990 int header_only;
1991 int file_num;
1992 long file_size;
1993 {
1994 FILE *msg_stream;
1995 char *p, *p_end, *file_name;
1996 int past_header, not_eof;
1997 long max_read;
1998 struct stat st;
1999 #define DEBUGDEBUG 1
2000 #ifdef DEBUGDEBUG
2001 char debug_buf[500];
2002 #endif
2003
2004 file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
2005 stream->mailbox, file_num);
2006 if(file_name == NULL)
2007 return(NULL); /* Just in case; should never be invalid if open */
2008 #ifdef DEBUGDEBUG
2009 sprintf(debug_buf, "READ RQ:\"%s\" HV:\"%s\" RQ_HD:%d HV_TXT:%d\n",
2010 file_name,
2011 LOCAL->buffered_file == NULL ? "" : LOCAL->buffered_file,
2012 header_only, LOCAL->buffered_header_and_text);
2013 mm_log(debug_buf, NIL);
2014 #endif
2015
2016 /*---- Check out what we have read already -----*/
2017 if(LOCAL->buffered_file != NULL &&
2018 strcmp(LOCAL->buffered_file, file_name) == 0) {
2019 /* The file is the same. Now have we read in the part
2020 that is wanted? If so return it.
2021 */
2022 if(header_only || LOCAL->buffered_header_and_text) {
2023 if(header_only) {
2024 #ifdef DEBUGDEBUG
2025 mm_log("....Returning buffered header\n", NIL);
2026 #endif
2027 return(LOCAL->msg_buf);
2028 } else {
2029 #ifdef DEBUGDEBUG
2030 mm_log("....Returning buffered text\n", NIL);
2031 #endif
2032 return(LOCAL->msg_buf_text_start);
2033 }
2034 }
2035 } else {
2036 /*-- Different file than last time, reset a few things --*/
2037 LOCAL->buffered_header_and_text = 0;
2038 LOCAL->msg_buf_text_offset = 0L;
2039 if(LOCAL->buffered_file != NULL)
2040 fs_give((void **)&(LOCAL->buffered_file));
2041 }
2042
2043 #ifdef DEBUGDEBUG
2044 mm_log("....Reading file\n", NIL);
2045 #endif
2046
2047 /*----- Open the file ------*/
2048 /* Would actually be more efficient not to use stdio here */
2049 msg_stream = fopen(file_name, "r");
2050 if(msg_stream == NULL) {
2051 strcat(file_name, ".wid");
2052 msg_stream = fopen(file_name, "r");
2053 if(msg_stream == NULL)
2054 return(NULL);
2055 }
2056
2057 /*---- Check the size of the file ----*/
2058 if(file_size == 0 && stat(file_name, &st) >= 0)
2059 file_size = st.st_size;
2060
2061
2062 /* ------Pick an amount to read -------
2063 Assume the header is less than MAX_HEADER. We don't want to
2064 allocate buffer for the whole message if we are just getting
2065 the header.
2066 */
2067 max_read = (file_size * 11) / 10;
2068 past_header = 0;
2069 p = LOCAL->msg_buf;
2070 if(header_only) {
2071 max_read = min(max_read, CARMEL_MAX_HEADER);
2072 } else if(LOCAL->msg_buf_text_offset > 0) {
2073 past_header = 1;
2074 p = LOCAL->msg_buf_text_start;
2075 fseek(msg_stream, LOCAL->msg_buf_text_offset, 0);
2076 }
2077 if(max_read > CARMEL_MAXMESSAGESIZE)
2078 max_read = CARMEL_MAXMESSAGESIZE;
2079 if(max_read == 0)
2080 max_read = 1; /* Forces buffer allocation below */
2081
2082
2083 /*----- Loop (re)reading the whole file 'till it fits ---*/
2084 /* This will fit the first time for all but the
2085 strangest cases.
2086 */
2087 do {
2088 /*---- Make sure buffer is the right size ----*/
2089 if(LOCAL->msg_buf_size < max_read) {
2090 /* Buffer not big, enough start whole read over */
2091 if(LOCAL->msg_buf != NULL)
2092 fs_give((void **)&(LOCAL->msg_buf));
2093 LOCAL->msg_buf = fs_get(max_read);
2094 LOCAL->msg_buf_size = max_read;
2095 fseek(msg_stream, 0, 0);
2096 past_header = 0;
2097 p = LOCAL->msg_buf;
2098 }
2099
2100 p_end = LOCAL->msg_buf + LOCAL->msg_buf_size - 3;
2101
2102 while(p < p_end && (not_eof =(fgets(p, p_end-p, msg_stream) != NULL))){
2103 if(*p == '\n' && !past_header) {
2104 *p++ = '\r';
2105 *p++ = '\n';
2106 *p++ = '\0';
2107 past_header = 1;
2108 LOCAL->msg_buf_text_offset = ftell(msg_stream);
2109 LOCAL->msg_buf_text_start = p;
2110 if(header_only)
2111 goto done;
2112 else
2113 continue;
2114 }
2115 p += strlen(p) - 1;
2116 *p++ = '\r';
2117 *p++ = '\n';
2118 }
2119 *p = '\0';
2120 if(!not_eof)
2121 goto done;
2122
2123 /* If we're here, the buffer wasn't big enough, which
2124 is due to a message with most lines less than 10 characters
2125 (the 10% addition for turning LF to CRLF wasn't enough).
2126 Increase it by 50% and try again, till we hit
2127 the largest message we can read
2128 */
2129 max_read = min(CARMEL_MAXMESSAGESIZE, (max_read * 15) / 10);
2130 fseek(msg_stream, 0, 0);
2131 } while (1);
2132 done:
2133 if(p == p_end)
2134 mm_log("Message truncated. It's is too large", WARN);
2135
2136 fclose(msg_stream);
2137
2138 /*---- Save the name of the file buffered file ----*/
2139 LOCAL->buffered_file = cpystr(file_name);
2140 LOCAL->buffered_header_and_text |= !header_only;
2141
2142 return(header_only ? LOCAL->msg_buf : LOCAL->msg_buf_text_start);
2143 }
2144
2145
2146 /*----------------------------------------------------------------------
2147 Parse basic/quick entries in a Carmel mailbox
2148
2149 Args: stream -- stream for mailbox
2150 file_pos -- File position in the index to start parsing at
2151
2152 Returns: 0 if parse succeeds
2153 -1 if it fails
2154 ----*/
2155 static int
carmel2_parse_mail(stream,file_pos)2156 carmel2_parse_mail(stream, file_pos)
2157 MAILSTREAM *stream;
2158 long file_pos;
2159 {
2160 MESSAGECACHE *mc;
2161 long nmsgs, next, last_line_read;
2162 int found;
2163
2164 nmsgs = stream->nmsgs;
2165
2166 /*---- Get the start-of-message record ------*/
2167 fseek(LOCAL->index_stream, file_pos, 0);
2168 if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),LOCAL->index_stream)==NULL)
2169 goto done_reading;
2170 if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len) != 0)
2171 goto bomb;
2172
2173 while(1) {
2174 if(strlen(carmel_20k_buf) != carmel2_s_o_m_len + 13)
2175 goto bomb;
2176
2177 nmsgs++;
2178 next = atol(carmel_20k_buf+24);
2179 mc = carmel2_new_mc(stream, nmsgs);
2180 mc->data1 = file_pos;
2181
2182 /*-- Get the status line, It must be the first line in the entry ----*/
2183 if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),
2184 LOCAL->index_stream) == NULL)
2185 goto done_reading;
2186 if(*carmel_20k_buf != 'U' || strlen(carmel_20k_buf) != 35)
2187 goto bomb; /* Invalid format! */
2188 mc->flagged = carmel_20k_buf[1] == 'F';
2189 mc->answered = carmel_20k_buf[3] == 'A';
2190 mc->deleted = carmel_20k_buf[4] == 'D';
2191 mc->seen = carmel_20k_buf[5] == 'S';
2192 mc->recent = 0;
2193
2194 /*---- Get the rest of the other interesting entries -----*/
2195 found = 0;
2196 while(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),
2197 LOCAL->index_stream) != NULL &&
2198 found < 3 &&
2199 strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len))
2200 if (*carmel_20k_buf == 'Z') {
2201 mc->rfc822_size = atol(carmel_20k_buf+1);
2202 found++;
2203 } else if(*carmel_20k_buf == 'D') {
2204 carmel2_parse_date(mc, carmel_20k_buf+1);
2205 found++;
2206 } else if(*carmel_20k_buf == 'P') {
2207 mc->data2 = atoi(carmel_20k_buf+1);
2208 found++;
2209 }
2210
2211 /*-------- Now find the next index entry ---------*/
2212 last_line_read = ftell(LOCAL->index_stream);
2213 file_pos += next;
2214 fseek(LOCAL->index_stream, file_pos, 0); /* try the offset first */
2215 if(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
2216 LOCAL->index_stream) == NULL)
2217 break;
2218 if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len) != 0){
2219 /*-- Didn't match what was seeked to, back off and read lines --*/
2220 fseek(LOCAL->index_stream, last_line_read, 0);
2221 do {
2222 file_pos = ftell(LOCAL->index_stream);
2223 if(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
2224 LOCAL->index_stream) == NULL)
2225 goto done_reading;
2226 }while(strncmp(carmel_20k_buf,carmel2_s_o_m,carmel2_s_o_m_len)!=0);
2227 }
2228 }
2229 done_reading:
2230 if(stream->nmsgs != nmsgs) {
2231 stream->nmsgs = nmsgs;
2232 mm_exists(stream, nmsgs);
2233 }
2234 return(0);
2235
2236 bomb:
2237 return(-1);
2238 }
2239
2240
2241
2242 /* This killer macro is from bezerk.h. It's only needed at sites that don't
2243 * escape "From " lines with ">From " unless absolutely necessary (The UW).
2244 */
2245
2246 /* Validate line known to start with ``F''
2247 * Accepts: pointer to candidate string to validate as a From header
2248 * return pointer to end of date/time field
2249 * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
2250 * return pointer to offset from t of time zone (if non-zero)
2251 * Returns: T if valid From string, t,ti,zn set; else NIL
2252 */
2253
2254 #define VALID(s,x,ti,zn) \
2255 (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && (s[4] == ' ') && \
2256 (x = strchr (s+5,'\n')) && \
2257 ((x-s < 41) || ((ti = ((x[-2] == ' ') ? -14 : (x[-3] == ' ') ? -15 : \
2258 (x[-4] == ' ') ? -16 : (x[-5] == ' ') ? -17 : \
2259 (x[-6] == ' ') ? -18 : (x[-7] == ' ') ? -19 : \
2260 (x[-8] == ' ') ? -20 : (x[-9] == ' ') ? -21 : \
2261 (x[-10]== ' ') ? -22 : (x[-11]== ' ') ? -23 : 0)) && \
2262 (x[ti] == ' ') && (x[ti+1] == 'r') && (x[ti+2] == 'e') && \
2263 (x[ti+3] == 'm') && (x[ti+4] == 'o') && (x[ti+5] == 't') && \
2264 (x[ti+6] == 'e') && (x[ti+7] == ' ') && (x[ti+8] == 'f') && \
2265 (x[ti+9] == 'r') && (x[ti+10]== 'o') && (x[ti+11]== 'm') && \
2266 (x += ti)) || T) && \
2267 (x-s >= 27) && \
2268 ((x[ti = -5] == ' ') ? ((x[-8] == ':') ? !(zn = 0) : \
2269 ((x[ti = zn = -9] == ' ') || \
2270 ((x[ti = zn = -11] == ' ') && \
2271 ((x[-10] == '+') || (x[-10] == '-'))))) : \
2272 ((x[zn = -4] == ' ') ? (x[ti = -9] == ' ') : \
2273 ((x[zn = -6] == ' ') && ((x[-5] == '+') || (x[-5] == '-')) && \
2274 (x[ti = -11] == ' ')))) && \
2275 (x[ti - 3] == ':') && (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
2276 (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && (x[ti - 11] == ' ')
2277
2278
2279 /* You are not expected to understand this macro, but read the next page if
2280 * you are not faint of heart.
2281 *
2282 * Known formats to the VALID macro are:
2283 * From user Wed Dec 2 05:53 1992
2284 * BSD From user Wed Dec 2 05:53:22 1992
2285 * SysV From user Wed Dec 2 05:53 PST 1992
2286 * rn From user Wed Dec 2 05:53:22 PST 1992
2287 * From user Wed Dec 2 05:53 -0700 1992
2288 * From user Wed Dec 2 05:53:22 -0700 1992
2289 * From user Wed Dec 2 05:53 1992 PST
2290 * From user Wed Dec 2 05:53:22 1992 PST
2291 * From user Wed Dec 2 05:53 1992 -0700
2292 * Solaris From user Wed Dec 2 05:53:22 1992 -0700
2293 *
2294 * Plus all of the above with `` remote from xxx'' after it. Thank you very
2295 * much, smail and Solaris, for making my life considerably more complicated.
2296 */
2297
2298 /*
2299 * What? You want to understand the VALID macro anyway? Alright, since you
2300 * insist. Actually, it isn't really all that difficult, provided that you
2301 * take it step by step.
2302 *
2303 * Line 1 Validates that the 2-5th characters are ``rom ''.
2304 * Line 2 Sets x to point to the end of the line.
2305 * Lines 3-12 First checks to see if the line is at least 41 characters long.
2306 * If so, it scans backwards up to 10 characters (the UUCP system
2307 * name length limit due to old versions of UNIX) to find a space.
2308 * If one is found, it backs up 12 characters from that point, and
2309 * sees if the string from there is `` remote from''. If so, it
2310 * sets x to that position. The ``|| T'' is there so the parse
2311 * will still continue.
2312 * Line 13 Makes sure that there are at least 27 characters in the line.
2313 * Lines 14-17 Checks if the date/time ends with the year. If so, It sees if
2314 * there is a colon 3 characters further back; this would mean
2315 * that there is no timezone field and zn is set to 0 and ti is
2316 * left in front of the year. If not, then it expects there to
2317 * either to be a space four characters back for a three-letter
2318 * timezone, or a space six characters back followed by a + or -
2319 * for a numeric timezone. If a timezone is found, both zn and
2320 * ti are the offset of the space immediately before it.
2321 * Lines 18-20 Are the failure case for a date/time not ending with a year in
2322 * line 14. If there is a space four characters back, it is a
2323 * three-letter timezone; there must be a space for the year nine
2324 * characters back. Otherwise, there must be a space six
2325 * characters back and a + or - five characters back to indicate a
2326 * numeric timezone and a space eleven characters back to indicate
2327 * a year. zn and ti are set appropriately.
2328 * Line 21 Make sure that the string before ti is of the form hh:mm or
2329 * hh:mm:ss. There must be a colon three characters back, and a
2330 * space six or nine characters back (depending upon whether or
2331 * not the character six characters back is a colon). ti is set
2332 * to be the offset of the space immediately before the time.
2333 * Line 22 Make sure the string before ti is of the form www mmm dd.
2334 * There must be a space three characters back (in front of the
2335 * day), one seven characters back (in front of the month), and
2336 * one eleven characters back (in front of the day of week).
2337 *
2338 * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
2339 * newer pipelined machines it is faster being open-coded than it would be if
2340 * subroutines are called.
2341 *
2342 * Why does it scan backwards from the end of the line, instead of doing the
2343 * much easier forward scan? There is no deterministic way to parse the
2344 * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
2345 * see if unquoted spaces were possible. They are, and I've encountered enough
2346 * evil mail to be totally unwilling to trust that ``it will never happen''.
2347 */
2348
2349
2350 /*----------------------------------------------------------------------
2351 Get the new mail out of the spooled mail file
2352
2353 Args: stream -- The inbox stream to add mail too
2354 spool -- The path name of the spool file
2355 mailbox -- Name user sees for this, used only for error reporting
2356
2357 Result:
2358
2359 - Lock the spool mail file with bezerk_lock
2360 - Get the carmel2 index open and remember where we started in it
2361 - Make buffer big enough for biggest header we'll mess with
2362 - Loop reading all the message out of the spool file:
2363 - Get a new data file for the message and open it
2364 - Read the header of the message into the big buffer while...
2365 - writing the message into the new data file
2366 - finish writing the text of the message into the data file
2367 - If all went well bump the message count and say it exists
2368 - Parse the header of the message to get an envelope, date and size
2369 - Write an entry into the carmel2 index
2370 - Unlink and create (to zero) the spool file and unlock it
2371
2372 The carmel inbox should be locked when this is called and mm_critical
2373 should be called around this function.
2374 ----*/
2375 void
carmel2_spool_mail(stream,spool,mailbox,clear_spool_file)2376 carmel2_spool_mail(stream, spool, mailbox, clear_spool_file)
2377 MAILSTREAM *stream;
2378 char *spool, *mailbox;
2379 int clear_spool_file;
2380 {
2381 char *p, *eof, *from_p;
2382 int n, size, fd, in_header, from_i1, from_i2;
2383 long file_pos, index_file_pos, byte_count, start_of_append;
2384 FILE *spool_stream, *data_stream;
2385 ENVELOPE *envelope;
2386 BODY *b;
2387 MESSAGECACHE *mc;
2388 STRING string_struct;
2389 #ifdef BWC
2390 int is_wide;
2391 #endif
2392
2393 /*--- Get the locks set and files open-----*/
2394 fd = carmel2_bezerk_lock(spool, mailbox);
2395 if(fd < 0) {
2396 return;
2397 }
2398 spool_stream = fdopen(fd, "r");
2399 fseek(LOCAL->index_stream, 0L, 2);
2400 start_of_append = ftell(LOCAL->index_stream);
2401
2402 /*--- Make buffer big enough for largest allowable header ----*/
2403 if(LOCAL->msg_buf_size < CARMEL_MAX_HEADER) {
2404 if(LOCAL->msg_buf != NULL)
2405 fs_give((void **)&(LOCAL->msg_buf));
2406 LOCAL->msg_buf_size = CARMEL_MAX_HEADER;
2407 LOCAL->msg_buf = fs_get(CARMEL_MAX_HEADER);
2408 }
2409 LOCAL->buffered_header_and_text = 0;
2410 LOCAL->msg_buf_text_offset = 0L;
2411 if(LOCAL->buffered_file != NULL)
2412 fs_give((void **)&(LOCAL->buffered_file));
2413
2414
2415 /*---- Read (and ignore) the first line with the "From " in it ----*/
2416 eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream);
2417
2418 /*----- Loop getting messages ----*/
2419 while(eof != NULL) {
2420
2421 /*----- get a data file for the new message ----*/
2422 n = carmel2_new_data_file(stream->local, stream->mailbox);
2423 data_stream = fopen((*LOCAL->calc_paths)(CalcPathCarmel2Data,
2424 stream->mailbox, n),"w");
2425 if(data_stream == NULL)
2426 goto backout;
2427 file_pos = ftell(spool_stream);
2428 p = LOCAL->msg_buf;
2429 in_header = 1;
2430 byte_count = 0L;
2431 #ifdef BWC
2432 is_wide = 0;
2433 #endif
2434
2435
2436 /*--------------------------------------------------------------------
2437 Read the message in line by line, writing it out to the
2438 new data file. Also acculamuate a copy of the header in
2439 a buffer for parsing
2440 ---*/
2441 eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream);
2442 while(eof != NULL){
2443 if(VALID(carmel_20k_buf, from_p, from_i1, from_i2))
2444 break;
2445
2446 if(in_header) {
2447 #ifdef BWC
2448 is_wide |= carmel_match_glyph_wide(carmel_20k_buf);
2449 #endif
2450 if(*carmel_20k_buf == '\n') {
2451 /* Encountered first blank line, end of header */
2452 in_header = 0;
2453 *p = '\0';
2454 } else {
2455 if(p - LOCAL->msg_buf + strlen(carmel_20k_buf) >
2456 LOCAL->msg_buf_size){
2457 /* out of room in buffer, end it */
2458 in_header = 0;
2459 *p = '\0';
2460 } else {
2461 strcpy(p, carmel_20k_buf);
2462 p +=strlen(p);
2463 }
2464 }
2465 }
2466
2467 /*----- Write the message into the file -----*/
2468 byte_count += strlen(carmel_20k_buf);
2469 if(carmel_20k_buf[0] == '>' && carmel_20k_buf[1] == 'F' &&
2470 carmel_20k_buf[2] == 'r' && carmel_20k_buf[3] == 'o' &&
2471 carmel_20k_buf[4] == 'm' && carmel_20k_buf[5] == ' ') {
2472 if(fputs(carmel_20k_buf + 1, data_stream) == EOF)
2473 goto backout;
2474 byte_count -= 1;
2475 } else {
2476 if(fputs(carmel_20k_buf, data_stream) == EOF)
2477 goto backout;
2478 }
2479 eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream);
2480 }
2481 fclose(data_stream);
2482 #ifdef BWC
2483 if(is_wide) {
2484 sprintf(carmel_path_buf, "%s.wid",
2485 (*LOCAL->calc_paths)(CalcPathCarmel2Data,stream->mailbox,n)
2486 );
2487 rename((*LOCAL->calc_paths)(CalcPathCarmel2Data,stream->mailbox,n),
2488 carmel_path_buf);
2489 }
2490 #endif
2491
2492 /*---- get a new MESSAGECACHE to store it in -----*/
2493 mc = carmel2_new_mc(stream, stream->nmsgs + 1);
2494
2495 /*------ Parse the message header ------*/
2496 INIT(&string_struct, mail_string, (void *)"", 0);
2497 rfc822_parse_msg(&envelope, &b, LOCAL->msg_buf, strlen(LOCAL->msg_buf),
2498 &string_struct, mylocalhost(), carmel_20k_buf);
2499 carmel2_parse_bezerk_status(mc, LOCAL->msg_buf);
2500 carmel2_rfc822_date(mc, LOCAL->msg_buf);
2501 mc->rfc822_size = byte_count;
2502 mc->data2 = n;
2503 mc->data1 = ftell(LOCAL->index_stream);
2504 mc->recent = 1;
2505
2506 /*----- Now add the message to the Carmel2 index ------*/
2507 if(carmel2_write_index(envelope, mc, LOCAL->index_stream) < 0)
2508 goto backout;
2509
2510 /*----- Write message into auxiliary index (plain carmel) ----*/
2511 if(LOCAL->carmel && LOCAL->aux_copy != NULL) {
2512 if((*LOCAL->aux_copy)(stream->local, mailbox, envelope, mc)) {
2513 /* BUG - this error may leave things half done, but will
2514 only result in duplicated mail */
2515 goto backout;
2516 }
2517 }
2518
2519 /*---- All went well, let the user know -----*/
2520 stream->nmsgs++;
2521 mm_exists(stream, stream->nmsgs);
2522
2523 mail_free_envelope(&envelope);
2524 }
2525
2526 fflush(LOCAL->index_stream); /* Force new index entries onto disk */
2527 fclose(spool_stream);
2528 if(clear_spool_file) {
2529 unlink(spool);
2530 close(creat(spool, 0600));
2531 }
2532 carmel2_bezerk_unlock(fd, spool);
2533 return;
2534
2535 backout:
2536 sprintf(carmel_error_buf, "Error incorporating new mail into \"%s\": %s",
2537 carmel_parse_mb_name(mailbox,'\0')->mailbox, strerror(errno));
2538 /* bug in above call to parse_mb -- should have version passed in */
2539 mm_log(carmel_error_buf, ERROR);
2540 fflush(LOCAL->index_stream);
2541 ftruncate(fileno(LOCAL->index_stream), start_of_append);
2542 carmel2_bezerk_unlock(fd, spool);
2543 }
2544
2545
2546
2547 /*----------------------------------------------------------------------
2548 Copy the actual data file when copying a message
2549
2550 Returns: -1 for failure
2551 otherwise the number of the new file,
2552
2553 ----*/
2554 static
carmel2_copy_msg_file(stream,orig_file_number,new_mailbox)2555 carmel2_copy_msg_file(stream, orig_file_number, new_mailbox)
2556 MAILSTREAM *stream;
2557 int orig_file_number;
2558 char *new_mailbox;
2559 {
2560 char *file_name;
2561 int wide, e, new_file_num, n, orig_fd, new_fd;
2562
2563 /*---- Open the original file ----*/
2564 wide = 0;
2565 file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
2566 new_mailbox,orig_file_number);
2567 if(file_name == NULL)
2568 return(-1);
2569
2570 orig_fd = open(file_name, O_RDONLY);
2571 if(orig_fd < 0 && LOCAL->carmel) {
2572 strcat(file_name, ".wid");
2573 orig_fd = open(file_name, O_RDONLY);
2574 if(orig_fd < 0)
2575 goto bomb;
2576 wide = 1;
2577 } else {
2578 goto bomb;
2579 }
2580
2581 /*----- Open and create the new file ------*/
2582 new_file_num = carmel2_new_data_file(stream->local, new_mailbox);
2583 if(new_file_num < 0)
2584 goto bomb;
2585 file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
2586 new_mailbox, new_file_num);
2587 if(wide)
2588 strcat(file_name, ".wid");
2589 new_fd = open(file_name, O_WRONLY | O_CREAT, 0600);
2590 if(new_fd < 0) {
2591 goto bomb;
2592 }
2593
2594 /*---- Copy the bits ------*/
2595 e = 0;
2596 while((n = read(orig_fd, carmel_20k_buf, sizeof(carmel_20k_buf))) >0) {
2597 if(write(new_fd, carmel_20k_buf, n) < 0) {
2598 e = errno;
2599 break;
2600 }
2601 }
2602
2603 /*---- Close the streams and handle any errors ----*/
2604 close(orig_fd);
2605 close(new_fd);
2606
2607 if(e == 0)
2608 return(new_file_num); /* All is OK */
2609
2610 /*--- something didn't go right ---*/
2611 bomb:
2612 unlink(file_name);
2613 sprintf(carmel_error_buf, "Error copying message to %s: %s",
2614 new_mailbox, strerror(errno));
2615 mm_log(carmel_error_buf, ERROR);
2616 return(-1);
2617 }
2618
2619
2620
2621
2622
2623
2624 /*----------------------------------------------------------------------
2625 Locking for Carmel and Pod formats
2626
2627 Args: stream -- Mail stream we're operating on
2628 file -- Mail file name
2629 write -- Flag indicating we want write access
2630
2631 Returns: -1 if lock fails, 0 if it succeeds
2632
2633 - There are two locks. Plain locks and write locks. They are created
2634 about the same way, but have different names. The time out on the write
2635 locks is much shorter, because it should never be held for very long.
2636
2637 - Hitching (links in file system) post locking is used so it will work
2638 across NFS. Flock() could be used as it has two advantages. First it
2639 is more efficient, second the locks will dissolve automatically when the
2640 process dies. The efficiency is not of great concern, and the
2641 process should not (theoretically) die unless it crashes due to a bug
2642 or it is abnormally terminated. The advantage of locking across NFS
2643 is considered greater than the advantages of flock().
2644
2645 - The mod time of the lock file is updated every time mail_check()
2646 or mail_ping() is called and the mod time on the lock file is recorded.
2647 This is so it can be determined that the lock file is current.
2648
2649 - When a read lock file over 30 or a write lock over 5 minutes old is
2650 encountered it is assumed the lock is old and should be overridden
2651 (because the process crashed or was killed).
2652
2653 - Every time the mod time on the lock file is updated (on calls to
2654 mail_check() and mail_ping()), the mod time of the lock file is
2655 checked against the record of what it was last set to. If the mod times
2656 don't match the lock has been broken and overridden. Then the original
2657 Pine should go into read-only mode.... This is only likely to happen if
2658 someone suspends (^Z's) the process for more than 30 minutes, and another
2659 process is started.
2660 ----*/
2661 int
carmel2_lock(local,file,write)2662 carmel2_lock(local, file, write)
2663 CARMEL2LOCAL *local;
2664 char *file;
2665 int write;
2666 {
2667 char *hitch, lock[CARMEL_PATHBUF_SIZE], error_mess[200], *l_path;
2668 struct stat sb;
2669 int n, link_result, hitch_fd, timer_set, l;
2670 long override, timer;
2671
2672 /* Set the length of time for override. It is 5 minutes for a
2673 write lock (no process should have it for that long) and
2674 30 minutes for a read lock, that's 30 minutes since the
2675 lock file was last touched. */
2676 override = 60 * (write ? 5 : 30);
2677 timer = -1;
2678
2679 /*----- Get the lock file and hitch names -----*/
2680 l_path = (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
2681 CalcPathCarmel2ReadLock,
2682 file, 0);
2683 if(l_path == NULL)
2684 return(-1);
2685 strcpy(lock, l_path);
2686 /* copy lock file into bufferl call it hitch, unique thing added below */
2687 hitch = carmel_path_buf;
2688 hitch = strcpy(hitch, lock);
2689 l = strlen(hitch);
2690
2691 do {
2692 /*--- First create hitching post ----*/
2693 for(n = time(0) % 6400; ; n += 10007) {
2694 /* Get random file name, that's not too long */
2695 sprintf(hitch + l, "_%c%c", '0' + (n % 80) , '0' + (n/80));
2696 if(stat(hitch, &sb) < 0)
2697 break; /* Got a name that doesn't exist */
2698 }
2699 hitch_fd = open(hitch, O_CREAT, 0666);
2700 if(hitch_fd < 0) {
2701 sprintf(error_mess, "Error creating lock file \"%s\": %s",
2702 hitch, strerror(errno));
2703 mm_log(error_mess, WARN);
2704 return(-1);
2705 }
2706 close(hitch_fd);
2707
2708 /*----- Got a hitching post, now try link -----*/
2709 link_result = link(hitch, lock);
2710 stat(lock, &sb);
2711 unlink(hitch);
2712 if(link_result == 0 && sb.st_nlink == 2) {
2713 /*------ Got the lock! ------*/
2714 stat(lock, &sb);
2715 if(write)
2716 local->write_lock_mod_time = sb.st_mtime;
2717 else
2718 local->read_lock_mod_time = sb.st_mtime;
2719 return(0);
2720 }
2721
2722 /*----- Check and override if lock is too old -----*/
2723 if(sb.st_mtime + override < time(0)) {
2724 unlink(lock); /* Lock is old, override it */
2725 timer = 100; /* Get us around the loop again */
2726 continue;
2727 } else {
2728 if(timer < 0) /* timer not set */
2729 timer = sb.st_mtime + override - time(0);
2730 }
2731
2732 /*----- Will user wait till time for override? -----*/
2733 if(!write || timer > 5 * 60) {
2734 return(-1); /* Not more that 5 minutes */
2735 }
2736
2737 /*----- Try again, and tell user we're trying -------*/
2738 if(!(timer % 15)) {
2739 sprintf(error_mess,
2740 "Please wait. Mailbox %s is locked for %d more seconds",
2741 file, timer);
2742 mm_log(error_mess, WARN);
2743 }
2744 timer--;
2745 sleep(1);
2746 } while(timer > 0);
2747
2748 return(-1);
2749 }
2750
2751
2752
2753 /*----------------------------------------------------------------------
2754 Unlock a carmel mail stream
2755
2756 Args: stream -- The mailstream that is locked
2757 mailbox -- FQN of mailbox to lock ( e.g. #carmel#foo )
2758 write -- flag to set if it is a write lock
2759
2760 Nothing is returned
2761 ----*/
2762 void
carmel2_unlock(local,mailbox,write)2763 carmel2_unlock(local, mailbox, write)
2764 CARMEL2LOCAL *local;
2765 char *mailbox;
2766 int write;
2767 {
2768 char lock[CARMEL_PATHBUF_SIZE];
2769 struct stat sb;
2770
2771 strcpy(lock, (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
2772 CalcPathCarmel2ReadLock,
2773 mailbox, 0));
2774
2775 if(stat(lock, &sb) < 0)
2776 return; /* Hmmm... lock already broken */
2777
2778 if(sb.st_mtime !=
2779 (write ? local->write_lock_mod_time : local->read_lock_mod_time))
2780 return; /* Hmmm... not our lock */
2781
2782 unlink(lock);
2783 }
2784
2785
2786
2787 /*----------------------------------------------------------------------
2788 Keep the mod date on the lock file fresh
2789
2790 Args: stream --
2791 file -- the name of the mailbox the lock is for
2792 write -- set if this is a write lock
2793
2794 Returns: 0 if update was OK
2795 -1 if something bad happened, like the lock was stolen
2796 ----*/
2797 static int
carmel2_update_lock(local,file,write)2798 carmel2_update_lock(local, file, write)
2799 CARMEL2LOCAL *local;
2800 char *file;
2801 int write;
2802 {
2803 char lock[CARMEL_PATHBUF_SIZE];
2804 struct timeval tp[2];
2805 struct stat sb;
2806
2807 strcpy(lock, (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
2808 CalcPathCarmel2ReadLock,
2809 file, 0));
2810
2811 if(stat(lock, &sb) < 0) {
2812 /* Lock file stolen, oh oh */
2813 return(-1);
2814 }
2815
2816 if(sb.st_mtime !=
2817 (write ? local->write_lock_mod_time : local->read_lock_mod_time)) {
2818 /* Not our lock anymore , oh oh */
2819 return(-1);
2820 }
2821
2822 gettimeofday (&tp[0],NIL); /* set atime to now */
2823 gettimeofday (&tp[1],NIL); /* set mtime to now */
2824 utimes(lock, tp);
2825
2826 if(write)
2827 local->write_lock_mod_time = tp[1].tv_sec;
2828 else
2829 local->read_lock_mod_time = tp[1].tv_sec;
2830 return(0);
2831 }
2832
2833
2834
2835 /*----------------------------------------------------------------------
2836 Berkeley open and lock mailbox
2837
2838 This is mostly ripped off from the Bezerk driver
2839 ----*/
2840
2841 static int
carmel2_bezerk_lock(spool,file)2842 carmel2_bezerk_lock (spool, file)
2843 char *spool, *file;
2844 {
2845 int fd,ld,j;
2846 int i = BEZERKLOCKTIMEOUT * 60 - 1;
2847 struct timeval tp;
2848 struct stat sb;
2849 char *hitch, *lock;
2850
2851 lock = carmel_path_buf;
2852 sprintf(lock, "%s.lock", spool);
2853 do { /* until OK or out of tries */
2854 gettimeofday (&tp,NIL); /* get the time now */
2855 #ifdef NFSKLUDGE
2856 /* SUN-OS had an NFS, As kludgy as an albatross;
2857 * And everywhere that it was installed, It was a total loss. -- MRC 9/25/91
2858 */
2859 /* build hitching post file name */
2860 hitch = carmel_20k_buf;
2861 sprintf(hitch, "%s.%d.%d.",carmel_path_buf,time (0),getpid ());
2862 j = strlen (hitch); /* append local host name */
2863 gethostname (hitch + j,(MAILTMPLEN - j) - 1);
2864 /* try to get hitching-post file */
2865 if ((ld = open (hitch, O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) {
2866 /* prot fail & non-ex, don't use lock files */
2867 if ((errno == EACCES) && (stat (hitch, &sb))) *lock = '\0';
2868 else { /* otherwise something strange is going on */
2869 sprintf (carmel_20k_buf,"Error creating %s: %s",lock,strerror (errno));
2870 mm_log (carmel_20k_buf, WARN); /* this is probably not good */
2871 /* don't use lock files if not one of these */
2872 if ((errno != EEXIST) && (errno != EACCES)) *lock = '\0';
2873 }
2874 }
2875 else { /* got a hitching-post */
2876 chmod (hitch,0666); /* make sure others can break the lock */
2877 close (ld); /* close the hitching-post */
2878 link (hitch,lock); /* tie hitching-post to lock, ignore failure */
2879 stat (hitch, &sb); /* get its data */
2880 unlink (hitch); /* flush hitching post */
2881 /* If link count .ne. 2, hitch failed. Set ld to -1 as if open() failed
2882 so we try again. If extant lock file and time now is .gt. file time
2883 plus timeout interval, flush the lock so can win next time around. */
2884 if ((ld = (sb.st_nlink != 2) ? -1 : 0) && (!stat (lock,&sb)) &&
2885 (tp.tv_sec > sb.st_ctime + BEZERKLOCKTIMEOUT * 60)) unlink (lock);
2886 }
2887
2888 #else
2889 /* This works on modern Unix systems which are not afflicted with NFS mail.
2890 * "Modern" means that O_EXCL works. I think that NFS mail is a terrible
2891 * idea -- that's what IMAP is for -- but some people insist upon losing...
2892 */
2893 /* try to get the lock */
2894 if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) switch (errno) {
2895 case EEXIST: /* if extant and old, grab it for ourselves */
2896 if ((!stat (lock,&sb)) && tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60)
2897 ld = open (lock,O_WRONLY|O_CREAT,0666);
2898 break;
2899 case EACCES: /* protection fail, ignore if non-ex or old */
2900 if (stat (lock,&sb) || (tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60))
2901 *lock = '\0'; /* assume no world write mail spool dir */
2902 break;
2903 default: /* some other failure */
2904 sprintf (tmp,"Error creating %s: %s",lock,strerror (errno));
2905 mm_log (tmp,WARN); /* this is probably not good */
2906 *lock = '\0'; /* don't use lock files */
2907 break;
2908 }
2909 if (ld >= 0) { /* if made a lock file */
2910 chmod (tmp,0666); /* make sure others can break the lock */
2911 close (ld); /* close the lock file */
2912 }
2913 #endif
2914 if ((ld < 0) && *lock) { /* if failed to make lock file and retry OK */
2915 if (!(i%15)) {
2916 sprintf (carmel_20k_buf,"Mailbox %s is locked, will override in %d seconds...",
2917 file,i);
2918 mm_log (carmel_20k_buf, WARN);
2919 }
2920 sleep (1); /* wait 1 second before next try */
2921 }
2922 } while (i-- && ld < 0 && *lock);
2923 /* open file */
2924 if ((fd = open (spool, O_RDONLY)) >= 0) flock (fd, LOCK_SH);
2925 else { /* open failed */
2926 j = errno; /* preserve error code */
2927 if (*lock) unlink (lock); /* flush the lock file if any */
2928 errno = j; /* restore error code */
2929 }
2930 return(fd);
2931 }
2932
2933
2934
2935 /*----------------------------------------------------------------------
2936 Berkeley unlock and close mailbox
2937 ----*/
2938 static void
carmel2_bezerk_unlock(fd,spool)2939 carmel2_bezerk_unlock (fd, spool)
2940 int fd;
2941 char *spool;
2942 {
2943 sprintf(carmel_path_buf, "%s.lock", spool);
2944
2945 flock (fd, LOCK_UN); /* release flock'ers */
2946 close (fd); /* close the file */
2947 /* flush the lock file if any */
2948 if (*carmel_path_buf) unlink (carmel_path_buf);
2949 }
2950
2951
2952
2953 /*----------------------------------------------------------------------
2954 Make sure directory exists and is writable
2955
2956 Args: dir - directory to check, should be full path
2957
2958 Result: returns -1 if not OK along with mm_logging a message
2959 0 if OK
2960 ----*/
2961
carmel2_check_dir(dir)2962 carmel2_check_dir(dir)
2963 char *dir;
2964 {
2965 struct stat sb;
2966 char error_mess[150];
2967
2968 if(stat(dir, &sb) < 0) {
2969 if(mkdir(dir, 0700) < 0) {
2970 sprintf(error_mess, "Error creating directory %-.30s %s",
2971 dir, strerror(errno));
2972 mm_log(error_mess, WARN);
2973 return(-1);
2974 }
2975 } else if(!(sb.st_mode & S_IFDIR)) {
2976 sprintf(error_mess, "Warning: %s is not a directory",dir);
2977 mm_log(error_mess, WARN);
2978 return(-1);
2979
2980 } else if(access(dir, W_OK) != 0) {
2981 sprintf(error_mess, "Warning: unable to write to %-.30s %s",
2982 dir, strerror(errno));
2983 mm_log(error_mess, WARN);
2984 return(-1);
2985 }
2986 return(0);
2987 }
2988
2989
2990
2991 /*----------------------------------------------------------------------
2992 Return the number for the next message file
2993
2994 Args: stream -- the Mail stream for the new data file
2995 mailbox -- The FQN of mailbox data file is for
2996
2997 Returns: file number or -1
2998 ----*/
2999 static
carmel2_new_data_file(local,mailbox)3000 carmel2_new_data_file(local, mailbox)
3001 CARMEL2LOCAL *local;
3002 char *mailbox;
3003 {
3004 char *path, num_buf[50], e_buf[100];
3005 int fd, num, bytes_read;
3006
3007 /*---- Get the full path of the .MAXNAME file for index ----*/
3008 path = (*local->calc_paths)(CalcPathCarmel2MAXNAME, mailbox, 0);
3009 if(path == NULL)
3010 return(-1);
3011
3012 fd = open(path, O_RDWR|O_CREAT, 0666);
3013 if(fd < 0) {
3014 sprintf(e_buf, "Error getting next number of mail file: %s",
3015 strerror(errno));
3016 mm_log(e_buf, ERROR);
3017 return(-1);
3018 }
3019
3020 bytes_read = read(fd, num_buf, sizeof(num_buf));
3021 if(bytes_read < 6) {
3022 num = 100000;
3023 } else {
3024 num = atoi(num_buf) + 1;
3025 if(num >= 999999)
3026 num = 100000;
3027 }
3028 sprintf(num_buf, "%-6d\n", num);
3029 lseek(fd, 0, 0);
3030 write(fd, num_buf, strlen(num_buf));
3031 close(fd);
3032 return(num);
3033 }
3034
3035
3036
3037 /*----------------------------------------------------------------------
3038 Do all the file name generation for carmel2 driver
3039
3040 The mailbox passed in is in either the format: #carmel2#folder or
3041 #carmel2#user%folder
3042 This generates that absolute paths for an index, or a data file or
3043 the .MAXNAME file.
3044
3045 Bug: This is untested!
3046 ----*/
3047 static char *
carmel2_calc_paths(operation,mailbox,data_file_num)3048 carmel2_calc_paths(operation, mailbox, data_file_num)
3049 int operation;
3050 char *mailbox;
3051 int data_file_num;
3052 {
3053 static char path[CARMEL_PATHBUF_SIZE], num[20];
3054 char *p, *home_dir;
3055 struct carmel_mb_name *parsed_name;
3056 struct passwd *pw;
3057
3058 parsed_name = carmel_parse_mb_name(mailbox,'2');
3059
3060 if(parsed_name == NULL) {
3061 mm_log("Internal error (bug). Invalid Carmel folder name",ERROR);
3062 return(NULL);
3063 }
3064
3065
3066 if(parsed_name->user != NULL) {
3067 /*---- has a user in mailbox name -----*/
3068 pw = getpwnam(parsed_name->user);
3069 if(pw == NULL) {
3070 sprintf(carmel_error_buf,
3071 "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
3072 mailbox, parsed_name->user);
3073 mm_log(carmel_error_buf, ERROR);
3074 carmel_free_mb_name(parsed_name);
3075 return(0);
3076 }
3077 home_dir = pw->pw_dir;
3078 } else {
3079 home_dir = myhomedir();
3080 }
3081 mailbox = parsed_name->mailbox;
3082
3083 switch(operation) {
3084
3085 case CalcPathCarmel2Data:
3086 sprintf(path, "%s/%s/%d", home_dir, CARMEL2_MSG_DIR, data_file_num);
3087
3088
3089 case CalcPathCarmel2Index:
3090 sprintf(path, "%s/%s/%s", home_dir, CARMEL2_INDEX_DIR, mailbox);
3091 break;
3092
3093 case CalcPathCarmel2MAXNAME:
3094 sprintf(path, "%s/%s", home_dir, CARMEL2_MAXFILE);
3095 break;
3096
3097 case CalcPathCarmel2WriteLock:
3098 sprintf(path, "%s/%s/.%s.wl", home_dir, CARMEL2_INDEX_DIR, mailbox);
3099 break;
3100
3101 case CalcPathCarmel2ReadLock:
3102 sprintf(path, "%s/%s/.%s.rl", home_dir, CARMEL2_INDEX_DIR, mailbox);
3103 break;
3104 }
3105
3106 carmel_free_mb_name(parsed_name);
3107 return(path);
3108 }
3109
3110
3111
3112 /*----------------------------------------------------------------------
3113 Find and parse the status line in a mail header
3114
3115 Args: mc -- the message cache where status is to be stored
3116 header -- the message header to parsen
3117 ----*/
3118 void
carmel2_parse_bezerk_status(mc,header)3119 carmel2_parse_bezerk_status(mc, header)
3120 MESSAGECACHE *mc;
3121 char *header;
3122 {
3123 register char *p;
3124 for(p = header; *p; p++) {
3125 if(*p != '\n')
3126 continue;
3127 p++;
3128 if(*p != 'S' && *p != 'X')
3129 continue;
3130 if(strncmp(p, "Status: ", 8) == 0 || strncmp(p,"X-Status: ",10)== 0) {
3131 mc->recent = 1;
3132 for(p += *p == 'X' ? 10: 8; *p && *p != '\n'; p++)
3133 switch(*p) {
3134 case 'R': mc->seen = 1; break;
3135 case 'O': mc->recent = 0; break;
3136 case 'D': mc->deleted = 1; break;
3137 case 'F': mc->flagged = 1; break;
3138 case 'A': mc->flagged = 1; break;
3139 }
3140 }
3141 }
3142 }
3143
3144
3145
3146 /*----------------------------------------------------------------------
3147 Turn a string of describing flags into a bit mask
3148
3149 Args: stream -- mail stream, unused
3150 flag -- string flag list
3151
3152 Returns: returns short with bits set; bits are defined in carmel2.h
3153 ----*/
3154 static short
carmel2_getflags(stream,flag)3155 carmel2_getflags (stream, flag)
3156 MAILSTREAM *stream;
3157 char *flag;
3158 {
3159 char *t, tmp[100];
3160 short f = 0;
3161 short i,j;
3162 if (flag && *flag) { /* no-op if no flag string */
3163 /* check if a list and make sure valid */
3164 if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
3165 mm_log ("Bad flag list",ERROR);
3166 return NIL;
3167 }
3168 /* copy the flag string w/o list construct */
3169 strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
3170 tmp[j] = '\0';
3171 t = ucase (tmp); /* uppercase only from now on */
3172
3173 while (*t) { /* parse the flags */
3174 if (*t == '\\') { /* system flag? */
3175 switch (*++t) { /* dispatch based on first character */
3176 case 'S': /* possible \Seen flag */
3177 if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
3178 t += 4; /* skip past flag name */
3179 break;
3180 case 'D': /* possible \Deleted flag */
3181 if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
3182 t[5] == 'E' && t[6] == 'D') i = fDELETED;
3183 t += 7; /* skip past flag name */
3184 break;
3185 case 'F': /* possible \Flagged flag */
3186 if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
3187 t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
3188 t += 7; /* skip past flag name */
3189 break;
3190 case 'A': /* possible \Answered flag */
3191 if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
3192 t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
3193 t += 8; /* skip past flag name */
3194 break;
3195 case 'R': /* possible \Answered flag */
3196 if (t[1] == 'E' && t[2] == 'C' && t[3] == 'E' && t[4] == 'N' &&
3197 t[5] == 'T') i = fRECENT;
3198 t += 6; /* skip past flag name */
3199 break;
3200 default: /* unknown */
3201 i = 0;
3202 break;
3203 }
3204 /* add flag to flags list */
3205 if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
3206 else { /* bitch about bogus flag */
3207 mm_log ("Unknown system flag",ERROR);
3208 return NIL;
3209 }
3210 }
3211 else { /* no user flags yet */
3212 mm_log ("Unknown flag",ERROR);
3213 return NIL;
3214 }
3215 }
3216 }
3217 return f;
3218 }
3219
3220
3221 /*----------------------------------------------------------------------
3222 Get a pointer to a MESSAGECACHE entry, allocating if needed
3223
3224 Args: stream -- message stream
3225 num -- Message number to allocate on for
3226
3227 Returns: The MESSAGECACHE entry
3228
3229 The message caches are allocated in blocks of 256 to save space taken up by
3230 pointers in a linked list and allocation overhead. The mc_blocks
3231 data structure is an array that points to each block. The macro MC()
3232 defined in carmel.h returns a pointer to a MESSAGECACHE given
3233 a message number. This function here can be called when a MESSAGECACHE
3234 is needed to a message number that might be new. It allocates a new
3235 block if needed and clears the MESSAGECACHE returned.
3236
3237 The MESSAGECACHE entries are currently about 28 bytes which implies 28Kb
3238 must be used per 1000 messages. If memory is limited this driver will be
3239 limited in the number of messages it can handle, and the limit is due to
3240 the fact that these MESSAGECACHEs must be in core.
3241
3242 It might be possible to reduce the size of each entry by a few bytes if
3243 the message numbers were reduced to a short, and the mostly unused keywords
3244 were removed. It would also be nice to add a day of the week (3 bits)
3245 ----*/
3246 static MESSAGECACHE *
carmel2_new_mc(stream,num)3247 carmel2_new_mc(stream, num)
3248 MAILSTREAM *stream;
3249 int num;
3250 {
3251 MESSAGECACHE *mc;
3252
3253 /* Make sure we've got a cache_entry */
3254 if(num >= LOCAL->cache_size) {
3255 if(LOCAL->mc_blocks == NULL)
3256 LOCAL->mc_blocks=(MESSAGECACHE **)fs_get(sizeof(MESSAGECACHE *));
3257 else
3258 fs_resize((void **)&(LOCAL->mc_blocks), ((num >>8) + 1) *
3259 sizeof(MESSAGECACHE *));
3260 LOCAL->mc_blocks[num >> 8] = (MESSAGECACHE *)
3261 fs_get(256 * sizeof(MESSAGECACHE));
3262 LOCAL->cache_size = ((num >> 8) + 1) * 256;
3263 }
3264
3265 mc = MC(num);
3266
3267 mc->user_flags = 0;
3268 mc->lockcount = 0;
3269 mc->seen = 0;
3270 mc->deleted = 0;
3271 mc->flagged = 0;
3272 mc->answered = 0;
3273 mc->recent = 0;
3274 mc->searched = 0;
3275 mc->sequence = 0;
3276 mc->spare = 0;
3277 mc->spare2 = 0;
3278 mc->spare3 = 0;
3279 mc->msgno = num;
3280
3281 /* Don't set the date, the size and the extra data,
3282 assume they will be set
3283 */
3284 return(mc);
3285 }
3286
3287
3288
3289 /*----------------------------------------------------------------------
3290 Do the real work of appending a message to a mailbox
3291
3292 Args: local -- The carmel2 local data structure, (a some what fake incomplete
3293 one, set up for the purposes here)
3294 mailbox -- Name of mailbox to append to
3295 message -- The rfc822 format of the message with \r\n's
3296
3297 Returns: T if all went OK, NIL if it did not
3298
3299 - Make sure index exists or can be created
3300 - lock index for write
3301 - get a data file name
3302 - Put the text in the file
3303 - Parse the string to get envelope and message cache
3304 - Write the entry into the index
3305 - Unlock the index
3306
3307 BUG: This needs some locking and some error messages
3308
3309 ----*/
carmel2_append2(stream,local,mailbox,flags,date,message)3310 carmel2_append2(stream, local, mailbox, flags, date, message)
3311 char *mailbox, *flags, *date;
3312 CARMEL2LOCAL *local;
3313 STRING *message;
3314 MAILSTREAM *stream;
3315 {
3316 char *index_name, *data_name, *p, c, *header_string;
3317 ENVELOPE *envelope;
3318 BODY *b;
3319 MESSAGECACHE mc;
3320 FILE *index_file, *data_file;
3321 struct stat sb;
3322 int last_was_crlf, saved_errno;
3323 STRING string_struct;
3324 long size;
3325 short flagbits;
3326
3327 /*------ Lock the mailbox for write ------*/
3328 if(carmel2_lock(local, mailbox, WRITE_LOCK) < 0) {
3329 sprintf(carmel_error_buf,
3330 "Mailbox \"%s\" is locked. Can't append to it.",
3331 mailbox);
3332 mm_log(carmel_error_buf, ERROR);
3333 return(NIL);
3334 }
3335
3336 /*----- Get the file name of carmel2 index -----*/
3337 index_name = (*local->calc_paths)(CalcPathCarmel2Index, mailbox, 0);
3338 if(index_name == NULL) {
3339 saved_errno = 0;
3340 goto bomb;
3341 }
3342
3343 /*------ See if it exists already or not ------*/
3344 if(stat(index_name, &sb) < 0) {
3345 mm_notify (stream,"[TRYCREATE] Must create mailbox before copy", NIL);
3346 carmel2_unlock(local, mailbox, WRITE_LOCK);
3347 return(NIL);
3348 }
3349
3350 index_file = fopen(index_name, "a");
3351 if(index_file == NULL)
3352 goto bomb;
3353
3354 mc.data2 = carmel2_new_data_file(local, mailbox);
3355
3356 flagbits = carmel2_getflags(NULL, flags);
3357
3358 if(flagbits & fSEEN) mc.seen = T;
3359 if(flagbits & fDELETED) mc.deleted = T;
3360 if(flagbits & fFLAGGED) mc.flagged = T;
3361 if(flagbits & fANSWERED) mc.answered = T;
3362 if(flagbits & fRECENT) mc.recent = T;
3363 mc.user_flags = 0;
3364
3365 /*----- Open the data file -----*/
3366 data_name = (*local->calc_paths)(CalcPathCarmel2Data, mailbox, mc.data2);
3367 if(data_name == NULL) {
3368 errno = 0; /* Don't generate an error message at all */
3369 goto bomb;
3370 }
3371 data_file = fopen(data_name, "w");
3372 if(data_file == NULL)
3373 goto bomb;
3374
3375 /*--- accumulate header as we go for later parsing ---*/
3376 header_string = carmel_20k_buf;
3377
3378 /*------ Write the message to the file, and get header in a string -----*/
3379 for(size = SIZE(message); size > 0; size--){
3380 c = SNX(message);
3381 if(c == '\r' && size > 1) {
3382 /* Turn CRLF into LF for UNIX */
3383 c = SNX(message);
3384 size--;
3385 if(c != '\n') {
3386 if(fputc('\r', data_file) < 0 || fputc(c, data_file) < 0)
3387 goto bomb;
3388 if(header_string != NULL) {
3389 *header_string++ = '\r';
3390 *header_string++ = c;
3391 }
3392 } else {
3393 if(fputc('\n', data_file) < 0)
3394 goto bomb;
3395 if(header_string != NULL) {
3396 if(last_was_crlf) {
3397 *header_string = '\0';
3398 header_string = NULL;
3399 } else {
3400 *header_string++ = '\r';
3401 *header_string++ = '\n';
3402 }
3403 }
3404 last_was_crlf = 1;
3405 }
3406 } else {
3407 if(fputc(c, data_file) == EOF)
3408 goto bomb;
3409 if(header_string != NULL)
3410 *header_string++ = c;
3411 last_was_crlf = 0;
3412 }
3413 }
3414 if(fclose(data_file) == EOF)
3415 goto bomb;
3416 data_file = NULL;
3417
3418
3419 /*----Get the size that we actually wrote -----*/
3420 stat(data_name, &sb);
3421 mc.rfc822_size = sb.st_size;
3422
3423 /* This blows the nice tight memory usage for the carmel2 driver :-( */
3424 header_string = cpystr(carmel_20k_buf);
3425
3426 #ifdef BWC
3427 /*--- For MIME type x-bwc-glyph-wide, store in a nnnn.wid file ----*/
3428 for(p = header_string; *p; p++) {
3429 if((p == header_string && carmel_match_glyph_wide(p)) ||
3430 (*p == '\r' && *(p+1) == '\n' && carmel_match_glyph_wide(p+2))) {
3431 sprintf(carmel_path_buf, "%s.wid", data_name);
3432 rename(data_name, carmel_path_buf);
3433 break;
3434 }
3435 }
3436 #endif
3437
3438 /*------ Parse the message to get envelope and message cache ----*/
3439 INIT(&string_struct, mail_string, (void *)"", 0);
3440 rfc822_parse_msg(&envelope, &b, header_string, strlen(header_string),
3441 &string_struct, mylocalhost(), carmel_20k_buf);
3442 carmel2_parse_bezerk_status(&mc, header_string);
3443 carmel2_rfc822_date(&mc, header_string);
3444
3445 /*------ Write the entry into the index ------*/
3446 if(carmel2_write_index(envelope, &mc, index_file) < 0)
3447 goto bomb;
3448
3449 if(local->aux_copy != NULL) /* Write carmel index if needed */
3450 (*local->aux_copy)(local, mailbox, envelope, &mc);
3451
3452 mail_free_envelope(&envelope);
3453 fs_give((void **)&header_string);
3454
3455 if(fclose(index_file) == EOF)
3456 goto bomb;
3457 carmel2_unlock(local, mailbox, WRITE_LOCK);
3458 return(T);
3459
3460 bomb:
3461 saved_errno = errno;
3462 if(index_file != NULL) {
3463 fclose(index_file);
3464 }
3465 if(data_file != NULL) {
3466 fclose(data_file);
3467 unlink(data_name);
3468 }
3469 carmel2_unlock(local, mailbox, WRITE_LOCK);
3470 if(saved_errno != 0) {
3471 sprintf(carmel_error_buf,"Message append failed: %s",
3472 strerror(saved_errno));
3473 mm_log(carmel_error_buf, ERROR);
3474 }
3475 return(NIL);
3476 }
3477
3478
3479
3480
3481 /* Search support routines
3482 * Accepts: MAIL stream
3483 * message number
3484 * pointer to additional data
3485 * pointer to temporary buffer
3486 * Returns: T if search matches, else NIL
3487 */
3488
3489 static char
carmel2_search_all(stream,msgno,d,n)3490 carmel2_search_all (stream,msgno,d,n)
3491 MAILSTREAM *stream;
3492 long msgno;
3493 char *d;
3494 long n;
3495 {
3496 return T; /* ALL always succeeds */
3497 }
3498
3499
3500 static char
carmel2_search_answered(stream,msgno,d,n)3501 carmel2_search_answered (stream,msgno,d,n)
3502 MAILSTREAM *stream;
3503 long msgno;
3504 char *d;
3505 long n;
3506 {
3507 return MC(msgno)->answered ? T : NIL;
3508 }
3509
3510
3511 static char
carmel2_search_deleted(stream,msgno,d,n)3512 carmel2_search_deleted (stream,msgno,d,n)
3513 MAILSTREAM *stream;
3514 long msgno;
3515 char *d;
3516 long n;
3517 {
3518 return MC(msgno)->deleted ? T : NIL;
3519 }
3520
3521
3522 static char
carmel2_search_flagged(stream,msgno,d,n)3523 carmel2_search_flagged (stream,msgno,d,n)
3524 MAILSTREAM *stream;
3525 long msgno;
3526 char *d;
3527 long n;
3528 {
3529 return MC(msgno)->flagged ? T : NIL;
3530 }
3531
3532
3533 static char
carmel2_search_keyword(stream,msgno,d,n)3534 carmel2_search_keyword (stream,msgno,d,n)
3535 MAILSTREAM *stream;
3536 long msgno;
3537 char *d;
3538 long n;
3539 {
3540 return NIL; /* keywords not supported yet */
3541 }
3542
3543
3544 static char
carmel2_search_new(stream,msgno,d,n)3545 carmel2_search_new (stream,msgno,d,n)
3546 MAILSTREAM *stream;
3547 long msgno;
3548 char *d;
3549 long n;
3550 {
3551 MESSAGECACHE *elt = MC(msgno);
3552 return (elt->recent && !elt->seen) ? T : NIL;
3553 }
3554
3555 static char
carmel2_search_old(stream,msgno,d,n)3556 carmel2_search_old (stream,msgno,d,n)
3557 MAILSTREAM *stream;
3558 long msgno;
3559 char *d;
3560 long n;
3561 {
3562 return MC(msgno)->recent ? NIL : T;
3563 }
3564
3565
3566 static char
carmel2_search_recent(stream,msgno,d,n)3567 carmel2_search_recent (stream,msgno,d,n)
3568 MAILSTREAM *stream;
3569 long msgno;
3570 char *d;
3571 long n;
3572 {
3573 return MC(msgno)->recent ? T : NIL;
3574 }
3575
3576
3577 static char
carmel2_search_seen(stream,msgno,d,n)3578 carmel2_search_seen (stream,msgno,d,n)
3579 MAILSTREAM *stream;
3580 long msgno;
3581 char *d;
3582 long n;
3583 {
3584 return MC(msgno)->seen ? T : NIL;
3585 }
3586
3587
3588 static char
carmel2_search_unanswered(stream,msgno,d,n)3589 carmel2_search_unanswered (stream,msgno,d,n)
3590 MAILSTREAM *stream;
3591 long msgno;
3592 char *d;
3593 long n;
3594 {
3595 return MC(msgno)->answered ? NIL : T;
3596 }
3597
3598
3599 static char
carmel2_search_undeleted(stream,msgno,d,n)3600 carmel2_search_undeleted (stream,msgno,d,n)
3601 MAILSTREAM *stream;
3602 long msgno;
3603 char *d;
3604 long n;
3605 {
3606 return MC(msgno)->deleted ? NIL : T;
3607 }
3608
3609
3610 static char
carmel2_search_unflagged(stream,msgno,d,n)3611 carmel2_search_unflagged (stream,msgno,d,n)
3612 MAILSTREAM *stream;
3613 long msgno;
3614 char *d;
3615 long n;
3616 {
3617 return MC(msgno)->flagged ? NIL : T;
3618 }
3619
3620
3621 static char
carmel2_search_unkeyword(stream,msgno,d,n)3622 carmel2_search_unkeyword (stream,msgno,d,n)
3623 MAILSTREAM *stream;
3624 long msgno;
3625 char *d;
3626 long n;
3627 {
3628 return T; /* keywords not supported yet */
3629 }
3630
3631
3632 static char
carmel2_search_unseen(stream,msgno,d,n)3633 carmel2_search_unseen (stream,msgno,d,n)
3634 MAILSTREAM *stream;
3635 long msgno;
3636 char *d;
3637 long n;
3638 {
3639 return MC(msgno)->seen ? NIL : T;
3640 }
3641
3642 static char
carmel2_search_before(stream,msgno,d,n)3643 carmel2_search_before (stream,msgno,d,n)
3644 MAILSTREAM *stream;
3645 long msgno;
3646 char *d;
3647 long n;
3648 {
3649 return (char) (carmel2_msgdate (stream,msgno) < n);
3650 }
3651
3652
3653 static char
carmel2_search_on(stream,msgno,d,n)3654 carmel2_search_on (stream,msgno,d,n)
3655 MAILSTREAM *stream;
3656 long msgno;
3657 char *d;
3658 long n;
3659 {
3660 return (char) (carmel2_msgdate (stream,msgno) == n);
3661 }
3662
3663
3664 static char
carmel2_search_since(stream,msgno,d,n)3665 carmel2_search_since (stream,msgno,d,n)
3666 MAILSTREAM *stream;
3667 long msgno;
3668 char *d;
3669 long n;
3670 {
3671 /* everybody interprets "since" as .GE. */
3672 return (char) (carmel2_msgdate (stream,msgno) >= n);
3673 }
3674
3675
3676 static unsigned long
carmel2_msgdate(stream,msgno)3677 carmel2_msgdate (stream,msgno)
3678 MAILSTREAM *stream;
3679 long msgno;
3680 {
3681 struct stat sbuf;
3682 struct tm *tm;
3683 MESSAGECACHE *elt = MC(msgno);
3684
3685 return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
3686 }
3687
3688 /*----------------------------------------------------------------------
3689 Search only the body of the text.
3690 BUG, probably need to unencode before searching
3691 ---*/
3692 static char
carmel2_search_body(stream,msgno,pat,pat_len)3693 carmel2_search_body (stream,msgno, pat, pat_len)
3694 MAILSTREAM *stream;
3695 long msgno,pat_len;
3696 char *pat;
3697 {
3698 char *t = carmel2_fetchtext(stream, msgno);
3699 return (t && search (t,strlen(t), pat, pat_len));
3700 }
3701
3702
3703 /*----------------------------------------------------------------------
3704 Search the subject field
3705 ----*/
3706 static char
carmel2_search_subject(stream,msgno,pat,pat_len)3707 carmel2_search_subject (stream,msgno, pat, pat_len)
3708 MAILSTREAM *stream;
3709 long msgno, pat_len;
3710 char *pat;
3711 {
3712 char *t = carmel2_fetchstructure (stream,msgno,NULL)->subject;
3713 return t ? search (t, strlen(t), pat, pat_len) : NIL;
3714 }
3715
3716
3717 /*----------------------------------------------------------------------
3718 Search the full header and body text of the message
3719 ---*/
3720 static char
carmel2_search_text(stream,msgno,pat,pat_len)3721 carmel2_search_text (stream, msgno, pat, pat_len)
3722 MAILSTREAM *stream;
3723 long msgno, pat_len;
3724 char *pat;
3725 {
3726 char *t = carmel2_fetchheader(stream,msgno);
3727 return (t && search(t, strlen(t), pat, pat_len)) ||
3728 carmel2_search_body(stream,msgno, pat, pat_len);
3729 }
3730
3731
3732 /*----------------------------------------------------------------------
3733 Search the Bcc field
3734 ---*/
3735 static char
carmel2_search_bcc(stream,msgno,d,n)3736 carmel2_search_bcc (stream,msgno,d,n)
3737 MAILSTREAM *stream;
3738 long msgno;
3739 char *d;
3740 long n;
3741 {
3742 carmel_20k_buf[0] = '\0';
3743 /* get text for address */
3744 rfc822_write_address (carmel_20k_buf,
3745 carmel2_fetchstructure (stream,msgno,NULL)->bcc);
3746 return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
3747 }
3748
3749
3750 static char
carmel2_search_cc(stream,msgno,d,n)3751 carmel2_search_cc (stream,msgno,d,n)
3752 MAILSTREAM *stream;
3753 long msgno;
3754 char *d;
3755 long n;
3756 {
3757 carmel_20k_buf[0] = '\0';
3758 /* get text for address */
3759 rfc822_write_address (carmel_20k_buf,
3760 carmel2_fetchstructure (stream, msgno, NULL)->cc);
3761 return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
3762 }
3763
3764
3765 static char
carmel2_search_from(stream,msgno,d,n)3766 carmel2_search_from (stream,msgno,d,n)
3767 MAILSTREAM *stream;
3768 long msgno;
3769 char *d;
3770 long n;
3771 {
3772 carmel_20k_buf[0] = '\0';
3773 /* get text for address */
3774 rfc822_write_address (carmel_20k_buf,
3775 carmel2_fetchstructure (stream,msgno,NULL)->from);
3776 return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
3777 }
3778
3779
3780 static char
carmel2_search_to(stream,msgno,pat,pat_len)3781 carmel2_search_to (stream,msgno, pat, pat_len)
3782 MAILSTREAM *stream;
3783 long msgno;
3784 char *pat;
3785 long pat_len;
3786 {
3787 carmel_20k_buf[0] = '\0';
3788 /* get text for address */
3789 rfc822_write_address (carmel_20k_buf,
3790 carmel2_fetchstructure (stream, msgno, NULL)->to);
3791 return(search(carmel_20k_buf,strlen(carmel_20k_buf), pat, pat_len));
3792 }
3793
3794 /* Search parsers */
3795
3796
3797 /* Parse a date
3798 * Accepts: function to return
3799 * pointer to date integer to return
3800 * Returns: function to return
3801 */
3802
3803 static search_t
carmel2_search_date(f,n)3804 carmel2_search_date (f, n)
3805 search_t f;
3806 long *n;
3807 {
3808 long i;
3809 char *s;
3810 MESSAGECACHE elt;
3811 /* parse the date and return fn if OK */
3812 return (carmel2_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
3813 (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
3814 }
3815
3816 /* Parse a flag
3817 * Accepts: function to return
3818 * pointer to string to return
3819 * Returns: function to return
3820 */
3821
3822 static search_t
carmel2_search_flag(f,d)3823 carmel2_search_flag (f,d)
3824 search_t f;
3825 char **d;
3826 {
3827 /* get a keyword, return if OK */
3828 return (*d = strtok (NIL," ")) ? f : NIL;
3829 }
3830
3831
3832 /* Parse a string
3833 * Accepts: function to return
3834 * pointer to string to return
3835 * pointer to string length to return
3836 * Returns: function to return
3837 */
3838
3839 static search_t
carmel2_search_string(f,d,n)3840 carmel2_search_string (f,d,n)
3841 search_t f;
3842 char **d;
3843 long *n;
3844 {
3845 char *c = strtok (NIL,""); /* remainder of criteria */
3846 if (c) { /* better be an argument */
3847 switch (*c) { /* see what the argument is */
3848 case '\0': /* catch bogons */
3849 case ' ':
3850 return NIL;
3851 case '"': /* quoted string */
3852 if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
3853 return NIL;
3854 break;
3855 case '{': /* literal string */
3856 *n = strtol (c+1,&c,10); /* get its length */
3857 if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
3858 *n > strlen (*d = c)) return NIL;
3859 c[*n] = '\255'; /* write new delimiter */
3860 strtok (c,"\255"); /* reset the strtok mechanism */
3861 break;
3862 default: /* atomic string */
3863 *n = strlen (*d = strtok (c," "));
3864 break;
3865 }
3866 return f;
3867 }
3868 else return NIL;
3869 }
3870
3871
3872
3873
3874
3875
3876 /*----------------------------------------------------------------------
3877 Some stuff to help out with the date parsing
3878 ---*/
3879 char *xdays2[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
3880
3881 char *
month_abbrev2(month_num)3882 month_abbrev2(month_num)
3883 int month_num;
3884 {
3885 static char *xmonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
3886 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
3887 if(month_num < 1 || month_num > 12)
3888 return("xxx");
3889 return(xmonths[month_num - 1]);
3890 }
3891
3892 struct time_zone_names {
3893 char *name;
3894 int hours;
3895 int minutes;
3896 } tz_names[] = {
3897 {"GMT", 0, 0},
3898 {"PST", -8, 0},
3899 {"PDT", -7, 0},
3900 {"MST", -7, 0},
3901 {"MDT", -6, 0},
3902 {"CST", -6, 0},
3903 {"CDT", -5, 0},
3904 {"EST", -5, 0},
3905 {"EDT", -4, 0},
3906 {"JST", 9, 0},
3907 {"IST", 2, 0},
3908 {"IDT", 3, 0},
3909 {NULL, 0, 0}};
3910
3911 /*----------------------------------------------------------------------
3912 Parse a date string into into a structure
3913
3914 Args: mc -- message cache to with structure to receive data
3915 given_date -- full header with date string somewhere to be found
3916
3917 This parses a number of date formats and produces a canonical date in
3918 a structure. The basic format is:
3919
3920 dd mm yy hh:mm:ss.t tz
3921
3922 It will also handle:
3923 ww dd mm yy hh:mm:ss.t tz mm dd yy hh:mm:ss.t tz
3924 ww dd mm hh:mm:ss.t yy tz mm dd hh:mm:ss.t yy tz
3925
3926 It knows many abbreviations for timezones, but is not complete.
3927 In general absolute time zones in hours +/- GMT are best.
3928 ----*/
3929 void
carmel2_rfc822_date(mc,given_date)3930 carmel2_rfc822_date(mc, given_date)
3931 char *given_date;
3932 MESSAGECACHE *mc;
3933 {
3934 char *p, **i, *q;
3935 int month, year;
3936 struct time_zone_names *tz;
3937
3938 mc->seconds = 0;
3939 mc->minutes = 0;
3940 mc->hours = 30;
3941 mc->day = 0;
3942 mc->month = 0;
3943 mc->year = 0;
3944 mc->zhours = 0;
3945 mc->zminutes = 0;
3946
3947 if(given_date == NULL)
3948 return;
3949
3950 p = given_date;
3951
3952 if(*p != 'D' && strncmp(p, "Date:",5))
3953 while(*p) {
3954 if(*p == '\n' && *(p+1) == 'D' && strncmp(p+1, "Date:", 5) == 0)
3955 break;
3956 p++;
3957 }
3958 if(!*p)
3959 return;
3960
3961 p += 6; /* Skip "\nDate: " */
3962 while(isspace(*p))
3963 p++;
3964
3965 /* Start with month, weekday or day ? */
3966 for(i = xdays2; *i != NULL; i++)
3967 if(struncmp2(p, *i, 3) == 0) /* Match first 3 letters */
3968 break;
3969 if(*i != NULL) {
3970 /* Started with week day .. buz over it*/
3971 while(*p && !isspace(*p) && *p != ',')
3972 p++;
3973 while(*p && (isspace(*p) || *p == ','))
3974 p++;
3975 }
3976 if(isdigit(*p)) {
3977 mc->day = atoi(p);
3978 while(*p && isdigit(*p))
3979 p++;
3980 while(*p && (*p == '-' || *p == ',' || isspace(*p)))
3981 p++;
3982 }
3983 for(month = 1; month <= 12; month++)
3984 if(struncmp2(p, month_abbrev2(month), 3) == 0)
3985 break;
3986 if(month < 13) {
3987 mc->month = month;
3988
3989 }
3990 /* Move over month, (or whatever is there) */
3991 while(*p && !isspace(*p) && *p != ',' && *p != '-')
3992 p++;
3993 while(*p && (isspace(*p) || *p == ',' || *p == '-'))
3994 p++;
3995
3996 /* Check again for day */
3997 if(isdigit(*p) && mc->day == -1) {
3998 mc->day = atoi(p);
3999 while(*p && isdigit(*p))
4000 p++;
4001 while(*p && (*p == '-' || *p == ',' || isspace(*p)))
4002 p++;
4003 }
4004
4005 /*-- Check for time --*/
4006 for(q = p; *q && isdigit(*q); q++);
4007 if(*q == ':') {
4008 /* It's the time (out of place) */
4009 mc->hours = atoi(p);
4010 while(*p && *p != ':' && !isspace(*p))
4011 p++;
4012 if(*p == ':') {
4013 mc->minutes = atoi(p);
4014 while(*p && *p != ':' && !isspace(*p))
4015 p++;
4016 if(*p == ':') {
4017 mc->seconds = atoi(p);
4018 while(*p && !isspace(*p))
4019 p++;
4020 }
4021 }
4022 while(*p && isspace(*p))
4023 p++;
4024 }
4025
4026 /* Get the year 0-50 is 2000-2050; 50-100 is 1950-1999 and
4027 101-9999 is 101-9999 */
4028 if(isdigit(*p)) {
4029 year = atoi(p);
4030 if(year < 50)
4031 year += 2000;
4032 else if(year < 100)
4033 year += 1900;
4034 mc->year = year - 1969;
4035 while(*p && isdigit(*p))
4036 p++;
4037 while(*p && (*p == '-' || *p == ',' || isspace(*p)))
4038 p++;
4039 } else {
4040 /* Something weird, skip it and try to resynch */
4041 while(*p && !isspace(*p) && *p != ',' && *p != '-')
4042 p++;
4043 while(*p && (isspace(*p) || *p == ',' || *p == '-'))
4044 p++;
4045 }
4046
4047 /*-- Now get hours minutes, seconds and ignore tenths --*/
4048 for(q = p; *q && isdigit(*q); q++);
4049 if(*q == ':' && mc->hours == 30) {
4050 mc->hours = atoi(p);
4051 while(*p && *p != ':' && !isspace(*p))
4052 p++;
4053 if(*p == ':') {
4054 p++;
4055 mc->minutes = atoi(p);
4056 while(*p && *p != ':' && !isspace(*p))
4057 p++;
4058 if(*p == ':') {
4059 p++;
4060 mc->seconds = atoi(p);
4061 while(*p && !isspace(*p) && *p != '+' && *p != '-')
4062 p++;
4063 }
4064 }
4065 }
4066 while(*p && isspace(*p))
4067 p++;
4068
4069
4070 /*-- The time zone --*/
4071 if(*p) {
4072 if(*p == '+' || *p == '-') {
4073 char tmp[3];
4074 mc->zoccident = (*p == '+' ? 1 : 0);
4075 p++;
4076 tmp[0] = *p++;
4077 tmp[1] = *p++;
4078 tmp[2] = '\0';
4079 mc->zhours = atoi(tmp);
4080 tmp[0] = *p++;
4081 tmp[1] = *p++;
4082 tmp[2] = '\0';
4083 mc->zminutes *= atoi(tmp);
4084 } else {
4085 for(tz = tz_names; tz->name != NULL; tz++) {
4086 if(struncmp2(p, tz->name, strlen(tz->name)) ==0) {
4087 if(tz->hours >= 0) {
4088 mc->zhours = tz->hours;
4089 mc->zoccident = 1;
4090 } else {
4091 mc->zhours = -tz->hours;
4092 mc->zoccident = 0;
4093 }
4094 mc->zminutes = tz->minutes;
4095 break;
4096 }
4097 }
4098 }
4099 }
4100 if(mc->hours == 30)
4101 mc->hours = 0;
4102 }
4103
4104
4105
4106 /*----------------------------------------------------------------------
4107 Print the date from the MESSAGECACHE into the string
4108 ----*/
4109 static void
carmel2_date2string(string,mc)4110 carmel2_date2string(string, mc)
4111 char *string;
4112 MESSAGECACHE *mc;
4113 {
4114 sprintf(string, "%d %s %d %d:%02d:%02d %s%04d",
4115 mc->day, month_abbrev2(mc->month), 1969+mc->year,
4116 mc->hours, mc->minutes, mc->seconds, mc->zoccident ? "-" : "",
4117 mc->zhours * 100 + mc->zminutes);
4118 }
4119
4120
4121
4122 /*----------------------------------------------------------------------
4123 Read the date into a structure from line in Carmel index
4124
4125 Args: mc -- The structure to contain the date (and other things)
4126 string -- String to be parsed. Format is:
4127 "yyyy^Amm^Add^Ahh^Amm^Ass^Azh^Azm"
4128
4129 ----*/
4130 static void
carmel2_parse_date(mc,string)4131 carmel2_parse_date(mc, string)
4132 MESSAGECACHE *mc;
4133 char *string;
4134 {
4135 int n;
4136 mc->year = next_num(&string) - 1969;
4137 mc->month = next_num(&string);
4138 mc->day = next_num(&string);
4139 mc->hours = next_num(&string);
4140 mc->minutes = next_num(&string);
4141 mc->seconds = next_num(&string);
4142
4143 n = next_num(&string);
4144 if(n < 0) {
4145 mc->zoccident = 0;
4146 mc->zhours = -n;
4147 } else {
4148 mc->zoccident = 1;
4149 mc->zhours = n;
4150 }
4151 mc->zminutes = next_num(&string);
4152 }
4153
4154
4155
4156 /*----------------------------------------------------------------------
4157 Read the next number out of string and return it, advancing the string
4158 ----*/
4159 static
next_num(string)4160 next_num(string)
4161 char **string;
4162 {
4163 int n;
4164 char *p;
4165
4166 if(string == NULL)
4167 return(0);
4168
4169 p = *string;
4170 n = atoi(p);
4171 while(*p > '\001')
4172 p++;
4173 if(*p)
4174 *string = p+1;
4175 else
4176 *string = NULL;
4177 return(n);
4178 }
4179
4180
4181 /*----------------------------------------------------------------------
4182 Take a (slightly ugly) FQ mailbox and and return the prettier
4183 last part of it
4184 ----*/
4185 char *
carmel_pretty_mailbox(mailbox)4186 carmel_pretty_mailbox(mailbox)
4187 char *mailbox;
4188 {
4189 char *pretty_mb;
4190
4191 for(pretty_mb = mailbox + strlen(mailbox) - 1;
4192 *pretty_mb != '#' && pretty_mb > mailbox;
4193 pretty_mb--)
4194 ;
4195 if(*pretty_mb == '#')
4196 pretty_mb++;
4197 return(pretty_mb);
4198 }
4199
4200 /*----------------------------------------------------------------------
4201 Parse a carmel mailbox name into its parts
4202
4203 Args: fq_name: The name to parse
4204 given_version: The version that must match; currently either \0 or '2'
4205
4206 Returns: NULL if not a valid carmel name, version of name and given_version
4207 do not match, or a malloced structure if it is.
4208 ----*/
4209 struct carmel_mb_name *
carmel_parse_mb_name(fq_name,given_version)4210 carmel_parse_mb_name(fq_name, given_version)
4211 char *fq_name;
4212 char given_version;
4213 {
4214 char *p, *q, version[2];
4215 struct carmel_mb_name *parsed_name;
4216
4217 if(struncmp2(fq_name, CARMEL_NAME_PREFIX, strlen(CARMEL_NAME_PREFIX))!= 0){
4218 return(0); /* BUG -- we won't work with non-FQN names for now */
4219 p = fq_name;
4220 version[0] = given_version;
4221 version[1] = '\0';
4222 } else {
4223 if(fq_name[7] == CARMEL_NAME_CHAR) {
4224 version[0] = '\0';
4225 p = fq_name + 8;
4226 } else if(fq_name[8] == CARMEL_NAME_CHAR) {
4227 version[0] = fq_name[7];
4228 version[1] = '\0';
4229 p = fq_name + 9;
4230 } else {
4231 return(NULL);
4232 }
4233 }
4234
4235 if(given_version != version[0])
4236 return(NULL);
4237
4238 parsed_name=(struct carmel_mb_name *)fs_get(sizeof(struct carmel_mb_name));
4239 parsed_name->version[0] = version[0];
4240 parsed_name->version[1] = version[1];
4241
4242 /*---- Find second # if there is one ---*/
4243 for(q = p; *q && *q != CARMEL_NAME_CHAR; q++);
4244
4245 if(*q == CARMEL_NAME_CHAR) {
4246 /*----- There is a user name -----*/
4247 parsed_name->user = fs_get((q - p) + 1);
4248 strncpy(parsed_name->user, p, q - p);
4249 parsed_name->user[q - p] = '\0';
4250 p = q + 1;
4251 } else {
4252 parsed_name->user = NULL;
4253 }
4254
4255 parsed_name->mailbox = cpystr(p);
4256
4257 return(parsed_name);
4258 }
4259
4260
4261 void
carmel_free_mb_name(mb_name)4262 carmel_free_mb_name(mb_name)
4263 struct carmel_mb_name *mb_name;
4264 {
4265 if(mb_name->user != NULL)
4266 fs_give((void **)(&(mb_name->user)));
4267 fs_give((void **)(&(mb_name->mailbox)));
4268 fs_give((void **)&mb_name);
4269 }
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280 /*--------------------------------------------------
4281 A case insensitive strcmp()
4282
4283 Args: o, r -- The two strings to compare
4284
4285 Result: integer indicating which is greater
4286 ---*/
strucmp2(o,r)4287 strucmp2(o, r)
4288 register char *r, *o;
4289 {
4290 if(r == NULL && o == NULL)
4291 return(0);
4292 if(o == NULL)
4293 return(1);
4294 if(r == NULL)
4295 return(-1);
4296
4297 while(*o && *r && (isupper(*o) ? tolower(*o) : *o) ==
4298 (isupper(*r) ? tolower(*r) : *r)) {
4299 o++, r++;
4300 }
4301 return((isupper(*o) ? tolower(*o): *o)-(isupper(*r) ? tolower(*r) : *r));
4302 }
4303
4304
4305
4306 /*----------------------------------------------------------------------
4307 A case insensitive strncmp()
4308
4309 Args: o, r -- The two strings to compare
4310 n -- length to stop comparing strings at
4311
4312 Result: integer indicating which is greater
4313
4314 ----*/
struncmp2(o,r,n)4315 struncmp2(o, r, n)
4316 register char *r, *o;
4317 register int n;
4318 {
4319 if(r == NULL && o == NULL)
4320 return(0);
4321 if(o == NULL)
4322 return(1);
4323 if(r == NULL)
4324 return(-1);
4325
4326 n--;
4327 while(n && *o && *r &&
4328 (isupper(*o)? tolower(*o): *o) == (isupper(*r)? tolower(*r): *r)) {
4329 o++; r++; n--;
4330 }
4331 return((isupper(*o)? tolower(*o): *o) - (isupper(*r)? tolower(*r): *r));
4332 }
4333
4334 /*----------------------------------------------------------------------
4335 A replacement for strchr or index ...
4336
4337 ....so we don't have to worry if it's there or not. We bring our own.
4338 If we really care about efficiency and think the local one is more
4339 efficient the local one can be used, but most of the things that take
4340 a long time are in the c-client and not in pine.
4341 ----*/
4342 char *
strindex2(buffer,ch)4343 strindex2(buffer, ch)
4344 char *buffer;
4345 int ch;
4346 {
4347 /** Returns a pointer to the first occurance of the character
4348 'ch' in the specified string or NULL if it doesn't occur **/
4349
4350 do
4351 if(*buffer == ch)
4352 return(buffer);
4353 while (*buffer++ != '\0');
4354
4355 return(NULL);
4356 }
4357
4358
4359 /*======================================================================
4360 */
4361
xopen(file,mode)4362 xopen(file, mode)
4363 char *file, *mode;
4364 {}
4365
4366
xclose(stream)4367 xclose(stream)
4368 FILE *stream;
4369 {}
4370
xread()4371 xread() {}
4372
xwrite()4373 xwrite() {}
4374
xlseek()4375 xlseek() {}
4376
4377 #ifdef BWC
carmel_match_glyph_wide(string)4378 carmel_match_glyph_wide(string)
4379 char *string;
4380 {
4381 extern char *ptspecials;
4382 char *s;
4383
4384 if(struncmp2(string, "content-type", 12))
4385 return(0); /* Nope */
4386 string += 12;
4387 while(*string && isspace(*string)) string++;
4388 if(*string != ':')
4389 return(0);
4390 string++;
4391 while(*string && isspace(*string)) string++;
4392 s = string;
4393 string = rfc822_parse_word(string, ptspecials);
4394 if(string == NULL)
4395 return(0);
4396 if(struncmp2(s, "text", 4))
4397 return(0);
4398 while(*string && isspace(*string)) string++;
4399 if(*string != '/')
4400 return;
4401 string++;
4402 while(*string && isspace(*string)) string++;
4403 s = string;
4404 string = rfc822_parse_word(string, ptspecials);
4405 if(string == NULL)
4406 return(0);
4407 if(struncmp2(s, "x-bwc-glyph-wide", 16))
4408 return(0);
4409 return(1);
4410 }
4411 #endif
4412
4413