imapext-2007
diff src/osdep/unix/mmdf.c @ 0:ada5e610ab86
imap-2007e
author | yuuji@gentei.org |
---|---|
date | Mon, 14 Sep 2009 15:17:45 +0900 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/osdep/unix/mmdf.c Mon Sep 14 15:17:45 2009 +0900 1.3 @@ -0,0 +1,2549 @@ 1.4 +/* ======================================================================== 1.5 + * Copyright 1988-2008 University of Washington 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * 1.14 + * ======================================================================== 1.15 + */ 1.16 + 1.17 +/* 1.18 + * Program: MMDF mail routines 1.19 + * 1.20 + * Author: Mark Crispin 1.21 + * UW Technology 1.22 + * University of Washington 1.23 + * Seattle, WA 98195 1.24 + * Internet: MRC@Washington.EDU 1.25 + * 1.26 + * Date: 20 December 1989 1.27 + * Last Edited: 27 March 2008 1.28 + */ 1.29 + 1.30 + 1.31 +#include <stdio.h> 1.32 +#include <ctype.h> 1.33 +#include <errno.h> 1.34 +extern int errno; /* just in case */ 1.35 +#include <signal.h> 1.36 +#include "mail.h" 1.37 +#include "osdep.h" 1.38 +#include <time.h> 1.39 +#include <sys/stat.h> 1.40 +#include "pseudo.h" 1.41 +#include "fdstring.h" 1.42 +#include "misc.h" 1.43 +#include "dummy.h" 1.44 + 1.45 +/* Supposedly, this page has everything the MMDF driver needs to know about 1.46 + * the MMDF delimiter. By changing these macros, the MMDF driver should 1.47 + * change with it. Note that if you change the length of MMDFHDRTXT you 1.48 + * also need to change the ISMMDF and RETIFMMDFWRD macros to reflect the new 1.49 + * size. 1.50 + */ 1.51 + 1.52 + 1.53 +/* Useful MMDF constants */ 1.54 + 1.55 +#define MMDFCHR '\01' /* MMDF character */ 1.56 +#define MMDFCHRS 0x01010101 /* MMDF header character spread in a word */ 1.57 + /* MMDF header text */ 1.58 +#define MMDFHDRTXT "\01\01\01\01\n" 1.59 + /* length of MMDF header text */ 1.60 +#define MMDFHDRLEN (sizeof (MMDFHDRTXT) - 1) 1.61 + 1.62 + 1.63 +/* Validate MMDF header 1.64 + * Accepts: pointer to candidate string to validate as an MMDF header 1.65 + * Returns: T if valid; else NIL 1.66 + */ 1.67 + 1.68 +#define ISMMDF(s) \ 1.69 + ((*(s) == MMDFCHR) && ((s)[1] == MMDFCHR) && ((s)[2] == MMDFCHR) && \ 1.70 + ((s)[3] == MMDFCHR) && ((s)[4] == '\n')) 1.71 + 1.72 + 1.73 +/* Return if a 32-bit word has the start of an MMDF header 1.74 + * Accepts: pointer to word of four bytes to validate as an MMDF header 1.75 + * Returns: pointer to MMDF header, else proceeds 1.76 + */ 1.77 + 1.78 +#define RETIFMMDFWRD(s) { \ 1.79 + if (s[3] == MMDFCHR) { \ 1.80 + if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == MMDFCHR) && \ 1.81 + (s[7] == '\n')) return s + 3; \ 1.82 + else if (s[2] == MMDFCHR) { \ 1.83 + if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == '\n')) \ 1.84 + return s + 2; \ 1.85 + else if (s[1] == MMDFCHR) { \ 1.86 + if ((s[4] == MMDFCHR) && (s[5] == '\n')) return s + 1; \ 1.87 + else if ((*s == MMDFCHR) && (s[4] == '\n')) return s; \ 1.88 + } \ 1.89 + } \ 1.90 + } \ 1.91 +} 1.92 + 1.93 +/* Validate line 1.94 + * Accepts: pointer to candidate string to validate as a From header 1.95 + * return pointer to end of date/time field 1.96 + * return pointer to offset from t of time (hours of ``mmm dd hh:mm'') 1.97 + * return pointer to offset from t of time zone (if non-zero) 1.98 + * Returns: t,ti,zn set if valid From string, else ti is NIL 1.99 + */ 1.100 + 1.101 +#define VALID(s,x,ti,zn) { \ 1.102 + ti = 0; \ 1.103 + if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \ 1.104 + (s[4] == ' ')) { \ 1.105 + for (x = s + 5; *x && *x != '\n'; x++); \ 1.106 + if (*x) { \ 1.107 + if (x - s >= 41) { \ 1.108 + for (zn = -1; x[zn] != ' '; zn--); \ 1.109 + if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \ 1.110 + (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \ 1.111 + (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \ 1.112 + (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\ 1.113 + x += zn - 12; \ 1.114 + } \ 1.115 + if (x - s >= 27) { \ 1.116 + if (x[-5] == ' ') { \ 1.117 + if (x[-8] == ':') zn = 0,ti = -5; \ 1.118 + else if (x[-9] == ' ') ti = zn = -9; \ 1.119 + else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \ 1.120 + ti = zn = -11; \ 1.121 + } \ 1.122 + else if (x[-4] == ' ') { \ 1.123 + if (x[-9] == ' ') zn = -4,ti = -9; \ 1.124 + } \ 1.125 + else if (x[-6] == ' ') { \ 1.126 + if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \ 1.127 + zn = -6,ti = -11; \ 1.128 + } \ 1.129 + if (ti && !((x[ti - 3] == ':') && \ 1.130 + (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \ 1.131 + (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \ 1.132 + (x[ti - 11] == ' '))) ti = 0; \ 1.133 + } \ 1.134 + } \ 1.135 + } \ 1.136 +} 1.137 + 1.138 +/* You are not expected to understand this macro, but read the next page if 1.139 + * you are not faint of heart. 1.140 + * 1.141 + * Known formats to the VALID macro are: 1.142 + * From user Wed Dec 2 05:53 1992 1.143 + * BSD From user Wed Dec 2 05:53:22 1992 1.144 + * SysV From user Wed Dec 2 05:53 PST 1992 1.145 + * rn From user Wed Dec 2 05:53:22 PST 1992 1.146 + * From user Wed Dec 2 05:53 -0700 1992 1.147 + * emacs From user Wed Dec 2 05:53:22 -0700 1992 1.148 + * From user Wed Dec 2 05:53 1992 PST 1.149 + * From user Wed Dec 2 05:53:22 1992 PST 1.150 + * From user Wed Dec 2 05:53 1992 -0700 1.151 + * Solaris From user Wed Dec 2 05:53:22 1992 -0700 1.152 + * 1.153 + * Plus all of the above with `` remote from xxx'' after it. Thank you very 1.154 + * much, smail and Solaris, for making my life considerably more complicated. 1.155 + */ 1.156 + 1.157 +/* 1.158 + * What? You want to understand the VALID macro anyway? Alright, since you 1.159 + * insist. Actually, it isn't really all that difficult, provided that you 1.160 + * take it step by step. 1.161 + * 1.162 + * Line 1 Initializes the return ti value to failure (0); 1.163 + * Lines 2-3 Validates that the 1st-5th characters are ``From ''. 1.164 + * Lines 4-5 Validates that there is an end of line and points x at it. 1.165 + * Lines 6-13 First checks to see if the line is at least 41 characters long. 1.166 + * If so, it scans backwards to find the rightmost space. From 1.167 + * that point, it scans backwards to see if the string matches 1.168 + * `` remote from''. If so, it sets x to point to the space at 1.169 + * the start of the string. 1.170 + * Line 14 Makes sure that there are at least 27 characters in the line. 1.171 + * Lines 15-20 Checks if the date/time ends with the year (there is a space 1.172 + * five characters back). If there is a colon three characters 1.173 + * further back, there is no timezone field, so zn is set to 0 1.174 + * and ti is set in front of the year. Otherwise, there must 1.175 + * either to be a space four characters back for a three-letter 1.176 + * timezone, or a space six characters back followed by a + or - 1.177 + * for a numeric timezone; in either case, zn and ti become the 1.178 + * offset of the space immediately before it. 1.179 + * Lines 21-23 Are the failure case for line 14. If there is a space four 1.180 + * characters back, it is a three-letter timezone; there must be a 1.181 + * space for the year nine characters back. zn is the zone 1.182 + * offset; ti is the offset of the space. 1.183 + * Lines 24-27 Are the failure case for line 20. If there is a space six 1.184 + * characters back, it is a numeric timezone; there must be a 1.185 + * space eleven characters back and a + or - five characters back. 1.186 + * zn is the zone offset; ti is the offset of the space. 1.187 + * Line 28-31 If ti is valid, make sure that the string before ti is of the 1.188 + * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise 1.189 + * invalidate ti. There must be a colon three characters back 1.190 + * and a space six or nine characters back (depending upon 1.191 + * whether or not the character six characters back is a colon). 1.192 + * There must be a space three characters further back (in front 1.193 + * of the day), one seven characters back (in front of the month), 1.194 + * and one eleven characters back (in front of the day of week). 1.195 + * ti is set to be the offset of the space before the time. 1.196 + * 1.197 + * Why a macro? It gets invoked a *lot* in a tight loop. On some of the 1.198 + * newer pipelined machines it is faster being open-coded than it would be if 1.199 + * subroutines are called. 1.200 + * 1.201 + * Why does it scan backwards from the end of the line, instead of doing the 1.202 + * much easier forward scan? There is no deterministic way to parse the 1.203 + * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to 1.204 + * see if unquoted spaces were possible. They are, and I've encountered enough 1.205 + * evil mail to be totally unwilling to trust that ``it will never happen''. 1.206 + */ 1.207 + 1.208 +/* Build parameters */ 1.209 + 1.210 +#define KODRETRY 15 /* kiss-of-death retry in seconds */ 1.211 +#define LOCKTIMEOUT 5 /* lock timeout in minutes */ 1.212 + 1.213 + 1.214 +/* MMDF I/O stream local data */ 1.215 + 1.216 +typedef struct mmdf_local { 1.217 + unsigned int dirty : 1; /* disk copy needs updating */ 1.218 + unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */ 1.219 + unsigned int pseudo : 1; /* uses a pseudo message */ 1.220 + unsigned int appending : 1; /* don't mark new messages as old */ 1.221 + int fd; /* mailbox file descriptor */ 1.222 + int ld; /* lock file descriptor */ 1.223 + char *lname; /* lock file name */ 1.224 + off_t filesize; /* file size parsed */ 1.225 + time_t filetime; /* last file time */ 1.226 + unsigned char *buf; /* temporary buffer */ 1.227 + unsigned long buflen; /* current size of temporary buffer */ 1.228 + unsigned long uid; /* current text uid */ 1.229 + SIZEDTEXT text; /* current text */ 1.230 + unsigned long textlen; /* current text length */ 1.231 + char *line; /* returned line */ 1.232 + char *linebuf; /* line readin buffer */ 1.233 + unsigned long linebuflen; /* current line readin buffer length */ 1.234 +} MMDFLOCAL; 1.235 + 1.236 + 1.237 +/* Convenient access to local data */ 1.238 + 1.239 +#define LOCAL ((MMDFLOCAL *) stream->local) 1.240 + 1.241 + 1.242 +/* MMDF protected file structure */ 1.243 + 1.244 +typedef struct mmdf_file { 1.245 + MAILSTREAM *stream; /* current stream */ 1.246 + off_t curpos; /* current file position */ 1.247 + off_t protect; /* protected position */ 1.248 + off_t filepos; /* current last written file position */ 1.249 + char *buf; /* overflow buffer */ 1.250 + size_t buflen; /* current overflow buffer length */ 1.251 + char *bufpos; /* current buffer position */ 1.252 +} MMDFFILE; 1.253 + 1.254 +/* Function prototypes */ 1.255 + 1.256 +DRIVER *mmdf_valid (char *name); 1.257 +long mmdf_isvalid (char *name,char *tmp); 1.258 +long mmdf_isvalid_fd (int fd,char *tmp); 1.259 +void *mmdf_parameters (long function,void *value); 1.260 +void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); 1.261 +void mmdf_list (MAILSTREAM *stream,char *ref,char *pat); 1.262 +void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat); 1.263 +long mmdf_create (MAILSTREAM *stream,char *mailbox); 1.264 +long mmdf_delete (MAILSTREAM *stream,char *mailbox); 1.265 +long mmdf_rename (MAILSTREAM *stream,char *old,char *newname); 1.266 +MAILSTREAM *mmdf_open (MAILSTREAM *stream); 1.267 +void mmdf_close (MAILSTREAM *stream,long options); 1.268 +char *mmdf_header (MAILSTREAM *stream,unsigned long msgno, 1.269 + unsigned long *length,long flags); 1.270 +long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); 1.271 +char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, 1.272 + unsigned long *length,long flags); 1.273 +void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); 1.274 +long mmdf_ping (MAILSTREAM *stream); 1.275 +void mmdf_check (MAILSTREAM *stream); 1.276 +long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options); 1.277 +long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); 1.278 +long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); 1.279 +int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, 1.280 + STRING *msg); 1.281 +int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set); 1.282 + 1.283 +void mmdf_abort (MAILSTREAM *stream); 1.284 +char *mmdf_file (char *dst,char *name); 1.285 +int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op); 1.286 +void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock); 1.287 +int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op); 1.288 +char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size); 1.289 +unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr); 1.290 +unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, 1.291 + unsigned long uid,long flag); 1.292 +long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock, 1.293 + long flags); 1.294 +long mmdf_extend (MAILSTREAM *stream,unsigned long size); 1.295 +void mmdf_write (MMDFFILE *f,char *s,unsigned long i); 1.296 +void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size); 1.297 + 1.298 +/* MMDF mail routines */ 1.299 + 1.300 + 1.301 +/* Driver dispatch used by MAIL */ 1.302 + 1.303 +DRIVER mmdfdriver = { 1.304 + "mmdf", /* driver name */ 1.305 + /* driver flags */ 1.306 + DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT, 1.307 + (DRIVER *) NIL, /* next driver */ 1.308 + mmdf_valid, /* mailbox is valid for us */ 1.309 + mmdf_parameters, /* manipulate parameters */ 1.310 + mmdf_scan, /* scan mailboxes */ 1.311 + mmdf_list, /* list mailboxes */ 1.312 + mmdf_lsub, /* list subscribed mailboxes */ 1.313 + NIL, /* subscribe to mailbox */ 1.314 + NIL, /* unsubscribe from mailbox */ 1.315 + mmdf_create, /* create mailbox */ 1.316 + mmdf_delete, /* delete mailbox */ 1.317 + mmdf_rename, /* rename mailbox */ 1.318 + mail_status_default, /* status of mailbox */ 1.319 + mmdf_open, /* open mailbox */ 1.320 + mmdf_close, /* close mailbox */ 1.321 + NIL, /* fetch message "fast" attributes */ 1.322 + NIL, /* fetch message flags */ 1.323 + NIL, /* fetch overview */ 1.324 + NIL, /* fetch message envelopes */ 1.325 + mmdf_header, /* fetch message header */ 1.326 + mmdf_text, /* fetch message text */ 1.327 + NIL, /* fetch partial message text */ 1.328 + NIL, /* unique identifier */ 1.329 + NIL, /* message number */ 1.330 + NIL, /* modify flags */ 1.331 + mmdf_flagmsg, /* per-message modify flags */ 1.332 + NIL, /* search for message based on criteria */ 1.333 + NIL, /* sort messages */ 1.334 + NIL, /* thread messages */ 1.335 + mmdf_ping, /* ping mailbox to see if still alive */ 1.336 + mmdf_check, /* check for new messages */ 1.337 + mmdf_expunge, /* expunge deleted messages */ 1.338 + mmdf_copy, /* copy messages to another mailbox */ 1.339 + mmdf_append, /* append string message to mailbox */ 1.340 + NIL /* garbage collect stream */ 1.341 +}; 1.342 + 1.343 + /* prototype stream */ 1.344 +MAILSTREAM mmdfproto = {&mmdfdriver}; 1.345 + 1.346 +char *mmdfhdr = MMDFHDRTXT; /* MMDF header */ 1.347 + 1.348 +/* MMDF mail validate mailbox 1.349 + * Accepts: mailbox name 1.350 + * Returns: our driver if name is valid, NIL otherwise 1.351 + */ 1.352 + 1.353 +DRIVER *mmdf_valid (char *name) 1.354 +{ 1.355 + char tmp[MAILTMPLEN]; 1.356 + return mmdf_isvalid (name,tmp) ? &mmdfdriver : NIL; 1.357 +} 1.358 + 1.359 + 1.360 +/* MMDF mail test for valid mailbox name 1.361 + * Accepts: mailbox name 1.362 + * scratch buffer 1.363 + * Returns: T if valid, NIL otherwise 1.364 + */ 1.365 + 1.366 +long mmdf_isvalid (char *name,char *tmp) 1.367 +{ 1.368 + int fd; 1.369 + int ret = NIL; 1.370 + char *t,file[MAILTMPLEN]; 1.371 + struct stat sbuf; 1.372 + time_t tp[2]; 1.373 + errno = EINVAL; /* assume invalid argument */ 1.374 + /* must be non-empty file */ 1.375 + if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) { 1.376 + if (!sbuf.st_size)errno = 0;/* empty file */ 1.377 + else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { 1.378 + /* error -1 for invalid format */ 1.379 + if (!(ret = mmdf_isvalid_fd (fd,tmp))) errno = -1; 1.380 + close (fd); /* close the file */ 1.381 + /* \Marked status? */ 1.382 + if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) { 1.383 + tp[0] = sbuf.st_atime; /* preserve atime and mtime */ 1.384 + tp[1] = sbuf.st_mtime; 1.385 + utime (file,tp); /* set the times */ 1.386 + } 1.387 + } 1.388 + } 1.389 + return ret; /* return what we should */ 1.390 +} 1.391 + 1.392 +/* MMDF mail test for valid mailbox 1.393 + * Accepts: file descriptor 1.394 + * scratch buffer 1.395 + * Returns: T if valid, NIL otherwise 1.396 + */ 1.397 + 1.398 +long mmdf_isvalid_fd (int fd,char *tmp) 1.399 +{ 1.400 + int ret = NIL; 1.401 + memset (tmp,'\0',MAILTMPLEN); 1.402 + if (read (fd,tmp,MAILTMPLEN-1) >= 0) ret = ISMMDF (tmp) ? T : NIL; 1.403 + return ret; /* return what we should */ 1.404 +} 1.405 + 1.406 + 1.407 +/* MMDF manipulate driver parameters 1.408 + * Accepts: function code 1.409 + * function-dependent value 1.410 + * Returns: function-dependent return value 1.411 + */ 1.412 + 1.413 +void *mmdf_parameters (long function,void *value) 1.414 +{ 1.415 + void *ret = NIL; 1.416 + switch ((int) function) { 1.417 + case GET_INBOXPATH: 1.418 + if (value) ret = dummy_file ((char *) value,"INBOX"); 1.419 + break; 1.420 + } 1.421 + return ret; 1.422 +} 1.423 + 1.424 +/* MMDF mail scan mailboxes 1.425 + * Accepts: mail stream 1.426 + * reference 1.427 + * pattern to search 1.428 + * string to scan 1.429 + */ 1.430 + 1.431 +void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) 1.432 +{ 1.433 + if (stream) dummy_scan (NIL,ref,pat,contents); 1.434 +} 1.435 + 1.436 + 1.437 +/* MMDF mail list mailboxes 1.438 + * Accepts: mail stream 1.439 + * reference 1.440 + * pattern to search 1.441 + */ 1.442 + 1.443 +void mmdf_list (MAILSTREAM *stream,char *ref,char *pat) 1.444 +{ 1.445 + if (stream) dummy_list (NIL,ref,pat); 1.446 +} 1.447 + 1.448 + 1.449 +/* MMDF mail list subscribed mailboxes 1.450 + * Accepts: mail stream 1.451 + * reference 1.452 + * pattern to search 1.453 + */ 1.454 + 1.455 +void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat) 1.456 +{ 1.457 + if (stream) dummy_lsub (NIL,ref,pat); 1.458 +} 1.459 + 1.460 +/* MMDF mail create mailbox 1.461 + * Accepts: MAIL stream 1.462 + * mailbox name to create 1.463 + * Returns: T on success, NIL on failure 1.464 + */ 1.465 + 1.466 +long mmdf_create (MAILSTREAM *stream,char *mailbox) 1.467 +{ 1.468 + char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN]; 1.469 + long ret = NIL; 1.470 + int i,fd; 1.471 + time_t ti = time (0); 1.472 + if (!(s = dummy_file (mbx,mailbox))) { 1.473 + sprintf (tmp,"Can't create %.80s: invalid name",mailbox); 1.474 + MM_LOG (tmp,ERROR); 1.475 + } 1.476 + /* create underlying file */ 1.477 + else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) { 1.478 + /* done if dir-only or whiner */ 1.479 + if (((s = strrchr (s,'/')) && !s[1]) || 1.480 + mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T; 1.481 + else if ((fd = open (mbx,O_WRONLY, 1.482 + (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) { 1.483 + sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno)); 1.484 + MM_LOG (tmp,ERROR); 1.485 + unlink (mbx); /* delete the file */ 1.486 + } 1.487 + else { /* initialize header */ 1.488 + memset (tmp,'\0',MAILTMPLEN); 1.489 + sprintf (tmp,"%sFrom %s %sDate: ",mmdfhdr,pseudo_from,ctime (&ti)); 1.490 + rfc822_date (s = tmp + strlen (tmp)); 1.491 + sprintf (s += strlen (s), /* write the pseudo-header */ 1.492 + "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000", 1.493 + pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, 1.494 + (unsigned long) ti); 1.495 + for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i)) 1.496 + sprintf (s += strlen (s)," %s",default_user_flag (i)); 1.497 + sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr); 1.498 + if (write (fd,tmp,strlen (tmp)) > 0) ret = T; 1.499 + else { 1.500 + sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx, 1.501 + strerror (errno)); 1.502 + MM_LOG (tmp,ERROR); 1.503 + unlink (mbx); /* delete the file */ 1.504 + } 1.505 + close (fd); /* close file */ 1.506 + } 1.507 + } 1.508 + /* set proper protections */ 1.509 + return ret ? set_mbx_protections (mailbox,mbx) : NIL; 1.510 +} 1.511 + 1.512 +/* MMDF mail delete mailbox 1.513 + * Accepts: MAIL stream 1.514 + * mailbox name to delete 1.515 + * Returns: T on success, NIL on failure 1.516 + */ 1.517 + 1.518 +long mmdf_delete (MAILSTREAM *stream,char *mailbox) 1.519 +{ 1.520 + return mmdf_rename (stream,mailbox,NIL); 1.521 +} 1.522 + 1.523 + 1.524 +/* MMDF mail rename mailbox 1.525 + * Accepts: MAIL stream 1.526 + * old mailbox name 1.527 + * new mailbox name (or NIL for delete) 1.528 + * Returns: T on success, NIL on failure 1.529 + */ 1.530 + 1.531 +long mmdf_rename (MAILSTREAM *stream,char *old,char *newname) 1.532 +{ 1.533 + long ret = NIL; 1.534 + char c,*s = NIL; 1.535 + char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; 1.536 + DOTLOCK lockx; 1.537 + int fd,ld; 1.538 + long i; 1.539 + struct stat sbuf; 1.540 + MM_CRITICAL (stream); /* get the c-client lock */ 1.541 + if (!dummy_file (file,old) || 1.542 + (newname && (!((s = mailboxfile (tmp,newname)) && *s) || 1.543 + ((s = strrchr (tmp,'/')) && !s[1])))) 1.544 + sprintf (tmp,newname ? 1.545 + "Can't rename mailbox %.80s to %.80s: invalid name" : 1.546 + "Can't delete mailbox %.80s: invalid name", 1.547 + old,newname); 1.548 + /* lock out other c-clients */ 1.549 + else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0) 1.550 + sprintf (tmp,"Mailbox %.80s is in use by another process",old); 1.551 + 1.552 + else { 1.553 + if ((fd = mmdf_lock (file,O_RDWR, 1.554 + (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL), 1.555 + &lockx,LOCK_EX)) < 0) 1.556 + sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno)); 1.557 + else { 1.558 + if (newname) { /* want rename? */ 1.559 + /* found superior to destination name? */ 1.560 + if (s = strrchr (s,'/')) { 1.561 + c = *++s; /* remember first character of inferior */ 1.562 + *s = '\0'; /* tie off to get just superior */ 1.563 + /* name doesn't exist, create it */ 1.564 + if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && 1.565 + !dummy_create_path (stream,tmp,get_dir_protection (newname))) { 1.566 + mmdf_unlock (fd,NIL,&lockx); 1.567 + mmdf_unlock (ld,NIL,NIL); 1.568 + unlink (lock); 1.569 + MM_NOCRITICAL (stream); 1.570 + return ret; /* return success or failure */ 1.571 + } 1.572 + *s = c; /* restore full name */ 1.573 + } 1.574 + if (rename (file,tmp)) 1.575 + sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, 1.576 + strerror (errno)); 1.577 + else ret = T; /* set success */ 1.578 + } 1.579 + else if (unlink (file)) 1.580 + sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); 1.581 + else ret = T; /* set success */ 1.582 + mmdf_unlock (fd,NIL,&lockx); 1.583 + } 1.584 + mmdf_unlock (ld,NIL,NIL); /* flush the lock */ 1.585 + unlink (lock); 1.586 + } 1.587 + MM_NOCRITICAL (stream); /* no longer critical */ 1.588 + if (!ret) MM_LOG (tmp,ERROR); /* log error */ 1.589 + return ret; /* return success or failure */ 1.590 +} 1.591 + 1.592 +/* MMDF mail open 1.593 + * Accepts: Stream to open 1.594 + * Returns: Stream on success, NIL on failure 1.595 + */ 1.596 + 1.597 +MAILSTREAM *mmdf_open (MAILSTREAM *stream) 1.598 +{ 1.599 + long i; 1.600 + int fd; 1.601 + char tmp[MAILTMPLEN]; 1.602 + DOTLOCK lock; 1.603 + long retry; 1.604 + /* return prototype for OP_PROTOTYPE call */ 1.605 + if (!stream) return user_flags (&mmdfproto); 1.606 + retry = stream->silent ? 1 : KODRETRY; 1.607 + if (stream->local) fatal ("mmdf recycle stream"); 1.608 + stream->local = memset (fs_get (sizeof (MMDFLOCAL)),0,sizeof (MMDFLOCAL)); 1.609 + /* note if an INBOX or not */ 1.610 + stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); 1.611 + /* canonicalize the stream mailbox name */ 1.612 + if (!dummy_file (tmp,stream->mailbox)) { 1.613 + sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); 1.614 + MM_LOG (tmp,ERROR); 1.615 + return NIL; 1.616 + } 1.617 + /* flush old name */ 1.618 + fs_give ((void **) &stream->mailbox); 1.619 + /* save canonical name */ 1.620 + stream->mailbox = cpystr (tmp); 1.621 + LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */ 1.622 + LOCAL->buf = (char *) fs_get (CHUNKSIZE); 1.623 + LOCAL->buflen = CHUNKSIZE - 1; 1.624 + LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE); 1.625 + LOCAL->text.size = CHUNKSIZE - 1; 1.626 + LOCAL->linebuf = (char *) fs_get (CHUNKSIZE); 1.627 + LOCAL->linebuflen = CHUNKSIZE - 1; 1.628 + stream->sequence++; /* bump sequence number */ 1.629 + 1.630 + /* make lock for read/write access */ 1.631 + if (!stream->rdonly) while (retry) { 1.632 + /* try to lock file */ 1.633 + if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) { 1.634 + /* suppressing kiss-of-death? */ 1.635 + if (stream->nokod) retry = 0; 1.636 + /* no, first time through? */ 1.637 + else if (retry-- == KODRETRY) { 1.638 + /* learned other guy's PID and can signal? */ 1.639 + if (i && !kill ((int) i,SIGUSR2)) { 1.640 + sprintf (tmp,"Trying to get mailbox lock from process %ld",i); 1.641 + MM_LOG (tmp,WARN); 1.642 + } 1.643 + else retry = 0; /* give up */ 1.644 + } 1.645 + if (!stream->silent) { /* nothing if silent stream */ 1.646 + if (retry) sleep (1); /* wait a second before trying again */ 1.647 + else MM_LOG ("Mailbox is open by another process, access is readonly", 1.648 + WARN); 1.649 + } 1.650 + } 1.651 + else { /* got the lock, nobody else can alter state */ 1.652 + LOCAL->ld = fd; /* note lock's fd and name */ 1.653 + LOCAL->lname = cpystr (tmp); 1.654 + /* make sure mode OK (don't use fchmod()) */ 1.655 + chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL)); 1.656 + if (stream->silent) i = 0;/* silent streams won't accept KOD */ 1.657 + else { /* note our PID in the lock */ 1.658 + sprintf (tmp,"%d",getpid ()); 1.659 + write (fd,tmp,(i = strlen (tmp))+1); 1.660 + } 1.661 + ftruncate (fd,i); /* make sure tied off */ 1.662 + fsync (fd); /* make sure it's available */ 1.663 + retry = 0; /* no more need to try */ 1.664 + } 1.665 + } 1.666 + 1.667 + /* parse mailbox */ 1.668 + stream->nmsgs = stream->recent = 0; 1.669 + /* will we be able to get write access? */ 1.670 + if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) { 1.671 + MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); 1.672 + flock (LOCAL->ld,LOCK_UN); /* release the lock */ 1.673 + close (LOCAL->ld); /* close the lock file */ 1.674 + LOCAL->ld = -1; /* no more lock fd */ 1.675 + unlink (LOCAL->lname); /* delete it */ 1.676 + } 1.677 + /* reset UID validity */ 1.678 + stream->uid_validity = stream->uid_last = 0; 1.679 + if (stream->silent && !stream->rdonly && (LOCAL->ld < 0)) 1.680 + mmdf_abort (stream); /* abort if can't get RW silent stream */ 1.681 + /* parse mailbox */ 1.682 + else if (mmdf_parse (stream,&lock,LOCK_SH)) { 1.683 + mmdf_unlock (LOCAL->fd,stream,&lock); 1.684 + mail_unlock (stream); 1.685 + MM_NOCRITICAL (stream); /* done with critical */ 1.686 + } 1.687 + if (!LOCAL) return NIL; /* failure if stream died */ 1.688 + /* make sure upper level knows readonly */ 1.689 + stream->rdonly = (LOCAL->ld < 0); 1.690 + /* notify about empty mailbox */ 1.691 + if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL); 1.692 + if (!stream->rdonly) { /* flags stick if readwrite */ 1.693 + stream->perm_seen = stream->perm_deleted = 1.694 + stream->perm_flagged = stream->perm_answered = stream->perm_draft = T; 1.695 + if (!stream->uid_nosticky) {/* users with lives get permanent keywords */ 1.696 + stream->perm_user_flags = 0xffffffff; 1.697 + /* and maybe can create them too! */ 1.698 + stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T; 1.699 + } 1.700 + } 1.701 + return stream; /* return stream alive to caller */ 1.702 +} 1.703 + 1.704 + 1.705 +/* MMDF mail close 1.706 + * Accepts: MAIL stream 1.707 + * close options 1.708 + */ 1.709 + 1.710 +void mmdf_close (MAILSTREAM *stream,long options) 1.711 +{ 1.712 + int silent = stream->silent; 1.713 + stream->silent = T; /* go silent */ 1.714 + /* expunge if requested */ 1.715 + if (options & CL_EXPUNGE) mmdf_expunge (stream,NIL,NIL); 1.716 + /* else dump final checkpoint */ 1.717 + else if (LOCAL->dirty) mmdf_check (stream); 1.718 + stream->silent = silent; /* restore old silence state */ 1.719 + mmdf_abort (stream); /* now punt the file and local data */ 1.720 +} 1.721 + 1.722 +/* MMDF mail fetch message header 1.723 + * Accepts: MAIL stream 1.724 + * message # to fetch 1.725 + * pointer to returned header text length 1.726 + * option flags 1.727 + * Returns: message header in RFC822 format 1.728 + */ 1.729 + 1.730 + /* lines to filter from header */ 1.731 +static STRINGLIST *mmdf_hlines = NIL; 1.732 + 1.733 +char *mmdf_header (MAILSTREAM *stream,unsigned long msgno, 1.734 + unsigned long *length,long flags) 1.735 +{ 1.736 + MESSAGECACHE *elt; 1.737 + unsigned char *s,*t,*tl; 1.738 + *length = 0; /* default to empty */ 1.739 + if (flags & FT_UID) return "";/* UID call "impossible" */ 1.740 + elt = mail_elt (stream,msgno);/* get cache */ 1.741 + if (!mmdf_hlines) { /* once only code */ 1.742 + STRINGLIST *lines = mmdf_hlines = mail_newstringlist (); 1.743 + lines->text.size = strlen ((char *) (lines->text.data = 1.744 + (unsigned char *) "Status")); 1.745 + lines = lines->next = mail_newstringlist (); 1.746 + lines->text.size = strlen ((char *) (lines->text.data = 1.747 + (unsigned char *) "X-Status")); 1.748 + lines = lines->next = mail_newstringlist (); 1.749 + lines->text.size = strlen ((char *) (lines->text.data = 1.750 + (unsigned char *) "X-Keywords")); 1.751 + lines = lines->next = mail_newstringlist (); 1.752 + lines->text.size = strlen ((char *) (lines->text.data = 1.753 + (unsigned char *) "X-UID")); 1.754 + lines = lines->next = mail_newstringlist (); 1.755 + lines->text.size = strlen ((char *) (lines->text.data = 1.756 + (unsigned char *) "X-IMAP")); 1.757 + lines = lines->next = mail_newstringlist (); 1.758 + lines->text.size = strlen ((char *) (lines->text.data = 1.759 + (unsigned char *) "X-IMAPbase")); 1.760 + } 1.761 + /* go to header position */ 1.762 + lseek (LOCAL->fd,elt->private.special.offset + 1.763 + elt->private.msg.header.offset,L_SET); 1.764 + 1.765 + if (flags & FT_INTERNAL) { /* initial data OK? */ 1.766 + if (elt->private.msg.header.text.size > LOCAL->buflen) { 1.767 + fs_give ((void **) &LOCAL->buf); 1.768 + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = 1.769 + elt->private.msg.header.text.size) + 1); 1.770 + } 1.771 + /* read message */ 1.772 + read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size); 1.773 + /* got text, tie off string */ 1.774 + LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0'; 1.775 + /* squeeze out CRs (in case from PC) */ 1.776 + for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++) 1.777 + if (*t != '\r') *s++ = *t; 1.778 + *s = '\0'; 1.779 + *length = s - LOCAL->buf; /* adjust length */ 1.780 + } 1.781 + else { /* need to make a CRLF version */ 1.782 + read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1), 1.783 + elt->private.msg.header.text.size); 1.784 + /* tie off string, and convert to CRLF */ 1.785 + s[elt->private.msg.header.text.size] = '\0'; 1.786 + *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s, 1.787 + elt->private.msg.header.text.size); 1.788 + fs_give ((void **) &s); /* free readin buffer */ 1.789 + /* squeeze out spurious CRs */ 1.790 + for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++) 1.791 + if ((*t != '\r') || (t[1] == '\n')) *s++ = *t; 1.792 + *s = '\0'; 1.793 + *length = s - LOCAL->buf; /* adjust length */ 1.794 + } 1.795 + *length = mail_filter (LOCAL->buf,*length,mmdf_hlines,FT_NOT); 1.796 + return (char *) LOCAL->buf; /* return processed copy */ 1.797 +} 1.798 + 1.799 +/* MMDF mail fetch message text 1.800 + * Accepts: MAIL stream 1.801 + * message # to fetch 1.802 + * pointer to returned stringstruct 1.803 + * option flags 1.804 + * Returns: T on success, NIL if failure 1.805 + */ 1.806 + 1.807 +long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) 1.808 +{ 1.809 + char *s; 1.810 + unsigned long i; 1.811 + MESSAGECACHE *elt; 1.812 + /* UID call "impossible" */ 1.813 + if (flags & FT_UID) return NIL; 1.814 + elt = mail_elt (stream,msgno);/* get cache element */ 1.815 + /* if message not seen */ 1.816 + if (!(flags & FT_PEEK) && !elt->seen) { 1.817 + /* mark message seen and dirty */ 1.818 + elt->seen = elt->private.dirty = LOCAL->dirty = T; 1.819 + MM_FLAGS (stream,msgno); 1.820 + } 1.821 + s = mmdf_text_work (stream,elt,&i,flags); 1.822 + INIT (bs,mail_string,s,i); /* set up stringstruct */ 1.823 + return T; /* success */ 1.824 +} 1.825 + 1.826 +/* MMDF mail fetch message text worker routine 1.827 + * Accepts: MAIL stream 1.828 + * message cache element 1.829 + * pointer to returned header text length 1.830 + * option flags 1.831 + */ 1.832 + 1.833 +char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt, 1.834 + unsigned long *length,long flags) 1.835 +{ 1.836 + FDDATA d; 1.837 + STRING bs; 1.838 + unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE]; 1.839 + /* go to text position */ 1.840 + lseek (LOCAL->fd,elt->private.special.offset + 1.841 + elt->private.msg.text.offset,L_SET); 1.842 + if (flags & FT_INTERNAL) { /* initial data OK? */ 1.843 + if (elt->private.msg.text.text.size > LOCAL->buflen) { 1.844 + fs_give ((void **) &LOCAL->buf); 1.845 + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = 1.846 + elt->private.msg.text.text.size) + 1); 1.847 + } 1.848 + /* read message */ 1.849 + read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size); 1.850 + /* got text, tie off string */ 1.851 + LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0'; 1.852 + /* squeeze out CRs (in case from PC) */ 1.853 + for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++) 1.854 + if (*t != '\r') *s++ = *t; 1.855 + *s = '\0'; 1.856 + *length = s - LOCAL->buf; /* adjust length */ 1.857 + return (char *) LOCAL->buf; 1.858 + } 1.859 + 1.860 + /* have it cached already? */ 1.861 + if (elt->private.uid != LOCAL->uid) { 1.862 + /* not cached, cache it now */ 1.863 + LOCAL->uid = elt->private.uid; 1.864 + /* is buffer big enough? */ 1.865 + if (elt->rfc822_size > LOCAL->text.size) { 1.866 + /* excessively conservative, but the right thing is too hard to do */ 1.867 + fs_give ((void **) &LOCAL->text.data); 1.868 + LOCAL->text.data = (unsigned char *) 1.869 + fs_get ((LOCAL->text.size = elt->rfc822_size) + 1); 1.870 + } 1.871 + d.fd = LOCAL->fd; /* yes, set up file descriptor */ 1.872 + d.pos = elt->private.special.offset + elt->private.msg.text.offset; 1.873 + d.chunk = tmp; /* initial buffer chunk */ 1.874 + d.chunksize = CHUNKSIZE; /* file chunk size */ 1.875 + INIT (&bs,fd_string,&d,elt->private.msg.text.text.size); 1.876 + for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) { 1.877 + case '\r': /* carriage return seen */ 1.878 + break; 1.879 + case '\n': 1.880 + *s++ = '\r'; /* insert a CR */ 1.881 + default: 1.882 + *s++ = c; /* copy characters */ 1.883 + } 1.884 + *s = '\0'; /* tie off buffer */ 1.885 + /* calculate length of cached data */ 1.886 + LOCAL->textlen = s - LOCAL->text.data; 1.887 + } 1.888 + *length = LOCAL->textlen; /* return from cache */ 1.889 + return (char *) LOCAL->text.data; 1.890 +} 1.891 + 1.892 +/* MMDF per-message modify flag 1.893 + * Accepts: MAIL stream 1.894 + * message cache element 1.895 + */ 1.896 + 1.897 +void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) 1.898 +{ 1.899 + /* only after finishing */ 1.900 + if (elt->valid) elt->private.dirty = LOCAL->dirty = T; 1.901 +} 1.902 + 1.903 + 1.904 +/* MMDF mail ping mailbox 1.905 + * Accepts: MAIL stream 1.906 + * Returns: T if stream alive, else NIL 1.907 + */ 1.908 + 1.909 +long mmdf_ping (MAILSTREAM *stream) 1.910 +{ 1.911 + DOTLOCK lock; 1.912 + struct stat sbuf; 1.913 + long reparse; 1.914 + /* big no-op if not readwrite */ 1.915 + if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) { 1.916 + if (stream->rdonly) { /* does he want to give up readwrite? */ 1.917 + /* checkpoint if we changed something */ 1.918 + if (LOCAL->dirty) mmdf_check (stream); 1.919 + flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */ 1.920 + close (LOCAL->ld); /* close the readwrite lock file */ 1.921 + LOCAL->ld = -1; /* no more readwrite lock fd */ 1.922 + unlink (LOCAL->lname); /* delete the readwrite lock file */ 1.923 + } 1.924 + else { /* see if need to reparse */ 1.925 + if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) { 1.926 + /* get current mailbox size */ 1.927 + if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf); 1.928 + else if (stat (stream->mailbox,&sbuf)) { 1.929 + sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s", 1.930 + strerror (errno)); 1.931 + MM_LOG (LOCAL->buf,ERROR); 1.932 + mmdf_abort (stream); 1.933 + return NIL; 1.934 + } 1.935 + reparse = (sbuf.st_size != LOCAL->filesize); 1.936 + } 1.937 + /* parse if mailbox changed */ 1.938 + if ((LOCAL->ddirty || reparse) && mmdf_parse (stream,&lock,LOCK_EX)) { 1.939 + /* force checkpoint if double-dirty */ 1.940 + if (LOCAL->ddirty) mmdf_rewrite (stream,NIL,&lock,NIL); 1.941 + /* unlock mailbox */ 1.942 + else mmdf_unlock (LOCAL->fd,stream,&lock); 1.943 + mail_unlock (stream); /* and stream */ 1.944 + /* done with critical */ 1.945 + MM_NOCRITICAL (stream); 1.946 + } 1.947 + } 1.948 + } 1.949 + return LOCAL ? LONGT : NIL; /* return if still alive */ 1.950 +} 1.951 + 1.952 +/* MMDF mail check mailbox 1.953 + * Accepts: MAIL stream 1.954 + */ 1.955 + 1.956 +void mmdf_check (MAILSTREAM *stream) 1.957 +{ 1.958 + DOTLOCK lock; 1.959 + /* parse and lock mailbox */ 1.960 + if (LOCAL && (LOCAL->ld >= 0) && !stream->lock && 1.961 + mmdf_parse (stream,&lock,LOCK_EX)) { 1.962 + /* any unsaved changes? */ 1.963 + if (LOCAL->dirty && mmdf_rewrite (stream,NIL,&lock,NIL)) { 1.964 + if (!stream->silent) MM_LOG ("Checkpoint completed",NIL); 1.965 + } 1.966 + /* no checkpoint needed, just unlock */ 1.967 + else mmdf_unlock (LOCAL->fd,stream,&lock); 1.968 + mail_unlock (stream); /* unlock the stream */ 1.969 + MM_NOCRITICAL (stream); /* done with critical */ 1.970 + } 1.971 +} 1.972 + 1.973 + 1.974 +/* MMDF mail expunge mailbox 1.975 + * Accepts: MAIL stream 1.976 + * sequence to expunge if non-NIL 1.977 + * expunge options 1.978 + * Returns: T, always 1.979 + */ 1.980 + 1.981 +long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options) 1.982 +{ 1.983 + long ret; 1.984 + unsigned long i; 1.985 + DOTLOCK lock; 1.986 + char *msg = NIL; 1.987 + if (ret = (sequence ? ((options & EX_UID) ? 1.988 + mail_uid_sequence (stream,sequence) : 1.989 + mail_sequence (stream,sequence)) : LONGT) && 1.990 + LOCAL && (LOCAL->ld >= 0) && !stream->lock && 1.991 + mmdf_parse (stream,&lock,LOCK_EX)) { 1.992 + /* check expunged messages if not dirty */ 1.993 + for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) { 1.994 + MESSAGECACHE *elt = mail_elt (stream,i); 1.995 + if (mail_elt (stream,i)->deleted) LOCAL->dirty = T; 1.996 + } 1.997 + if (!LOCAL->dirty) { /* not dirty and no expunged messages */ 1.998 + mmdf_unlock (LOCAL->fd,stream,&lock); 1.999 + msg = "No messages deleted, so no update needed"; 1.1000 + } 1.1001 + else if (mmdf_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) { 1.1002 + if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i); 1.1003 + else msg = "Mailbox checkpointed, but no messages expunged"; 1.1004 + } 1.1005 + /* rewrite failed */ 1.1006 + else mmdf_unlock (LOCAL->fd,stream,&lock); 1.1007 + mail_unlock (stream); /* unlock the stream */ 1.1008 + MM_NOCRITICAL (stream); /* done with critical */ 1.1009 + if (msg && !stream->silent) MM_LOG (msg,NIL); 1.1010 + } 1.1011 + else if (!stream->silent) 1.1012 + MM_LOG ("Expunge ignored on readonly mailbox",WARN); 1.1013 + return ret; 1.1014 +} 1.1015 + 1.1016 +/* MMDF mail copy message(s) 1.1017 + * Accepts: MAIL stream 1.1018 + * sequence 1.1019 + * destination mailbox 1.1020 + * copy options 1.1021 + * Returns: T if copy successful, else NIL 1.1022 + */ 1.1023 + 1.1024 +long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) 1.1025 +{ 1.1026 + struct stat sbuf; 1.1027 + int fd; 1.1028 + char *s,file[MAILTMPLEN]; 1.1029 + DOTLOCK lock; 1.1030 + time_t tp[2]; 1.1031 + unsigned long i,j; 1.1032 + MESSAGECACHE *elt; 1.1033 + long ret = T; 1.1034 + mailproxycopy_t pc = 1.1035 + (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); 1.1036 + copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? 1.1037 + NIL : mail_parameters (NIL,GET_COPYUID,NIL)); 1.1038 + SEARCHSET *source = cu ? mail_newsearchset () : NIL; 1.1039 + SEARCHSET *dest = cu ? mail_newsearchset () : NIL; 1.1040 + MAILSTREAM *tstream = NIL; 1.1041 + if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : 1.1042 + mail_sequence (stream,sequence))) return NIL; 1.1043 + /* make sure destination is valid */ 1.1044 + if (!(mmdf_valid (mailbox) || !errno)) 1.1045 + switch (errno) { 1.1046 + case ENOENT: /* no such file? */ 1.1047 + if (compare_cstring (mailbox,"INBOX")) { 1.1048 + MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); 1.1049 + return NIL; 1.1050 + } 1.1051 + if (pc) return (*pc) (stream,sequence,mailbox,options); 1.1052 + mmdf_create (NIL,"INBOX");/* create empty INBOX */ 1.1053 + case EACCES: /* file protected */ 1.1054 + sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox); 1.1055 + MM_LOG (LOCAL->buf,ERROR); 1.1056 + return NIL; 1.1057 + case EINVAL: 1.1058 + if (pc) return (*pc) (stream,sequence,mailbox,options); 1.1059 + sprintf (LOCAL->buf,"Invalid MMDF-format mailbox name: %.80s",mailbox); 1.1060 + MM_LOG (LOCAL->buf,ERROR); 1.1061 + return NIL; 1.1062 + default: 1.1063 + if (pc) return (*pc) (stream,sequence,mailbox,options); 1.1064 + sprintf (LOCAL->buf,"Not a MMDF-format mailbox: %.80s",mailbox); 1.1065 + MM_LOG (LOCAL->buf,ERROR); 1.1066 + return NIL; 1.1067 + } 1.1068 + 1.1069 + /* try to open rewrite for UIDPLUS */ 1.1070 + if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox, 1.1071 + OP_SILENT|OP_NOKOD)) && tstream->rdonly) 1.1072 + tstream = mail_close (tstream); 1.1073 + if (cu && !tstream) { /* wanted a COPYUID? */ 1.1074 + sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s", 1.1075 + mailbox); 1.1076 + MM_LOG (LOCAL->buf,WARN); 1.1077 + cu = NIL; /* don't try to do COPYUID */ 1.1078 + } 1.1079 + LOCAL->buf[0] = '\0'; 1.1080 + MM_CRITICAL (stream); /* go critical */ 1.1081 + if ((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND, 1.1082 + (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL), 1.1083 + &lock,LOCK_EX)) < 0) { 1.1084 + MM_NOCRITICAL (stream); /* done with critical */ 1.1085 + sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno)); 1.1086 + MM_LOG (LOCAL->buf,ERROR); /* log the error */ 1.1087 + return NIL; /* failed */ 1.1088 + } 1.1089 + fstat (fd,&sbuf); /* get current file size */ 1.1090 + /* write all requested messages to mailbox */ 1.1091 + for (i = 1; ret && (i <= stream->nmsgs); i++) 1.1092 + if ((elt = mail_elt (stream,i))->sequence) { 1.1093 + lseek (LOCAL->fd,elt->private.special.offset,L_SET); 1.1094 + read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); 1.1095 + if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL; 1.1096 + else { /* internal header succeeded */ 1.1097 + s = mmdf_header (stream,i,&j,FT_INTERNAL); 1.1098 + /* header size, sans trailing newline */ 1.1099 + if (j && (s[j - 2] == '\n')) j--; 1.1100 + if (write (fd,s,j) < 0) ret = NIL; 1.1101 + else { /* message header succeeded */ 1.1102 + j = tstream ? /* write UIDPLUS data if have readwrite */ 1.1103 + mmdf_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) : 1.1104 + mmdf_xstatus (stream,LOCAL->buf,elt,NIL,NIL); 1.1105 + if (write (fd,LOCAL->buf,j) < 0) ret = NIL; 1.1106 + else { /* message status succeeded */ 1.1107 + s = mmdf_text_work (stream,elt,&j,FT_INTERNAL); 1.1108 + if ((write (fd,s,j) < 0) || (write (fd,mmdfhdr,MMDFHDRLEN) < 0)) 1.1109 + ret = NIL; 1.1110 + else if (cu) { /* need to pass back new UID? */ 1.1111 + mail_append_set (source,mail_uid (stream,i)); 1.1112 + mail_append_set (dest,tstream->uid_last); 1.1113 + } 1.1114 + } 1.1115 + } 1.1116 + } 1.1117 + } 1.1118 + 1.1119 + if (!ret || fsync (fd)) { /* force out the update */ 1.1120 + sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno)); 1.1121 + ftruncate (fd,sbuf.st_size); 1.1122 + ret = NIL; 1.1123 + } 1.1124 + /* force UIDVALIDITY assignment now */ 1.1125 + if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0); 1.1126 + /* return sets if doing COPYUID */ 1.1127 + if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest); 1.1128 + else { /* flush any sets we may have built */ 1.1129 + mail_free_searchset (&source); 1.1130 + mail_free_searchset (&dest); 1.1131 + } 1.1132 + tp[1] = time (0); /* set mtime to now */ 1.1133 + if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ 1.1134 + else tp[0] = /* else preserve \Marked status */ 1.1135 + ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? 1.1136 + sbuf.st_atime : tp[1]; 1.1137 + utime (file,tp); /* set the times */ 1.1138 + mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */ 1.1139 + if (tstream) { /* update last UID if we can */ 1.1140 + MMDFLOCAL *local = (MMDFLOCAL *) tstream->local; 1.1141 + local->dirty = T; /* do a rewrite */ 1.1142 + local->appending = T; /* but not at the cost of marking as old */ 1.1143 + tstream = mail_close (tstream); 1.1144 + } 1.1145 + /* log the error */ 1.1146 + if (!ret) MM_LOG (LOCAL->buf,ERROR); 1.1147 + /* delete if requested message */ 1.1148 + else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++) 1.1149 + if ((elt = mail_elt (stream,i))->sequence) 1.1150 + elt->deleted = elt->private.dirty = LOCAL->dirty = T; 1.1151 + MM_NOCRITICAL (stream); /* release critical */ 1.1152 + return ret; 1.1153 +} 1.1154 + 1.1155 +/* MMDF mail append message from stringstruct 1.1156 + * Accepts: MAIL stream 1.1157 + * destination mailbox 1.1158 + * append callback 1.1159 + * data for callback 1.1160 + * Returns: T if append successful, else NIL 1.1161 + */ 1.1162 + 1.1163 +#define BUFLEN 8*MAILTMPLEN 1.1164 + 1.1165 +long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) 1.1166 +{ 1.1167 + struct stat sbuf; 1.1168 + int fd; 1.1169 + unsigned long i; 1.1170 + char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN]; 1.1171 + time_t tp[2]; 1.1172 + FILE *sf,*df; 1.1173 + MESSAGECACHE elt; 1.1174 + DOTLOCK lock; 1.1175 + STRING *message; 1.1176 + unsigned long uidlocation = 0; 1.1177 + appenduid_t au = (appenduid_t) 1.1178 + (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL : 1.1179 + mail_parameters (NIL,GET_APPENDUID,NIL)); 1.1180 + SEARCHSET *dst = au ? mail_newsearchset () : NIL; 1.1181 + long ret = LONGT; 1.1182 + MAILSTREAM *tstream = NIL; 1.1183 + /* default stream to prototype */ 1.1184 + if (!stream) { /* stream specified? */ 1.1185 + stream = &mmdfproto; /* no, default stream to prototype */ 1.1186 + for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i) 1.1187 + fs_give ((void **) &stream->user_flags[i]); 1.1188 + } 1.1189 + if (!mmdf_valid (mailbox)) switch (errno) { 1.1190 + case ENOENT: /* no such file? */ 1.1191 + if (compare_cstring (mailbox,"INBOX")) { 1.1192 + MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); 1.1193 + return NIL; 1.1194 + } 1.1195 + mmdf_create (NIL,"INBOX"); /* create empty INBOX */ 1.1196 + case 0: /* merely empty file? */ 1.1197 + tstream = stream; 1.1198 + break; 1.1199 + case EACCES: /* file protected */ 1.1200 + sprintf (tmp,"Can't access destination: %.80s",mailbox); 1.1201 + MM_LOG (tmp,ERROR); 1.1202 + return NIL; 1.1203 + case EINVAL: 1.1204 + sprintf (tmp,"Invalid MMDF-format mailbox name: %.80s",mailbox); 1.1205 + MM_LOG (tmp,ERROR); 1.1206 + return NIL; 1.1207 + default: 1.1208 + sprintf (tmp,"Not a MMDF-format mailbox: %.80s",mailbox); 1.1209 + MM_LOG (tmp,ERROR); 1.1210 + return NIL; 1.1211 + } 1.1212 + /* get sniffing stream for keywords */ 1.1213 + else if (!(tstream = mail_open (NIL,mailbox, 1.1214 + OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) { 1.1215 + sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox); 1.1216 + MM_LOG (tmp,ERROR); 1.1217 + return NIL; 1.1218 + } 1.1219 + 1.1220 + /* get first message */ 1.1221 + if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL; 1.1222 + if (!(sf = tmpfile ())) { /* must have scratch file */ 1.1223 + sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ()); 1.1224 + if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) { 1.1225 + sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno)); 1.1226 + MM_LOG (tmp,ERROR); 1.1227 + return NIL; 1.1228 + } 1.1229 + unlink (tmp); 1.1230 + } 1.1231 + do { /* parse date */ 1.1232 + if (!date) rfc822_date (date = tmp); 1.1233 + if (!mail_parse_date (&elt,date)) { 1.1234 + sprintf (tmp,"Bad date in append: %.80s",date); 1.1235 + MM_LOG (tmp,ERROR); 1.1236 + } 1.1237 + else { /* user wants to suppress time zones? */ 1.1238 + if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) { 1.1239 + time_t when = mail_longdate (&elt); 1.1240 + date = ctime (&when); /* use traditional date */ 1.1241 + } 1.1242 + /* use POSIX-style date */ 1.1243 + else date = mail_cdate (tmp,&elt); 1.1244 + if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR); 1.1245 + else if (!mmdf_collect_msg (tstream,sf,flags,date,message)) { 1.1246 + sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno)); 1.1247 + MM_LOG (tmp,ERROR); 1.1248 + } 1.1249 + /* get next message */ 1.1250 + else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue; 1.1251 + } 1.1252 + fclose (sf); /* punt scratch file */ 1.1253 + return NIL; /* give up */ 1.1254 + } while (message); /* until no more messages */ 1.1255 + if (fflush (sf)) { 1.1256 + sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno)); 1.1257 + MM_LOG (tmp,ERROR); 1.1258 + fclose (sf); /* punt scratch file */ 1.1259 + return NIL; /* give up */ 1.1260 + } 1.1261 + i = ftell (sf); /* size of scratch file */ 1.1262 + if (tstream != stream) tstream = mail_close (tstream); 1.1263 + 1.1264 + MM_CRITICAL (stream); /* go critical */ 1.1265 + /* try to open readwrite for UIDPLUS */ 1.1266 + if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox, 1.1267 + OP_SILENT|OP_NOKOD)) && tstream->rdonly) 1.1268 + tstream = mail_close (tstream); 1.1269 + if (au && !tstream) { /* wanted an APPENDUID? */ 1.1270 + sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox); 1.1271 + MM_LOG (tmp,WARN); 1.1272 + au = NIL; 1.1273 + } 1.1274 + if (((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND, 1.1275 + (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL), 1.1276 + &lock,LOCK_EX)) < 0) || 1.1277 + !(df = fdopen (fd,"ab"))) { 1.1278 + MM_NOCRITICAL (stream); /* done with critical */ 1.1279 + sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); 1.1280 + MM_LOG (tmp,ERROR); 1.1281 + return NIL; 1.1282 + } 1.1283 + fstat (fd,&sbuf); /* get current file size */ 1.1284 + rewind (sf); 1.1285 + tp[1] = time (0); /* set mtime to now */ 1.1286 + /* write all messages */ 1.1287 + if (!mmdf_append_msgs (tstream,sf,df,au ? dst : NIL) || 1.1288 + (fflush (df) == EOF) || fsync (fd)) { 1.1289 + sprintf (buf,"Message append failed: %s",strerror (errno)); 1.1290 + MM_LOG (buf,ERROR); 1.1291 + ftruncate (fd,sbuf.st_size); 1.1292 + tp[0] = /* preserve \Marked status */ 1.1293 + ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ? 1.1294 + sbuf.st_atime : tp[1]; 1.1295 + ret = NIL; /* return error */ 1.1296 + } 1.1297 + else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */ 1.1298 + utime (file,tp); /* set the times */ 1.1299 + fclose (sf); /* done with scratch file */ 1.1300 + /* force UIDVALIDITY assignment now */ 1.1301 + if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0); 1.1302 + /* return sets if doing APPENDUID */ 1.1303 + if (au && ret) (*au) (mailbox,tstream->uid_validity,dst); 1.1304 + else mail_free_searchset (&dst); 1.1305 + mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */ 1.1306 + fclose (df); 1.1307 + if (tstream) { /* update last UID if we can */ 1.1308 + MMDFLOCAL *local = (MMDFLOCAL *) tstream->local; 1.1309 + local->dirty = T; /* do a rewrite */ 1.1310 + local->appending = T; /* but not at the cost of marking as old */ 1.1311 + tstream = mail_close (tstream); 1.1312 + } 1.1313 + MM_NOCRITICAL (stream); /* release critical */ 1.1314 + return ret; 1.1315 +} 1.1316 + 1.1317 +/* Collect and write single message to append scratch file 1.1318 + * Accepts: MAIL stream 1.1319 + * scratch file 1.1320 + * flags 1.1321 + * date 1.1322 + * message stringstruct 1.1323 + * Returns: NIL if write error, else T 1.1324 + */ 1.1325 + 1.1326 +int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date, 1.1327 + STRING *msg) 1.1328 +{ 1.1329 + unsigned char *s,*t; 1.1330 + unsigned long uf; 1.1331 + long f = mail_parse_flags (stream,flags,&uf); 1.1332 + /* write metadata, note date ends with NL */ 1.1333 + if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL; 1.1334 + while (uf) /* write user flags */ 1.1335 + if ((s = stream->user_flags[find_rightmost_bit (&uf)]) && 1.1336 + (fprintf (sf," %s",s) < 0)) return NIL; 1.1337 + if (putc ('\n',sf) == EOF) return NIL; 1.1338 + while (SIZE (msg)) { /* copy text to scratch file */ 1.1339 + for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s) 1.1340 + if (!*s) *s = 0x80; /* disallow NUL */ 1.1341 + /* write buffered text */ 1.1342 + if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize) 1.1343 + SETPOS (msg,GETPOS (msg) + msg->cursize); 1.1344 + else return NIL; /* failed */ 1.1345 + } 1.1346 + /* write trailing newline and return */ 1.1347 + return (putc ('\n',sf) == EOF) ? NIL : T; 1.1348 +} 1.1349 + 1.1350 +/* Append messages from scratch file to mailbox 1.1351 + * Accepts: MAIL stream 1.1352 + * source file 1.1353 + * destination file 1.1354 + * uidset to update if non-NIL 1.1355 + * Returns: T if success, NIL if failure 1.1356 + */ 1.1357 + 1.1358 +int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set) 1.1359 +{ 1.1360 + int c; 1.1361 + long f; 1.1362 + unsigned long i,j; 1.1363 + char *x,tmp[MAILTMPLEN]; 1.1364 + int hdrp = T; 1.1365 + /* get message metadata line */ 1.1366 + while (fgets (tmp,MAILTMPLEN,sf)) { 1.1367 + if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL; 1.1368 + f = strtol (tmp,&x,10); /* get flags */ 1.1369 + if (!((*x++ == ' ') && isdigit (*x))) return NIL; 1.1370 + i = strtoul (x,&x,10); /* get message size */ 1.1371 + if ((*x++ != ' ') || /* build initial header */ 1.1372 + (fprintf (df,"%sFrom %s@%s %sStatus: ",mmdfhdr,myusername(), 1.1373 + mylocalhost(),x) < 0) || 1.1374 + (f&fSEEN && (putc ('R',df) == EOF)) || 1.1375 + (fputs ("\nX-Status: ",df) == EOF) || 1.1376 + (f&fDELETED && (putc ('D',df) == EOF)) || 1.1377 + (f&fFLAGGED && (putc ('F',df) == EOF)) || 1.1378 + (f&fANSWERED && (putc ('A',df) == EOF)) || 1.1379 + (f&fDRAFT && (putc ('T',df) == EOF)) || 1.1380 + (fputs ("\nX-Keywords:",df) == EOF)) return NIL; 1.1381 + /* copy keywords */ 1.1382 + while ((c = getc (sf)) != '\n') switch (c) { 1.1383 + case EOF: 1.1384 + return NIL; 1.1385 + default: 1.1386 + if (putc (c,df) == EOF) return NIL; 1.1387 + } 1.1388 + if ((putc ('\n',df) == EOF) || 1.1389 + (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0))) 1.1390 + return NIL; 1.1391 + 1.1392 + for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) { 1.1393 + /* get read line length */ 1.1394 + if (i < (j = strlen (tmp))) fatal ("mmdf_append_msgs overrun"); 1.1395 + i -= j; /* number of bytes left */ 1.1396 + /* squish out ^A and CRs (note copies NUL) */ 1.1397 + for (x = tmp; x = strpbrk (x,"\01\r"); --j) memmove (x,x+1,j-(x-tmp)); 1.1398 + if (!j) continue; /* do nothing if line emptied */ 1.1399 + /* start of line? */ 1.1400 + if ((c == '\n')) switch (tmp[0]) { 1.1401 + case 'S': case 's': /* possible "Status:" */ 1.1402 + if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) && 1.1403 + ((tmp[2] == 'a') || (tmp[2] == 'A')) && 1.1404 + ((tmp[3] == 't') || (tmp[3] == 'T')) && 1.1405 + ((tmp[4] == 'u') || (tmp[4] == 'U')) && 1.1406 + ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') && 1.1407 + (fputs ("X-Original-",df) == EOF)) return NIL; 1.1408 + break; 1.1409 + case 'X': case 'x': /* possible X-??? header */ 1.1410 + if (hdrp && (tmp[1] == '-') && 1.1411 + /* possible X-UID: */ 1.1412 + (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) && 1.1413 + ((tmp[3] == 'I') || (tmp[3] == 'i')) && 1.1414 + ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) || 1.1415 + /* possible X-IMAP: */ 1.1416 + ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) && 1.1417 + ((tmp[3] == 'M') || (tmp[3] == 'm')) && 1.1418 + ((tmp[4] == 'A') || (tmp[4] == 'a')) && 1.1419 + ((tmp[5] == 'P') || (tmp[5] == 'p')) && 1.1420 + ((tmp[6] == ':') || 1.1421 + /* or X-IMAPbase: */ 1.1422 + ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) && 1.1423 + ((tmp[7] == 'a') || (tmp[7] == 'A')) && 1.1424 + ((tmp[8] == 's') || (tmp[8] == 'S')) && 1.1425 + ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) || 1.1426 + /* possible X-Status: */ 1.1427 + ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) && 1.1428 + ((tmp[3] == 't') || (tmp[3] == 'T')) && 1.1429 + ((tmp[4] == 'a') || (tmp[4] == 'A')) && 1.1430 + ((tmp[5] == 't') || (tmp[5] == 'T')) && 1.1431 + ((tmp[6] == 'u') || (tmp[6] == 'U')) && 1.1432 + ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) || 1.1433 + /* possible X-Keywords: */ 1.1434 + ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) && 1.1435 + ((tmp[3] == 'e') || (tmp[3] == 'E')) && 1.1436 + ((tmp[4] == 'y') || (tmp[4] == 'Y')) && 1.1437 + ((tmp[5] == 'w') || (tmp[5] == 'W')) && 1.1438 + ((tmp[6] == 'o') || (tmp[6] == 'O')) && 1.1439 + ((tmp[7] == 'r') || (tmp[7] == 'R')) && 1.1440 + ((tmp[8] == 'd') || (tmp[8] == 'D')) && 1.1441 + ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) && 1.1442 + (fputs ("X-Original-",df) == EOF)) return NIL; 1.1443 + break; 1.1444 + case '\n': /* blank line */ 1.1445 + hdrp = NIL; 1.1446 + break; 1.1447 + default: /* nothing to do */ 1.1448 + break; 1.1449 + } 1.1450 + /* just write the line */ 1.1451 + if (fwrite (tmp,1,j,df) != j) return NIL; 1.1452 + } 1.1453 + /* make sure read entire msg & wrote trailer */ 1.1454 + if (i || (fputs (mmdfhdr,df) == EOF)) return NIL; 1.1455 + /* update set */ 1.1456 + if (stream) mail_append_set (set,stream->uid_last); 1.1457 + } 1.1458 + return T; 1.1459 +} 1.1460 + 1.1461 +/* Internal routines */ 1.1462 + 1.1463 + 1.1464 +/* MMDF mail abort stream 1.1465 + * Accepts: MAIL stream 1.1466 + */ 1.1467 + 1.1468 +void mmdf_abort (MAILSTREAM *stream) 1.1469 +{ 1.1470 + if (LOCAL) { /* only if a file is open */ 1.1471 + if (LOCAL->fd >= 0) close (LOCAL->fd); 1.1472 + if (LOCAL->ld >= 0) { /* have a mailbox lock? */ 1.1473 + flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */ 1.1474 + close (LOCAL->ld); /* close the lock file */ 1.1475 + unlink (LOCAL->lname); /* and delete it */ 1.1476 + } 1.1477 + if (LOCAL->lname) fs_give ((void **) &LOCAL->lname); 1.1478 + /* free local text buffers */ 1.1479 + if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); 1.1480 + if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); 1.1481 + if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf); 1.1482 + if (LOCAL->line) fs_give ((void **) &LOCAL->line); 1.1483 + /* nuke the local data */ 1.1484 + fs_give ((void **) &stream->local); 1.1485 + stream->dtb = NIL; /* log out the DTB */ 1.1486 + } 1.1487 +} 1.1488 + 1.1489 +/* MMDF open and lock mailbox 1.1490 + * Accepts: file name to open/lock 1.1491 + * file open mode 1.1492 + * destination buffer for lock file name 1.1493 + * type of locking operation (LOCK_SH or LOCK_EX) 1.1494 + */ 1.1495 + 1.1496 +int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op) 1.1497 +{ 1.1498 + int fd; 1.1499 + blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); 1.1500 + (*bn) (BLOCK_FILELOCK,NIL); 1.1501 + /* try locking the easy way */ 1.1502 + if (dotlock_lock (file,lock,-1)) { 1.1503 + /* got dotlock file, easy open */ 1.1504 + if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); 1.1505 + else dotlock_unlock (lock); /* open failed, free the dotlock */ 1.1506 + } 1.1507 + /* no dot lock file, open file now */ 1.1508 + else if ((fd = open (file,flags,mode)) >= 0) { 1.1509 + /* try paranoid way to make a dot lock file */ 1.1510 + if (dotlock_lock (file,lock,fd)) { 1.1511 + close (fd); /* get fresh fd in case of timing race */ 1.1512 + if ((fd = open (file,flags,mode)) >= 0) flock (fd,op); 1.1513 + /* open failed, free the dotlock */ 1.1514 + else dotlock_unlock (lock); 1.1515 + } 1.1516 + else flock (fd,op); /* paranoid way failed, just flock() it */ 1.1517 + } 1.1518 + (*bn) (BLOCK_NONE,NIL); 1.1519 + return fd; 1.1520 +} 1.1521 + 1.1522 +/* MMDF unlock and close mailbox 1.1523 + * Accepts: file descriptor 1.1524 + * (optional) mailbox stream to check atime/mtime 1.1525 + * (optional) lock file name 1.1526 + */ 1.1527 + 1.1528 +void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock) 1.1529 +{ 1.1530 + if (stream) { /* need to muck with times? */ 1.1531 + struct stat sbuf; 1.1532 + time_t tp[2]; 1.1533 + time_t now = time (0); 1.1534 + fstat (fd,&sbuf); /* get file times */ 1.1535 + if (LOCAL->ld >= 0) { /* yes, readwrite session? */ 1.1536 + tp[0] = now; /* set atime to now */ 1.1537 + /* set mtime to (now - 1) if necessary */ 1.1538 + tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; 1.1539 + } 1.1540 + else if (stream->recent) { /* readonly with recent messages */ 1.1541 + if ((sbuf.st_atime >= sbuf.st_mtime) || 1.1542 + (sbuf.st_atime >= sbuf.st_ctime)) 1.1543 + /* keep past mtime, whack back atime */ 1.1544 + tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1; 1.1545 + else now = 0; /* no time change needed */ 1.1546 + } 1.1547 + /* readonly with no recent messages */ 1.1548 + else if ((sbuf.st_atime < sbuf.st_mtime) || 1.1549 + (sbuf.st_atime < sbuf.st_ctime)) { 1.1550 + tp[0] = now; /* set atime to now */ 1.1551 + /* set mtime to (now - 1) if necessary */ 1.1552 + tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1; 1.1553 + } 1.1554 + else now = 0; /* no time change needed */ 1.1555 + /* set the times, note change */ 1.1556 + if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; 1.1557 + } 1.1558 + flock (fd,LOCK_UN); /* release flock'ers */ 1.1559 + if (!stream) close (fd); /* close the file if no stream */ 1.1560 + dotlock_unlock (lock); /* flush the lock file if any */ 1.1561 +} 1.1562 + 1.1563 +/* MMDF mail parse and lock mailbox 1.1564 + * Accepts: MAIL stream 1.1565 + * space to write lock file name 1.1566 + * type of locking operation 1.1567 + * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure 1.1568 + */ 1.1569 + 1.1570 +int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op) 1.1571 +{ 1.1572 + int ti,zn,m; 1.1573 + unsigned long i,j,k; 1.1574 + unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30]; 1.1575 + int retain = T; 1.1576 + unsigned long nmsgs = stream->nmsgs; 1.1577 + unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0; 1.1578 + unsigned long recent = stream->recent; 1.1579 + unsigned long oldnmsgs = stream->nmsgs; 1.1580 + short silent = stream->silent; 1.1581 + short pseudoseen = NIL; 1.1582 + struct stat sbuf; 1.1583 + STRING bs; 1.1584 + FDDATA d; 1.1585 + MESSAGECACHE *elt; 1.1586 + mail_lock (stream); /* guard against recursion or pingers */ 1.1587 + /* toss out previous descriptor */ 1.1588 + if (LOCAL->fd >= 0) close (LOCAL->fd); 1.1589 + MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */ 1.1590 + if ((LOCAL->fd = mmdf_lock (stream->mailbox,(LOCAL->ld >= 0) ? 1.1591 + O_RDWR : O_RDONLY, 1.1592 + (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL), 1.1593 + lock,op)) < 0) { 1.1594 + sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno)); 1.1595 + MM_LOG (tmp,ERROR); 1.1596 + mmdf_abort (stream); 1.1597 + mail_unlock (stream); 1.1598 + MM_NOCRITICAL (stream); /* done with critical */ 1.1599 + return NIL; 1.1600 + } 1.1601 + fstat (LOCAL->fd,&sbuf); /* get status */ 1.1602 + /* validate change in size */ 1.1603 + if (sbuf.st_size < LOCAL->filesize) { 1.1604 + sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted", 1.1605 + (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size); 1.1606 + MM_LOG (tmp,ERROR); /* this is pretty bad */ 1.1607 + mmdf_unlock (LOCAL->fd,stream,lock); 1.1608 + mmdf_abort (stream); 1.1609 + mail_unlock (stream); 1.1610 + MM_NOCRITICAL (stream); /* done with critical */ 1.1611 + return NIL; 1.1612 + } 1.1613 + 1.1614 + /* new data? */ 1.1615 + else if (i = sbuf.st_size - LOCAL->filesize) { 1.1616 + d.fd = LOCAL->fd; /* yes, set up file descriptor */ 1.1617 + d.pos = LOCAL->filesize; /* get to that position in the file */ 1.1618 + d.chunk = LOCAL->buf; /* initial buffer chunk */ 1.1619 + d.chunksize = CHUNKSIZE; /* file chunk size */ 1.1620 + INIT (&bs,fd_string,&d,i); /* initialize stringstruct */ 1.1621 + /* skip leading whitespace for broken MTAs */ 1.1622 + while (((c = CHR (&bs)) == '\n') || (c == '\r') || 1.1623 + (c == ' ') || (c == '\t')) SNX (&bs); 1.1624 + if (SIZE (&bs)) { /* read new data */ 1.1625 + /* remember internal header position */ 1.1626 + j = LOCAL->filesize + GETPOS (&bs); 1.1627 + s = mmdf_mbxline (stream,&bs,&i); 1.1628 + stream->silent = T; /* quell main program new message events */ 1.1629 + do { /* read MMDF header */ 1.1630 + if (!(i && ISMMDF (s))){/* see if valid MMDF header */ 1.1631 + sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s", 1.1632 + (char *) s); 1.1633 + /* see if we can back up to a line */ 1.1634 + if (i && (j > MMDFHDRLEN)) { 1.1635 + SETPOS (&bs,j -= MMDFHDRLEN); 1.1636 + /* read previous line */ 1.1637 + s = mmdf_mbxline (stream,&bs,&i); 1.1638 + /* kill the error if it looks good */ 1.1639 + if (i && ISMMDF (s)) tmp[0] = '\0'; 1.1640 + } 1.1641 + if (tmp[0]) { 1.1642 + MM_LOG (tmp,ERROR); 1.1643 + mmdf_unlock (LOCAL->fd,stream,lock); 1.1644 + mmdf_abort (stream); 1.1645 + mail_unlock (stream); 1.1646 + MM_NOCRITICAL (stream); 1.1647 + return NIL; 1.1648 + } 1.1649 + } 1.1650 + /* instantiate first new message */ 1.1651 + mail_exists (stream,++nmsgs); 1.1652 + (elt = mail_elt (stream,nmsgs))->valid = T; 1.1653 + recent++; /* assume recent by default */ 1.1654 + elt->recent = T; 1.1655 + /* note position/size of internal header */ 1.1656 + elt->private.special.offset = j; 1.1657 + elt->private.special.text.size = i; 1.1658 + 1.1659 + s = mmdf_mbxline (stream,&bs,&i); 1.1660 + ti = 0; /* assume not a valid date */ 1.1661 + zn = 0,t = NIL; 1.1662 + if (i) VALID (s,t,ti,zn); 1.1663 + if (ti) { /* generate plausible IMAPish date string */ 1.1664 + /* this is also part of header */ 1.1665 + elt->private.special.text.size += i; 1.1666 + date[2] = date[6] = date[20] = '-'; date[11] = ' '; 1.1667 + date[14] = date[17] = ':'; 1.1668 + /* dd */ 1.1669 + date[0] = t[ti - 2]; date[1] = t[ti - 1]; 1.1670 + /* mmm */ 1.1671 + date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4]; 1.1672 + /* hh */ 1.1673 + date[12] = t[ti + 1]; date[13] = t[ti + 2]; 1.1674 + /* mm */ 1.1675 + date[15] = t[ti + 4]; date[16] = t[ti + 5]; 1.1676 + if (t[ti += 6]==':'){ /* ss */ 1.1677 + date[18] = t[++ti]; date[19] = t[++ti]; 1.1678 + ti++; /* move to space */ 1.1679 + } 1.1680 + else date[18] = date[19] = '0'; 1.1681 + /* yy -- advance over timezone if necessary */ 1.1682 + if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4); 1.1683 + date[7] = t[ti + 1]; date[8] = t[ti + 2]; 1.1684 + date[9] = t[ti + 3]; date[10] = t[ti + 4]; 1.1685 + /* zzz */ 1.1686 + t = zn ? (t + zn + 1) : (unsigned char *) "LCL"; 1.1687 + date[21] = *t++; date[22] = *t++; date[23] = *t++; 1.1688 + if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0'; 1.1689 + else { /* numeric time zone */ 1.1690 + date[24] = *t++; date[25] = *t++; 1.1691 + date[26] = '\0'; date[20] = ' '; 1.1692 + } 1.1693 + /* set internal date */ 1.1694 + if (!mail_parse_date (elt,date)) { 1.1695 + sprintf (tmp,"Unable to parse internal date: %s",(char *) date); 1.1696 + MM_LOG (tmp,WARN); 1.1697 + } 1.1698 + } 1.1699 + else { /* make date from file date */ 1.1700 + struct tm *tm = gmtime (&sbuf.st_mtime); 1.1701 + elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; 1.1702 + elt->year = tm->tm_year + 1900 - BASEYEAR; 1.1703 + elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; 1.1704 + elt->seconds = tm->tm_sec; 1.1705 + elt->zhours = 0; elt->zminutes = 0; 1.1706 + t = NIL; /* suppress line read */ 1.1707 + } 1.1708 + /* header starts here */ 1.1709 + elt->private.msg.header.offset = elt->private.special.text.size; 1.1710 + 1.1711 + do { /* look for message body */ 1.1712 + j = GETPOS (&bs); /* note position before line */ 1.1713 + if (t) s = t = mmdf_mbxline (stream,&bs,&i); 1.1714 + else t = s; /* this line read was suppressed */ 1.1715 + if (ISMMDF (s)) { /* found terminator in header? */ 1.1716 + SETPOS (&bs,j); /* oops, back up before line */ 1.1717 + /* must insert a newline */ 1.1718 + elt->private.spare.data++; 1.1719 + break; /* punt */ 1.1720 + } 1.1721 + /* this line is part of header */ 1.1722 + elt->private.msg.header.text.size += i; 1.1723 + if (i) switch (*s) { /* check header lines */ 1.1724 + case 'X': /* possible X-???: line */ 1.1725 + if (s[1] == '-') { /* must be immediately followed by hyphen */ 1.1726 + /* X-Status: becomes Status: in S case */ 1.1727 + if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' && 1.1728 + s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2; 1.1729 + /* possible X-Keywords */ 1.1730 + else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' && 1.1731 + s[5] == 'w' && s[6] == 'o' && s[7] == 'r' && 1.1732 + s[8] == 'd' && s[9] == 's' && s[10] == ':') { 1.1733 + SIZEDTEXT uf; 1.1734 + retain = NIL; /* don't retain continuation */ 1.1735 + s += 11; /* flush leading whitespace */ 1.1736 + while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){ 1.1737 + while (*s == ' ') s++; 1.1738 + /* find end of keyword */ 1.1739 + if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s); 1.1740 + /* got a keyword? */ 1.1741 + if ((k = (u - s)) && (k <= MAXUSERFLAG)) { 1.1742 + uf.data = (unsigned char *) s; 1.1743 + uf.size = k; 1.1744 + for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j) 1.1745 + if (!compare_csizedtext (stream->user_flags[j],&uf)) { 1.1746 + elt->user_flags |= ((long) 1) << j; 1.1747 + break; 1.1748 + } 1.1749 + } 1.1750 + s = u; /* advance to next keyword */ 1.1751 + } 1.1752 + break; 1.1753 + } 1.1754 + 1.1755 + /* possible X-IMAP */ 1.1756 + else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') && 1.1757 + (s[5] == 'P') && ((m = (s[6] == ':')) || 1.1758 + ((s[6] == 'b') && (s[7] == 'a') && 1.1759 + (s[8] == 's') && (s[9] == 'e') && 1.1760 + (s[10] == ':')))) { 1.1761 + retain = NIL; /* don't retain continuation */ 1.1762 + if ((nmsgs == 1) && !stream->uid_validity) { 1.1763 + /* advance to data */ 1.1764 + s += m ? 7 : 11; 1.1765 + /* flush whitespace */ 1.1766 + while (*s == ' ') s++; 1.1767 + j = 0; /* slurp UID validity */ 1.1768 + /* found a digit? */ 1.1769 + while (isdigit (*s)) { 1.1770 + j *= 10; /* yes, add it in */ 1.1771 + j += *s++ - '0'; 1.1772 + } 1.1773 + /* flush whitespace */ 1.1774 + while (*s == ' ') s++; 1.1775 + /* must have valid UID validity and UID last */ 1.1776 + if (j && isdigit (*s)) { 1.1777 + /* pseudo-header seen if X-IMAP */ 1.1778 + if (m) pseudoseen = LOCAL->pseudo = T; 1.1779 + /* save UID validity */ 1.1780 + stream->uid_validity = j; 1.1781 + j = 0; /* slurp UID last */ 1.1782 + while (isdigit (*s)) { 1.1783 + j *= 10; /* yes, add it in */ 1.1784 + j += *s++ - '0'; 1.1785 + } 1.1786 + /* save UID last */ 1.1787 + stream->uid_last = j; 1.1788 + /* process keywords */ 1.1789 + for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n')); 1.1790 + s = u,j++) { 1.1791 + /* flush leading whitespace */ 1.1792 + while (*s == ' ') s++; 1.1793 + u = strpbrk (s," \n\r"); 1.1794 + /* got a keyword? */ 1.1795 + if ((j < NUSERFLAGS) && (k = (u - s)) && 1.1796 + (k <= MAXUSERFLAG)) { 1.1797 + if (stream->user_flags[j]) 1.1798 + fs_give ((void **) &stream->user_flags[j]); 1.1799 + stream->user_flags[j] = (char *) fs_get (k + 1); 1.1800 + strncpy (stream->user_flags[j],s,k); 1.1801 + stream->user_flags[j][k] = '\0'; 1.1802 + } 1.1803 + } 1.1804 + } 1.1805 + } 1.1806 + break; 1.1807 + } 1.1808 + 1.1809 + /* possible X-UID */ 1.1810 + else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' && 1.1811 + s[5] == ':') { 1.1812 + retain = NIL; /* don't retain continuation */ 1.1813 + /* only believe if have a UID validity */ 1.1814 + if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) { 1.1815 + s += 6; /* advance to UID value */ 1.1816 + /* flush whitespace */ 1.1817 + while (*s == ' ') s++; 1.1818 + j = 0; 1.1819 + /* found a digit? */ 1.1820 + while (isdigit (*s)) { 1.1821 + j *= 10; /* yes, add it in */ 1.1822 + j += *s++ - '0'; 1.1823 + } 1.1824 + /* flush remainder of line */ 1.1825 + while (*s != '\n') s++; 1.1826 + /* make sure not duplicated */ 1.1827 + if (elt->private.uid) 1.1828 + sprintf (tmp,"Message %lu UID %lu already has UID %lu", 1.1829 + pseudoseen ? elt->msgno - 1 : elt->msgno, 1.1830 + j,elt->private.uid); 1.1831 + /* make sure UID doesn't go backwards */ 1.1832 + else if (j <= prevuid) 1.1833 + sprintf (tmp,"Message %lu UID %lu less than %lu", 1.1834 + pseudoseen ? elt->msgno - 1 : elt->msgno, 1.1835 + j,prevuid + 1); 1.1836 +#if 0 /* this is currently broken by UIDPLUS */ 1.1837 + /* or skip by mailbox's recorded last */ 1.1838 + else if (j > stream->uid_last) 1.1839 + sprintf (tmp,"Message %lu UID %lu greater than last %lu", 1.1840 + pseudoseen ? elt->msgno - 1 : elt->msgno, 1.1841 + j,stream->uid_last); 1.1842 +#endif 1.1843 + else { /* normal UID case */ 1.1844 + prevuid = elt->private.uid = j; 1.1845 +#if 1 /* temporary kludge for UIDPLUS */ 1.1846 + if (prevuid > stream->uid_last) { 1.1847 + stream->uid_last = prevuid; 1.1848 + LOCAL->ddirty = LOCAL->dirty = T; 1.1849 + } 1.1850 +#endif 1.1851 + break; /* exit this cruft */ 1.1852 + } 1.1853 + MM_LOG (tmp,WARN); 1.1854 + /* invalidate UID validity */ 1.1855 + stream->uid_validity = 0; 1.1856 + elt->private.uid = 0; 1.1857 + } 1.1858 + break; 1.1859 + } 1.1860 + } 1.1861 + /* otherwise fall into S case */ 1.1862 + 1.1863 + case 'S': /* possible Status: line */ 1.1864 + if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' && 1.1865 + s[4] == 'u' && s[5] == 's' && s[6] == ':') { 1.1866 + retain = NIL; /* don't retain continuation */ 1.1867 + s += 6; /* advance to status flags */ 1.1868 + do switch (*s++) {/* parse flags */ 1.1869 + case 'R': /* message read */ 1.1870 + elt->seen = T; 1.1871 + break; 1.1872 + case 'O': /* message old */ 1.1873 + if (elt->recent) { 1.1874 + elt->recent = NIL; 1.1875 + recent--; /* it really wasn't recent */ 1.1876 + } 1.1877 + break; 1.1878 + case 'D': /* message deleted */ 1.1879 + elt->deleted = T; 1.1880 + break; 1.1881 + case 'F': /* message flagged */ 1.1882 + elt->flagged = T; 1.1883 + break; 1.1884 + case 'A': /* message answered */ 1.1885 + elt->answered = T; 1.1886 + break; 1.1887 + case 'T': /* message is a draft */ 1.1888 + elt->draft = T; 1.1889 + break; 1.1890 + default: /* some other crap */ 1.1891 + break; 1.1892 + } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))); 1.1893 + break; /* all done */ 1.1894 + } 1.1895 + /* otherwise fall into default case */ 1.1896 + 1.1897 + default: /* ordinary header line */ 1.1898 + if ((*s == 'S') || (*s == 's') || 1.1899 + (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) { 1.1900 + unsigned char *e,*v; 1.1901 + /* must match what mail_filter() does */ 1.1902 + for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1); 1.1903 + (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') && 1.1904 + ((c > ' ') || ((c != ' ') && (c != '\t') && 1.1905 + (c != '\r') && (c != '\n'))); 1.1906 + *v++ = *u++); 1.1907 + *v = '\0'; /* tie off */ 1.1908 + /* matches internal header? */ 1.1909 + if (!compare_cstring (tmp,"STATUS") || 1.1910 + !compare_cstring (tmp,"X-STATUS") || 1.1911 + !compare_cstring (tmp,"X-KEYWORDS") || 1.1912 + !compare_cstring (tmp,"X-UID") || 1.1913 + !compare_cstring (tmp,"X-IMAP") || 1.1914 + !compare_cstring (tmp,"X-IMAPBASE")) { 1.1915 + char err[MAILTMPLEN]; 1.1916 + sprintf (err,"Discarding bogus %s header in message %lu", 1.1917 + (char *) tmp,elt->msgno); 1.1918 + MM_LOG (err,WARN); 1.1919 + retain = NIL; /* don't retain continuation */ 1.1920 + break; /* different case or something */ 1.1921 + } 1.1922 + } 1.1923 + /* retain or non-continuation? */ 1.1924 + if (retain || ((*s != ' ') && (*s != '\t'))) { 1.1925 + retain = T; /* retaining continuation now */ 1.1926 + /* line length in LF format newline */ 1.1927 + for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k; 1.1928 + /* "internal" header size */ 1.1929 + elt->private.spare.data += k; 1.1930 + /* message size */ 1.1931 + elt->rfc822_size += k + 1; 1.1932 + } 1.1933 + else { 1.1934 + char err[MAILTMPLEN]; 1.1935 + sprintf (err,"Discarding bogus continuation in msg %lu: %.80s", 1.1936 + elt->msgno,(char *) s); 1.1937 + if (u = strpbrk (err,"\r\n")) *u = '\0'; 1.1938 + MM_LOG (err,WARN); 1.1939 + break; /* different case or something */ 1.1940 + } 1.1941 + break; 1.1942 + } 1.1943 + } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n'))); 1.1944 + /* "internal" header sans trailing newline */ 1.1945 + if (i) elt->private.spare.data--; 1.1946 + /* assign a UID if none found */ 1.1947 + if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) { 1.1948 + prevuid = elt->private.uid = ++stream->uid_last; 1.1949 + elt->private.dirty = T; 1.1950 + } 1.1951 + else elt->private.dirty = elt->recent; 1.1952 + 1.1953 + /* note size of header, location of text */ 1.1954 + elt->private.msg.header.text.size = 1.1955 + (elt->private.msg.text.offset = 1.1956 + (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) - 1.1957 + elt->private.special.text.size; 1.1958 + /* note current position */ 1.1959 + j = LOCAL->filesize + GETPOS (&bs); 1.1960 + if (i) do { /* look for next message */ 1.1961 + s = mmdf_mbxline (stream,&bs,&i); 1.1962 + if (i) { /* got new data? */ 1.1963 + if (ISMMDF (s)) break; 1.1964 + else { /* not a header line, add it to message */ 1.1965 + elt->rfc822_size += i; 1.1966 + for (j = 0; j < i; ++j) switch (s[j]) { 1.1967 + case '\r': /* squeeze out CRs */ 1.1968 + elt->rfc822_size -= 1; 1.1969 + break; 1.1970 + case '\n': /* LF becomes CRLF */ 1.1971 + elt->rfc822_size += 1; 1.1972 + break; 1.1973 + default: 1.1974 + break; 1.1975 + } 1.1976 + /* update current position */ 1.1977 + j = LOCAL->filesize + GETPOS (&bs); 1.1978 + } 1.1979 + } 1.1980 + } while (i); /* until found a header */ 1.1981 + elt->private.msg.text.text.size = j - 1.1982 + (elt->private.special.offset + elt->private.msg.text.offset); 1.1983 + if (i) { /* get next header line */ 1.1984 + /* remember first internal header position */ 1.1985 + j = LOCAL->filesize + GETPOS (&bs); 1.1986 + s = mmdf_mbxline (stream,&bs,&i); 1.1987 + } 1.1988 + /* until end of buffer */ 1.1989 + } while (!stream->sniff && i); 1.1990 + if (pseudoseen) { /* flush pseudo-message if present */ 1.1991 + /* decrement recent count */ 1.1992 + if (mail_elt (stream,1)->recent) recent--; 1.1993 + /* and the exists count */ 1.1994 + mail_exists (stream,nmsgs--); 1.1995 + mail_expunged(stream,1);/* fake an expunge of that message */ 1.1996 + } 1.1997 + /* need to start a new UID validity? */ 1.1998 + if (!stream->uid_validity) { 1.1999 + stream->uid_validity = time (0); 1.2000 + /* in case a whiner with no life */ 1.2001 + if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) 1.2002 + stream->uid_nosticky = T; 1.2003 + else if (nmsgs) { /* don't bother if empty file */ 1.2004 + /* make dirty to restart UID epoch */ 1.2005 + LOCAL->ddirty = LOCAL->dirty = T; 1.2006 + /* need to rewrite msg 1 if not pseudo */ 1.2007 + if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T; 1.2008 + MM_LOG ("Assigning new unique identifiers to all messages",NIL); 1.2009 + } 1.2010 + } 1.2011 + stream->nmsgs = oldnmsgs; /* whack it back down */ 1.2012 + stream->silent = silent; /* restore old silent setting */ 1.2013 + /* notify upper level of new mailbox sizes */ 1.2014 + mail_exists (stream,nmsgs); 1.2015 + mail_recent (stream,recent); 1.2016 + /* mark dirty so O flags are set */ 1.2017 + if (recent) LOCAL->dirty = T; 1.2018 + } 1.2019 + } 1.2020 + /* no change, don't babble if never got time */ 1.2021 + else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime) 1.2022 + MM_LOG ("New mailbox modification time but apparently no changes",WARN); 1.2023 + /* update parsed file size and time */ 1.2024 + LOCAL->filesize = sbuf.st_size; 1.2025 + LOCAL->filetime = sbuf.st_mtime; 1.2026 + return T; /* return the winnage */ 1.2027 +} 1.2028 + 1.2029 +/* MMDF read line from mailbox 1.2030 + * Accepts: mail stream 1.2031 + * stringstruct 1.2032 + * pointer to line size 1.2033 + * Returns: pointer to input line 1.2034 + */ 1.2035 + 1.2036 +char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size) 1.2037 +{ 1.2038 + unsigned long i,j,k,m; 1.2039 + char *s,*t,*te; 1.2040 + char *ret = ""; 1.2041 + /* flush old buffer */ 1.2042 + if (LOCAL->line) fs_give ((void **) &LOCAL->line); 1.2043 + /* if buffer needs refreshing */ 1.2044 + if (!bs->cursize) SETPOS (bs,GETPOS (bs)); 1.2045 + if (SIZE (bs)) { /* find newline */ 1.2046 + /* end of fast scan */ 1.2047 + te = (t = (s = bs->curpos) + bs->cursize) - 12; 1.2048 + while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || 1.2049 + (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || 1.2050 + (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || 1.2051 + (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { 1.2052 + --s; /* back up */ 1.2053 + break; /* exit loop */ 1.2054 + } 1.2055 + /* final character-at-a-time scan */ 1.2056 + while ((s < t) && (*s != '\n')) ++s; 1.2057 + /* difficult case if line spans buffer */ 1.2058 + if ((i = s - bs->curpos) == bs->cursize) { 1.2059 + /* have space in line buffer? */ 1.2060 + if (i > LOCAL->linebuflen) { 1.2061 + fs_give ((void **) &LOCAL->linebuf); 1.2062 + LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i); 1.2063 + } 1.2064 + /* remember what we have so far */ 1.2065 + memcpy (LOCAL->linebuf,bs->curpos,i); 1.2066 + /* load next buffer */ 1.2067 + SETPOS (bs,k = GETPOS (bs) + i); 1.2068 + /* end of fast scan */ 1.2069 + te = (t = (s = bs->curpos) + bs->cursize) - 12; 1.2070 + /* fast scan in overlap buffer */ 1.2071 + while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || 1.2072 + (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || 1.2073 + (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') || 1.2074 + (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) { 1.2075 + --s; /* back up */ 1.2076 + break; /* exit loop */ 1.2077 + } 1.2078 + 1.2079 + /* final character-at-a-time scan */ 1.2080 + while ((s < t) && (*s != '\n')) ++s; 1.2081 + /* huge line? */ 1.2082 + if ((j = s - bs->curpos) == bs->cursize) { 1.2083 + SETPOS (bs,GETPOS (bs) + j); 1.2084 + /* look for end of line (s-l-o-w!!) */ 1.2085 + for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j); 1.2086 + SETPOS (bs,k); /* go back to where it started */ 1.2087 + } 1.2088 + /* got size of data, make buffer for return */ 1.2089 + ret = LOCAL->line = (char *) fs_get (i + j + 2); 1.2090 + /* copy first chunk */ 1.2091 + memcpy (ret,LOCAL->linebuf,i); 1.2092 + while (j) { /* copy remainder */ 1.2093 + if (!bs->cursize) SETPOS (bs,GETPOS (bs)); 1.2094 + memcpy (ret + i,bs->curpos,k = min (j,bs->cursize)); 1.2095 + i += k; /* account for this much read in */ 1.2096 + j -= k; 1.2097 + bs->curpos += k; /* increment new position */ 1.2098 + bs->cursize -= k; /* eat that many bytes */ 1.2099 + } 1.2100 + /* read newline at end */ 1.2101 + if (SIZE (bs)) ret[i++] = SNX (bs); 1.2102 + ret[i] = '\0'; /* makes debugging easier */ 1.2103 + } 1.2104 + else { /* this is easy */ 1.2105 + ret = bs->curpos; /* string it at this position */ 1.2106 + bs->curpos += ++i; /* increment new position */ 1.2107 + bs->cursize -= i; /* eat that many bytes */ 1.2108 + } 1.2109 + *size = i; /* return that to user */ 1.2110 + } 1.2111 + else *size = 0; /* end of data, return empty */ 1.2112 + /* embedded MMDF header at end of line? */ 1.2113 + if ((*size > sizeof (MMDFHDRTXT)) && 1.2114 + (s = ret + *size - (i = sizeof (MMDFHDRTXT) - 1)) && ISMMDF (s)) { 1.2115 + SETPOS (bs,GETPOS (bs) - i);/* back up to start of MMDF header */ 1.2116 + *size -= i; /* reduce length of line */ 1.2117 + ret[*size - 1] = '\n'; /* force newline at end */ 1.2118 + } 1.2119 + return ret; 1.2120 +} 1.2121 + 1.2122 +/* MMDF make pseudo-header 1.2123 + * Accepts: MAIL stream 1.2124 + * buffer to write pseudo-header 1.2125 + * Returns: length of pseudo-header 1.2126 + */ 1.2127 + 1.2128 +unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr) 1.2129 +{ 1.2130 + int i; 1.2131 + char *s,tmp[MAILTMPLEN]; 1.2132 + time_t now = time (0); 1.2133 + rfc822_fixed_date (tmp); 1.2134 + sprintf (hdr,"%sFrom %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu", 1.2135 + mmdfhdr,pseudo_from,ctime (&now), 1.2136 + tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject, 1.2137 + (unsigned long) now,mylocalhost (),stream->uid_validity, 1.2138 + stream->uid_last); 1.2139 + for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i) 1.2140 + if (stream->user_flags[i]) 1.2141 + sprintf (s += strlen (s)," %s",stream->user_flags[i]); 1.2142 + sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr); 1.2143 + return strlen (hdr); 1.2144 +} 1.2145 + 1.2146 +/* MMDF make status string 1.2147 + * Accepts: MAIL stream 1.2148 + * destination string to write 1.2149 + * message cache entry 1.2150 + * UID to write if non-zero (else use elt->private.uid) 1.2151 + * non-zero flag to write UID (.LT. 0 to write UID base info too) 1.2152 + * Returns: length of string 1.2153 + */ 1.2154 + 1.2155 +unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt, 1.2156 + unsigned long uid,long flag) 1.2157 +{ 1.2158 + char *t,stack[64]; 1.2159 + char *s = status; 1.2160 + unsigned long n; 1.2161 + int pad = 50; 1.2162 + int sticky = uid ? T : !stream->uid_nosticky; 1.2163 + /* This used to use sprintf(), but thanks to certain cretinous C libraries 1.2164 + with horribly slow implementations of sprintf() I had to change it to this 1.2165 + mess. At least it should be fast. */ 1.2166 + if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */ 1.2167 + *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P'; 1.2168 + *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' '; 1.2169 + t = stack; 1.2170 + n = stream->uid_validity; /* push UID validity digits on the stack */ 1.2171 + do *t++ = (char) (n % 10) + '0'; 1.2172 + while (n /= 10); 1.2173 + /* pop UID validity digits from stack */ 1.2174 + while (t > stack) *s++ = *--t; 1.2175 + *s++ = ' '; 1.2176 + n = stream->uid_last; /* push UID last digits on the stack */ 1.2177 + do *t++ = (char) (n % 10) + '0'; 1.2178 + while (n /= 10); 1.2179 + /* pop UID last digits from stack */ 1.2180 + while (t > stack) *s++ = *--t; 1.2181 + for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n]) 1.2182 + for (*s++ = ' '; *t; *s++ = *t++); 1.2183 + *s++ = '\n'; 1.2184 + pad += 30; /* increased padding if have IMAPbase */ 1.2185 + } 1.2186 + *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's'; 1.2187 + *s++ = ':'; *s++ = ' '; 1.2188 + if (elt->seen) *s++ = 'R'; 1.2189 + /* only write O if have a UID */ 1.2190 + if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O'; 1.2191 + *s++ = '\n'; 1.2192 + *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; 1.2193 + *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' '; 1.2194 + if (elt->deleted) *s++ = 'D'; 1.2195 + if (elt->flagged) *s++ = 'F'; 1.2196 + if (elt->answered) *s++ = 'A'; 1.2197 + if (elt->draft) *s++ = 'T'; 1.2198 + *s++ = '\n'; 1.2199 + 1.2200 + if (sticky) { /* only do this if UIDs sticky */ 1.2201 + *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w'; 1.2202 + *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':'; 1.2203 + if (n = elt->user_flags) do { 1.2204 + *s++ = ' '; 1.2205 + for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++); 1.2206 + } while (n); 1.2207 + n = s - status; /* get size of stuff so far */ 1.2208 + /* pad X-Keywords to make size constant */ 1.2209 + if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' '; 1.2210 + *s++ = '\n'; 1.2211 + if (flag) { /* want to include UID? */ 1.2212 + t = stack; 1.2213 + /* push UID digits on the stack */ 1.2214 + n = uid ? uid : elt->private.uid; 1.2215 + do *t++ = (char) (n % 10) + '0'; 1.2216 + while (n /= 10); 1.2217 + *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':'; 1.2218 + *s++ = ' '; 1.2219 + /* pop UID from stack */ 1.2220 + while (t > stack) *s++ = *--t; 1.2221 + *s++ = '\n'; 1.2222 + } 1.2223 + } 1.2224 + *s++ = '\n'; *s = '\0'; /* end of extended message status */ 1.2225 + return s - status; /* return size of resulting string */ 1.2226 +} 1.2227 + 1.2228 +/* Rewrite mailbox file 1.2229 + * Accepts: MAIL stream, must be critical and locked 1.2230 + * return pointer to number of expunged messages if want expunge 1.2231 + * lock file name 1.2232 + * expunge sequence, not deleted flag 1.2233 + * Returns: T if success and mailbox unlocked, NIL if failure 1.2234 + */ 1.2235 + 1.2236 +#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */ 1.2237 + 1.2238 +long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock, 1.2239 + long flags) 1.2240 +{ 1.2241 + MESSAGECACHE *elt; 1.2242 + MMDFFILE f; 1.2243 + char *s; 1.2244 + time_t tp[2]; 1.2245 + long ret,flag; 1.2246 + unsigned long i,j; 1.2247 + unsigned long recent = stream->recent; 1.2248 + unsigned long size = LOCAL->pseudo ? mmdf_pseudo (stream,LOCAL->buf) : 0; 1.2249 + if (nexp) *nexp = 0; /* initially nothing expunged */ 1.2250 + /* calculate size of mailbox after rewrite */ 1.2251 + for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) { 1.2252 + elt = mail_elt (stream,i); /* get cache */ 1.2253 + if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) { 1.2254 + /* add RFC822 size of this message */ 1.2255 + size += elt->private.special.text.size + elt->private.spare.data + 1.2256 + mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag) + 1.2257 + elt->private.msg.text.text.size + MMDFHDRLEN; 1.2258 + flag = 1; /* only count X-IMAPbase once */ 1.2259 + } 1.2260 + } 1.2261 + /* no messages, has a life, and no pseudo */ 1.2262 + if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) { 1.2263 + LOCAL->pseudo = T; /* so make a pseudo-message now */ 1.2264 + size = mmdf_pseudo (stream,LOCAL->buf); 1.2265 + } 1.2266 + /* extend the file as necessary */ 1.2267 + if (ret = mmdf_extend (stream,size)) { 1.2268 + /* Set up buffered I/O file structure 1.2269 + * curpos current position being written through buffering 1.2270 + * filepos current position being written physically to the disk 1.2271 + * bufpos current position being written in the buffer 1.2272 + * protect current maximum position that can be written to the disk 1.2273 + * before buffering is forced 1.2274 + * The code tries to buffer so that that disk is written in multiples of 1.2275 + * OVERBLOWBUFLEN bytes. 1.2276 + */ 1.2277 + f.stream = stream; /* note mail stream */ 1.2278 + f.curpos = f.filepos = 0; /* start of file */ 1.2279 + f.protect = stream->nmsgs ? /* initial protection pointer */ 1.2280 + mail_elt (stream,1)->private.special.offset : 8192; 1.2281 + f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN); 1.2282 + 1.2283 + if (LOCAL->pseudo) /* update pseudo-header */ 1.2284 + mmdf_write (&f,LOCAL->buf,mmdf_pseudo (stream,LOCAL->buf)); 1.2285 + /* loop through all messages */ 1.2286 + for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) { 1.2287 + elt = mail_elt (stream,i);/* get cache */ 1.2288 + /* expunge this message? */ 1.2289 + if (nexp && elt->deleted && (flags ? elt->sequence : T)) { 1.2290 + /* one less recent message */ 1.2291 + if (elt->recent) --recent; 1.2292 + mail_expunged(stream,i);/* notify upper levels */ 1.2293 + ++*nexp; /* count up one more expunged message */ 1.2294 + } 1.2295 + else { /* preserve this message */ 1.2296 + i++; /* advance to next message */ 1.2297 + if ((flag < 0) || /* need to rewrite message? */ 1.2298 + elt->private.dirty || (f.curpos != elt->private.special.offset) || 1.2299 + (elt->private.msg.header.text.size != 1.2300 + (elt->private.spare.data + 1.2301 + mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) { 1.2302 + unsigned long newoffset = f.curpos; 1.2303 + /* yes, seek to internal header */ 1.2304 + lseek (LOCAL->fd,elt->private.special.offset,L_SET); 1.2305 + read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size); 1.2306 + /* see if need to squeeze out a CR */ 1.2307 + if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') { 1.2308 + LOCAL->buf[--elt->private.special.text.size - 1] = '\n'; 1.2309 + --size; /* squeezed out a CR from PC */ 1.2310 + } 1.2311 + /* protection pointer moves to RFC822 header */ 1.2312 + f.protect = elt->private.special.offset + 1.2313 + elt->private.msg.header.offset; 1.2314 + /* write internal header */ 1.2315 + mmdf_write (&f,LOCAL->buf,elt->private.special.text.size); 1.2316 + /* get RFC822 header */ 1.2317 + s = mmdf_header (stream,elt->msgno,&j,FT_INTERNAL); 1.2318 + /* in case this got decremented */ 1.2319 + elt->private.msg.header.offset = elt->private.special.text.size; 1.2320 + /* header size, sans trailing newline */ 1.2321 + if ((j < 2) || (s[j - 2] == '\n')) j--; 1.2322 + /* this can happen if CRs were squeezed */ 1.2323 + if (j < elt->private.spare.data) { 1.2324 + /* so fix up counts */ 1.2325 + size -= elt->private.spare.data - j; 1.2326 + elt->private.spare.data = j; 1.2327 + } 1.2328 + else if (j != elt->private.spare.data) 1.2329 + fatal ("header size inconsistent"); 1.2330 + /* protection pointer moves to RFC822 text */ 1.2331 + f.protect = elt->private.special.offset + 1.2332 + elt->private.msg.text.offset; 1.2333 + mmdf_write (&f,s,j); /* write RFC822 header */ 1.2334 + /* write status and UID */ 1.2335 + mmdf_write (&f,LOCAL->buf, 1.2336 + j = mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag)); 1.2337 + flag = 1; /* only write X-IMAPbase once */ 1.2338 + /* new file header size */ 1.2339 + elt->private.msg.header.text.size = elt->private.spare.data + j; 1.2340 + 1.2341 + /* did text move? */ 1.2342 + if (f.curpos != f.protect) { 1.2343 + /* get message text */ 1.2344 + s = mmdf_text_work (stream,elt,&j,FT_INTERNAL); 1.2345 + /* this can happen if CRs were squeezed */ 1.2346 + if (j < elt->private.msg.text.text.size) { 1.2347 + /* so fix up counts */ 1.2348 + size -= elt->private.msg.text.text.size - j; 1.2349 + elt->private.msg.text.text.size = j; 1.2350 + } 1.2351 + /* can't happen it says here */ 1.2352 + else if (j > elt->private.msg.text.text.size) 1.2353 + fatal ("text size inconsistent"); 1.2354 + /* new text offset, status/UID may change it */ 1.2355 + elt->private.msg.text.offset = f.curpos - newoffset; 1.2356 + /* protection pointer moves to next message */ 1.2357 + f.protect = (i <= stream->nmsgs) ? 1.2358 + mail_elt (stream,i)->private.special.offset : 1.2359 + (f.curpos + j + MMDFHDRLEN); 1.2360 + mmdf_write (&f,s,j);/* write text */ 1.2361 + /* write trailing newline */ 1.2362 + mmdf_write (&f,mmdfhdr,MMDFHDRLEN); 1.2363 + } 1.2364 + else { /* tie off header and status */ 1.2365 + mmdf_write (&f,NIL,NIL); 1.2366 + f.curpos = f.protect =/* restart everything at end of message */ 1.2367 + f.filepos += elt->private.msg.text.text.size + MMDFHDRLEN; 1.2368 + } 1.2369 + /* new internal header offset */ 1.2370 + elt->private.special.offset = newoffset; 1.2371 + elt->private.dirty =NIL;/* message is now clean */ 1.2372 + } 1.2373 + else { /* no need to rewrite this message */ 1.2374 + /* tie off previous message if needed */ 1.2375 + mmdf_write (&f,NIL,NIL); 1.2376 + f.curpos = f.protect =/* restart everything at end of message */ 1.2377 + f.filepos += elt->private.special.text.size + 1.2378 + elt->private.msg.header.text.size + 1.2379 + elt->private.msg.text.text.size + MMDFHDRLEN; 1.2380 + } 1.2381 + } 1.2382 + } 1.2383 + 1.2384 + mmdf_write (&f,NIL,NIL); /* tie off final message */ 1.2385 + if (size != f.filepos) fatal ("file size inconsistent"); 1.2386 + fs_give ((void **) &f.buf); /* free buffer */ 1.2387 + /* make sure tied off */ 1.2388 + ftruncate (LOCAL->fd,LOCAL->filesize = size); 1.2389 + fsync (LOCAL->fd); /* make sure the updates take */ 1.2390 + if (size && (flag < 0)) fatal ("lost UID base information"); 1.2391 + /* no longer dirty */ 1.2392 + LOCAL->ddirty = LOCAL->dirty = NIL; 1.2393 + /* notify upper level of new mailbox sizes */ 1.2394 + mail_exists (stream,stream->nmsgs); 1.2395 + mail_recent (stream,recent); 1.2396 + /* set atime to now, mtime a second earlier */ 1.2397 + tp[1] = (tp[0] = time (0)) - 1; 1.2398 + /* set the times, note change */ 1.2399 + if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1]; 1.2400 + close (LOCAL->fd); /* close and reopen file */ 1.2401 + if ((LOCAL->fd = open (stream->mailbox,O_RDWR, 1.2402 + (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) 1.2403 + < 0) { 1.2404 + sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno)); 1.2405 + MM_LOG (LOCAL->buf,ERROR); 1.2406 + mmdf_abort (stream); 1.2407 + } 1.2408 + dotlock_unlock (lock); /* flush the lock file */ 1.2409 + } 1.2410 + return ret; /* return state from algorithm */ 1.2411 +} 1.2412 + 1.2413 +/* Extend MMDF mailbox file 1.2414 + * Accepts: MAIL stream 1.2415 + * new desired size 1.2416 + * Return: T if success, else NIL 1.2417 + */ 1.2418 + 1.2419 +long mmdf_extend (MAILSTREAM *stream,unsigned long size) 1.2420 +{ 1.2421 + unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0; 1.2422 + if (i) { /* does the mailbox need to grow? */ 1.2423 + if (i > LOCAL->buflen) { /* make sure have enough space */ 1.2424 + /* this user won the lottery all right */ 1.2425 + fs_give ((void **) &LOCAL->buf); 1.2426 + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1); 1.2427 + } 1.2428 + memset (LOCAL->buf,'\0',i); /* get a block of nulls */ 1.2429 + while (T) { /* until write successful or punt */ 1.2430 + lseek (LOCAL->fd,LOCAL->filesize,L_SET); 1.2431 + if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break; 1.2432 + else { 1.2433 + long e = errno; /* note error before doing ftruncate */ 1.2434 + ftruncate (LOCAL->fd,LOCAL->filesize); 1.2435 + if (MM_DISKERROR (stream,e,NIL)) { 1.2436 + fsync (LOCAL->fd); /* user chose to punt */ 1.2437 + sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e)); 1.2438 + if (!stream->silent) MM_LOG (LOCAL->buf,ERROR); 1.2439 + return NIL; 1.2440 + } 1.2441 + } 1.2442 + } 1.2443 + } 1.2444 + return LONGT; 1.2445 +} 1.2446 + 1.2447 +/* Write data to buffered file 1.2448 + * Accepts: buffered file pointer 1.2449 + * file data or NIL to indicate "flush buffer" 1.2450 + * date size (ignored for "flush buffer") 1.2451 + * Does not return until success 1.2452 + */ 1.2453 + 1.2454 +void mmdf_write (MMDFFILE *f,char *buf,unsigned long size) 1.2455 +{ 1.2456 + unsigned long i,j,k; 1.2457 + if (buf) { /* doing buffered write? */ 1.2458 + i = f->bufpos - f->buf; /* yes, get size of current buffer data */ 1.2459 + /* yes, have space in current buffer chunk? */ 1.2460 + if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) { 1.2461 + /* yes, fill up buffer as much as we can */ 1.2462 + memcpy (f->bufpos,buf,k = min (j,size)); 1.2463 + f->bufpos += k; /* new buffer position */ 1.2464 + f->curpos += k; /* new current position */ 1.2465 + if (j -= k) return; /* all done if still have buffer free space */ 1.2466 + buf += k; /* full, get new unwritten data pointer */ 1.2467 + size -= k; /* new data size */ 1.2468 + i += k; /* new buffer data size */ 1.2469 + } 1.2470 + /* This chunk of the buffer is full. See if can make some space by 1.2471 + * writing to the disk, if there's enough unprotected space to do so. 1.2472 + * Try to fill out any unaligned chunk, along with any subsequent full 1.2473 + * chunks that will fit in unprotected space. 1.2474 + */ 1.2475 + /* any unprotected space we can write to? */ 1.2476 + if (j = min (i,f->protect - f->filepos)) { 1.2477 + /* yes, filepos not at chunk boundary? */ 1.2478 + if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j)) 1.2479 + j -= k; /* yes, and can write out partial chunk */ 1.2480 + else k = 0; /* no partial chunk to write */ 1.2481 + /* if at least a chunk free, write that too */ 1.2482 + if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN); 1.2483 + if (k) { /* write data if there is anything we can */ 1.2484 + mmdf_phys_write (f,f->buf,k); 1.2485 + /* slide buffer */ 1.2486 + if (i -= k) memmove (f->buf,f->buf + k,i); 1.2487 + f->bufpos = f->buf + i; /* new end of buffer */ 1.2488 + } 1.2489 + } 1.2490 + 1.2491 + /* Have flushed the buffer as best as possible. All done if no more 1.2492 + * data to write. Otherwise, if the buffer is empty AND if the unwritten 1.2493 + * data is larger than a chunk AND the unprotected space is also larger 1.2494 + * than a chunk, then write as many chunks as we can directly from the 1.2495 + * data. Buffer the rest, expanding the buffer as needed. 1.2496 + */ 1.2497 + if (size) { /* have more data that we need to buffer? */ 1.2498 + /* can write any of it to disk instead? */ 1.2499 + if ((f->bufpos == f->buf) && 1.2500 + ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) { 1.2501 + /* write as much as we can right now */ 1.2502 + mmdf_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN)); 1.2503 + buf += j; /* new data pointer */ 1.2504 + size -= j; /* new data size */ 1.2505 + f->curpos += j; /* advance current pointer */ 1.2506 + } 1.2507 + if (size) { /* still have data that we need to buffer? */ 1.2508 + /* yes, need to expand the buffer? */ 1.2509 + if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) { 1.2510 + /* note current position in buffer */ 1.2511 + j = f->bufpos - f->buf; 1.2512 + i += OVERFLOWBUFLEN; /* yes, grow another chunk */ 1.2513 + fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN)); 1.2514 + /* in case buffer relocated */ 1.2515 + f->bufpos = f->buf + j; 1.2516 + } 1.2517 + /* buffer remaining data */ 1.2518 + memcpy (f->bufpos,buf,size); 1.2519 + f->bufpos += size; /* new end of buffer */ 1.2520 + f->curpos += size; /* advance current pointer */ 1.2521 + } 1.2522 + } 1.2523 + } 1.2524 + else { /* flush buffer to disk */ 1.2525 + mmdf_phys_write (f,f->buf,i = f->bufpos - f->buf); 1.2526 + f->bufpos = f->buf; /* reset buffer */ 1.2527 + /* update positions */ 1.2528 + f->curpos = f->protect = f->filepos; 1.2529 + } 1.2530 +} 1.2531 + 1.2532 +/* Physical disk write 1.2533 + * Accepts: buffered file pointer 1.2534 + * buffer address 1.2535 + * buffer size 1.2536 + * Does not return until success 1.2537 + */ 1.2538 + 1.2539 +void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size) 1.2540 +{ 1.2541 + MAILSTREAM *stream = f->stream; 1.2542 + /* write data at desired position */ 1.2543 + while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) || 1.2544 + (write (LOCAL->fd,buf,size) < 0))) { 1.2545 + int e; 1.2546 + char tmp[MAILTMPLEN]; 1.2547 + sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno)); 1.2548 + MM_LOG (tmp,ERROR); 1.2549 + MM_DISKERROR (NIL,e,T); /* serious problem, must retry */ 1.2550 + } 1.2551 + f->filepos += size; /* update file position */ 1.2552 +}