imapext-2007

diff src/osdep/amiga/unix.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/osdep/amiga/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 +}

UW-IMAP'd extensions by yuuji