imapext-2007
diff src/osdep/nt/mtxnt.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/nt/mtxnt.c Mon Sep 14 15:17:45 2009 +0900 1.3 @@ -0,0 +1,1232 @@ 1.4 +/* ======================================================================== 1.5 + * Copyright 1988-2007 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: MTX mail routines 1.19 + * 1.20 + * Author: Mark Crispin 1.21 + * Networks and Distributed Computing 1.22 + * Computing & Communications 1.23 + * University of Washington 1.24 + * Administration Building, AG-44 1.25 + * Seattle, WA 98195 1.26 + * Internet: MRC@CAC.Washington.EDU 1.27 + * 1.28 + * Date: 22 May 1990 1.29 + * Last Edited: 15 June 2007 1.30 + */ 1.31 + 1.32 + 1.33 +/* FILE TIME SEMANTICS 1.34 + * 1.35 + * The atime is the last read time of the file. 1.36 + * The mtime is the last flags update time of the file. 1.37 + * The ctime is the last write time of the file. 1.38 + */ 1.39 + 1.40 +#include <stdio.h> 1.41 +#include <ctype.h> 1.42 +#include <errno.h> 1.43 +extern int errno; /* just in case */ 1.44 +#include "mail.h" 1.45 +#include "osdep.h" 1.46 +#include <fcntl.h> 1.47 +#include <time.h> 1.48 +#include <sys/stat.h> 1.49 +#include <sys/utime.h> 1.50 +#include "misc.h" 1.51 +#include "dummy.h" 1.52 +#include "fdstring.h" 1.53 + 1.54 +/* MTX I/O stream local data */ 1.55 + 1.56 +typedef struct mtx_local { 1.57 + unsigned int shouldcheck: 1; /* if ping should do a check instead */ 1.58 + unsigned int mustcheck: 1; /* if ping must do a check instead */ 1.59 + int fd; /* file descriptor for I/O */ 1.60 + off_t filesize; /* file size parsed */ 1.61 + time_t filetime; /* last file time */ 1.62 + time_t lastsnarf; /* last snarf time */ 1.63 + unsigned char *buf; /* temporary buffer */ 1.64 + unsigned long buflen; /* current size of temporary buffer */ 1.65 +} MTXLOCAL; 1.66 + 1.67 + 1.68 +/* Convenient access to local data */ 1.69 + 1.70 +#define LOCAL ((MTXLOCAL *) stream->local) 1.71 + 1.72 + 1.73 +/* Function prototypes */ 1.74 + 1.75 +DRIVER *mtx_valid (char *name); 1.76 +int mtx_isvalid (char *name,char *file); 1.77 +void *mtx_parameters (long function,void *value); 1.78 +void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); 1.79 +void mtx_list (MAILSTREAM *stream,char *ref,char *pat); 1.80 +void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat); 1.81 +long mtx_create (MAILSTREAM *stream,char *mailbox); 1.82 +long mtx_delete (MAILSTREAM *stream,char *mailbox); 1.83 +long mtx_rename (MAILSTREAM *stream,char *old,char *newname); 1.84 +long mtx_status (MAILSTREAM *stream,char *mbx,long flags); 1.85 +MAILSTREAM *mtx_open (MAILSTREAM *stream); 1.86 +void mtx_close (MAILSTREAM *stream,long options); 1.87 +void mtx_flags (MAILSTREAM *stream,char *sequence,long flags); 1.88 +char *mtx_header (MAILSTREAM *stream,unsigned long msgno, 1.89 + unsigned long *length,long flags); 1.90 +long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); 1.91 +void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); 1.92 +void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); 1.93 +long mtx_ping (MAILSTREAM *stream); 1.94 +void mtx_check (MAILSTREAM *stream); 1.95 +void mtx_snarf (MAILSTREAM *stream); 1.96 +long mtx_expunge (MAILSTREAM *stream,char *sequence,long options); 1.97 +long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); 1.98 +long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); 1.99 + 1.100 +long mtx_parse (MAILSTREAM *stream); 1.101 +MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno); 1.102 +void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); 1.103 +void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag); 1.104 +unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, 1.105 + unsigned long *size); 1.106 + 1.107 + 1.108 +/* MTX mail routines */ 1.109 + 1.110 + 1.111 +/* Driver dispatch used by MAIL */ 1.112 + 1.113 +DRIVER mtxdriver = { 1.114 + "mtx", /* driver name */ 1.115 + /* driver flags */ 1.116 + DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY, 1.117 + (DRIVER *) NIL, /* next driver */ 1.118 + mtx_valid, /* mailbox is valid for us */ 1.119 + mtx_parameters, /* manipulate parameters */ 1.120 + mtx_scan, /* scan mailboxes */ 1.121 + mtx_list, /* list mailboxes */ 1.122 + mtx_lsub, /* list subscribed mailboxes */ 1.123 + NIL, /* subscribe to mailbox */ 1.124 + NIL, /* unsubscribe from mailbox */ 1.125 + mtx_create, /* create mailbox */ 1.126 + mtx_delete, /* delete mailbox */ 1.127 + mtx_rename, /* rename mailbox */ 1.128 + mail_status_default, /* status of mailbox */ 1.129 + mtx_open, /* open mailbox */ 1.130 + mtx_close, /* close mailbox */ 1.131 + mtx_flags, /* fetch message "fast" attributes */ 1.132 + mtx_flags, /* fetch message flags */ 1.133 + NIL, /* fetch overview */ 1.134 + NIL, /* fetch message envelopes */ 1.135 + mtx_header, /* fetch message header */ 1.136 + mtx_text, /* fetch message body */ 1.137 + NIL, /* fetch partial message text */ 1.138 + NIL, /* unique identifier */ 1.139 + NIL, /* message number */ 1.140 + mtx_flag, /* modify flags */ 1.141 + mtx_flagmsg, /* per-message modify flags */ 1.142 + NIL, /* search for message based on criteria */ 1.143 + NIL, /* sort messages */ 1.144 + NIL, /* thread messages */ 1.145 + mtx_ping, /* ping mailbox to see if still alive */ 1.146 + mtx_check, /* check for new messages */ 1.147 + mtx_expunge, /* expunge deleted messages */ 1.148 + mtx_copy, /* copy messages to another mailbox */ 1.149 + mtx_append, /* append string message to mailbox */ 1.150 + NIL /* garbage collect stream */ 1.151 +}; 1.152 + 1.153 + /* prototype stream */ 1.154 +MAILSTREAM mtxproto = {&mtxdriver}; 1.155 + 1.156 +/* MTX mail validate mailbox 1.157 + * Accepts: mailbox name 1.158 + * Returns: our driver if name is valid, NIL otherwise 1.159 + */ 1.160 + 1.161 +DRIVER *mtx_valid (char *name) 1.162 +{ 1.163 + char tmp[MAILTMPLEN]; 1.164 + return mtx_isvalid (name,tmp) ? &mtxdriver : NIL; 1.165 +} 1.166 + 1.167 + 1.168 +/* MTX mail test for valid mailbox 1.169 + * Accepts: mailbox name 1.170 + * buffer to return file name 1.171 + * Returns: T if valid, NIL otherwise 1.172 + */ 1.173 + 1.174 +int mtx_isvalid (char *name,char *file) 1.175 +{ 1.176 + int fd; 1.177 + int ret = NIL; 1.178 + char *s,tmp[MAILTMPLEN]; 1.179 + struct stat sbuf; 1.180 + struct utimbuf times; 1.181 + errno = EINVAL; /* assume invalid argument */ 1.182 + /* if file, get its status */ 1.183 + if ((s = dummy_file (file,name)) && !stat (s,&sbuf) && 1.184 + ((sbuf.st_mode & S_IFMT) == S_IFREG)) { 1.185 + if (!sbuf.st_size)errno = 0;/* empty file */ 1.186 + else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) { 1.187 + memset (tmp,'\0',MAILTMPLEN); 1.188 + if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) && 1.189 + (s[1] == '\012')) { /* valid format? */ 1.190 + *s = '\0'; /* tie off header */ 1.191 + /* must begin with dd-mmm-yy" */ 1.192 + ret = (((tmp[2] == '-' && tmp[6] == '-') || 1.193 + (tmp[1] == '-' && tmp[5] == '-')) && 1.194 + (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; 1.195 + } 1.196 + else errno = -1; /* bogus format */ 1.197 + close (fd); /* close the file */ 1.198 + /* \Marked status? */ 1.199 + if (sbuf.st_ctime > sbuf.st_atime) { 1.200 + /* preserve atime and mtime */ 1.201 + times.actime = sbuf.st_atime; 1.202 + times.modtime = sbuf.st_mtime; 1.203 + utime (file,×); /* set the times */ 1.204 + } 1.205 + } 1.206 + } 1.207 + /* in case INBOX but not mtx format */ 1.208 + else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; 1.209 + return ret; /* return what we should */ 1.210 +} 1.211 + 1.212 + 1.213 +/* MTX manipulate driver parameters 1.214 + * Accepts: function code 1.215 + * function-dependent value 1.216 + * Returns: function-dependent return value 1.217 + */ 1.218 + 1.219 +void *mtx_parameters (long function,void *value) 1.220 +{ 1.221 + return NIL; 1.222 +} 1.223 + 1.224 +/* MTX mail scan mailboxes 1.225 + * Accepts: mail stream 1.226 + * reference 1.227 + * pattern to search 1.228 + * string to scan 1.229 + */ 1.230 + 1.231 +void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) 1.232 +{ 1.233 + if (stream) dummy_scan (NIL,ref,pat,contents); 1.234 +} 1.235 + 1.236 + 1.237 +/* MTX mail list mailboxes 1.238 + * Accepts: mail stream 1.239 + * reference 1.240 + * pattern to search 1.241 + */ 1.242 + 1.243 +void mtx_list (MAILSTREAM *stream,char *ref,char *pat) 1.244 +{ 1.245 + if (stream) dummy_list (NIL,ref,pat); 1.246 +} 1.247 + 1.248 + 1.249 +/* MTX mail list subscribed mailboxes 1.250 + * Accepts: mail stream 1.251 + * reference 1.252 + * pattern to search 1.253 + */ 1.254 + 1.255 +void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat) 1.256 +{ 1.257 + if (stream) dummy_lsub (NIL,ref,pat); 1.258 +} 1.259 + 1.260 +/* MTX mail create mailbox 1.261 + * Accepts: MAIL stream 1.262 + * mailbox name to create 1.263 + * Returns: T on success, NIL on failure 1.264 + */ 1.265 + 1.266 +long mtx_create (MAILSTREAM *stream,char *mailbox) 1.267 +{ 1.268 + char *s,mbx[MAILTMPLEN]; 1.269 + if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s); 1.270 + sprintf (mbx,"Can't create %.80s: invalid name",mailbox); 1.271 + mm_log (mbx,ERROR); 1.272 + return NIL; 1.273 +} 1.274 + 1.275 + 1.276 +/* MTX mail delete mailbox 1.277 + * Accepts: MAIL stream 1.278 + * mailbox name to delete 1.279 + * Returns: T on success, NIL on failure 1.280 + */ 1.281 + 1.282 +long mtx_delete (MAILSTREAM *stream,char *mailbox) 1.283 +{ 1.284 + return mtx_rename (stream,mailbox,NIL); 1.285 +} 1.286 + 1.287 +/* MTX mail rename mailbox 1.288 + * Accepts: MAIL stream 1.289 + * old mailbox name 1.290 + * new mailbox name (or NIL for delete) 1.291 + * Returns: T on success, NIL on failure 1.292 + */ 1.293 + 1.294 +long mtx_rename (MAILSTREAM *stream,char *old,char *newname) 1.295 +{ 1.296 + long ret = LONGT; 1.297 + char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; 1.298 + int fd,ld; 1.299 + struct stat sbuf; 1.300 + if (!dummy_file (file,old) || 1.301 + (newname && (!((s = mailboxfile (tmp,newname)) && *s) || 1.302 + ((s = strrchr (tmp,'\\')) && !s[1])))) { 1.303 + sprintf (tmp,newname ? 1.304 + "Can't rename mailbox %.80s to %.80s: invalid name" : 1.305 + "Can't delete mailbox %.80s: invalid name", 1.306 + old,newname); 1.307 + mm_log (tmp,ERROR); 1.308 + return NIL; 1.309 + } 1.310 + if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) { 1.311 + sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); 1.312 + mm_log (tmp,ERROR); 1.313 + return NIL; 1.314 + } 1.315 + /* get exclusive parse/append permission */ 1.316 + if ((ld = lockname (lock,file,LOCK_EX)) < 0) { 1.317 + mm_log ("Unable to lock rename mailbox",ERROR); 1.318 + return NIL; 1.319 + } 1.320 + /* lock out other users */ 1.321 + if (flock (fd,LOCK_EX|LOCK_NB)) { 1.322 + close (fd); /* couldn't lock, give up on it then */ 1.323 + sprintf (tmp,"Mailbox %.80s is in use by another process",old); 1.324 + mm_log (tmp,ERROR); 1.325 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.326 + return NIL; 1.327 + } 1.328 + 1.329 + if (newname) { /* want rename? */ 1.330 + /* found superior to destination name? */ 1.331 + if ((s = strrchr (tmp,'\\')) && (s != tmp) && 1.332 + ((tmp[1] != ':') || (s != tmp + 2))) { 1.333 + c = s[1]; /* remember character after delimiter */ 1.334 + *s = s[1] = '\0'; /* tie off name at delimiter */ 1.335 + /* name doesn't exist, create it */ 1.336 + if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { 1.337 + *s = '\\'; /* restore delimiter */ 1.338 + if (!dummy_create (stream,tmp)) ret = NIL; 1.339 + } 1.340 + else *s = '\\'; /* restore delimiter */ 1.341 + s[1] = c; /* restore character after delimiter */ 1.342 + } 1.343 + flock (fd,LOCK_UN); /* release lock on the file */ 1.344 + close (fd); /* pacify NTFS */ 1.345 + /* rename the file */ 1.346 + if (ret && rename (file,tmp)) { 1.347 + sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, 1.348 + strerror (errno)); 1.349 + mm_log (tmp,ERROR); 1.350 + ret = NIL; /* set failure */ 1.351 + } 1.352 + } 1.353 + else { 1.354 + flock (fd,LOCK_UN); /* release lock on the file */ 1.355 + close (fd); /* pacify NTFS */ 1.356 + if (unlink (file)) { 1.357 + sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno)); 1.358 + mm_log (tmp,ERROR); 1.359 + ret = NIL; /* set failure */ 1.360 + } 1.361 + } 1.362 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.363 + return ret; /* return success */ 1.364 +} 1.365 + 1.366 +/* MTX mail open 1.367 + * Accepts: stream to open 1.368 + * Returns: stream on success, NIL on failure 1.369 + */ 1.370 + 1.371 +MAILSTREAM *mtx_open (MAILSTREAM *stream) 1.372 +{ 1.373 + int fd,ld; 1.374 + char tmp[MAILTMPLEN]; 1.375 + /* return prototype for OP_PROTOTYPE call */ 1.376 + if (!stream) return &mtxproto; 1.377 + if (stream->local) fatal ("mtx recycle stream"); 1.378 + /* canonicalize the mailbox name */ 1.379 + if (!dummy_file (tmp,stream->mailbox)) { 1.380 + sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); 1.381 + mm_log (tmp,ERROR); 1.382 + } 1.383 + if (stream->rdonly || 1.384 + (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) { 1.385 + if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) { 1.386 + sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno)); 1.387 + mm_log (tmp,ERROR); 1.388 + return NIL; 1.389 + } 1.390 + else if (!stream->rdonly) { /* got it, but readonly */ 1.391 + mm_log ("Can't get write access to mailbox, access is readonly",WARN); 1.392 + stream->rdonly = T; 1.393 + } 1.394 + } 1.395 + stream->local = fs_get (sizeof (MTXLOCAL)); 1.396 + LOCAL->fd = fd; /* bind the file */ 1.397 + LOCAL->buf = (char *) fs_get (CHUNKSIZE); 1.398 + LOCAL->buflen = CHUNKSIZE - 1; 1.399 + /* note if an INBOX or not */ 1.400 + stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); 1.401 + fs_give ((void **) &stream->mailbox); 1.402 + stream->mailbox = cpystr (tmp); 1.403 + /* get shared parse permission */ 1.404 + if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) { 1.405 + mm_log ("Unable to lock open mailbox",ERROR); 1.406 + return NIL; 1.407 + } 1.408 + flock (LOCAL->fd,LOCK_SH); /* lock the file */ 1.409 + unlockfd (ld,tmp); /* release shared parse permission */ 1.410 + LOCAL->filesize = 0; /* initialize parsed file size */ 1.411 + LOCAL->filetime = 0; /* time not set up yet */ 1.412 + LOCAL->mustcheck = LOCAL->shouldcheck = NIL; 1.413 + stream->sequence++; /* bump sequence number */ 1.414 + stream->uid_validity = (unsigned long) time (0); 1.415 + /* parse mailbox */ 1.416 + stream->nmsgs = stream->recent = 0; 1.417 + if (mtx_ping (stream) && !stream->nmsgs) 1.418 + mm_log ("Mailbox is empty",(long) NIL); 1.419 + if (!LOCAL) return NIL; /* failure if stream died */ 1.420 + stream->perm_seen = stream->perm_deleted = 1.421 + stream->perm_flagged = stream->perm_answered = stream->perm_draft = 1.422 + stream->rdonly ? NIL : T; 1.423 + stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; 1.424 + return stream; /* return stream to caller */ 1.425 +} 1.426 + 1.427 +/* MTX mail close 1.428 + * Accepts: MAIL stream 1.429 + * close options 1.430 + */ 1.431 + 1.432 +void mtx_close (MAILSTREAM *stream,long options) 1.433 +{ 1.434 + if (stream && LOCAL) { /* only if a file is open */ 1.435 + int silent = stream->silent; 1.436 + stream->silent = T; /* note this stream is dying */ 1.437 + if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL); 1.438 + stream->silent = silent; /* restore previous status */ 1.439 + flock (LOCAL->fd,LOCK_UN); /* unlock local file */ 1.440 + close (LOCAL->fd); /* close the local file */ 1.441 + /* free local text buffer */ 1.442 + if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); 1.443 + /* nuke the local data */ 1.444 + fs_give ((void **) &stream->local); 1.445 + stream->dtb = NIL; /* log out the DTB */ 1.446 + } 1.447 +} 1.448 + 1.449 + 1.450 +/* MTX mail fetch flags 1.451 + * Accepts: MAIL stream 1.452 + * sequence 1.453 + * option flags 1.454 + * Sniffs at file to see if some other process changed the flags 1.455 + */ 1.456 + 1.457 +void mtx_flags (MAILSTREAM *stream,char *sequence,long flags) 1.458 +{ 1.459 + unsigned long i; 1.460 + if (mtx_ping (stream) && /* ping mailbox, get new status for messages */ 1.461 + ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : 1.462 + mail_sequence (stream,sequence))) 1.463 + for (i = 1; i <= stream->nmsgs; i++) 1.464 + if (mail_elt (stream,i)->sequence) mtx_elt (stream,i); 1.465 +} 1.466 + 1.467 +/* MTX mail fetch message header 1.468 + * Accepts: MAIL stream 1.469 + * message # to fetch 1.470 + * pointer to returned header text length 1.471 + * option flags 1.472 + * Returns: message header in RFC822 format 1.473 + */ 1.474 + 1.475 +char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, 1.476 + long flags) 1.477 +{ 1.478 + *length = 0; /* default to empty */ 1.479 + if (flags & FT_UID) return "";/* UID call "impossible" */ 1.480 + /* get to header position */ 1.481 + lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET); 1.482 + /* is buffer big enough? */ 1.483 + if (*length > LOCAL->buflen) { 1.484 + fs_give ((void **) &LOCAL->buf); 1.485 + LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1); 1.486 + } 1.487 + LOCAL->buf[*length] = '\0'; /* tie off string */ 1.488 + /* slurp the data */ 1.489 + read (LOCAL->fd,LOCAL->buf,*length); 1.490 + return LOCAL->buf; 1.491 +} 1.492 + 1.493 +/* MTX mail fetch message text (body only) 1.494 + * Accepts: MAIL stream 1.495 + * message # to fetch 1.496 + * pointer to returned header text length 1.497 + * option flags 1.498 + * Returns: T, always 1.499 + */ 1.500 + 1.501 +long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) 1.502 +{ 1.503 + FDDATA d; 1.504 + unsigned long i,j; 1.505 + MESSAGECACHE *elt; 1.506 + /* UID call "impossible" */ 1.507 + if (flags & FT_UID) return NIL; 1.508 + elt = mtx_elt (stream,msgno); /* get message status */ 1.509 + /* if message not seen */ 1.510 + if (!(flags & FT_PEEK) && !elt->seen) { 1.511 + elt->seen = T; /* mark message as seen */ 1.512 + /* recalculate status */ 1.513 + mtx_update_status (stream,msgno,NIL); 1.514 + mm_flags (stream,msgno); 1.515 + } 1.516 + /* find header position */ 1.517 + i = mtx_hdrpos (stream,msgno,&j); 1.518 + d.fd = LOCAL->fd; /* set up file descriptor */ 1.519 + d.pos = i + j; 1.520 + d.chunk = LOCAL->buf; /* initial buffer chunk */ 1.521 + d.chunksize = CHUNKSIZE; 1.522 + INIT (bs,fd_string,&d,elt->rfc822_size - j); 1.523 + return T; /* success */ 1.524 +} 1.525 + 1.526 +/* MTX mail modify flags 1.527 + * Accepts: MAIL stream 1.528 + * sequence 1.529 + * flag(s) 1.530 + * option flags 1.531 + */ 1.532 + 1.533 +void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) 1.534 +{ 1.535 + struct utimbuf times; 1.536 + struct stat sbuf; 1.537 + if (!stream->rdonly) { /* make sure the update takes */ 1.538 + fsync (LOCAL->fd); 1.539 + fstat (LOCAL->fd,&sbuf); /* get current write time */ 1.540 + times.modtime = LOCAL->filetime = sbuf.st_mtime; 1.541 + times.actime = time (0); /* make sure read comes after all that */ 1.542 + utime (stream->mailbox,×); 1.543 + } 1.544 +} 1.545 + 1.546 + 1.547 +/* MTX mail per-message modify flags 1.548 + * Accepts: MAIL stream 1.549 + * message cache element 1.550 + */ 1.551 + 1.552 +void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) 1.553 +{ 1.554 + struct stat sbuf; 1.555 + /* maybe need to do a checkpoint? */ 1.556 + if (LOCAL->filetime && !LOCAL->shouldcheck) { 1.557 + fstat (LOCAL->fd,&sbuf); /* get current write time */ 1.558 + if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; 1.559 + LOCAL->filetime = 0; /* don't do this test for any other messages */ 1.560 + } 1.561 + /* recalculate status */ 1.562 + mtx_update_status (stream,elt->msgno,NIL); 1.563 +} 1.564 + 1.565 +/* MTX mail ping mailbox 1.566 + * Accepts: MAIL stream 1.567 + * Returns: T if stream still alive, NIL if not 1.568 + */ 1.569 + 1.570 +long mtx_ping (MAILSTREAM *stream) 1.571 +{ 1.572 + unsigned long i = 1; 1.573 + long r = T; 1.574 + int ld; 1.575 + char lock[MAILTMPLEN]; 1.576 + struct stat sbuf; 1.577 + if (stream && LOCAL) { /* only if stream already open */ 1.578 + fstat (LOCAL->fd,&sbuf); /* get current file poop */ 1.579 + if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && 1.580 + (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; 1.581 + /* check for changed message status */ 1.582 + if (LOCAL->mustcheck || LOCAL->shouldcheck) { 1.583 + LOCAL->filetime = sbuf.st_mtime; 1.584 + if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ 1.585 + mm_notify (stream,"[CHECK] Checking for flag updates",NIL); 1.586 + while (i <= stream->nmsgs) mtx_elt (stream,i++); 1.587 + LOCAL->mustcheck = LOCAL->shouldcheck = NIL; 1.588 + } 1.589 + /* get shared parse/append permission */ 1.590 + if ((sbuf.st_size != LOCAL->filesize) && 1.591 + ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) { 1.592 + /* parse resulting mailbox */ 1.593 + r = (mtx_parse (stream)) ? T : NIL; 1.594 + unlockfd (ld,lock); /* release shared parse/append permission */ 1.595 + } 1.596 + } 1.597 + return r; /* return result of the parse */ 1.598 +} 1.599 + 1.600 + 1.601 +/* MTX mail check mailbox (reparses status too) 1.602 + * Accepts: MAIL stream 1.603 + */ 1.604 + 1.605 +void mtx_check (MAILSTREAM *stream) 1.606 +{ 1.607 + /* mark that a check is desired */ 1.608 + if (LOCAL) LOCAL->mustcheck = T; 1.609 + if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL); 1.610 +} 1.611 + 1.612 +/* MTX mail expunge mailbox 1.613 + * sequence to expunge if non-NIL 1.614 + * expunge options 1.615 + * Returns: T, always 1.616 + */ 1.617 + 1.618 +long mtx_expunge (MAILSTREAM *stream,char *sequence,long options) 1.619 +{ 1.620 + long ret; 1.621 + struct utimbuf times; 1.622 + struct stat sbuf; 1.623 + off_t pos = 0; 1.624 + int ld; 1.625 + unsigned long i = 1; 1.626 + unsigned long j,k,m,recent; 1.627 + unsigned long n = 0; 1.628 + unsigned long delta = 0; 1.629 + char lock[MAILTMPLEN]; 1.630 + MESSAGECACHE *elt; 1.631 + if (!(ret = (sequence ? ((options & EX_UID) ? 1.632 + mail_uid_sequence (stream,sequence) : 1.633 + mail_sequence (stream,sequence)) : LONGT) && 1.634 + mtx_ping (stream))); /* parse sequence if given, ping stream */ 1.635 + else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN); 1.636 + else { 1.637 + if (LOCAL->filetime && !LOCAL->shouldcheck) { 1.638 + fstat (LOCAL->fd,&sbuf); /* get current write time */ 1.639 + if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; 1.640 + } 1.641 + /* get exclusive parse/append permission */ 1.642 + if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) 1.643 + mm_log ("Unable to lock expunge mailbox",ERROR); 1.644 + /* make sure see any newly-arrived messages */ 1.645 + else if (!mtx_parse (stream)); 1.646 + /* get exclusive access */ 1.647 + else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { 1.648 + flock (LOCAL->fd,LOCK_SH);/* recover previous lock */ 1.649 + mm_log ("Can't expunge because mailbox is in use by another process", 1.650 + ERROR); 1.651 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.652 + } 1.653 + 1.654 + else { 1.655 + mm_critical (stream); /* go critical */ 1.656 + recent = stream->recent; /* get recent now that pinged and locked */ 1.657 + /* for each message */ 1.658 + while (i <= stream->nmsgs) { 1.659 + /* get cache element */ 1.660 + elt = mtx_elt (stream,i); 1.661 + /* number of bytes to smash or preserve */ 1.662 + k = elt->private.special.text.size + elt->rfc822_size; 1.663 + /* if need to expunge this message */ 1.664 + if (elt->deleted && (sequence ? elt->sequence : T)) { 1.665 + /* if recent, note one less recent message */ 1.666 + if (elt->recent) --recent; 1.667 + delta += k; /* number of bytes to delete */ 1.668 + /* notify upper levels */ 1.669 + mail_expunged (stream,i); 1.670 + n++; /* count up one more expunged message */ 1.671 + } 1.672 + else if (i++ && delta) {/* preserved message */ 1.673 + /* first byte to preserve */ 1.674 + j = elt->private.special.offset; 1.675 + do { /* read from source position */ 1.676 + m = min (k,LOCAL->buflen); 1.677 + lseek (LOCAL->fd,j,L_SET); 1.678 + read (LOCAL->fd,LOCAL->buf,m); 1.679 + pos = j - delta; /* write to destination position */ 1.680 + while (T) { 1.681 + lseek (LOCAL->fd,pos,L_SET); 1.682 + if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; 1.683 + mm_notify (stream,strerror (errno),WARN); 1.684 + mm_diskerror (stream,errno,T); 1.685 + } 1.686 + pos += m; /* new position */ 1.687 + j += m; /* next chunk, perhaps */ 1.688 + } while (k -= m); /* until done */ 1.689 + /* note the new address of this text */ 1.690 + elt->private.special.offset -= delta; 1.691 + } 1.692 + /* preserved but no deleted messages */ 1.693 + else pos = elt->private.special.offset + k; 1.694 + } 1.695 + if (n) { /* truncate file after last message */ 1.696 + if (pos != (LOCAL->filesize -= delta)) { 1.697 + sprintf (LOCAL->buf, 1.698 + "Calculated size mismatch %lu != %lu, delta = %lu", 1.699 + (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); 1.700 + mm_log (LOCAL->buf,WARN); 1.701 + LOCAL->filesize = pos;/* fix it then */ 1.702 + } 1.703 + ftruncate (LOCAL->fd,LOCAL->filesize); 1.704 + sprintf (LOCAL->buf,"Expunged %lu messages",n); 1.705 + /* output the news */ 1.706 + mm_log (LOCAL->buf,(long) NIL); 1.707 + } 1.708 + else mm_log ("No messages deleted, so no update needed",(long) NIL); 1.709 + fsync (LOCAL->fd); /* force disk update */ 1.710 + fstat (LOCAL->fd,&sbuf); /* get new write time */ 1.711 + times.modtime = LOCAL->filetime = sbuf.st_mtime; 1.712 + times.actime = time (0); /* reset atime to now */ 1.713 + utime (stream->mailbox,×); 1.714 + mm_nocritical (stream); /* release critical */ 1.715 + /* notify upper level of new mailbox size */ 1.716 + mail_exists (stream,stream->nmsgs); 1.717 + mail_recent (stream,recent); 1.718 + flock (LOCAL->fd,LOCK_SH);/* allow sharers again */ 1.719 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.720 + } 1.721 + } 1.722 + return ret; 1.723 +} 1.724 + 1.725 +/* MTX mail copy message(s) 1.726 + * Accepts: MAIL stream 1.727 + * sequence 1.728 + * destination mailbox 1.729 + * copy options 1.730 + * Returns: T if success, NIL if failed 1.731 + */ 1.732 + 1.733 +long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) 1.734 +{ 1.735 + struct stat sbuf; 1.736 + struct utimbuf times; 1.737 + MESSAGECACHE *elt; 1.738 + unsigned long i,j,k; 1.739 + long ret = LONGT; 1.740 + int fd,ld; 1.741 + char file[MAILTMPLEN],lock[MAILTMPLEN]; 1.742 + mailproxycopy_t pc = 1.743 + (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); 1.744 + /* make sure valid mailbox */ 1.745 + if (!mtx_isvalid (mailbox,file)) switch (errno) { 1.746 + case ENOENT: /* no such file? */ 1.747 + mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL); 1.748 + return NIL; 1.749 + case 0: /* merely empty file? */ 1.750 + break; 1.751 + case EACCES: /* file protected */ 1.752 + sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox); 1.753 + MM_LOG (LOCAL->buf,ERROR); 1.754 + return NIL; 1.755 + case EINVAL: 1.756 + if (pc) return (*pc) (stream,sequence,mailbox,options); 1.757 + sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox); 1.758 + mm_log (LOCAL->buf,ERROR); 1.759 + return NIL; 1.760 + default: 1.761 + if (pc) return (*pc) (stream,sequence,mailbox,options); 1.762 + sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox); 1.763 + mm_log (LOCAL->buf,ERROR); 1.764 + return NIL; 1.765 + } 1.766 + if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : 1.767 + mail_sequence (stream,sequence))) return NIL; 1.768 + /* got file? */ 1.769 + if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) { 1.770 + sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno)); 1.771 + mm_log (LOCAL->buf,ERROR); 1.772 + return NIL; 1.773 + } 1.774 + mm_critical (stream); /* go critical */ 1.775 + /* get exclusive parse/append permission */ 1.776 + if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) { 1.777 + mm_log ("Unable to lock copy mailbox",ERROR); 1.778 + mm_nocritical (stream); 1.779 + return NIL; 1.780 + } 1.781 + fstat (fd,&sbuf); /* get current file size */ 1.782 + lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ 1.783 + 1.784 + /* for each requested message */ 1.785 + for (i = 1; ret && (i <= stream->nmsgs); i++) 1.786 + if ((elt = mail_elt (stream,i))->sequence) { 1.787 + lseek (LOCAL->fd,elt->private.special.offset,L_SET); 1.788 + /* number of bytes to copy */ 1.789 + k = elt->private.special.text.size + elt->rfc822_size; 1.790 + do { /* read from source position */ 1.791 + j = min (k,LOCAL->buflen); 1.792 + read (LOCAL->fd,LOCAL->buf,j); 1.793 + if (write (fd,LOCAL->buf,j) < 0) ret = NIL; 1.794 + } while (ret && (k -= j));/* until done */ 1.795 + } 1.796 + /* make sure all the updates take */ 1.797 + if (!(ret && (ret = !fsync (fd)))) { 1.798 + sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); 1.799 + mm_log (LOCAL->buf,ERROR); 1.800 + ftruncate (fd,sbuf.st_size); 1.801 + } 1.802 + /* set atime to now-1 if successful copy */ 1.803 + if (ret) times.actime = time (0) - 1; 1.804 + /* else preserved \Marked status */ 1.805 + else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? 1.806 + sbuf.st_atime : time (0); 1.807 + times.modtime = sbuf.st_mtime;/* preserve mtime */ 1.808 + utime (file,×); /* set the times */ 1.809 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.810 + close (fd); /* close the file */ 1.811 + mm_nocritical (stream); /* release critical */ 1.812 + /* delete all requested messages */ 1.813 + if (ret && (options & CP_MOVE)) { 1.814 + for (i = 1; i <= stream->nmsgs; i++) 1.815 + if ((elt = mtx_elt (stream,i))->sequence) { 1.816 + elt->deleted = T; /* mark message deleted */ 1.817 + /* recalculate status */ 1.818 + mtx_update_status (stream,i,NIL); 1.819 + } 1.820 + if (!stream->rdonly) { /* make sure the update takes */ 1.821 + fsync (LOCAL->fd); 1.822 + fstat (LOCAL->fd,&sbuf); /* get current write time */ 1.823 + times.modtime = LOCAL->filetime = sbuf.st_mtime; 1.824 + times.actime = time (0); /* make sure atime remains greater */ 1.825 + utime (stream->mailbox,×); 1.826 + } 1.827 + } 1.828 + if (ret && mail_parameters (NIL,GET_COPYUID,NIL)) 1.829 + mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN); 1.830 + return ret; 1.831 +} 1.832 + 1.833 +/* MTX mail append message from stringstruct 1.834 + * Accepts: MAIL stream 1.835 + * destination mailbox 1.836 + * append callback 1.837 + * data for callback 1.838 + * Returns: T if append successful, else NIL 1.839 + */ 1.840 + 1.841 +long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) 1.842 +{ 1.843 + struct stat sbuf; 1.844 + int fd,ld,c; 1.845 + char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; 1.846 + struct utimbuf times; 1.847 + FILE *df; 1.848 + MESSAGECACHE elt; 1.849 + long f; 1.850 + unsigned long i,uf; 1.851 + STRING *message; 1.852 + long ret = LONGT; 1.853 + /* default stream to prototype */ 1.854 + if (!stream) stream = &mtxproto; 1.855 + /* make sure valid mailbox */ 1.856 + if (!mtx_isvalid (mailbox,file)) switch (errno) { 1.857 + case ENOENT: /* no such file? */ 1.858 + if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX"); 1.859 + else { 1.860 + mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); 1.861 + return NIL; 1.862 + } 1.863 + /* falls through */ 1.864 + case 0: /* merely empty file? */ 1.865 + break; 1.866 + case EACCES: /* file protected */ 1.867 + sprintf (tmp,"Can't access destination: %.80s",mailbox); 1.868 + MM_LOG (tmp,ERROR); 1.869 + return NIL; 1.870 + case EINVAL: 1.871 + sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox); 1.872 + mm_log (tmp,ERROR); 1.873 + return NIL; 1.874 + default: 1.875 + sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox); 1.876 + mm_log (tmp,ERROR); 1.877 + return NIL; 1.878 + } 1.879 + /* get first message */ 1.880 + if (!(*af) (stream,data,&flags,&date,&message)) return NIL; 1.881 + 1.882 + /* open destination mailbox */ 1.883 + if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) 1.884 + < 0) || !(df = fdopen (fd,"ab"))) { 1.885 + sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); 1.886 + mm_log (tmp,ERROR); 1.887 + return NIL; 1.888 + } 1.889 + /* get parse/append permission */ 1.890 + if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) { 1.891 + mm_log ("Unable to lock append mailbox",ERROR); 1.892 + close (fd); 1.893 + return NIL; 1.894 + } 1.895 + mm_critical (stream); /* go critical */ 1.896 + fstat (fd,&sbuf); /* get current file size */ 1.897 + errno = 0; 1.898 + do { /* parse flags */ 1.899 + if (!SIZE (message)) { /* guard against zero-length */ 1.900 + mm_log ("Append of zero-length message",ERROR); 1.901 + ret = NIL; 1.902 + break; 1.903 + } 1.904 + f = mail_parse_flags (stream,flags,&i); 1.905 + /* reverse bits (dontcha wish we had CIRC?) */ 1.906 + for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); 1.907 + if (date) { /* parse date if given */ 1.908 + if (!mail_parse_date (&elt,date)) { 1.909 + sprintf (tmp,"Bad date in append: %.80s",date); 1.910 + mm_log (tmp,ERROR); 1.911 + ret = NIL; /* mark failure */ 1.912 + break; 1.913 + } 1.914 + mail_date (tmp,&elt); /* write preseved date */ 1.915 + } 1.916 + else internal_date (tmp); /* get current date in IMAP format */ 1.917 + /* write header */ 1.918 + if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf, 1.919 + (unsigned long) f) < 0) ret = NIL; 1.920 + else { /* write message */ 1.921 + if (i) do c = 0xff & SNX (message); 1.922 + while ((putc (c,df) != EOF) && --i); 1.923 + /* get next message */ 1.924 + if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL; 1.925 + } 1.926 + } while (ret && message); 1.927 + /* if error... */ 1.928 + if (!ret || (fflush (df) == EOF)) { 1.929 + ftruncate (fd,sbuf.st_size);/* revert file */ 1.930 + close (fd); /* make sure fclose() doesn't corrupt us */ 1.931 + if (errno) { 1.932 + sprintf (tmp,"Message append failed: %s",strerror (errno)); 1.933 + mm_log (tmp,ERROR); 1.934 + } 1.935 + ret = NIL; 1.936 + } 1.937 + if (ret) times.actime = time (0) - 1; 1.938 + /* else preserved \Marked status */ 1.939 + else times.actime = (sbuf.st_ctime > sbuf.st_atime) ? 1.940 + sbuf.st_atime : time (0); 1.941 + times.modtime = sbuf.st_mtime;/* preserve mtime */ 1.942 + utime (file,×); /* set the times */ 1.943 + fclose (df); /* close the file */ 1.944 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.945 + mm_nocritical (stream); /* release critical */ 1.946 + if (ret && mail_parameters (NIL,GET_APPENDUID,NIL)) 1.947 + mm_log ("Can not return meaningful APPENDUID with this mailbox format", 1.948 + WARN); 1.949 + return ret; 1.950 +} 1.951 + 1.952 +/* Internal routines */ 1.953 + 1.954 + 1.955 +/* MTX mail parse mailbox 1.956 + * Accepts: MAIL stream 1.957 + * Returns: T if parse OK 1.958 + * NIL if failure, stream aborted 1.959 + */ 1.960 + 1.961 +long mtx_parse (MAILSTREAM *stream) 1.962 +{ 1.963 + struct stat sbuf; 1.964 + MESSAGECACHE *elt = NIL; 1.965 + unsigned char c,*s,*t,*x; 1.966 + char tmp[MAILTMPLEN]; 1.967 + unsigned long i,j; 1.968 + long curpos = LOCAL->filesize; 1.969 + long nmsgs = stream->nmsgs; 1.970 + long recent = stream->recent; 1.971 + short added = NIL; 1.972 + short silent = stream->silent; 1.973 + fstat (LOCAL->fd,&sbuf); /* get status */ 1.974 + if (sbuf.st_size < curpos) { /* sanity check */ 1.975 + sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size); 1.976 + mm_log (tmp,ERROR); 1.977 + mtx_close (stream,NIL); 1.978 + return NIL; 1.979 + } 1.980 + stream->silent = T; /* don't pass up mm_exists() events yet */ 1.981 + while (sbuf.st_size - curpos){/* while there is stuff to parse */ 1.982 + /* get to that position in the file */ 1.983 + lseek (LOCAL->fd,curpos,L_SET); 1.984 + if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { 1.985 + sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", 1.986 + (unsigned long) curpos,(unsigned long) sbuf.st_size, 1.987 + i ? strerror (errno) : "no data read"); 1.988 + mm_log (tmp,ERROR); 1.989 + mtx_close (stream,NIL); 1.990 + return NIL; 1.991 + } 1.992 + LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ 1.993 + if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) { 1.994 + sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s", 1.995 + (unsigned long) curpos,i,(char *) LOCAL->buf); 1.996 + mm_log (tmp,ERROR); 1.997 + mtx_close (stream,NIL); 1.998 + return NIL; 1.999 + } 1.1000 + *s = '\0'; /* tie off header line */ 1.1001 + i = (s + 2) - LOCAL->buf; /* note start of text offset */ 1.1002 + if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { 1.1003 + sprintf (tmp,"Unable to parse internal header at %lu: %s", 1.1004 + (unsigned long) curpos,(char *) LOCAL->buf); 1.1005 + mm_log (tmp,ERROR); 1.1006 + mtx_close (stream,NIL); 1.1007 + return NIL; 1.1008 + } 1.1009 + *s++ = '\0'; *t++ = '\0'; /* tie off fields */ 1.1010 + 1.1011 + added = T; /* note that a new message was added */ 1.1012 + /* swell the cache */ 1.1013 + mail_exists (stream,++nmsgs); 1.1014 + /* instantiate an elt for this message */ 1.1015 + (elt = mail_elt (stream,nmsgs))->valid = T; 1.1016 + elt->private.uid = ++stream->uid_last; 1.1017 + /* note file offset of header */ 1.1018 + elt->private.special.offset = curpos; 1.1019 + /* in case error */ 1.1020 + elt->private.special.text.size = 0; 1.1021 + /* header size not known yet */ 1.1022 + elt->private.msg.header.text.size = 0; 1.1023 + x = s; /* parse the header components */ 1.1024 + if (mail_parse_date (elt,LOCAL->buf) && 1.1025 + (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) && 1.1026 + isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && 1.1027 + isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && 1.1028 + isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && 1.1029 + isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) 1.1030 + elt->private.special.text.size = i; 1.1031 + else { /* oops */ 1.1032 + sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", 1.1033 + curpos,(char *) LOCAL->buf,(char *) x,(char *) t); 1.1034 + mm_log (tmp,ERROR); 1.1035 + mtx_close (stream,NIL); 1.1036 + return NIL; 1.1037 + } 1.1038 + /* make sure didn't run off end of file */ 1.1039 + if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) { 1.1040 + sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", 1.1041 + elt->private.special.offset,(unsigned long) curpos, 1.1042 + (unsigned long) sbuf.st_size); 1.1043 + mm_log (tmp,ERROR); 1.1044 + mtx_close (stream,NIL); 1.1045 + return NIL; 1.1046 + } 1.1047 + c = t[10]; /* remember first system flags byte */ 1.1048 + t[10] = '\0'; /* tie off flags */ 1.1049 + j = strtoul (t,NIL,8); /* get user flags value */ 1.1050 + t[10] = c; /* restore first system flags byte */ 1.1051 + /* set up all valid user flags (reversed!) */ 1.1052 + while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && 1.1053 + stream->user_flags[i]) elt->user_flags |= 1 << i; 1.1054 + /* calculate system flags */ 1.1055 + if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; 1.1056 + if (j & fDELETED) elt->deleted = T; 1.1057 + if (j & fFLAGGED) elt->flagged = T; 1.1058 + if (j & fANSWERED) elt->answered = T; 1.1059 + if (j & fDRAFT) elt->draft = T; 1.1060 + if (!(j & fOLD)) { /* newly arrived message? */ 1.1061 + elt->recent = T; 1.1062 + recent++; /* count up a new recent message */ 1.1063 + /* mark it as old */ 1.1064 + mtx_update_status (stream,nmsgs,NIL); 1.1065 + } 1.1066 + } 1.1067 + fsync (LOCAL->fd); /* make sure all the fOLD flags take */ 1.1068 + /* update parsed file size and time */ 1.1069 + LOCAL->filesize = sbuf.st_size; 1.1070 + fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ 1.1071 + LOCAL->filetime = sbuf.st_mtime; 1.1072 + if (added && !stream->rdonly){/* make sure atime updated */ 1.1073 + struct utimbuf times; 1.1074 + times.actime = time (0); 1.1075 + times.modtime = LOCAL->filetime; 1.1076 + utime (stream->mailbox,×); 1.1077 + } 1.1078 + stream->silent = silent; /* can pass up events now */ 1.1079 + mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ 1.1080 + mail_recent (stream,recent); /* and of change in recent messages */ 1.1081 + return LONGT; /* return the winnage */ 1.1082 +} 1.1083 + 1.1084 +/* MTX get cache element with status updating from file 1.1085 + * Accepts: MAIL stream 1.1086 + * message number 1.1087 + * Returns: cache element 1.1088 + */ 1.1089 + 1.1090 +MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno) 1.1091 +{ 1.1092 + MESSAGECACHE *elt = mail_elt (stream,msgno); 1.1093 + struct { /* old flags */ 1.1094 + unsigned int seen : 1; 1.1095 + unsigned int deleted : 1; 1.1096 + unsigned int flagged : 1; 1.1097 + unsigned int answered : 1; 1.1098 + unsigned int draft : 1; 1.1099 + unsigned long user_flags; 1.1100 + } old; 1.1101 + old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; 1.1102 + old.answered = elt->answered; old.draft = elt->draft; 1.1103 + old.user_flags = elt->user_flags; 1.1104 + mtx_read_flags (stream,elt); 1.1105 + if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || 1.1106 + (old.flagged != elt->flagged) || (old.answered != elt->answered) || 1.1107 + (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) 1.1108 + mm_flags (stream,msgno); /* let top level know */ 1.1109 + return elt; 1.1110 +} 1.1111 + 1.1112 +/* MTX read flags from file 1.1113 + * Accepts: MAIL stream 1.1114 + * Returns: cache element 1.1115 + */ 1.1116 + 1.1117 +void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) 1.1118 +{ 1.1119 + unsigned long i,j; 1.1120 + /* noop if readonly and have valid flags */ 1.1121 + if (stream->rdonly && elt->valid) return; 1.1122 + /* set the seek pointer */ 1.1123 + lseek (LOCAL->fd,(off_t) elt->private.special.offset + 1.1124 + elt->private.special.text.size - 14,L_SET); 1.1125 + /* read the new flags */ 1.1126 + if (read (LOCAL->fd,LOCAL->buf,12) < 0) { 1.1127 + sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); 1.1128 + fatal (LOCAL->buf); 1.1129 + } 1.1130 + /* calculate system flags */ 1.1131 + i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); 1.1132 + elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; 1.1133 + elt->flagged = i & fFLAGGED ? T : NIL; 1.1134 + elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; 1.1135 + LOCAL->buf[10] = '\0'; /* tie off flags */ 1.1136 + j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ 1.1137 + /* set up all valid user flags (reversed!) */ 1.1138 + while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && 1.1139 + stream->user_flags[i]) elt->user_flags |= 1 << i; 1.1140 + elt->valid = T; /* have valid flags now */ 1.1141 +} 1.1142 + 1.1143 +/* MTX update status string 1.1144 + * Accepts: MAIL stream 1.1145 + * message number 1.1146 + * flag saying whether or not to sync 1.1147 + */ 1.1148 + 1.1149 +void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) 1.1150 +{ 1.1151 + struct utimbuf times; 1.1152 + struct stat sbuf; 1.1153 + MESSAGECACHE *elt = mail_elt (stream,msgno); 1.1154 + unsigned long j,k = 0; 1.1155 + /* readonly */ 1.1156 + if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt); 1.1157 + else { /* readwrite */ 1.1158 + j = elt->user_flags; /* get user flags */ 1.1159 + /* reverse bits (dontcha wish we had CIRC?) */ 1.1160 + while (j) k |= 1 << (29 - find_rightmost_bit (&j)); 1.1161 + /* print new flag string */ 1.1162 + sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) 1.1163 + (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + 1.1164 + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + 1.1165 + (fDRAFT * elt->draft))); 1.1166 + while (T) { /* get to that place in the file */ 1.1167 + lseek (LOCAL->fd,(off_t) elt->private.special.offset + 1.1168 + elt->private.special.text.size - 14,L_SET); 1.1169 + /* write new flags */ 1.1170 + if (write (LOCAL->fd,LOCAL->buf,12) > 0) break; 1.1171 + mm_notify (stream,strerror (errno),WARN); 1.1172 + mm_diskerror (stream,errno,T); 1.1173 + } 1.1174 + if (syncflag) { /* sync if requested */ 1.1175 + fsync (LOCAL->fd); 1.1176 + fstat (LOCAL->fd,&sbuf); /* get new write time */ 1.1177 + times.modtime = LOCAL->filetime = sbuf.st_mtime; 1.1178 + times.actime = time (0); /* make sure read is later */ 1.1179 + utime (stream->mailbox,×); 1.1180 + } 1.1181 + } 1.1182 +} 1.1183 + 1.1184 +/* MTX locate header for a message 1.1185 + * Accepts: MAIL stream 1.1186 + * message number 1.1187 + * pointer to returned header size 1.1188 + * Returns: position of header in file 1.1189 + */ 1.1190 + 1.1191 +unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno, 1.1192 + unsigned long *size) 1.1193 +{ 1.1194 + unsigned long siz; 1.1195 + long i = 0; 1.1196 + int q = 0; 1.1197 + char *s,tmp[MAILTMPLEN]; 1.1198 + MESSAGECACHE *elt = mtx_elt (stream,msgno); 1.1199 + unsigned long ret = elt->private.special.offset + 1.1200 + elt->private.special.text.size; 1.1201 + /* is header size known? */ 1.1202 + if (!(*size = elt->private.msg.header.text.size)) { 1.1203 + lseek (LOCAL->fd,ret,L_SET);/* get to header position */ 1.1204 + /* search message for CRLF CRLF */ 1.1205 + for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) { 1.1206 + /* read another buffer as necessary */ 1.1207 + if ((--i <= 0) && /* buffer empty? */ 1.1208 + (read (LOCAL->fd,s = tmp, 1.1209 + i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)) 1.1210 + return ret; /* I/O error? */ 1.1211 + switch (q) { /* sniff at buffer */ 1.1212 + case 0: /* first character */ 1.1213 + q = (*s++ == '\015') ? 1 : 0; 1.1214 + break; 1.1215 + case 1: /* second character */ 1.1216 + q = (*s++ == '\012') ? 2 : 0; 1.1217 + break; 1.1218 + case 2: /* third character */ 1.1219 + q = (*s++ == '\015') ? 3 : 0; 1.1220 + break; 1.1221 + case 3: /* fourth character */ 1.1222 + if (*s++ == '\012') { /* have the sequence? */ 1.1223 + /* yes, note for later */ 1.1224 + elt->private.msg.header.text.size = *size = siz; 1.1225 + return ret; 1.1226 + } 1.1227 + q = 0; /* lost... */ 1.1228 + break; 1.1229 + } 1.1230 + } 1.1231 + /* header consumes entire message */ 1.1232 + elt->private.msg.header.text.size = *size = elt->rfc822_size; 1.1233 + } 1.1234 + return ret; 1.1235 +}