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, &section, 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, &section,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