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