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