* Implement a different way to delete a password from the cache.
[alpine.git] / imap / src / osdep / os2 / mtxnt.c
blob7a4d0440f4f2969b4d33a158496b0fef70fd6d8e
1 /* ========================================================================
2 * Copyright 1988-2007 University of Washington
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
11 * ========================================================================
15 * Program: MTX mail routines
17 * Author: Mark Crispin
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
22 * Seattle, WA 98195
23 * Internet: MRC@CAC.Washington.EDU
25 * Date: 22 May 1990
26 * Last Edited: 15 June 2007
30 /* FILE TIME SEMANTICS
32 * The atime is the last read time of the file.
33 * The mtime is the last flags update time of the file.
34 * The ctime is the last write time of the file.
37 #include <stdio.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #ifndef errno
41 extern int errno; /* just in case */
42 #endif
43 #include "mail.h"
44 #include "osdep.h"
45 #include <fcntl.h>
46 #include <time.h>
47 #include <sys/stat.h>
48 #include <sys/utime.h>
49 #include "misc.h"
50 #include "dummy.h"
51 #include "fdstring.h"
53 /* MTX I/O stream local data */
55 typedef struct mtx_local {
56 unsigned int shouldcheck: 1; /* if ping should do a check instead */
57 unsigned int mustcheck: 1; /* if ping must do a check instead */
58 int fd; /* file descriptor for I/O */
59 off_t filesize; /* file size parsed */
60 time_t filetime; /* last file time */
61 time_t lastsnarf; /* last snarf time */
62 unsigned char *buf; /* temporary buffer */
63 unsigned long buflen; /* current size of temporary buffer */
64 } MTXLOCAL;
67 /* Convenient access to local data */
69 #define LOCAL ((MTXLOCAL *) stream->local)
72 /* Function prototypes */
74 DRIVER *mtx_valid (char *name);
75 int mtx_isvalid (char *name,char *file);
76 void *mtx_parameters (long function,void *value);
77 void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
78 void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
79 void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
80 long mtx_create (MAILSTREAM *stream,char *mailbox);
81 long mtx_delete (MAILSTREAM *stream,char *mailbox);
82 long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
83 long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
84 MAILSTREAM *mtx_open (MAILSTREAM *stream);
85 void mtx_close (MAILSTREAM *stream,long options);
86 void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
87 char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
88 unsigned long *length,long flags);
89 long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
90 void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
91 void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
92 long mtx_ping (MAILSTREAM *stream);
93 void mtx_check (MAILSTREAM *stream);
94 void mtx_snarf (MAILSTREAM *stream);
95 long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
96 long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
97 long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
99 long mtx_parse (MAILSTREAM *stream);
100 MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
101 void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
102 void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
103 unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
104 unsigned long *size);
107 /* MTX mail routines */
110 /* Driver dispatch used by MAIL */
112 DRIVER mtxdriver = {
113 "mtx", /* driver name */
114 /* driver flags */
115 DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY,
116 (DRIVER *) NIL, /* next driver */
117 mtx_valid, /* mailbox is valid for us */
118 mtx_parameters, /* manipulate parameters */
119 mtx_scan, /* scan mailboxes */
120 mtx_list, /* list mailboxes */
121 mtx_lsub, /* list subscribed mailboxes */
122 NIL, /* subscribe to mailbox */
123 NIL, /* unsubscribe from mailbox */
124 mtx_create, /* create mailbox */
125 mtx_delete, /* delete mailbox */
126 mtx_rename, /* rename mailbox */
127 mail_status_default, /* status of mailbox */
128 mtx_open, /* open mailbox */
129 mtx_close, /* close mailbox */
130 mtx_flags, /* fetch message "fast" attributes */
131 mtx_flags, /* fetch message flags */
132 NIL, /* fetch overview */
133 NIL, /* fetch message envelopes */
134 mtx_header, /* fetch message header */
135 mtx_text, /* fetch message body */
136 NIL, /* fetch partial message text */
137 NIL, /* unique identifier */
138 NIL, /* message number */
139 mtx_flag, /* modify flags */
140 mtx_flagmsg, /* per-message modify flags */
141 NIL, /* search for message based on criteria */
142 NIL, /* sort messages */
143 NIL, /* thread messages */
144 mtx_ping, /* ping mailbox to see if still alive */
145 mtx_check, /* check for new messages */
146 mtx_expunge, /* expunge deleted messages */
147 mtx_copy, /* copy messages to another mailbox */
148 mtx_append, /* append string message to mailbox */
149 NIL /* garbage collect stream */
152 /* prototype stream */
153 MAILSTREAM mtxproto = {&mtxdriver};
155 /* MTX mail validate mailbox
156 * Accepts: mailbox name
157 * Returns: our driver if name is valid, NIL otherwise
160 DRIVER *mtx_valid (char *name)
162 char tmp[MAILTMPLEN];
163 return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
167 /* MTX mail test for valid mailbox
168 * Accepts: mailbox name
169 * buffer to return file name
170 * Returns: T if valid, NIL otherwise
173 int mtx_isvalid (char *name,char *file)
175 int fd;
176 int ret = NIL;
177 char *s,tmp[MAILTMPLEN];
178 struct stat sbuf;
179 struct utimbuf times;
180 errno = EINVAL; /* assume invalid argument */
181 /* if file, get its status */
182 if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
183 ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
184 if (!sbuf.st_size)errno = 0;/* empty file */
185 else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
186 memset (tmp,'\0',MAILTMPLEN);
187 if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
188 (s[1] == '\012')) { /* valid format? */
189 *s = '\0'; /* tie off header */
190 /* must begin with dd-mmm-yy" */
191 ret = (((tmp[2] == '-' && tmp[6] == '-') ||
192 (tmp[1] == '-' && tmp[5] == '-')) &&
193 (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
195 else errno = -1; /* bogus format */
196 close (fd); /* close the file */
197 /* \Marked status? */
198 if (sbuf.st_ctime > sbuf.st_atime) {
199 /* preserve atime and mtime */
200 times.actime = sbuf.st_atime;
201 times.modtime = sbuf.st_mtime;
202 utime (file,&times); /* set the times */
206 /* in case INBOX but not mtx format */
207 else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
208 return ret; /* return what we should */
212 /* MTX manipulate driver parameters
213 * Accepts: function code
214 * function-dependent value
215 * Returns: function-dependent return value
218 void *mtx_parameters (long function,void *value)
220 return NIL;
223 /* MTX mail scan mailboxes
224 * Accepts: mail stream
225 * reference
226 * pattern to search
227 * string to scan
230 void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
232 if (stream) dummy_scan (NIL,ref,pat,contents);
236 /* MTX mail list mailboxes
237 * Accepts: mail stream
238 * reference
239 * pattern to search
242 void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
244 if (stream) dummy_list (NIL,ref,pat);
248 /* MTX mail list subscribed mailboxes
249 * Accepts: mail stream
250 * reference
251 * pattern to search
254 void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
256 if (stream) dummy_lsub (NIL,ref,pat);
259 /* MTX mail create mailbox
260 * Accepts: MAIL stream
261 * mailbox name to create
262 * Returns: T on success, NIL on failure
265 long mtx_create (MAILSTREAM *stream,char *mailbox)
267 char *s,mbx[MAILTMPLEN];
268 if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
269 sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
270 mm_log (mbx,ERROR);
271 return NIL;
275 /* MTX mail delete mailbox
276 * Accepts: MAIL stream
277 * mailbox name to delete
278 * Returns: T on success, NIL on failure
281 long mtx_delete (MAILSTREAM *stream,char *mailbox)
283 return mtx_rename (stream,mailbox,NIL);
286 /* MTX mail rename mailbox
287 * Accepts: MAIL stream
288 * old mailbox name
289 * new mailbox name (or NIL for delete)
290 * Returns: T on success, NIL on failure
293 long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
295 long ret = LONGT;
296 char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
297 int fd,ld;
298 struct stat sbuf;
299 if (!dummy_file (file,old) ||
300 (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
301 ((s = strrchr (tmp,'\\')) && !s[1])))) {
302 sprintf (tmp,newname ?
303 "Can't rename mailbox %.80s to %.80s: invalid name" :
304 "Can't delete mailbox %.80s: invalid name",
305 old,newname);
306 mm_log (tmp,ERROR);
307 return NIL;
309 if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
310 sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
311 mm_log (tmp,ERROR);
312 return NIL;
314 /* get exclusive parse/append permission */
315 if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
316 mm_log ("Unable to lock rename mailbox",ERROR);
317 return NIL;
319 /* lock out other users */
320 if (flock (fd,LOCK_EX|LOCK_NB)) {
321 close (fd); /* couldn't lock, give up on it then */
322 sprintf (tmp,"Mailbox %.80s is in use by another process",old);
323 mm_log (tmp,ERROR);
324 unlockfd (ld,lock); /* release exclusive parse/append permission */
325 return NIL;
328 if (newname) { /* want rename? */
329 /* found superior to destination name? */
330 if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
331 ((tmp[1] != ':') || (s != tmp + 2))) {
332 c = s[1]; /* remember character after delimiter */
333 *s = s[1] = '\0'; /* tie off name at delimiter */
334 /* name doesn't exist, create it */
335 if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
336 *s = '\\'; /* restore delimiter */
337 if (!dummy_create (stream,tmp)) ret = NIL;
339 else *s = '\\'; /* restore delimiter */
340 s[1] = c; /* restore character after delimiter */
342 flock (fd,LOCK_UN); /* release lock on the file */
343 close (fd); /* pacify NTFS */
344 /* rename the file */
345 if (ret && rename (file,tmp)) {
346 sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
347 strerror (errno));
348 mm_log (tmp,ERROR);
349 ret = NIL; /* set failure */
352 else {
353 flock (fd,LOCK_UN); /* release lock on the file */
354 close (fd); /* pacify NTFS */
355 if (unlink (file)) {
356 sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
357 mm_log (tmp,ERROR);
358 ret = NIL; /* set failure */
361 unlockfd (ld,lock); /* release exclusive parse/append permission */
362 return ret; /* return success */
365 /* MTX mail open
366 * Accepts: stream to open
367 * Returns: stream on success, NIL on failure
370 MAILSTREAM *mtx_open (MAILSTREAM *stream)
372 int fd,ld;
373 char tmp[MAILTMPLEN];
374 /* return prototype for OP_PROTOTYPE call */
375 if (!stream) return &mtxproto;
376 if (stream->local) fatal ("mtx recycle stream");
377 /* canonicalize the mailbox name */
378 if (!dummy_file (tmp,stream->mailbox)) {
379 sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
380 mm_log (tmp,ERROR);
382 if (stream->rdonly ||
383 (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
384 if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
385 sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
386 mm_log (tmp,ERROR);
387 return NIL;
389 else if (!stream->rdonly) { /* got it, but readonly */
390 mm_log ("Can't get write access to mailbox, access is readonly",WARN);
391 stream->rdonly = T;
394 stream->local = fs_get (sizeof (MTXLOCAL));
395 LOCAL->fd = fd; /* bind the file */
396 LOCAL->buf = (char *) fs_get (CHUNKSIZE);
397 LOCAL->buflen = CHUNKSIZE - 1;
398 /* note if an INBOX or not */
399 stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
400 fs_give ((void **) &stream->mailbox);
401 stream->mailbox = cpystr (tmp);
402 /* get shared parse permission */
403 if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
404 mm_log ("Unable to lock open mailbox",ERROR);
405 return NIL;
407 flock (LOCAL->fd,LOCK_SH); /* lock the file */
408 unlockfd (ld,tmp); /* release shared parse permission */
409 LOCAL->filesize = 0; /* initialize parsed file size */
410 LOCAL->filetime = 0; /* time not set up yet */
411 LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
412 stream->sequence++; /* bump sequence number */
413 stream->uid_validity = (unsigned long) time (0);
414 /* parse mailbox */
415 stream->nmsgs = stream->recent = 0;
416 if (mtx_ping (stream) && !stream->nmsgs)
417 mm_log ("Mailbox is empty",(long) NIL);
418 if (!LOCAL) return NIL; /* failure if stream died */
419 stream->perm_seen = stream->perm_deleted =
420 stream->perm_flagged = stream->perm_answered = stream->perm_draft =
421 stream->rdonly ? NIL : T;
422 stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
423 return stream; /* return stream to caller */
426 /* MTX mail close
427 * Accepts: MAIL stream
428 * close options
431 void mtx_close (MAILSTREAM *stream,long options)
433 if (stream && LOCAL) { /* only if a file is open */
434 int silent = stream->silent;
435 stream->silent = T; /* note this stream is dying */
436 if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
437 stream->silent = silent; /* restore previous status */
438 flock (LOCAL->fd,LOCK_UN); /* unlock local file */
439 close (LOCAL->fd); /* close the local file */
440 /* free local text buffer */
441 if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
442 /* nuke the local data */
443 fs_give ((void **) &stream->local);
444 stream->dtb = NIL; /* log out the DTB */
449 /* MTX mail fetch flags
450 * Accepts: MAIL stream
451 * sequence
452 * option flags
453 * Sniffs at file to see if some other process changed the flags
456 void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
458 unsigned long i;
459 if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
460 ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
461 mail_sequence (stream,sequence)))
462 for (i = 1; i <= stream->nmsgs; i++)
463 if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
466 /* MTX mail fetch message header
467 * Accepts: MAIL stream
468 * message # to fetch
469 * pointer to returned header text length
470 * option flags
471 * Returns: message header in RFC822 format
474 char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
475 long flags)
477 *length = 0; /* default to empty */
478 if (flags & FT_UID) return "";/* UID call "impossible" */
479 /* get to header position */
480 lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
481 /* is buffer big enough? */
482 if (*length > LOCAL->buflen) {
483 fs_give ((void **) &LOCAL->buf);
484 LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
486 LOCAL->buf[*length] = '\0'; /* tie off string */
487 /* slurp the data */
488 read (LOCAL->fd,LOCAL->buf,*length);
489 return LOCAL->buf;
492 /* MTX mail fetch message text (body only)
493 * Accepts: MAIL stream
494 * message # to fetch
495 * pointer to returned header text length
496 * option flags
497 * Returns: T, always
500 long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
502 FDDATA d;
503 unsigned long i,j;
504 MESSAGECACHE *elt;
505 /* UID call "impossible" */
506 if (flags & FT_UID) return NIL;
507 elt = mtx_elt (stream,msgno); /* get message status */
508 /* if message not seen */
509 if (!(flags & FT_PEEK) && !elt->seen) {
510 elt->seen = T; /* mark message as seen */
511 /* recalculate status */
512 mtx_update_status (stream,msgno,NIL);
513 mm_flags (stream,msgno);
515 /* find header position */
516 i = mtx_hdrpos (stream,msgno,&j);
517 d.fd = LOCAL->fd; /* set up file descriptor */
518 d.pos = i + j;
519 d.chunk = LOCAL->buf; /* initial buffer chunk */
520 d.chunksize = CHUNKSIZE;
521 INIT (bs,fd_string,&d,elt->rfc822_size - j);
522 return T; /* success */
525 /* MTX mail modify flags
526 * Accepts: MAIL stream
527 * sequence
528 * flag(s)
529 * option flags
532 void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
534 struct utimbuf times;
535 struct stat sbuf;
536 if (!stream->rdonly) { /* make sure the update takes */
537 fsync (LOCAL->fd);
538 fstat (LOCAL->fd,&sbuf); /* get current write time */
539 times.modtime = LOCAL->filetime = sbuf.st_mtime;
540 times.actime = time (0); /* make sure read comes after all that */
541 utime (stream->mailbox,&times);
546 /* MTX mail per-message modify flags
547 * Accepts: MAIL stream
548 * message cache element
551 void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
553 struct stat sbuf;
554 /* maybe need to do a checkpoint? */
555 if (LOCAL->filetime && !LOCAL->shouldcheck) {
556 fstat (LOCAL->fd,&sbuf); /* get current write time */
557 if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
558 LOCAL->filetime = 0; /* don't do this test for any other messages */
560 /* recalculate status */
561 mtx_update_status (stream,elt->msgno,NIL);
564 /* MTX mail ping mailbox
565 * Accepts: MAIL stream
566 * Returns: T if stream still alive, NIL if not
569 long mtx_ping (MAILSTREAM *stream)
571 unsigned long i = 1;
572 long r = T;
573 int ld;
574 char lock[MAILTMPLEN];
575 struct stat sbuf;
576 if (stream && LOCAL) { /* only if stream already open */
577 fstat (LOCAL->fd,&sbuf); /* get current file poop */
578 if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
579 (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
580 /* check for changed message status */
581 if (LOCAL->mustcheck || LOCAL->shouldcheck) {
582 LOCAL->filetime = sbuf.st_mtime;
583 if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
584 mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
585 while (i <= stream->nmsgs) mtx_elt (stream,i++);
586 LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
588 /* get shared parse/append permission */
589 if ((sbuf.st_size != LOCAL->filesize) &&
590 ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
591 /* parse resulting mailbox */
592 r = (mtx_parse (stream)) ? T : NIL;
593 unlockfd (ld,lock); /* release shared parse/append permission */
596 return r; /* return result of the parse */
600 /* MTX mail check mailbox (reparses status too)
601 * Accepts: MAIL stream
604 void mtx_check (MAILSTREAM *stream)
606 /* mark that a check is desired */
607 if (LOCAL) LOCAL->mustcheck = T;
608 if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
611 /* MTX mail expunge mailbox
612 * sequence to expunge if non-NIL
613 * expunge options
614 * Returns: T, always
617 long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
619 long ret;
620 struct utimbuf times;
621 struct stat sbuf;
622 off_t pos = 0;
623 int ld;
624 unsigned long i = 1;
625 unsigned long j,k,m,recent;
626 unsigned long n = 0;
627 unsigned long delta = 0;
628 char lock[MAILTMPLEN];
629 MESSAGECACHE *elt;
630 if (!(ret = (sequence ? ((options & EX_UID) ?
631 mail_uid_sequence (stream,sequence) :
632 mail_sequence (stream,sequence)) : LONGT) &&
633 mtx_ping (stream))); /* parse sequence if given, ping stream */
634 else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
635 else {
636 if (LOCAL->filetime && !LOCAL->shouldcheck) {
637 fstat (LOCAL->fd,&sbuf); /* get current write time */
638 if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
640 /* get exclusive parse/append permission */
641 if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
642 mm_log ("Unable to lock expunge mailbox",ERROR);
643 /* make sure see any newly-arrived messages */
644 else if (!mtx_parse (stream));
645 /* get exclusive access */
646 else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
647 flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
648 mm_log ("Can't expunge because mailbox is in use by another process",
649 ERROR);
650 unlockfd (ld,lock); /* release exclusive parse/append permission */
653 else {
654 mm_critical (stream); /* go critical */
655 recent = stream->recent; /* get recent now that pinged and locked */
656 /* for each message */
657 while (i <= stream->nmsgs) {
658 /* get cache element */
659 elt = mtx_elt (stream,i);
660 /* number of bytes to smash or preserve */
661 k = elt->private.special.text.size + elt->rfc822_size;
662 /* if need to expunge this message */
663 if (elt->deleted && (sequence ? elt->sequence : T)) {
664 /* if recent, note one less recent message */
665 if (elt->recent) --recent;
666 delta += k; /* number of bytes to delete */
667 /* notify upper levels */
668 mail_expunged (stream,i);
669 n++; /* count up one more expunged message */
671 else if (i++ && delta) {/* preserved message */
672 /* first byte to preserve */
673 j = elt->private.special.offset;
674 do { /* read from source position */
675 m = min (k,LOCAL->buflen);
676 lseek (LOCAL->fd,j,L_SET);
677 read (LOCAL->fd,LOCAL->buf,m);
678 pos = j - delta; /* write to destination position */
679 while (T) {
680 lseek (LOCAL->fd,pos,L_SET);
681 if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
682 mm_notify (stream,strerror (errno),WARN);
683 mm_diskerror (stream,errno,T);
685 pos += m; /* new position */
686 j += m; /* next chunk, perhaps */
687 } while (k -= m); /* until done */
688 /* note the new address of this text */
689 elt->private.special.offset -= delta;
691 /* preserved but no deleted messages */
692 else pos = elt->private.special.offset + k;
694 if (n) { /* truncate file after last message */
695 if (pos != (LOCAL->filesize -= delta)) {
696 sprintf (LOCAL->buf,
697 "Calculated size mismatch %lu != %lu, delta = %lu",
698 (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
699 mm_log (LOCAL->buf,WARN);
700 LOCAL->filesize = pos;/* fix it then */
702 ftruncate (LOCAL->fd,LOCAL->filesize);
703 sprintf (LOCAL->buf,"Expunged %lu messages",n);
704 /* output the news */
705 mm_log (LOCAL->buf,(long) NIL);
707 else mm_log ("No messages deleted, so no update needed",(long) NIL);
708 fsync (LOCAL->fd); /* force disk update */
709 fstat (LOCAL->fd,&sbuf); /* get new write time */
710 times.modtime = LOCAL->filetime = sbuf.st_mtime;
711 times.actime = time (0); /* reset atime to now */
712 utime (stream->mailbox,&times);
713 mm_nocritical (stream); /* release critical */
714 /* notify upper level of new mailbox size */
715 mail_exists (stream,stream->nmsgs);
716 mail_recent (stream,recent);
717 flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
718 unlockfd (ld,lock); /* release exclusive parse/append permission */
721 return ret;
724 /* MTX mail copy message(s)
725 * Accepts: MAIL stream
726 * sequence
727 * destination mailbox
728 * copy options
729 * Returns: T if success, NIL if failed
732 long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
734 struct stat sbuf;
735 struct utimbuf times;
736 MESSAGECACHE *elt;
737 unsigned long i,j,k;
738 long ret = LONGT;
739 int fd,ld;
740 char file[MAILTMPLEN],lock[MAILTMPLEN];
741 mailproxycopy_t pc =
742 (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
743 /* make sure valid mailbox */
744 if (!mtx_isvalid (mailbox,file)) switch (errno) {
745 case ENOENT: /* no such file? */
746 mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
747 return NIL;
748 case 0: /* merely empty file? */
749 break;
750 case EACCES: /* file protected */
751 sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
752 MM_LOG (LOCAL->buf,ERROR);
753 return NIL;
754 case EINVAL:
755 if (pc) return (*pc) (stream,sequence,mailbox,options);
756 sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
757 mm_log (LOCAL->buf,ERROR);
758 return NIL;
759 default:
760 if (pc) return (*pc) (stream,sequence,mailbox,options);
761 sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
762 mm_log (LOCAL->buf,ERROR);
763 return NIL;
765 if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
766 mail_sequence (stream,sequence))) return NIL;
767 /* got file? */
768 if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
769 sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
770 mm_log (LOCAL->buf,ERROR);
771 return NIL;
773 mm_critical (stream); /* go critical */
774 /* get exclusive parse/append permission */
775 if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
776 mm_log ("Unable to lock copy mailbox",ERROR);
777 mm_nocritical (stream);
778 return NIL;
780 fstat (fd,&sbuf); /* get current file size */
781 lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
783 /* for each requested message */
784 for (i = 1; ret && (i <= stream->nmsgs); i++)
785 if ((elt = mail_elt (stream,i))->sequence) {
786 lseek (LOCAL->fd,elt->private.special.offset,L_SET);
787 /* number of bytes to copy */
788 k = elt->private.special.text.size + elt->rfc822_size;
789 do { /* read from source position */
790 j = min (k,LOCAL->buflen);
791 read (LOCAL->fd,LOCAL->buf,j);
792 if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
793 } while (ret && (k -= j));/* until done */
795 /* make sure all the updates take */
796 if (!(ret && (ret = !fsync (fd)))) {
797 sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
798 mm_log (LOCAL->buf,ERROR);
799 ftruncate (fd,sbuf.st_size);
801 /* set atime to now-1 if successful copy */
802 if (ret) times.actime = time (0) - 1;
803 /* else preserved \Marked status */
804 else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
805 sbuf.st_atime : time (0);
806 times.modtime = sbuf.st_mtime;/* preserve mtime */
807 utime (file,&times); /* set the times */
808 unlockfd (ld,lock); /* release exclusive parse/append permission */
809 close (fd); /* close the file */
810 mm_nocritical (stream); /* release critical */
811 /* delete all requested messages */
812 if (ret && (options & CP_MOVE)) {
813 for (i = 1; i <= stream->nmsgs; i++)
814 if ((elt = mtx_elt (stream,i))->sequence) {
815 elt->deleted = T; /* mark message deleted */
816 /* recalculate status */
817 mtx_update_status (stream,i,NIL);
819 if (!stream->rdonly) { /* make sure the update takes */
820 fsync (LOCAL->fd);
821 fstat (LOCAL->fd,&sbuf); /* get current write time */
822 times.modtime = LOCAL->filetime = sbuf.st_mtime;
823 times.actime = time (0); /* make sure atime remains greater */
824 utime (stream->mailbox,&times);
827 if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
828 mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
829 return ret;
832 /* MTX mail append message from stringstruct
833 * Accepts: MAIL stream
834 * destination mailbox
835 * append callback
836 * data for callback
837 * Returns: T if append successful, else NIL
840 long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
842 struct stat sbuf;
843 int fd,ld,c;
844 char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
845 struct utimbuf times;
846 FILE *df;
847 MESSAGECACHE elt;
848 long f;
849 unsigned long i,uf;
850 STRING *message;
851 long ret = LONGT;
852 /* default stream to prototype */
853 if (!stream) stream = &mtxproto;
854 /* make sure valid mailbox */
855 if (!mtx_isvalid (mailbox,file)) switch (errno) {
856 case ENOENT: /* no such file? */
857 if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX");
858 else {
859 mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
860 return NIL;
862 /* falls through */
863 case 0: /* merely empty file? */
864 break;
865 case EACCES: /* file protected */
866 sprintf (tmp,"Can't access destination: %.80s",mailbox);
867 MM_LOG (tmp,ERROR);
868 return NIL;
869 case EINVAL:
870 sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
871 mm_log (tmp,ERROR);
872 return NIL;
873 default:
874 sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
875 mm_log (tmp,ERROR);
876 return NIL;
878 /* get first message */
879 if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
881 /* open destination mailbox */
882 if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
883 < 0) || !(df = fdopen (fd,"ab"))) {
884 sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
885 mm_log (tmp,ERROR);
886 return NIL;
888 /* get parse/append permission */
889 if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
890 mm_log ("Unable to lock append mailbox",ERROR);
891 close (fd);
892 return NIL;
894 mm_critical (stream); /* go critical */
895 fstat (fd,&sbuf); /* get current file size */
896 errno = 0;
897 do { /* parse flags */
898 if (!SIZE (message)) { /* guard against zero-length */
899 mm_log ("Append of zero-length message",ERROR);
900 ret = NIL;
901 break;
903 f = mail_parse_flags (stream,flags,&i);
904 /* reverse bits (dontcha wish we had CIRC?) */
905 for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
906 if (date) { /* parse date if given */
907 if (!mail_parse_date (&elt,date)) {
908 sprintf (tmp,"Bad date in append: %.80s",date);
909 mm_log (tmp,ERROR);
910 ret = NIL; /* mark failure */
911 break;
913 mail_date (tmp,&elt); /* write preserved date */
915 else internal_date (tmp); /* get current date in IMAP format */
916 /* write header */
917 if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
918 (unsigned long) f) < 0) ret = NIL;
919 else { /* write message */
920 if (i) do c = 0xff & SNX (message);
921 while ((putc (c,df) != EOF) && --i);
922 /* get next message */
923 if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
925 } while (ret && message);
926 /* if error... */
927 if (!ret || (fflush (df) == EOF)) {
928 ftruncate (fd,sbuf.st_size);/* revert file */
929 close (fd); /* make sure fclose() doesn't corrupt us */
930 if (errno) {
931 sprintf (tmp,"Message append failed: %s",strerror (errno));
932 mm_log (tmp,ERROR);
934 ret = NIL;
936 if (ret) times.actime = time (0) - 1;
937 /* else preserved \Marked status */
938 else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
939 sbuf.st_atime : time (0);
940 times.modtime = sbuf.st_mtime;/* preserve mtime */
941 utime (file,&times); /* set the times */
942 fclose (df); /* close the file */
943 unlockfd (ld,lock); /* release exclusive parse/append permission */
944 mm_nocritical (stream); /* release critical */
945 if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
946 mm_log ("Can not return meaningful APPENDUID with this mailbox format",
947 WARN);
948 return ret;
951 /* Internal routines */
954 /* MTX mail parse mailbox
955 * Accepts: MAIL stream
956 * Returns: T if parse OK
957 * NIL if failure, stream aborted
960 long mtx_parse (MAILSTREAM *stream)
962 struct stat sbuf;
963 MESSAGECACHE *elt = NIL;
964 unsigned char c,*s,*t,*x;
965 char tmp[MAILTMPLEN];
966 unsigned long i,j;
967 long curpos = LOCAL->filesize;
968 long nmsgs = stream->nmsgs;
969 long recent = stream->recent;
970 short added = NIL;
971 short silent = stream->silent;
972 fstat (LOCAL->fd,&sbuf); /* get status */
973 if (sbuf.st_size < curpos) { /* sanity check */
974 sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
975 mm_log (tmp,ERROR);
976 mtx_close (stream,NIL);
977 return NIL;
979 stream->silent = T; /* don't pass up mm_exists() events yet */
980 while (sbuf.st_size - curpos){/* while there is stuff to parse */
981 /* get to that position in the file */
982 lseek (LOCAL->fd,curpos,L_SET);
983 if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
984 sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
985 (unsigned long) curpos,(unsigned long) sbuf.st_size,
986 i ? strerror (errno) : "no data read");
987 mm_log (tmp,ERROR);
988 mtx_close (stream,NIL);
989 return NIL;
991 LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
992 if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
993 sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
994 (unsigned long) curpos,i,(char *) LOCAL->buf);
995 mm_log (tmp,ERROR);
996 mtx_close (stream,NIL);
997 return NIL;
999 *s = '\0'; /* tie off header line */
1000 i = (s + 2) - LOCAL->buf; /* note start of text offset */
1001 if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
1002 sprintf (tmp,"Unable to parse internal header at %lu: %s",
1003 (unsigned long) curpos,(char *) LOCAL->buf);
1004 mm_log (tmp,ERROR);
1005 mtx_close (stream,NIL);
1006 return NIL;
1008 *s++ = '\0'; *t++ = '\0'; /* tie off fields */
1010 added = T; /* note that a new message was added */
1011 /* swell the cache */
1012 mail_exists (stream,++nmsgs);
1013 /* instantiate an elt for this message */
1014 (elt = mail_elt (stream,nmsgs))->valid = T;
1015 elt->private.uid = ++stream->uid_last;
1016 /* note file offset of header */
1017 elt->private.special.offset = curpos;
1018 /* in case error */
1019 elt->private.special.text.size = 0;
1020 /* header size not known yet */
1021 elt->private.msg.header.text.size = 0;
1022 x = s; /* parse the header components */
1023 if (mail_parse_date (elt,LOCAL->buf) &&
1024 (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
1025 isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
1026 isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
1027 isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
1028 isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
1029 elt->private.special.text.size = i;
1030 else { /* oops */
1031 sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
1032 curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
1033 mm_log (tmp,ERROR);
1034 mtx_close (stream,NIL);
1035 return NIL;
1037 /* make sure didn't run off end of file */
1038 if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
1039 sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
1040 elt->private.special.offset,(unsigned long) curpos,
1041 (unsigned long) sbuf.st_size);
1042 mm_log (tmp,ERROR);
1043 mtx_close (stream,NIL);
1044 return NIL;
1046 c = t[10]; /* remember first system flags byte */
1047 t[10] = '\0'; /* tie off flags */
1048 j = strtoul (t,NIL,8); /* get user flags value */
1049 t[10] = c; /* restore first system flags byte */
1050 /* set up all valid user flags (reversed!) */
1051 while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
1052 stream->user_flags[i]) elt->user_flags |= 1 << i;
1053 /* calculate system flags */
1054 if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
1055 if (j & fDELETED) elt->deleted = T;
1056 if (j & fFLAGGED) elt->flagged = T;
1057 if (j & fANSWERED) elt->answered = T;
1058 if (j & fDRAFT) elt->draft = T;
1059 if (!(j & fOLD)) { /* newly arrived message? */
1060 elt->recent = T;
1061 recent++; /* count up a new recent message */
1062 /* mark it as old */
1063 mtx_update_status (stream,nmsgs,NIL);
1066 fsync (LOCAL->fd); /* make sure all the fOLD flags take */
1067 /* update parsed file size and time */
1068 LOCAL->filesize = sbuf.st_size;
1069 fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
1070 LOCAL->filetime = sbuf.st_mtime;
1071 if (added && !stream->rdonly){/* make sure atime updated */
1072 struct utimbuf times;
1073 times.actime = time (0);
1074 times.modtime = LOCAL->filetime;
1075 utime (stream->mailbox,&times);
1077 stream->silent = silent; /* can pass up events now */
1078 mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
1079 mail_recent (stream,recent); /* and of change in recent messages */
1080 return LONGT; /* return the winnage */
1083 /* MTX get cache element with status updating from file
1084 * Accepts: MAIL stream
1085 * message number
1086 * Returns: cache element
1089 MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
1091 MESSAGECACHE *elt = mail_elt (stream,msgno);
1092 struct { /* old flags */
1093 unsigned int seen : 1;
1094 unsigned int deleted : 1;
1095 unsigned int flagged : 1;
1096 unsigned int answered : 1;
1097 unsigned int draft : 1;
1098 unsigned long user_flags;
1099 } old;
1100 old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
1101 old.answered = elt->answered; old.draft = elt->draft;
1102 old.user_flags = elt->user_flags;
1103 mtx_read_flags (stream,elt);
1104 if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
1105 (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
1106 (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
1107 mm_flags (stream,msgno); /* let top level know */
1108 return elt;
1111 /* MTX read flags from file
1112 * Accepts: MAIL stream
1113 * Returns: cache element
1116 void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
1118 unsigned long i,j;
1119 /* noop if readonly and have valid flags */
1120 if (stream->rdonly && elt->valid) return;
1121 /* set the seek pointer */
1122 lseek (LOCAL->fd,(off_t) elt->private.special.offset +
1123 elt->private.special.text.size - 14,L_SET);
1124 /* read the new flags */
1125 if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
1126 sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
1127 fatal (LOCAL->buf);
1129 /* calculate system flags */
1130 i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
1131 elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
1132 elt->flagged = i & fFLAGGED ? T : NIL;
1133 elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
1134 LOCAL->buf[10] = '\0'; /* tie off flags */
1135 j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
1136 /* set up all valid user flags (reversed!) */
1137 while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
1138 stream->user_flags[i]) elt->user_flags |= 1 << i;
1139 elt->valid = T; /* have valid flags now */
1142 /* MTX update status string
1143 * Accepts: MAIL stream
1144 * message number
1145 * flag saying whether or not to sync
1148 void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
1150 struct utimbuf times;
1151 struct stat sbuf;
1152 MESSAGECACHE *elt = mail_elt (stream,msgno);
1153 unsigned long j,k = 0;
1154 /* readonly */
1155 if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
1156 else { /* readwrite */
1157 j = elt->user_flags; /* get user flags */
1158 /* reverse bits (dontcha wish we had CIRC?) */
1159 while (j) k |= 1 << (29 - find_rightmost_bit (&j));
1160 /* print new flag string */
1161 sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
1162 (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
1163 (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
1164 (fDRAFT * elt->draft)));
1165 while (T) { /* get to that place in the file */
1166 lseek (LOCAL->fd,(off_t) elt->private.special.offset +
1167 elt->private.special.text.size - 14,L_SET);
1168 /* write new flags */
1169 if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
1170 mm_notify (stream,strerror (errno),WARN);
1171 mm_diskerror (stream,errno,T);
1173 if (syncflag) { /* sync if requested */
1174 fsync (LOCAL->fd);
1175 fstat (LOCAL->fd,&sbuf); /* get new write time */
1176 times.modtime = LOCAL->filetime = sbuf.st_mtime;
1177 times.actime = time (0); /* make sure read is later */
1178 utime (stream->mailbox,&times);
1183 /* MTX locate header for a message
1184 * Accepts: MAIL stream
1185 * message number
1186 * pointer to returned header size
1187 * Returns: position of header in file
1190 unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
1191 unsigned long *size)
1193 unsigned long siz;
1194 long i = 0;
1195 int q = 0;
1196 char *s,tmp[MAILTMPLEN];
1197 MESSAGECACHE *elt = mtx_elt (stream,msgno);
1198 unsigned long ret = elt->private.special.offset +
1199 elt->private.special.text.size;
1200 /* is header size known? */
1201 if (!(*size = elt->private.msg.header.text.size)) {
1202 lseek (LOCAL->fd,ret,L_SET);/* get to header position */
1203 /* search message for CRLF CRLF */
1204 for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
1205 /* read another buffer as necessary */
1206 if ((--i <= 0) && /* buffer empty? */
1207 (read (LOCAL->fd,s = tmp,
1208 i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
1209 return ret; /* I/O error? */
1210 switch (q) { /* sniff at buffer */
1211 case 0: /* first character */
1212 q = (*s++ == '\015') ? 1 : 0;
1213 break;
1214 case 1: /* second character */
1215 q = (*s++ == '\012') ? 2 : 0;
1216 break;
1217 case 2: /* third character */
1218 q = (*s++ == '\015') ? 3 : 0;
1219 break;
1220 case 3: /* fourth character */
1221 if (*s++ == '\012') { /* have the sequence? */
1222 /* yes, note for later */
1223 elt->private.msg.header.text.size = *size = siz;
1224 return ret;
1226 q = 0; /* lost... */
1227 break;
1230 /* header consumes entire message */
1231 elt->private.msg.header.text.size = *size = elt->rfc822_size;
1233 return ret;