imapext-2007
diff src/osdep/unix/tenex.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/tenex.c Mon Sep 14 15:17:45 2009 +0900 1.3 @@ -0,0 +1,1470 @@ 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: Tenex 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: 11 October 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 + * TEXT SIZE SEMANTICS 1.40 + * 1.41 + * Most of the text sizes are in internal (LF-only) form, except for the 1.42 + * msg.text size. Beware. 1.43 + */ 1.44 + 1.45 +#include <stdio.h> 1.46 +#include <ctype.h> 1.47 +#include <errno.h> 1.48 +extern int errno; /* just in case */ 1.49 +#include "mail.h" 1.50 +#include "osdep.h" 1.51 +#include <sys/stat.h> 1.52 +#include "misc.h" 1.53 +#include "dummy.h" 1.54 + 1.55 +/* TENEX I/O stream local data */ 1.56 + 1.57 +typedef struct tenex_local { 1.58 + unsigned int shouldcheck: 1; /* if ping should do a check instead */ 1.59 + unsigned int mustcheck: 1; /* if ping must do a check instead */ 1.60 + int fd; /* file descriptor for I/O */ 1.61 + off_t filesize; /* file size parsed */ 1.62 + time_t filetime; /* last file time */ 1.63 + time_t lastsnarf; /* local snarf time */ 1.64 + unsigned char *buf; /* temporary buffer */ 1.65 + unsigned long buflen; /* current size of temporary buffer */ 1.66 + unsigned long uid; /* current text uid */ 1.67 + SIZEDTEXT text; /* current text */ 1.68 +} TENEXLOCAL; 1.69 + 1.70 + 1.71 +/* Convenient access to local data */ 1.72 + 1.73 +#define LOCAL ((TENEXLOCAL *) stream->local) 1.74 + 1.75 + 1.76 +/* Function prototypes */ 1.77 + 1.78 +DRIVER *tenex_valid (char *name); 1.79 +int tenex_isvalid (char *name,char *tmp); 1.80 +void *tenex_parameters (long function,void *value); 1.81 +void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); 1.82 +void tenex_list (MAILSTREAM *stream,char *ref,char *pat); 1.83 +void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat); 1.84 +long tenex_create (MAILSTREAM *stream,char *mailbox); 1.85 +long tenex_delete (MAILSTREAM *stream,char *mailbox); 1.86 +long tenex_rename (MAILSTREAM *stream,char *old,char *newname); 1.87 +long tenex_status (MAILSTREAM *stream,char *mbx,long flags); 1.88 +MAILSTREAM *tenex_open (MAILSTREAM *stream); 1.89 +void tenex_close (MAILSTREAM *stream,long options); 1.90 +void tenex_fast (MAILSTREAM *stream,char *sequence,long flags); 1.91 +void tenex_flags (MAILSTREAM *stream,char *sequence,long flags); 1.92 +char *tenex_header (MAILSTREAM *stream,unsigned long msgno, 1.93 + unsigned long *length,long flags); 1.94 +long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); 1.95 +void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags); 1.96 +void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt); 1.97 +long tenex_ping (MAILSTREAM *stream); 1.98 +void tenex_check (MAILSTREAM *stream); 1.99 +void tenex_snarf (MAILSTREAM *stream); 1.100 +long tenex_expunge (MAILSTREAM *stream,char *sequence,long options); 1.101 +long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options); 1.102 +long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); 1.103 + 1.104 +unsigned long tenex_size (MAILSTREAM *stream,unsigned long m); 1.105 +char *tenex_file (char *dst,char *name); 1.106 +long tenex_parse (MAILSTREAM *stream); 1.107 +MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno); 1.108 +void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt); 1.109 +void tenex_update_status (MAILSTREAM *stream,unsigned long msgno, 1.110 + long syncflag); 1.111 +unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, 1.112 + unsigned long *size); 1.113 + 1.114 +/* Tenex mail routines */ 1.115 + 1.116 + 1.117 +/* Driver dispatch used by MAIL */ 1.118 + 1.119 +DRIVER tenexdriver = { 1.120 + "tenex", /* driver name */ 1.121 + DR_LOCAL|DR_MAIL|DR_NOSTICKY|DR_LOCKING, 1.122 + /* driver flags */ 1.123 + (DRIVER *) NIL, /* next driver */ 1.124 + tenex_valid, /* mailbox is valid for us */ 1.125 + tenex_parameters, /* manipulate parameters */ 1.126 + tenex_scan, /* scan mailboxes */ 1.127 + tenex_list, /* list mailboxes */ 1.128 + tenex_lsub, /* list subscribed mailboxes */ 1.129 + NIL, /* subscribe to mailbox */ 1.130 + NIL, /* unsubscribe from mailbox */ 1.131 + dummy_create, /* create mailbox */ 1.132 + tenex_delete, /* delete mailbox */ 1.133 + tenex_rename, /* rename mailbox */ 1.134 + tenex_status, /* status of mailbox */ 1.135 + tenex_open, /* open mailbox */ 1.136 + tenex_close, /* close mailbox */ 1.137 + tenex_fast, /* fetch message "fast" attributes */ 1.138 + tenex_flags, /* fetch message flags */ 1.139 + NIL, /* fetch overview */ 1.140 + NIL, /* fetch message envelopes */ 1.141 + tenex_header, /* fetch message header */ 1.142 + tenex_text, /* fetch message body */ 1.143 + NIL, /* fetch partial message text */ 1.144 + NIL, /* unique identifier */ 1.145 + NIL, /* message number */ 1.146 + tenex_flag, /* modify flags */ 1.147 + tenex_flagmsg, /* per-message modify flags */ 1.148 + NIL, /* search for message based on criteria */ 1.149 + NIL, /* sort messages */ 1.150 + NIL, /* thread messages */ 1.151 + tenex_ping, /* ping mailbox to see if still alive */ 1.152 + tenex_check, /* check for new messages */ 1.153 + tenex_expunge, /* expunge deleted messages */ 1.154 + tenex_copy, /* copy messages to another mailbox */ 1.155 + tenex_append, /* append string message to mailbox */ 1.156 + NIL /* garbage collect stream */ 1.157 +}; 1.158 + 1.159 + /* prototype stream */ 1.160 +MAILSTREAM tenexproto = {&tenexdriver}; 1.161 + 1.162 +/* Tenex mail validate mailbox 1.163 + * Accepts: mailbox name 1.164 + * Returns: our driver if name is valid, NIL otherwise 1.165 + */ 1.166 + 1.167 +DRIVER *tenex_valid (char *name) 1.168 +{ 1.169 + char tmp[MAILTMPLEN]; 1.170 + return tenex_isvalid (name,tmp) ? &tenexdriver : NIL; 1.171 +} 1.172 + 1.173 + 1.174 +/* Tenex mail test for valid mailbox 1.175 + * Accepts: mailbox name 1.176 + * Returns: T if valid, NIL otherwise 1.177 + */ 1.178 + 1.179 +int tenex_isvalid (char *name,char *tmp) 1.180 +{ 1.181 + int fd; 1.182 + int ret = NIL; 1.183 + char *s,file[MAILTMPLEN]; 1.184 + struct stat sbuf; 1.185 + time_t tp[2]; 1.186 + errno = EINVAL; /* assume invalid argument */ 1.187 + /* if file, get its status */ 1.188 + if ((s = tenex_file (file,name)) && !stat (s,&sbuf)) { 1.189 + if (!sbuf.st_size) { /* allow empty file if INBOX */ 1.190 + if ((s = mailboxfile (tmp,name)) && !*s) ret = T; 1.191 + else errno = 0; /* empty file */ 1.192 + } 1.193 + else if ((fd = open (file,O_RDONLY,NIL)) >= 0) { 1.194 + memset (tmp,'\0',MAILTMPLEN); 1.195 + if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) && 1.196 + (s[-1] != '\015')) { /* valid format? */ 1.197 + *s = '\0'; /* tie off header */ 1.198 + /* must begin with dd-mmm-yy" */ 1.199 + ret = (((tmp[2] == '-' && tmp[6] == '-') || 1.200 + (tmp[1] == '-' && tmp[5] == '-')) && 1.201 + (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL; 1.202 + } 1.203 + else errno = -1; /* bogus format */ 1.204 + close (fd); /* close the file */ 1.205 + /* \Marked status? */ 1.206 + if (sbuf.st_ctime > sbuf.st_atime) { 1.207 + tp[0] = sbuf.st_atime; /* preserve atime and mtime */ 1.208 + tp[1] = sbuf.st_mtime; 1.209 + utime (file,tp); /* set the times */ 1.210 + } 1.211 + } 1.212 + } 1.213 + /* in case INBOX but not tenex format */ 1.214 + else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1; 1.215 + return ret; /* return what we should */ 1.216 +} 1.217 + 1.218 +/* Tenex manipulate driver parameters 1.219 + * Accepts: function code 1.220 + * function-dependent value 1.221 + * Returns: function-dependent return value 1.222 + */ 1.223 + 1.224 +void *tenex_parameters (long function,void *value) 1.225 +{ 1.226 + void *ret = NIL; 1.227 + switch ((int) function) { 1.228 + case GET_INBOXPATH: 1.229 + if (value) ret = tenex_file ((char *) value,"INBOX"); 1.230 + break; 1.231 + } 1.232 + return ret; 1.233 +} 1.234 + 1.235 + 1.236 +/* Tenex mail scan mailboxes 1.237 + * Accepts: mail stream 1.238 + * reference 1.239 + * pattern to search 1.240 + * string to scan 1.241 + */ 1.242 + 1.243 +void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) 1.244 +{ 1.245 + if (stream) dummy_scan (NIL,ref,pat,contents); 1.246 +} 1.247 + 1.248 + 1.249 +/* Tenex mail list mailboxes 1.250 + * Accepts: mail stream 1.251 + * reference 1.252 + * pattern to search 1.253 + */ 1.254 + 1.255 +void tenex_list (MAILSTREAM *stream,char *ref,char *pat) 1.256 +{ 1.257 + if (stream) dummy_list (NIL,ref,pat); 1.258 +} 1.259 + 1.260 + 1.261 +/* Tenex mail list subscribed mailboxes 1.262 + * Accepts: mail stream 1.263 + * reference 1.264 + * pattern to search 1.265 + */ 1.266 + 1.267 +void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat) 1.268 +{ 1.269 + if (stream) dummy_lsub (NIL,ref,pat); 1.270 +} 1.271 + 1.272 +/* Tenex mail delete mailbox 1.273 + * Accepts: MAIL stream 1.274 + * mailbox name to delete 1.275 + * Returns: T on success, NIL on failure 1.276 + */ 1.277 + 1.278 +long tenex_delete (MAILSTREAM *stream,char *mailbox) 1.279 +{ 1.280 + return tenex_rename (stream,mailbox,NIL); 1.281 +} 1.282 + 1.283 + 1.284 +/* Tenex mail rename mailbox 1.285 + * Accepts: MAIL stream 1.286 + * old mailbox name 1.287 + * new mailbox name (or NIL for delete) 1.288 + * Returns: T on success, NIL on failure 1.289 + */ 1.290 + 1.291 +long tenex_rename (MAILSTREAM *stream,char *old,char *newname) 1.292 +{ 1.293 + long ret = T; 1.294 + char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; 1.295 + int fd,ld; 1.296 + struct stat sbuf; 1.297 + if (!tenex_file (file,old) || 1.298 + (newname && (!((s = mailboxfile (tmp,newname)) && *s) || 1.299 + ((s = strrchr (tmp,'/')) && !s[1])))) { 1.300 + sprintf (tmp,newname ? 1.301 + "Can't rename mailbox %.80s to %.80s: invalid name" : 1.302 + "Can't delete mailbox %.80s: invalid name", 1.303 + old,newname); 1.304 + MM_LOG (tmp,ERROR); 1.305 + return NIL; 1.306 + } 1.307 + else if ((fd = open (file,O_RDWR,NIL)) < 0) { 1.308 + sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno)); 1.309 + MM_LOG (tmp,ERROR); 1.310 + return NIL; 1.311 + } 1.312 + /* get exclusive parse/append permission */ 1.313 + if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) { 1.314 + MM_LOG ("Unable to lock rename mailbox",ERROR); 1.315 + return NIL; 1.316 + } 1.317 + /* lock out other users */ 1.318 + if (flock (fd,LOCK_EX|LOCK_NB)) { 1.319 + close (fd); /* couldn't lock, give up on it then */ 1.320 + sprintf (tmp,"Mailbox %.80s is in use by another process",old); 1.321 + MM_LOG (tmp,ERROR); 1.322 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.323 + return NIL; 1.324 + } 1.325 + 1.326 + if (newname) { /* want rename? */ 1.327 + if (s = strrchr (tmp,'/')) {/* found superior to destination name? */ 1.328 + c = *++s; /* remember first character of inferior */ 1.329 + *s = '\0'; /* tie off to get just superior */ 1.330 + /* name doesn't exist, create it */ 1.331 + if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && 1.332 + !dummy_create_path (stream,tmp,get_dir_protection (newname))) 1.333 + ret = NIL; 1.334 + else *s = c; /* restore full name */ 1.335 + } 1.336 + /* rename the file */ 1.337 + if (ret && rename (file,tmp)) { 1.338 + sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname, 1.339 + strerror (errno)); 1.340 + MM_LOG (tmp,ERROR); 1.341 + ret = NIL; /* set failure */ 1.342 + } 1.343 + } 1.344 + else if (unlink (file)) { 1.345 + sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno)); 1.346 + MM_LOG (tmp,ERROR); 1.347 + ret = NIL; /* set failure */ 1.348 + } 1.349 + flock (fd,LOCK_UN); /* release lock on the file */ 1.350 + close (fd); /* close the file */ 1.351 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.352 + /* recreate file if renamed INBOX */ 1.353 + if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"mail.txt"); 1.354 + return ret; /* return success */ 1.355 +} 1.356 + 1.357 +/* Tenex Mail status 1.358 + * Accepts: mail stream 1.359 + * mailbox name 1.360 + * status flags 1.361 + * Returns: T on success, NIL on failure 1.362 + */ 1.363 + 1.364 +long tenex_status (MAILSTREAM *stream,char *mbx,long flags) 1.365 +{ 1.366 + MAILSTATUS status; 1.367 + unsigned long i; 1.368 + MAILSTREAM *tstream = NIL; 1.369 + MAILSTREAM *systream = NIL; 1.370 + /* make temporary stream (unless this mbx) */ 1.371 + if (!stream && !(stream = tstream = 1.372 + mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL; 1.373 + status.flags = flags; /* return status values */ 1.374 + status.messages = stream->nmsgs; 1.375 + status.recent = stream->recent; 1.376 + if (flags & SA_UNSEEN) /* must search to get unseen messages */ 1.377 + for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++) 1.378 + if (!mail_elt (stream,i)->seen) status.unseen++; 1.379 + status.uidnext = stream->uid_last + 1; 1.380 + status.uidvalidity = stream->uid_validity; 1.381 + /* calculate post-snarf results */ 1.382 + if (!status.recent && stream->inbox && 1.383 + (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) { 1.384 + status.messages += systream->nmsgs; 1.385 + status.recent += systream->recent; 1.386 + if (flags & SA_UNSEEN) /* must search to get unseen messages */ 1.387 + for (i = 1; i <= systream->nmsgs; i++) 1.388 + if (!mail_elt (systream,i)->seen) status.unseen++; 1.389 + /* kludge but probably good enough */ 1.390 + status.uidnext += systream->nmsgs; 1.391 + } 1.392 + MM_STATUS(stream,mbx,&status);/* pass status to main program */ 1.393 + if (tstream) mail_close (tstream); 1.394 + if (systream) mail_close (systream); 1.395 + return T; /* success */ 1.396 +} 1.397 + 1.398 +/* Tenex mail open 1.399 + * Accepts: stream to open 1.400 + * Returns: stream on success, NIL on failure 1.401 + */ 1.402 + 1.403 +MAILSTREAM *tenex_open (MAILSTREAM *stream) 1.404 +{ 1.405 + int fd,ld; 1.406 + char tmp[MAILTMPLEN]; 1.407 + blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); 1.408 + /* return prototype for OP_PROTOTYPE call */ 1.409 + if (!stream) return user_flags (&tenexproto); 1.410 + if (stream->local) fatal ("tenex recycle stream"); 1.411 + user_flags (stream); /* set up user flags */ 1.412 + /* canonicalize the mailbox name */ 1.413 + if (!tenex_file (tmp,stream->mailbox)) { 1.414 + sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox); 1.415 + MM_LOG (tmp,ERROR); 1.416 + } 1.417 + if (stream->rdonly || 1.418 + (fd = open (tmp,O_RDWR,NIL)) < 0) { 1.419 + if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { 1.420 + sprintf (tmp,"Can't open mailbox: %s",strerror (errno)); 1.421 + MM_LOG (tmp,ERROR); 1.422 + return NIL; 1.423 + } 1.424 + else if (!stream->rdonly) { /* got it, but readonly */ 1.425 + MM_LOG ("Can't get write access to mailbox, access is readonly",WARN); 1.426 + stream->rdonly = T; 1.427 + } 1.428 + } 1.429 + stream->local = fs_get (sizeof (TENEXLOCAL)); 1.430 + LOCAL->buf = (char *) fs_get (CHUNKSIZE); 1.431 + LOCAL->buflen = CHUNKSIZE - 1; 1.432 + LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE); 1.433 + LOCAL->text.size = CHUNKSIZE - 1; 1.434 + 1.435 + /* note if an INBOX or not */ 1.436 + stream->inbox = !compare_cstring (stream->mailbox,"INBOX"); 1.437 + LOCAL->fd = fd; /* bind the file */ 1.438 + /* flush old name */ 1.439 + fs_give ((void **) &stream->mailbox); 1.440 + /* save canonical name */ 1.441 + stream->mailbox = cpystr (tmp); 1.442 + /* get shared parse permission */ 1.443 + if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) { 1.444 + MM_LOG ("Unable to lock open mailbox",ERROR); 1.445 + return NIL; 1.446 + } 1.447 + (*bn) (BLOCK_FILELOCK,NIL); 1.448 + flock (LOCAL->fd,LOCK_SH); /* lock the file */ 1.449 + (*bn) (BLOCK_NONE,NIL); 1.450 + unlockfd (ld,tmp); /* release shared parse permission */ 1.451 + LOCAL->filesize = 0; /* initialize parsed file size */ 1.452 + /* time not set up yet */ 1.453 + LOCAL->lastsnarf = LOCAL->filetime = 0; 1.454 + LOCAL->mustcheck = LOCAL->shouldcheck = NIL; 1.455 + stream->sequence++; /* bump sequence number */ 1.456 + /* parse mailbox */ 1.457 + stream->nmsgs = stream->recent = 0; 1.458 + if (tenex_ping (stream) && !stream->nmsgs) 1.459 + MM_LOG ("Mailbox is empty",(long) NIL); 1.460 + if (!LOCAL) return NIL; /* failure if stream died */ 1.461 + stream->perm_seen = stream->perm_deleted = 1.462 + stream->perm_flagged = stream->perm_answered = stream->perm_draft = 1.463 + stream->rdonly ? NIL : T; 1.464 + stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff; 1.465 + return stream; /* return stream to caller */ 1.466 +} 1.467 + 1.468 +/* Tenex mail close 1.469 + * Accepts: MAIL stream 1.470 + * close options 1.471 + */ 1.472 + 1.473 +void tenex_close (MAILSTREAM *stream,long options) 1.474 +{ 1.475 + if (stream && LOCAL) { /* only if a file is open */ 1.476 + int silent = stream->silent; 1.477 + stream->silent = T; /* note this stream is dying */ 1.478 + if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL); 1.479 + stream->silent = silent; /* restore previous status */ 1.480 + flock (LOCAL->fd,LOCK_UN); /* unlock local file */ 1.481 + close (LOCAL->fd); /* close the local file */ 1.482 + /* free local text buffer */ 1.483 + if (LOCAL->buf) fs_give ((void **) &LOCAL->buf); 1.484 + if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data); 1.485 + /* nuke the local data */ 1.486 + fs_give ((void **) &stream->local); 1.487 + stream->dtb = NIL; /* log out the DTB */ 1.488 + } 1.489 +} 1.490 + 1.491 +/* Tenex mail fetch fast data 1.492 + * Accepts: MAIL stream 1.493 + * sequence 1.494 + * option flags 1.495 + */ 1.496 + 1.497 +void tenex_fast (MAILSTREAM *stream,char *sequence,long flags) 1.498 +{ 1.499 + STRING bs; 1.500 + MESSAGECACHE *elt; 1.501 + unsigned long i; 1.502 + if (stream && LOCAL && 1.503 + ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : 1.504 + mail_sequence (stream,sequence))) 1.505 + for (i = 1; i <= stream->nmsgs; i++) 1.506 + if ((elt = mail_elt (stream,i))->sequence) { 1.507 + if (!elt->rfc822_size) { /* have header size yet? */ 1.508 + lseek (LOCAL->fd,elt->private.special.offset + 1.509 + elt->private.special.text.size,L_SET); 1.510 + /* resize bigbuf if necessary */ 1.511 + if (LOCAL->buflen < elt->private.msg.full.text.size) { 1.512 + fs_give ((void **) &LOCAL->buf); 1.513 + LOCAL->buflen = elt->private.msg.full.text.size; 1.514 + LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1); 1.515 + } 1.516 + /* tie off string */ 1.517 + LOCAL->buf[elt->private.msg.full.text.size] = '\0'; 1.518 + /* read in the message */ 1.519 + read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size); 1.520 + INIT (&bs,mail_string,(void *) LOCAL->buf, 1.521 + elt->private.msg.full.text.size); 1.522 + /* calculate its CRLF size */ 1.523 + elt->rfc822_size = strcrlflen (&bs); 1.524 + } 1.525 + tenex_elt (stream,i); /* get current flags from file */ 1.526 + } 1.527 +} 1.528 + 1.529 + 1.530 +/* Tenex mail fetch flags 1.531 + * Accepts: MAIL stream 1.532 + * sequence 1.533 + * option flags 1.534 + * Sniffs at file to get flags 1.535 + */ 1.536 + 1.537 +void tenex_flags (MAILSTREAM *stream,char *sequence,long flags) 1.538 +{ 1.539 + unsigned long i; 1.540 + if (stream && LOCAL && 1.541 + ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) : 1.542 + mail_sequence (stream,sequence))) 1.543 + for (i = 1; i <= stream->nmsgs; i++) 1.544 + if (mail_elt (stream,i)->sequence) tenex_elt (stream,i); 1.545 +} 1.546 + 1.547 +/* TENEX mail fetch message header 1.548 + * Accepts: MAIL stream 1.549 + * message # to fetch 1.550 + * pointer to returned header text length 1.551 + * option flags 1.552 + * Returns: message header in RFC822 format 1.553 + */ 1.554 + 1.555 +char *tenex_header (MAILSTREAM *stream,unsigned long msgno, 1.556 + unsigned long *length,long flags) 1.557 +{ 1.558 + char *s; 1.559 + unsigned long i; 1.560 + *length = 0; /* default to empty */ 1.561 + if (flags & FT_UID) return "";/* UID call "impossible" */ 1.562 + /* get to header position */ 1.563 + lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET); 1.564 + if (flags & FT_INTERNAL) { 1.565 + if (i > LOCAL->buflen) { /* resize if not enough space */ 1.566 + fs_give ((void **) &LOCAL->buf); 1.567 + LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); 1.568 + } 1.569 + /* slurp the data */ 1.570 + read (LOCAL->fd,LOCAL->buf,*length = i); 1.571 + } 1.572 + else { 1.573 + s = (char *) fs_get (i + 1);/* get readin buffer */ 1.574 + s[i] = '\0'; /* tie off string */ 1.575 + read (LOCAL->fd,s,i); /* slurp the data */ 1.576 + /* make CRLF copy of string */ 1.577 + *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i); 1.578 + fs_give ((void **) &s); /* free readin buffer */ 1.579 + } 1.580 + return (char *) LOCAL->buf; 1.581 +} 1.582 + 1.583 +/* TENEX mail fetch message text (body only) 1.584 + * Accepts: MAIL stream 1.585 + * message # to fetch 1.586 + * pointer to returned stringstruct 1.587 + * option flags 1.588 + * Returns: T, always 1.589 + */ 1.590 + 1.591 +long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) 1.592 +{ 1.593 + char *s; 1.594 + unsigned long i,j; 1.595 + MESSAGECACHE *elt; 1.596 + /* UID call "impossible" */ 1.597 + if (flags & FT_UID) return NIL; 1.598 + /* get message status */ 1.599 + elt = tenex_elt (stream,msgno); 1.600 + /* if message not seen */ 1.601 + if (!(flags & FT_PEEK) && !elt->seen) { 1.602 + elt->seen = T; /* mark message as seen */ 1.603 + /* recalculate status */ 1.604 + tenex_update_status (stream,msgno,T); 1.605 + MM_FLAGS (stream,msgno); 1.606 + } 1.607 + if (flags & FT_INTERNAL) { /* if internal representation wanted */ 1.608 + /* find header position */ 1.609 + i = tenex_hdrpos (stream,msgno,&j); 1.610 + if (i > LOCAL->buflen) { /* resize if not enough space */ 1.611 + fs_give ((void **) &LOCAL->buf); 1.612 + LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1); 1.613 + } 1.614 + /* go to text position */ 1.615 + lseek (LOCAL->fd,i + j,L_SET); 1.616 + /* slurp the data */ 1.617 + read (LOCAL->fd,LOCAL->buf,i); 1.618 + /* set up stringstruct for internal */ 1.619 + INIT (bs,mail_string,LOCAL->buf,i); 1.620 + } 1.621 + else { /* normal form, previous text cached? */ 1.622 + if (elt->private.uid == LOCAL->uid) 1.623 + i = elt->private.msg.text.text.size; 1.624 + else { /* not cached, cache it now */ 1.625 + LOCAL->uid = elt->private.uid; 1.626 + /* find header position */ 1.627 + i = tenex_hdrpos (stream,msgno,&j); 1.628 + /* go to text position */ 1.629 + lseek (LOCAL->fd,i + j,L_SET); 1.630 + s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1); 1.631 + s[i] = '\0'; /* tie off string */ 1.632 + read (LOCAL->fd,s,i); /* slurp the data */ 1.633 + /* make CRLF copy of string */ 1.634 + i = elt->private.msg.text.text.size = 1.635 + strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i); 1.636 + fs_give ((void **) &s); /* free readin buffer */ 1.637 + } 1.638 + /* set up stringstruct */ 1.639 + INIT (bs,mail_string,LOCAL->text.data,i); 1.640 + } 1.641 + return T; /* success */ 1.642 +} 1.643 + 1.644 +/* Tenex mail modify flags 1.645 + * Accepts: MAIL stream 1.646 + * sequence 1.647 + * flag(s) 1.648 + * option flags 1.649 + */ 1.650 + 1.651 +void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags) 1.652 +{ 1.653 + time_t tp[2]; 1.654 + struct stat sbuf; 1.655 + if (!stream->rdonly) { /* make sure the update takes */ 1.656 + fsync (LOCAL->fd); 1.657 + fstat (LOCAL->fd,&sbuf); /* get current write time */ 1.658 + tp[1] = LOCAL->filetime = sbuf.st_mtime; 1.659 + tp[0] = time (0); /* make sure read comes after all that */ 1.660 + utime (stream->mailbox,tp); 1.661 + } 1.662 +} 1.663 + 1.664 + 1.665 +/* Tenex mail per-message modify flags 1.666 + * Accepts: MAIL stream 1.667 + * message cache element 1.668 + */ 1.669 + 1.670 +void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt) 1.671 +{ 1.672 + struct stat sbuf; 1.673 + /* maybe need to do a checkpoint? */ 1.674 + if (LOCAL->filetime && !LOCAL->shouldcheck) { 1.675 + fstat (LOCAL->fd,&sbuf); /* get current write time */ 1.676 + if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; 1.677 + LOCAL->filetime = 0; /* don't do this test for any other messages */ 1.678 + } 1.679 + /* recalculate status */ 1.680 + tenex_update_status (stream,elt->msgno,NIL); 1.681 +} 1.682 + 1.683 +/* Tenex mail ping mailbox 1.684 + * Accepts: MAIL stream 1.685 + * Returns: T if stream still alive, NIL if not 1.686 + */ 1.687 + 1.688 +long tenex_ping (MAILSTREAM *stream) 1.689 +{ 1.690 + unsigned long i = 1; 1.691 + long r = T; 1.692 + int ld; 1.693 + char lock[MAILTMPLEN]; 1.694 + struct stat sbuf; 1.695 + if (stream && LOCAL) { /* only if stream already open */ 1.696 + fstat (LOCAL->fd,&sbuf); /* get current file poop */ 1.697 + if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) && 1.698 + (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T; 1.699 + /* check for changed message status */ 1.700 + if (LOCAL->mustcheck || LOCAL->shouldcheck) { 1.701 + LOCAL->filetime = sbuf.st_mtime; 1.702 + if (LOCAL->shouldcheck) /* babble when we do this unilaterally */ 1.703 + MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL); 1.704 + while (i <= stream->nmsgs) tenex_elt (stream,i++); 1.705 + LOCAL->mustcheck = LOCAL->shouldcheck = NIL; 1.706 + } 1.707 + /* get shared parse/append permission */ 1.708 + if ((sbuf.st_size != LOCAL->filesize) && 1.709 + ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { 1.710 + /* parse resulting mailbox */ 1.711 + r = (tenex_parse (stream)) ? T : NIL; 1.712 + unlockfd (ld,lock); /* release shared parse/append permission */ 1.713 + } 1.714 + if (LOCAL) { /* stream must still be alive */ 1.715 + /* snarf if this is a read-write inbox */ 1.716 + if (stream->inbox && !stream->rdonly) { 1.717 + tenex_snarf (stream); 1.718 + fstat (LOCAL->fd,&sbuf);/* see if file changed now */ 1.719 + if ((sbuf.st_size != LOCAL->filesize) && 1.720 + ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) { 1.721 + /* parse resulting mailbox */ 1.722 + r = (tenex_parse (stream)) ? T : NIL; 1.723 + unlockfd (ld,lock); /* release shared parse/append permission */ 1.724 + } 1.725 + } 1.726 + } 1.727 + } 1.728 + return r; /* return result of the parse */ 1.729 +} 1.730 + 1.731 + 1.732 +/* Tenex mail check mailbox (reparses status too) 1.733 + * Accepts: MAIL stream 1.734 + */ 1.735 + 1.736 +void tenex_check (MAILSTREAM *stream) 1.737 +{ 1.738 + /* mark that a check is desired */ 1.739 + if (LOCAL) LOCAL->mustcheck = T; 1.740 + if (tenex_ping (stream)) MM_LOG ("Check completed",(long) NIL); 1.741 +} 1.742 + 1.743 +/* Tenex mail snarf messages from system inbox 1.744 + * Accepts: MAIL stream 1.745 + */ 1.746 + 1.747 +void tenex_snarf (MAILSTREAM *stream) 1.748 +{ 1.749 + unsigned long i = 0; 1.750 + unsigned long j,r,hdrlen,txtlen; 1.751 + struct stat sbuf; 1.752 + char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN]; 1.753 + MESSAGECACHE *elt; 1.754 + MAILSTREAM *sysibx = NIL; 1.755 + int ld; 1.756 + /* give up if can't get exclusive permission */ 1.757 + if ((time (0) >= (LOCAL->lastsnarf + 1.758 + (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) && 1.759 + strcmp (sysinbox (),stream->mailbox) && 1.760 + ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) { 1.761 + MM_CRITICAL (stream); /* go critical */ 1.762 + /* sizes match and anything in sysinbox? */ 1.763 + if (!stat (sysinbox (),&sbuf) && sbuf.st_size && 1.764 + !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) && 1.765 + (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && 1.766 + (!sysibx->rdonly) && (r = sysibx->nmsgs)) { 1.767 + /* yes, go to end of file in our mailbox */ 1.768 + lseek (LOCAL->fd,sbuf.st_size,L_SET); 1.769 + /* for each message in sysibx mailbox */ 1.770 + while (r && (++i <= sysibx->nmsgs)) { 1.771 + /* snarf message from system INBOX */ 1.772 + hdr = cpystr (mail_fetchheader_full(sysibx,i,NIL,&hdrlen,FT_INTERNAL)); 1.773 + txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_INTERNAL|FT_PEEK); 1.774 + /* if have a message */ 1.775 + if (j = hdrlen + txtlen) { 1.776 + /* calculate header line */ 1.777 + mail_date (LOCAL->buf,elt = mail_elt (sysibx,i)); 1.778 + sprintf (LOCAL->buf + strlen (LOCAL->buf), 1.779 + ",%lu;0000000000%02o\n",j,(unsigned) 1.780 + ((fSEEN * elt->seen) + (fDELETED * elt->deleted) + 1.781 + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + 1.782 + (fDRAFT * elt->draft))); 1.783 + /* copy message */ 1.784 + if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) || 1.785 + (write (LOCAL->fd,hdr,hdrlen) < 0) || 1.786 + (write (LOCAL->fd,txt,txtlen) < 0)) r = 0; 1.787 + } 1.788 + fs_give ((void **) &hdr); 1.789 + } 1.790 + 1.791 + /* make sure all the updates take */ 1.792 + if (fsync (LOCAL->fd)) r = 0; 1.793 + if (r) { /* delete all the messages we copied */ 1.794 + if (r == 1) strcpy (tmp,"1"); 1.795 + else sprintf (tmp,"1:%lu",r); 1.796 + mail_flag (sysibx,tmp,"\\Deleted",ST_SET); 1.797 + mail_expunge (sysibx); /* now expunge all those messages */ 1.798 + } 1.799 + else { 1.800 + sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno)); 1.801 + MM_LOG (LOCAL->buf,WARN); 1.802 + ftruncate (LOCAL->fd,sbuf.st_size); 1.803 + } 1.804 + fstat (LOCAL->fd,&sbuf); /* yes, get current file size */ 1.805 + LOCAL->filetime = sbuf.st_mtime; 1.806 + } 1.807 + if (sysibx) mail_close (sysibx); 1.808 + MM_NOCRITICAL (stream); /* release critical */ 1.809 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.810 + LOCAL->lastsnarf = time (0);/* note time of last snarf */ 1.811 + } 1.812 +} 1.813 + 1.814 +/* Tenex mail expunge mailbox 1.815 + * Accepts: MAIL stream 1.816 + * sequence to expunge if non-NIL 1.817 + * expunge options 1.818 + * Returns: T, always 1.819 + */ 1.820 + 1.821 +long tenex_expunge (MAILSTREAM *stream,char *sequence,long options) 1.822 +{ 1.823 + long ret; 1.824 + time_t tp[2]; 1.825 + struct stat sbuf; 1.826 + off_t pos = 0; 1.827 + int ld; 1.828 + unsigned long i = 1; 1.829 + unsigned long j,k,m,recent; 1.830 + unsigned long n = 0; 1.831 + unsigned long delta = 0; 1.832 + char lock[MAILTMPLEN]; 1.833 + MESSAGECACHE *elt; 1.834 + blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL); 1.835 + if (!(ret = (sequence ? ((options & EX_UID) ? 1.836 + mail_uid_sequence (stream,sequence) : 1.837 + mail_sequence (stream,sequence)) : LONGT) && 1.838 + tenex_ping (stream))); /* parse sequence if given, ping stream */ 1.839 + else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN); 1.840 + else { 1.841 + if (LOCAL->filetime && !LOCAL->shouldcheck) { 1.842 + fstat (LOCAL->fd,&sbuf); /* get current write time */ 1.843 + if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T; 1.844 + } 1.845 + /* The cretins who designed flock() created a window of vulnerability in 1.846 + * upgrading locks from shared to exclusive or downgrading from exclusive 1.847 + * to shared. Rather than maintain the lock at shared status at a minimum, 1.848 + * flock() actually *releases* the former lock. Obviously they never talked 1.849 + * to any database guys. Fortunately, we have the parse/append permission 1.850 + * lock. If we require this lock before going exclusive on the mailbox, 1.851 + * another process can not sneak in and steal the exclusive mailbox lock on 1.852 + * us, because it will block on trying to get parse/append permission first. 1.853 + */ 1.854 + /* get exclusive parse/append permission */ 1.855 + if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) 1.856 + MM_LOG ("Unable to lock expunge mailbox",ERROR); 1.857 + /* make sure see any newly-arrived messages */ 1.858 + else if (!tenex_parse (stream)); 1.859 + /* get exclusive access */ 1.860 + else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) { 1.861 + (*bn) (BLOCK_FILELOCK,NIL); 1.862 + flock (LOCAL->fd,LOCK_SH);/* recover previous lock */ 1.863 + (*bn) (BLOCK_NONE,NIL); 1.864 + MM_LOG ("Can't expunge because mailbox is in use by another process", 1.865 + ERROR); 1.866 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.867 + } 1.868 + 1.869 + else { 1.870 + MM_CRITICAL (stream); /* go critical */ 1.871 + recent = stream->recent; /* get recent now that pinged and locked */ 1.872 + /* for each message */ 1.873 + while (i <= stream->nmsgs) { 1.874 + /* get cache element */ 1.875 + elt = tenex_elt (stream,i); 1.876 + /* number of bytes to smash or preserve */ 1.877 + k = elt->private.special.text.size + tenex_size (stream,i); 1.878 + /* if need to expunge this message */ 1.879 + if (elt->deleted && (sequence ? elt->sequence : T)) { 1.880 + /* if recent, note one less recent message */ 1.881 + if (elt->recent) --recent; 1.882 + delta += k; /* number of bytes to delete */ 1.883 + /* notify upper levels */ 1.884 + mail_expunged (stream,i); 1.885 + n++; /* count up one more expunged message */ 1.886 + } 1.887 + else if (i++ && delta) {/* preserved message */ 1.888 + /* first byte to preserve */ 1.889 + j = elt->private.special.offset; 1.890 + do { /* read from source position */ 1.891 + m = min (k,LOCAL->buflen); 1.892 + lseek (LOCAL->fd,j,L_SET); 1.893 + read (LOCAL->fd,LOCAL->buf,m); 1.894 + pos = j - delta; /* write to destination position */ 1.895 + lseek (LOCAL->fd,pos,L_SET); 1.896 + while (T) { 1.897 + lseek (LOCAL->fd,pos,L_SET); 1.898 + if (write (LOCAL->fd,LOCAL->buf,m) > 0) break; 1.899 + MM_NOTIFY (stream,strerror (errno),WARN); 1.900 + MM_DISKERROR (stream,errno,T); 1.901 + } 1.902 + pos += m; /* new position */ 1.903 + j += m; /* next chunk, perhaps */ 1.904 + } while (k -= m); /* until done */ 1.905 + /* note the new address of this text */ 1.906 + elt->private.special.offset -= delta; 1.907 + } 1.908 + /* preserved but no deleted messages */ 1.909 + else pos = elt->private.special.offset + k; 1.910 + } 1.911 + 1.912 + if (n) { /* truncate file after last message */ 1.913 + if (pos != (LOCAL->filesize -= delta)) { 1.914 + sprintf (LOCAL->buf, 1.915 + "Calculated size mismatch %lu != %lu, delta = %lu", 1.916 + (unsigned long) pos,(unsigned long) LOCAL->filesize,delta); 1.917 + MM_LOG (LOCAL->buf,WARN); 1.918 + LOCAL->filesize = pos;/* fix it then */ 1.919 + } 1.920 + ftruncate (LOCAL->fd,LOCAL->filesize); 1.921 + sprintf (LOCAL->buf,"Expunged %lu messages",n); 1.922 + /* output the news */ 1.923 + MM_LOG (LOCAL->buf,(long) NIL); 1.924 + } 1.925 + else MM_LOG ("No messages deleted, so no update needed",(long) NIL); 1.926 + fsync (LOCAL->fd); /* force disk update */ 1.927 + fstat (LOCAL->fd,&sbuf); /* get new write time */ 1.928 + tp[1] = LOCAL->filetime = sbuf.st_mtime; 1.929 + tp[0] = time (0); /* reset atime to now */ 1.930 + utime (stream->mailbox,tp); 1.931 + MM_NOCRITICAL (stream); /* release critical */ 1.932 + /* notify upper level of new mailbox size */ 1.933 + mail_exists (stream,stream->nmsgs); 1.934 + mail_recent (stream,recent); 1.935 + (*bn) (BLOCK_FILELOCK,NIL); 1.936 + flock (LOCAL->fd,LOCK_SH);/* allow sharers again */ 1.937 + (*bn) (BLOCK_NONE,NIL); 1.938 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.939 + } 1.940 + } 1.941 + return LONGT; 1.942 +} 1.943 + 1.944 +/* Tenex mail copy message(s) 1.945 + * Accepts: MAIL stream 1.946 + * sequence 1.947 + * destination mailbox 1.948 + * copy options 1.949 + * Returns: T if success, NIL if failed 1.950 + */ 1.951 + 1.952 +long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) 1.953 +{ 1.954 + struct stat sbuf; 1.955 + time_t tp[2]; 1.956 + MESSAGECACHE *elt; 1.957 + unsigned long i,j,k; 1.958 + long ret = LONGT; 1.959 + int fd,ld; 1.960 + char file[MAILTMPLEN],lock[MAILTMPLEN]; 1.961 + mailproxycopy_t pc = 1.962 + (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL); 1.963 + /* make sure valid mailbox */ 1.964 + if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) { 1.965 + case ENOENT: /* no such file? */ 1.966 + MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL); 1.967 + return NIL; 1.968 + case 0: /* merely empty file? */ 1.969 + break; 1.970 + case EACCES: /* file protected */ 1.971 + sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox); 1.972 + MM_LOG (LOCAL->buf,ERROR); 1.973 + return NIL; 1.974 + case EINVAL: 1.975 + if (pc) return (*pc) (stream,sequence,mailbox,options); 1.976 + sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox); 1.977 + MM_LOG (LOCAL->buf,ERROR); 1.978 + return NIL; 1.979 + default: 1.980 + if (pc) return (*pc) (stream,sequence,mailbox,options); 1.981 + sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox); 1.982 + MM_LOG (LOCAL->buf,ERROR); 1.983 + return NIL; 1.984 + } 1.985 + if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) : 1.986 + mail_sequence (stream,sequence))) return NIL; 1.987 + /* got file? */ 1.988 + if ((fd = open (tenex_file(file,mailbox),O_RDWR,NIL)) < 0) { 1.989 + sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno)); 1.990 + MM_LOG (LOCAL->buf,ERROR); 1.991 + return NIL; 1.992 + } 1.993 + MM_CRITICAL (stream); /* go critical */ 1.994 + /* get exclusive parse/append permission */ 1.995 + if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) { 1.996 + MM_LOG ("Unable to lock copy mailbox",ERROR); 1.997 + MM_NOCRITICAL (stream); 1.998 + return NIL; 1.999 + } 1.1000 + fstat (fd,&sbuf); /* get current file size */ 1.1001 + lseek (fd,sbuf.st_size,L_SET);/* move to end of file */ 1.1002 + 1.1003 + /* for each requested message */ 1.1004 + for (i = 1; ret && (i <= stream->nmsgs); i++) 1.1005 + if ((elt = mail_elt (stream,i))->sequence) { 1.1006 + lseek (LOCAL->fd,elt->private.special.offset,L_SET); 1.1007 + /* number of bytes to copy */ 1.1008 + k = elt->private.special.text.size + tenex_size (stream,i); 1.1009 + do { /* read from source position */ 1.1010 + j = min (k,LOCAL->buflen); 1.1011 + read (LOCAL->fd,LOCAL->buf,j); 1.1012 + if (write (fd,LOCAL->buf,j) < 0) ret = NIL; 1.1013 + } while (ret && (k -= j));/* until done */ 1.1014 + } 1.1015 + /* make sure all the updates take */ 1.1016 + if (!(ret && (ret = !fsync (fd)))) { 1.1017 + sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno)); 1.1018 + MM_LOG (LOCAL->buf,ERROR); 1.1019 + ftruncate (fd,sbuf.st_size); 1.1020 + } 1.1021 + if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ 1.1022 + /* else preserve \Marked status */ 1.1023 + else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); 1.1024 + tp[1] = sbuf.st_mtime; /* preserve mtime */ 1.1025 + utime (file,tp); /* set the times */ 1.1026 + close (fd); /* close the file */ 1.1027 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.1028 + MM_NOCRITICAL (stream); /* release critical */ 1.1029 + /* delete all requested messages */ 1.1030 + if (ret && (options & CP_MOVE)) { 1.1031 + for (i = 1; i <= stream->nmsgs; i++) 1.1032 + if ((elt = tenex_elt (stream,i))->sequence) { 1.1033 + elt->deleted = T; /* mark message deleted */ 1.1034 + /* recalculate status */ 1.1035 + tenex_update_status (stream,i,NIL); 1.1036 + } 1.1037 + if (!stream->rdonly) { /* make sure the update takes */ 1.1038 + fsync (LOCAL->fd); 1.1039 + fstat (LOCAL->fd,&sbuf); /* get current write time */ 1.1040 + tp[1] = LOCAL->filetime = sbuf.st_mtime; 1.1041 + tp[0] = time (0); /* make sure atime remains greater */ 1.1042 + utime (stream->mailbox,tp); 1.1043 + } 1.1044 + } 1.1045 + if (ret && mail_parameters (NIL,GET_COPYUID,NIL)) 1.1046 + MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN); 1.1047 + return ret; 1.1048 +} 1.1049 + 1.1050 +/* Tenex mail append message from stringstruct 1.1051 + * Accepts: MAIL stream 1.1052 + * destination mailbox 1.1053 + * append callback 1.1054 + * data for callback 1.1055 + * Returns: T if append successful, else NIL 1.1056 + */ 1.1057 + 1.1058 +long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) 1.1059 +{ 1.1060 + struct stat sbuf; 1.1061 + int fd,ld,c; 1.1062 + char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN]; 1.1063 + time_t tp[2]; 1.1064 + FILE *df; 1.1065 + MESSAGECACHE elt; 1.1066 + long f; 1.1067 + unsigned long i,j,uf,size; 1.1068 + STRING *message; 1.1069 + long ret = LONGT; 1.1070 + /* default stream to prototype */ 1.1071 + if (!stream) stream = user_flags (&tenexproto); 1.1072 + /* make sure valid mailbox */ 1.1073 + if (!tenex_isvalid (mailbox,tmp)) switch (errno) { 1.1074 + case ENOENT: /* no such file? */ 1.1075 + if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"mail.txt"); 1.1076 + else { 1.1077 + MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL); 1.1078 + return NIL; 1.1079 + } 1.1080 + /* falls through */ 1.1081 + case 0: /* merely empty file? */ 1.1082 + break; 1.1083 + case EACCES: /* file protected */ 1.1084 + sprintf (tmp,"Can't access destination: %.80s",mailbox); 1.1085 + MM_LOG (tmp,ERROR); 1.1086 + return NIL; 1.1087 + case EINVAL: 1.1088 + sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox); 1.1089 + MM_LOG (tmp,ERROR); 1.1090 + return NIL; 1.1091 + default: 1.1092 + sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox); 1.1093 + MM_LOG (tmp,ERROR); 1.1094 + return NIL; 1.1095 + } 1.1096 + /* get first message */ 1.1097 + if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL; 1.1098 + 1.1099 + /* open destination mailbox */ 1.1100 + if (((fd = open (tenex_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) || 1.1101 + !(df = fdopen (fd,"ab"))) { 1.1102 + sprintf (tmp,"Can't open append mailbox: %s",strerror (errno)); 1.1103 + MM_LOG (tmp,ERROR); 1.1104 + return NIL; 1.1105 + } 1.1106 + /* get parse/append permission */ 1.1107 + if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) { 1.1108 + MM_LOG ("Unable to lock append mailbox",ERROR); 1.1109 + close (fd); 1.1110 + return NIL; 1.1111 + } 1.1112 + MM_CRITICAL (stream); /* go critical */ 1.1113 + fstat (fd,&sbuf); /* get current file size */ 1.1114 + errno = 0; 1.1115 + do { /* parse flags */ 1.1116 + if (!SIZE (message)) { /* guard against zero-length */ 1.1117 + MM_LOG ("Append of zero-length message",ERROR); 1.1118 + ret = NIL; 1.1119 + break; 1.1120 + } 1.1121 + f = mail_parse_flags (stream,flags,&i); 1.1122 + /* reverse bits (dontcha wish we had CIRC?) */ 1.1123 + for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i))); 1.1124 + if (date) { /* parse date if given */ 1.1125 + if (!mail_parse_date (&elt,date)) { 1.1126 + sprintf (tmp,"Bad date in append: %.80s",date); 1.1127 + MM_LOG (tmp,ERROR); 1.1128 + ret = NIL; /* mark failure */ 1.1129 + break; 1.1130 + } 1.1131 + mail_date (tmp,&elt); /* write preseved date */ 1.1132 + } 1.1133 + else internal_date (tmp); /* get current date in IMAP format */ 1.1134 + i = GETPOS (message); /* remember current position */ 1.1135 + for (j = SIZE (message), size = 0; j; --j) 1.1136 + if (SNX (message) != '\015') ++size; 1.1137 + SETPOS (message,i); /* restore position */ 1.1138 + /* write header */ 1.1139 + if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0) 1.1140 + ret = NIL; 1.1141 + else { /* write message */ 1.1142 + while (size) if ((c = 0xff & SNX (message)) != '\015') { 1.1143 + if (putc (c,df) != EOF) --size; 1.1144 + else break; 1.1145 + } 1.1146 + /* get next message */ 1.1147 + if (size || !MM_APPEND (af) (stream,data,&flags,&date,&message)) 1.1148 + ret = NIL; 1.1149 + } 1.1150 + } while (ret && message); 1.1151 + /* if error... */ 1.1152 + if (!ret || (fflush (df) == EOF)) { 1.1153 + ftruncate (fd,sbuf.st_size);/* revert file */ 1.1154 + close (fd); /* make sure fclose() doesn't corrupt us */ 1.1155 + if (errno) { 1.1156 + sprintf (tmp,"Message append failed: %s",strerror (errno)); 1.1157 + MM_LOG (tmp,ERROR); 1.1158 + } 1.1159 + ret = NIL; 1.1160 + } 1.1161 + if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */ 1.1162 + /* else preserve \Marked status */ 1.1163 + else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0); 1.1164 + tp[1] = sbuf.st_mtime; /* preserve mtime */ 1.1165 + utime (file,tp); /* set the times */ 1.1166 + fclose (df); /* close the file */ 1.1167 + unlockfd (ld,lock); /* release exclusive parse/append permission */ 1.1168 + MM_NOCRITICAL (stream); /* release critical */ 1.1169 + if (ret && mail_parameters (NIL,GET_APPENDUID,NIL)) 1.1170 + MM_LOG ("Can not return meaningful APPENDUID with this mailbox format", 1.1171 + WARN); 1.1172 + return ret; 1.1173 +} 1.1174 + 1.1175 +/* Internal routines */ 1.1176 + 1.1177 + 1.1178 +/* Tenex mail return internal message size in bytes 1.1179 + * Accepts: MAIL stream 1.1180 + * message # 1.1181 + * Returns: internal size of message 1.1182 + */ 1.1183 + 1.1184 +unsigned long tenex_size (MAILSTREAM *stream,unsigned long m) 1.1185 +{ 1.1186 + MESSAGECACHE *elt = mail_elt (stream,m); 1.1187 + return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset : 1.1188 + LOCAL->filesize) - 1.1189 + (elt->private.special.offset + elt->private.special.text.size); 1.1190 +} 1.1191 + 1.1192 + 1.1193 +/* Tenex mail generate file string 1.1194 + * Accepts: temporary buffer to write into 1.1195 + * mailbox name string 1.1196 + * Returns: local file string or NIL if failure 1.1197 + */ 1.1198 + 1.1199 +char *tenex_file (char *dst,char *name) 1.1200 +{ 1.1201 + char tmp[MAILTMPLEN]; 1.1202 + char *s = mailboxfile (dst,name); 1.1203 + /* return our standard inbox */ 1.1204 + return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/INBOX",tmp) ? 1.1205 + "~/INBOX" : "mail.txt") : s; 1.1206 +} 1.1207 + 1.1208 +/* Tenex mail parse mailbox 1.1209 + * Accepts: MAIL stream 1.1210 + * Returns: T if parse OK 1.1211 + * NIL if failure, stream aborted 1.1212 + */ 1.1213 + 1.1214 +long tenex_parse (MAILSTREAM *stream) 1.1215 +{ 1.1216 + struct stat sbuf; 1.1217 + MESSAGECACHE *elt = NIL; 1.1218 + unsigned char c,*s,*t,*x; 1.1219 + char tmp[MAILTMPLEN]; 1.1220 + unsigned long i,j; 1.1221 + long curpos = LOCAL->filesize; 1.1222 + long nmsgs = stream->nmsgs; 1.1223 + long recent = stream->recent; 1.1224 + short added = NIL; 1.1225 + short silent = stream->silent; 1.1226 + fstat (LOCAL->fd,&sbuf); /* get status */ 1.1227 + if (sbuf.st_size < curpos) { /* sanity check */ 1.1228 + sprintf (tmp,"Mailbox shrank from %lu to %lu!", 1.1229 + (unsigned long) curpos,(unsigned long) sbuf.st_size); 1.1230 + MM_LOG (tmp,ERROR); 1.1231 + tenex_close (stream,NIL); 1.1232 + return NIL; 1.1233 + } 1.1234 + stream->silent = T; /* don't pass up exists events yet */ 1.1235 + while (sbuf.st_size - curpos){/* while there is stuff to parse */ 1.1236 + /* get to that position in the file */ 1.1237 + lseek (LOCAL->fd,curpos,L_SET); 1.1238 + if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) { 1.1239 + sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s", 1.1240 + (unsigned long) curpos,(unsigned long) sbuf.st_size, 1.1241 + i ? strerror (errno) : "no data read"); 1.1242 + MM_LOG (tmp,ERROR); 1.1243 + tenex_close (stream,NIL); 1.1244 + return NIL; 1.1245 + } 1.1246 + LOCAL->buf[i] = '\0'; /* tie off buffer just in case */ 1.1247 + if (!(s = strchr (LOCAL->buf,'\012'))) { 1.1248 + sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s", 1.1249 + (unsigned long) curpos,i,(char *) LOCAL->buf); 1.1250 + MM_LOG (tmp,ERROR); 1.1251 + tenex_close (stream,NIL); 1.1252 + return NIL; 1.1253 + } 1.1254 + *s = '\0'; /* tie off header line */ 1.1255 + i = (s + 1) - LOCAL->buf; /* note start of text offset */ 1.1256 + if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) { 1.1257 + sprintf (tmp,"Unable to parse internal header at %lu: %s", 1.1258 + (unsigned long) curpos,(char *) LOCAL->buf); 1.1259 + MM_LOG (tmp,ERROR); 1.1260 + tenex_close (stream,NIL); 1.1261 + return NIL; 1.1262 + } 1.1263 + *s++ = '\0'; *t++ = '\0'; /* tie off fields */ 1.1264 + 1.1265 + added = T; /* note that a new message was added */ 1.1266 + /* swell the cache */ 1.1267 + mail_exists (stream,++nmsgs); 1.1268 + /* instantiate an elt for this message */ 1.1269 + (elt = mail_elt (stream,nmsgs))->valid = T; 1.1270 + elt->private.uid = ++stream->uid_last; 1.1271 + /* note file offset of header */ 1.1272 + elt->private.special.offset = curpos; 1.1273 + /* in case error */ 1.1274 + elt->private.special.text.size = 0; 1.1275 + /* header size not known yet */ 1.1276 + elt->private.msg.header.text.size = 0; 1.1277 + x = s; /* parse the header components */ 1.1278 + if (mail_parse_date (elt,LOCAL->buf) && 1.1279 + (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) && 1.1280 + (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) && 1.1281 + isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) && 1.1282 + isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) && 1.1283 + isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12]) 1.1284 + elt->private.special.text.size = i; 1.1285 + else { /* oops */ 1.1286 + sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s", 1.1287 + curpos,(char *) LOCAL->buf,(char *) x,(char *) t); 1.1288 + MM_LOG (tmp,ERROR); 1.1289 + tenex_close (stream,NIL); 1.1290 + return NIL; 1.1291 + } 1.1292 + /* make sure didn't run off end of file */ 1.1293 + if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) { 1.1294 + sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)", 1.1295 + elt->private.special.offset,(unsigned long) curpos, 1.1296 + (unsigned long) sbuf.st_size); 1.1297 + MM_LOG (tmp,ERROR); 1.1298 + tenex_close (stream,NIL); 1.1299 + return NIL; 1.1300 + } 1.1301 + c = t[10]; /* remember first system flags byte */ 1.1302 + t[10] = '\0'; /* tie off flags */ 1.1303 + j = strtoul (t,NIL,8); /* get user flags value */ 1.1304 + t[10] = c; /* restore first system flags byte */ 1.1305 + /* set up all valid user flags (reversed!) */ 1.1306 + while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && 1.1307 + stream->user_flags[i]) elt->user_flags |= 1 << i; 1.1308 + /* calculate system flags */ 1.1309 + if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T; 1.1310 + if (j & fDELETED) elt->deleted = T; 1.1311 + if (j & fFLAGGED) elt->flagged = T; 1.1312 + if (j & fANSWERED) elt->answered = T; 1.1313 + if (j & fDRAFT) elt->draft = T; 1.1314 + if (!(j & fOLD)) { /* newly arrived message? */ 1.1315 + elt->recent = T; 1.1316 + recent++; /* count up a new recent message */ 1.1317 + /* mark it as old */ 1.1318 + tenex_update_status (stream,nmsgs,NIL); 1.1319 + } 1.1320 + } 1.1321 + fsync (LOCAL->fd); /* make sure all the fOLD flags take */ 1.1322 + /* update parsed file size and time */ 1.1323 + LOCAL->filesize = sbuf.st_size; 1.1324 + fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */ 1.1325 + LOCAL->filetime = sbuf.st_mtime; 1.1326 + if (added && !stream->rdonly){/* make sure atime updated */ 1.1327 + time_t tp[2]; 1.1328 + tp[0] = time (0); 1.1329 + tp[1] = LOCAL->filetime; 1.1330 + utime (stream->mailbox,tp); 1.1331 + } 1.1332 + stream->silent = silent; /* can pass up events now */ 1.1333 + mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */ 1.1334 + mail_recent (stream,recent); /* and of change in recent messages */ 1.1335 + return LONGT; /* return the winnage */ 1.1336 +} 1.1337 + 1.1338 +/* Tenex get cache element with status updating from file 1.1339 + * Accepts: MAIL stream 1.1340 + * message number 1.1341 + * Returns: cache element 1.1342 + */ 1.1343 + 1.1344 +MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno) 1.1345 +{ 1.1346 + MESSAGECACHE *elt = mail_elt (stream,msgno); 1.1347 + struct { /* old flags */ 1.1348 + unsigned int seen : 1; 1.1349 + unsigned int deleted : 1; 1.1350 + unsigned int flagged : 1; 1.1351 + unsigned int answered : 1; 1.1352 + unsigned int draft : 1; 1.1353 + unsigned long user_flags; 1.1354 + } old; 1.1355 + old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged; 1.1356 + old.answered = elt->answered; old.draft = elt->draft; 1.1357 + old.user_flags = elt->user_flags; 1.1358 + tenex_read_flags (stream,elt); 1.1359 + if ((old.seen != elt->seen) || (old.deleted != elt->deleted) || 1.1360 + (old.flagged != elt->flagged) || (old.answered != elt->answered) || 1.1361 + (old.draft != elt->draft) || (old.user_flags != elt->user_flags)) 1.1362 + MM_FLAGS (stream,msgno); /* let top level know */ 1.1363 + return elt; 1.1364 +} 1.1365 + 1.1366 +/* Tenex read flags from file 1.1367 + * Accepts: MAIL stream 1.1368 + * Returns: cache element 1.1369 + */ 1.1370 + 1.1371 +void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt) 1.1372 +{ 1.1373 + unsigned long i,j; 1.1374 + /* noop if readonly and have valid flags */ 1.1375 + if (stream->rdonly && elt->valid) return; 1.1376 + /* set the seek pointer */ 1.1377 + lseek (LOCAL->fd,(off_t) elt->private.special.offset + 1.1378 + elt->private.special.text.size - 13,L_SET); 1.1379 + /* read the new flags */ 1.1380 + if (read (LOCAL->fd,LOCAL->buf,12) < 0) { 1.1381 + sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno)); 1.1382 + fatal (LOCAL->buf); 1.1383 + } 1.1384 + /* calculate system flags */ 1.1385 + i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0'); 1.1386 + elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL; 1.1387 + elt->flagged = i & fFLAGGED ? T : NIL; 1.1388 + elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL; 1.1389 + LOCAL->buf[10] = '\0'; /* tie off flags */ 1.1390 + j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */ 1.1391 + /* set up all valid user flags (reversed!) */ 1.1392 + while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) && 1.1393 + stream->user_flags[i]) elt->user_flags |= 1 << i; 1.1394 + elt->valid = T; /* have valid flags now */ 1.1395 +} 1.1396 + 1.1397 +/* Tenex update status string 1.1398 + * Accepts: MAIL stream 1.1399 + * message number 1.1400 + * flag saying whether or not to sync 1.1401 + */ 1.1402 + 1.1403 +void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag) 1.1404 +{ 1.1405 + time_t tp[2]; 1.1406 + struct stat sbuf; 1.1407 + MESSAGECACHE *elt = mail_elt (stream,msgno); 1.1408 + unsigned long j,k = 0; 1.1409 + /* readonly */ 1.1410 + if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt); 1.1411 + else { /* readwrite */ 1.1412 + j = elt->user_flags; /* get user flags */ 1.1413 + /* reverse bits (dontcha wish we had CIRC?) */ 1.1414 + while (j) k |= 1 << (29 - find_rightmost_bit (&j)); 1.1415 + /* print new flag string */ 1.1416 + sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned) 1.1417 + (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) + 1.1418 + (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) + 1.1419 + (fDRAFT * elt->draft))); 1.1420 + /* get to that place in the file */ 1.1421 + lseek (LOCAL->fd,(off_t) elt->private.special.offset + 1.1422 + elt->private.special.text.size - 13,L_SET); 1.1423 + /* write new flags */ 1.1424 + write (LOCAL->fd,LOCAL->buf,12); 1.1425 + if (syncflag) { /* sync if requested */ 1.1426 + fsync (LOCAL->fd); 1.1427 + fstat (LOCAL->fd,&sbuf); /* get new write time */ 1.1428 + tp[1] = LOCAL->filetime = sbuf.st_mtime; 1.1429 + tp[0] = time (0); /* make sure read is later */ 1.1430 + utime (stream->mailbox,tp); 1.1431 + } 1.1432 + } 1.1433 +} 1.1434 + 1.1435 +/* Tenex locate header for a message 1.1436 + * Accepts: MAIL stream 1.1437 + * message number 1.1438 + * pointer to returned header size 1.1439 + * Returns: position of header in file 1.1440 + */ 1.1441 + 1.1442 +unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno, 1.1443 + unsigned long *size) 1.1444 +{ 1.1445 + unsigned long siz; 1.1446 + long i = 0; 1.1447 + char c = '\0'; 1.1448 + char *s = NIL; 1.1449 + MESSAGECACHE *elt = tenex_elt (stream,msgno); 1.1450 + unsigned long ret = elt->private.special.offset + 1.1451 + elt->private.special.text.size; 1.1452 + unsigned long msiz = tenex_size (stream,msgno); 1.1453 + /* is header size known? */ 1.1454 + if (!(*size = elt->private.msg.header.text.size)) { 1.1455 + lseek (LOCAL->fd,ret,L_SET);/* get to header position */ 1.1456 + /* search message for LF LF */ 1.1457 + for (siz = 0; siz < msiz; siz++) { 1.1458 + if (--i <= 0) /* read another buffer as necessary */ 1.1459 + read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN)); 1.1460 + /* two newline sequence? */ 1.1461 + if ((c == '\012') && (*s == '\012')) { 1.1462 + /* yes, note for later */ 1.1463 + elt->private.msg.header.text.size = (*size = siz + 1); 1.1464 + 1.1465 + return ret; /* return to caller */ 1.1466 + } 1.1467 + else c = *s++; /* next character */ 1.1468 + } 1.1469 + /* header consumes entire message */ 1.1470 + elt->private.msg.header.text.size = *size = msiz; 1.1471 + } 1.1472 + return ret; 1.1473 +}