~ubuntu-branches/debian/stretch/alpine/stretch

« back to all changes in this revision

Viewing changes to imap/src/osdep/amiga/unix.c

  • Committer: Bazaar Package Importer
  • Author(s): Asheesh Laroia
  • Date: 2007-02-17 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070217131742-99x5c6cpg1pbkdhw
Tags: upstream-0.82+dfsg
ImportĀ upstreamĀ versionĀ 0.82+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ========================================================================
 
2
 * Copyright 1988-2006 University of Washington
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * 
 
11
 * ========================================================================
 
12
 */
 
13
 
 
14
/*
 
15
 * Program:     UNIX mail routines
 
16
 *
 
17
 * Author:      Mark Crispin
 
18
 *              Networks and Distributed Computing
 
19
 *              Computing & Communications
 
20
 *              University of Washington
 
21
 *              Administration Building, AG-44
 
22
 *              Seattle, WA  98195
 
23
 *              Internet: MRC@CAC.Washington.EDU
 
24
 *
 
25
 * Date:        20 December 1989
 
26
 * Last Edited: 20 December 2006
 
27
 */
 
28
 
 
29
 
 
30
/*                              DEDICATION
 
31
 *
 
32
 *  This file is dedicated to my dog, Unix, also known as Yun-chan and
 
33
 * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast.  Unix
 
34
 * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
 
35
 * a two-month bout with cirrhosis of the liver.
 
36
 *
 
37
 *  He was a dear friend, and I miss him terribly.
 
38
 *
 
39
 *  Lift a leg, Yunie.  Luv ya forever!!!!
 
40
 */
 
41
 
 
42
#include <stdio.h>
 
43
#include <ctype.h>
 
44
#include <errno.h>
 
45
extern int errno;               /* just in case */
 
46
#include <signal.h>
 
47
#include "mail.h"
 
48
#include "osdep.h"
 
49
#include <time.h>
 
50
#include <sys/stat.h>
 
51
#include "unix.h"
 
52
#include "pseudo.h"
 
53
#include "fdstring.h"
 
54
#include "misc.h"
 
55
#include "dummy.h"
 
56
 
 
57
/* UNIX I/O stream local data */
 
58
 
 
59
typedef struct unix_local {
 
60
  unsigned int dirty : 1;       /* disk copy needs updating */
 
61
  unsigned int ddirty : 1;      /* double-dirty, ping becomes checkpoint */
 
62
  unsigned int pseudo : 1;      /* uses a pseudo message */
 
63
  int fd;                       /* mailbox file descriptor */
 
64
  int ld;                       /* lock file descriptor */
 
65
  char *lname;                  /* lock file name */
 
66
  off_t filesize;               /* file size parsed */
 
67
  time_t filetime;              /* last file time */
 
68
  time_t lastsnarf;             /* last snarf time (for mbox driver) */
 
69
  unsigned char *buf;           /* temporary buffer */
 
70
  unsigned long buflen;         /* current size of temporary buffer */
 
71
  unsigned long uid;            /* current text uid */
 
72
  SIZEDTEXT text;               /* current text */
 
73
  unsigned long textlen;        /* current text length */
 
74
  char *line;                   /* returned line */
 
75
  char *linebuf;                /* line readin buffer */
 
76
  unsigned long linebuflen;     /* current line readin buffer length */
 
77
} UNIXLOCAL;
 
78
 
 
79
 
 
80
/* Convenient access to local data */
 
81
 
 
82
#define LOCAL ((UNIXLOCAL *) stream->local)
 
83
 
 
84
 
 
85
/* UNIX protected file structure */
 
86
 
 
87
typedef struct unix_file {
 
88
  MAILSTREAM *stream;           /* current stream */
 
89
  off_t curpos;                 /* current file position */
 
90
  off_t protect;                /* protected position */
 
91
  off_t filepos;                /* current last written file position */
 
92
  char *buf;                    /* overflow buffer */
 
93
  size_t buflen;                /* current overflow buffer length */
 
94
  char *bufpos;                 /* current buffer position */
 
95
} UNIXFILE;
 
96
 
 
97
/* Function prototypes */
 
98
 
 
99
DRIVER *unix_valid (char *name);
 
100
long unix_isvalid_fd (int fd);
 
101
void *unix_parameters (long function,void *value);
 
102
void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
 
103
void unix_list (MAILSTREAM *stream,char *ref,char *pat);
 
104
void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
 
105
long unix_create (MAILSTREAM *stream,char *mailbox);
 
106
long unix_delete (MAILSTREAM *stream,char *mailbox);
 
107
long unix_rename (MAILSTREAM *stream,char *old,char *newname);
 
108
MAILSTREAM *unix_open (MAILSTREAM *stream);
 
109
void unix_close (MAILSTREAM *stream,long options);
 
110
char *unix_header (MAILSTREAM *stream,unsigned long msgno,
 
111
                   unsigned long *length,long flags);
 
112
long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
 
113
char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
 
114
                      unsigned long *length,long flags);
 
115
void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
 
116
long unix_ping (MAILSTREAM *stream);
 
117
void unix_check (MAILSTREAM *stream);
 
118
long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
 
119
long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
 
120
long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
 
121
int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
 
122
                     STRING *msg);
 
123
int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
 
124
 
 
125
void unix_abort (MAILSTREAM *stream);
 
126
char *unix_file (char *dst,char *name);
 
127
int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
 
128
void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
 
129
int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
 
130
char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
 
131
unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
 
132
unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
 
133
                            unsigned long uid,long flag);
 
134
long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
 
135
                   long flags);
 
136
long unix_extend (MAILSTREAM *stream,unsigned long size);
 
137
void unix_write (UNIXFILE *f,char *s,unsigned long i);
 
138
void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
 
139
 
 
140
/* mbox mail routines */
 
141
 
 
142
/* Function prototypes */
 
143
 
 
144
DRIVER *mbox_valid (char *name);
 
145
long mbox_create (MAILSTREAM *stream,char *mailbox);
 
146
long mbox_delete (MAILSTREAM *stream,char *mailbox);
 
147
long mbox_rename (MAILSTREAM *stream,char *old,char *newname);
 
148
long mbox_status (MAILSTREAM *stream,char *mbx,long flags);
 
149
MAILSTREAM *mbox_open (MAILSTREAM *stream);
 
150
long mbox_ping (MAILSTREAM *stream);
 
151
void mbox_check (MAILSTREAM *stream);
 
152
long mbox_expunge (MAILSTREAM *stream,char *sequence,long options);
 
153
long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
 
154
 
 
155
 
 
156
/* UNIX mail routines */
 
157
 
 
158
 
 
159
/* Driver dispatch used by MAIL */
 
160
 
 
161
DRIVER unixdriver = {
 
162
  "unix",                       /* driver name */
 
163
                                /* driver flags */
 
164
  DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY,
 
165
  (DRIVER *) NIL,               /* next driver */
 
166
  unix_valid,                   /* mailbox is valid for us */
 
167
  unix_parameters,              /* manipulate parameters */
 
168
  unix_scan,                    /* scan mailboxes */
 
169
  unix_list,                    /* list mailboxes */
 
170
  unix_lsub,                    /* list subscribed mailboxes */
 
171
  NIL,                          /* subscribe to mailbox */
 
172
  NIL,                          /* unsubscribe from mailbox */
 
173
  unix_create,                  /* create mailbox */
 
174
  unix_delete,                  /* delete mailbox */
 
175
  unix_rename,                  /* rename mailbox */
 
176
  mail_status_default,          /* status of mailbox */
 
177
  unix_open,                    /* open mailbox */
 
178
  unix_close,                   /* close mailbox */
 
179
  NIL,                          /* fetch message "fast" attributes */
 
180
  NIL,                          /* fetch message flags */
 
181
  NIL,                          /* fetch overview */
 
182
  NIL,                          /* fetch message envelopes */
 
183
  unix_header,                  /* fetch message header */
 
184
  unix_text,                    /* fetch message text */
 
185
  NIL,                          /* fetch partial message text */
 
186
  NIL,                          /* unique identifier */
 
187
  NIL,                          /* message number */
 
188
  NIL,                          /* modify flags */
 
189
  unix_flagmsg,                 /* per-message modify flags */
 
190
  NIL,                          /* search for message based on criteria */
 
191
  NIL,                          /* sort messages */
 
192
  NIL,                          /* thread messages */
 
193
  unix_ping,                    /* ping mailbox to see if still alive */
 
194
  unix_check,                   /* check for new messages */
 
195
  unix_expunge,                 /* expunge deleted messages */
 
196
  unix_copy,                    /* copy messages to another mailbox */
 
197
  unix_append,                  /* append string message to mailbox */
 
198
  NIL                           /* garbage collect stream */
 
199
};
 
200
 
 
201
                                /* prototype stream */
 
202
MAILSTREAM unixproto = {&unixdriver};
 
203
 
 
204
                                /* driver parameters */
 
205
static long unix_fromwidget = T;
 
206
 
 
207
/* UNIX mail validate mailbox
 
208
 * Accepts: mailbox name
 
209
 * Returns: our driver if name is valid, NIL otherwise
 
210
 */
 
211
 
 
212
DRIVER *unix_valid (char *name)
 
213
{
 
214
  int fd;
 
215
  DRIVER *ret = NIL;
 
216
  char *t,file[MAILTMPLEN];
 
217
  struct stat sbuf;
 
218
  time_t tp[2];
 
219
  errno = EINVAL;               /* assume invalid argument */
 
220
                                /* must be non-empty file */
 
221
  if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
 
222
    if (!sbuf.st_size)errno = 0;/* empty file */
 
223
    else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
 
224
                                /* OK if mailbox format good */
 
225
      if (unix_isvalid_fd (fd)) ret = &unixdriver;
 
226
      else errno = -1;          /* invalid format */
 
227
      close (fd);               /* close the file */
 
228
                                /* \Marked status? */
 
229
      if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
 
230
        tp[0] = sbuf.st_atime;  /* yes, preserve atime and mtime */
 
231
        tp[1] = sbuf.st_mtime;
 
232
        utime (file,tp);        /* set the times */
 
233
      }
 
234
    }
 
235
  }
 
236
  return ret;                   /* return what we should */
 
237
}
 
238
 
 
239
/* UNIX mail test for valid mailbox
 
240
 * Accepts: file descriptor
 
241
 *          scratch buffer
 
242
 * Returns: T if valid, NIL otherwise
 
243
 */
 
244
 
 
245
long unix_isvalid_fd (int fd)
 
246
{
 
247
  int zn;
 
248
  int ret = NIL;
 
249
  char tmp[MAILTMPLEN],*s,*t,c = '\n';
 
250
  memset (tmp,'\0',MAILTMPLEN);
 
251
  if (read (fd,tmp,MAILTMPLEN-1) >= 0) {
 
252
    for (s = tmp; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');)
 
253
      c = *s++;
 
254
    if (c == '\n') VALID (s,t,ret,zn);
 
255
  }
 
256
  return ret;                   /* return what we should */
 
257
}
 
258
 
 
259
 
 
260
/* UNIX manipulate driver parameters
 
261
 * Accepts: function code
 
262
 *          function-dependent value
 
263
 * Returns: function-dependent return value
 
264
 */
 
265
 
 
266
void *unix_parameters (long function,void *value)
 
267
{
 
268
  void *ret = NIL;
 
269
  switch ((int) function) {
 
270
  case GET_INBOXPATH:
 
271
    if (value) ret = dummy_file ((char *) value,"INBOX");
 
272
    break;
 
273
  case SET_FROMWIDGET:
 
274
    unix_fromwidget = (long) value;
 
275
  case GET_FROMWIDGET:
 
276
    ret = (void *) unix_fromwidget;
 
277
    break;
 
278
  }
 
279
  return ret;
 
280
}
 
281
 
 
282
/* UNIX mail scan mailboxes
 
283
 * Accepts: mail stream
 
284
 *          reference
 
285
 *          pattern to search
 
286
 *          string to scan
 
287
 */
 
288
 
 
289
void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
 
290
{
 
291
  if (stream) dummy_scan (NIL,ref,pat,contents);
 
292
}
 
293
 
 
294
 
 
295
/* UNIX mail list mailboxes
 
296
 * Accepts: mail stream
 
297
 *          reference
 
298
 *          pattern to search
 
299
 */
 
300
 
 
301
void unix_list (MAILSTREAM *stream,char *ref,char *pat)
 
302
{
 
303
  if (stream) dummy_list (NIL,ref,pat);
 
304
}
 
305
 
 
306
 
 
307
/* UNIX mail list subscribed mailboxes
 
308
 * Accepts: mail stream
 
309
 *          reference
 
310
 *          pattern to search
 
311
 */
 
312
 
 
313
void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
 
314
{
 
315
  if (stream) dummy_lsub (NIL,ref,pat);
 
316
}
 
317
 
 
318
/* UNIX mail create mailbox
 
319
 * Accepts: MAIL stream
 
320
 *          mailbox name to create
 
321
 * Returns: T on success, NIL on failure
 
322
 */
 
323
 
 
324
long unix_create (MAILSTREAM *stream,char *mailbox)
 
325
{
 
326
  char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
 
327
  long ret = NIL;
 
328
  int i,fd;
 
329
  time_t ti = time (0);
 
330
  if (!(s = dummy_file (mbx,mailbox))) {
 
331
    sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
 
332
    MM_LOG (tmp,ERROR);
 
333
  }
 
334
                                /* create underlying file */
 
335
  else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
 
336
                                /* done if dir-only or whiner */
 
337
    if (((s = strrchr (s,'/')) && !s[1]) ||
 
338
        mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
 
339
    else if ((fd = open (mbx,O_WRONLY,
 
340
                    (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
 
341
      sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
 
342
      MM_LOG (tmp,ERROR);
 
343
      unlink (mbx);             /* delete the file */
 
344
    }
 
345
    else {                      /* initialize header */
 
346
      memset (tmp,'\0',MAILTMPLEN);
 
347
      sprintf (tmp,"From %s %sDate: ",pseudo_from,ctime (&ti));
 
348
      rfc822_fixed_date (s = tmp + strlen (tmp));
 
349
                                /* write the pseudo-header */
 
350
      sprintf (s += strlen (s),
 
351
               "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
 
352
               pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
 
353
               (unsigned long) ti);
 
354
      for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
 
355
        sprintf (s += strlen (s)," %s",default_user_flag (i));
 
356
      sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
 
357
      if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
 
358
      else {
 
359
        sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
 
360
                 strerror (errno));
 
361
        MM_LOG (tmp,ERROR);
 
362
        unlink (mbx);           /* delete the file */
 
363
      }
 
364
      close (fd);               /* close file */
 
365
    }
 
366
  }
 
367
                                /* set proper protections */
 
368
  return ret ? set_mbx_protections (mailbox,mbx) : NIL;
 
369
}
 
370
 
 
371
/* UNIX mail delete mailbox
 
372
 * Accepts: MAIL stream
 
373
 *          mailbox name to delete
 
374
 * Returns: T on success, NIL on failure
 
375
 */
 
376
 
 
377
long unix_delete (MAILSTREAM *stream,char *mailbox)
 
378
{
 
379
  return unix_rename (stream,mailbox,NIL);
 
380
}
 
381
 
 
382
 
 
383
/* UNIX mail rename mailbox
 
384
 * Accepts: MAIL stream
 
385
 *          old mailbox name
 
386
 *          new mailbox name (or NIL for delete)
 
387
 * Returns: T on success, NIL on failure
 
388
 */
 
389
 
 
390
long unix_rename (MAILSTREAM *stream,char *old,char *newname)
 
391
{
 
392
  long ret = NIL;
 
393
  char c,*s = NIL;
 
394
  char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
 
395
  DOTLOCK lockx;
 
396
  int fd,ld;
 
397
  long i;
 
398
  struct stat sbuf;
 
399
  MM_CRITICAL (stream);         /* get the c-client lock */
 
400
  if (!dummy_file (file,old) ||
 
401
      (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
 
402
                   ((s = strrchr (tmp,'/')) && !s[1]))))
 
403
    sprintf (tmp,newname ?
 
404
             "Can't rename mailbox %.80s to %.80s: invalid name" :
 
405
             "Can't delete mailbox %.80s: invalid name",
 
406
             old,newname);
 
407
                                /* lock out other c-clients */
 
408
  else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
 
409
    sprintf (tmp,"Mailbox %.80s is in use by another process",old);
 
410
 
 
411
  else {
 
412
    if ((fd = unix_lock (file,O_RDWR,
 
413
                         (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
 
414
                         &lockx,LOCK_EX)) < 0)
 
415
      sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
 
416
    else {
 
417
      if (newname) {            /* want rename? */
 
418
                                /* found superior to destination name? */
 
419
        if (s = strrchr (s,'/')) {
 
420
          c = *++s;             /* remember first character of inferior */
 
421
          *s = '\0';            /* tie off to get just superior */
 
422
                                /* name doesn't exist, create it */
 
423
          if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
 
424
              !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
 
425
            unix_unlock (fd,NIL,&lockx);
 
426
            unix_unlock (ld,NIL,NIL);
 
427
            unlink (lock);
 
428
            MM_NOCRITICAL (stream);
 
429
            return ret;         /* return success or failure */
 
430
          }
 
431
          *s = c;               /* restore full name */
 
432
        }
 
433
        if (rename (file,tmp))
 
434
          sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
 
435
                   strerror (errno));
 
436
        else ret = T;           /* set success */
 
437
      }
 
438
      else if (unlink (file))
 
439
        sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
 
440
      else ret = T;             /* set success */
 
441
      unix_unlock (fd,NIL,&lockx);
 
442
    }
 
443
    unix_unlock (ld,NIL,NIL);   /* flush the lock */
 
444
    unlink (lock);
 
445
  }
 
446
  MM_NOCRITICAL (stream);       /* no longer critical */
 
447
  if (!ret) MM_LOG (tmp,ERROR); /* log error */
 
448
  return ret;                   /* return success or failure */
 
449
}
 
450
 
 
451
/* UNIX mail open
 
452
 * Accepts: Stream to open
 
453
 * Returns: Stream on success, NIL on failure
 
454
 */
 
455
 
 
456
MAILSTREAM *unix_open (MAILSTREAM *stream)
 
457
{
 
458
  long i;
 
459
  int fd;
 
460
  char tmp[MAILTMPLEN];
 
461
  DOTLOCK lock;
 
462
  long retry;
 
463
                                /* return prototype for OP_PROTOTYPE call */
 
464
  if (!stream) return user_flags (&unixproto);
 
465
  retry = stream->silent ? 1 : KODRETRY;
 
466
  if (stream->local) fatal ("unix recycle stream");
 
467
  stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
 
468
                                /* note if an INBOX or not */
 
469
  stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
 
470
                                /* canonicalize the stream mailbox name */
 
471
  if (!dummy_file (tmp,stream->mailbox)) {
 
472
    sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
 
473
    MM_LOG (tmp,ERROR);
 
474
    return NIL;
 
475
  }
 
476
                                /* flush old name */
 
477
  fs_give ((void **) &stream->mailbox);
 
478
                                /* save canonical name */
 
479
  stream->mailbox = cpystr (tmp);
 
480
  LOCAL->fd = LOCAL->ld = -1;   /* no file or state locking yet */
 
481
  LOCAL->buf = (char *) fs_get (CHUNKSIZE);
 
482
  LOCAL->buflen = CHUNKSIZE - 1;
 
483
  LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
 
484
  LOCAL->text.size = CHUNKSIZE - 1;
 
485
  LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
 
486
  LOCAL->linebuflen = CHUNKSIZE - 1;
 
487
  stream->sequence++;           /* bump sequence number */
 
488
 
 
489
                                /* make lock for read/write access */
 
490
  if (!stream->rdonly) while (retry) {
 
491
                                /* try to lock file */
 
492
    if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
 
493
                                /* suppressing kiss-of-death? */
 
494
      if (stream->nokod) retry = 0;
 
495
                                /* no, first time through? */
 
496
      else if (retry-- == KODRETRY) {
 
497
                                /* learned other guy's PID and can signal? */
 
498
        if (i && !kill ((int) i,SIGUSR2)) {
 
499
          sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
 
500
          MM_LOG (tmp,WARN);
 
501
        }
 
502
        else retry = 0;         /* give up */
 
503
      }
 
504
      if (!stream->silent) {    /* nothing if silent stream */
 
505
        if (retry) sleep (1);   /* wait a second before trying again */
 
506
        else MM_LOG ("Mailbox is open by another process, access is readonly",
 
507
                     WARN);
 
508
      }
 
509
    }
 
510
    else {                      /* got the lock, nobody else can alter state */
 
511
      LOCAL->ld = fd;           /* note lock's fd and name */
 
512
      LOCAL->lname = cpystr (tmp);
 
513
                                /* make sure mode OK (don't use fchmod()) */
 
514
      chmod (LOCAL->lname,(int) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
 
515
      if (stream->silent) i = 0;/* silent streams won't accept KOD */
 
516
      else {                    /* note our PID in the lock */
 
517
        sprintf (tmp,"%d",getpid ());
 
518
        write (fd,tmp,(i = strlen (tmp))+1);
 
519
      }
 
520
      ftruncate (fd,i);         /* make sure tied off */
 
521
      fsync (fd);               /* make sure it's available */
 
522
      retry = 0;                /* no more need to try */
 
523
    }
 
524
  }
 
525
 
 
526
                                /* parse mailbox */
 
527
  stream->nmsgs = stream->recent = 0;
 
528
                                /* will we be able to get write access? */
 
529
  if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
 
530
    MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
 
531
    flock (LOCAL->ld,LOCK_UN);  /* release the lock */
 
532
    close (LOCAL->ld);          /* close the lock file */
 
533
    LOCAL->ld = -1;             /* no more lock fd */
 
534
    unlink (LOCAL->lname);      /* delete it */
 
535
  }
 
536
                                /* reset UID validity */
 
537
  stream->uid_validity = stream->uid_last = 0;
 
538
  if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
 
539
    unix_abort (stream);        /* abort if can't get RW silent stream */
 
540
                                /* parse mailbox */
 
541
  else if (unix_parse (stream,&lock,LOCK_SH)) {
 
542
    unix_unlock (LOCAL->fd,stream,&lock);
 
543
    mail_unlock (stream);
 
544
    MM_NOCRITICAL (stream);     /* done with critical */
 
545
  }
 
546
  if (!LOCAL) return NIL;       /* failure if stream died */
 
547
                                /* make sure upper level knows readonly */
 
548
  stream->rdonly = (LOCAL->ld < 0);
 
549
                                /* notify about empty mailbox */
 
550
  if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
 
551
  if (!stream->rdonly) {        /* flags stick if readwrite */
 
552
    stream->perm_seen = stream->perm_deleted =
 
553
      stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
 
554
    if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
 
555
      stream->perm_user_flags = 0xffffffff;
 
556
                                /* and maybe can create them too! */
 
557
      stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
 
558
    }
 
559
  }
 
560
  return stream;                /* return stream alive to caller */
 
561
}
 
562
 
 
563
 
 
564
/* UNIX mail close
 
565
 * Accepts: MAIL stream
 
566
 *          close options
 
567
 */
 
568
 
 
569
void unix_close (MAILSTREAM *stream,long options)
 
570
{
 
571
  int silent = stream->silent;
 
572
  stream->silent = T;           /* go silent */
 
573
                                /* expunge if requested */
 
574
  if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
 
575
                                /* else dump final checkpoint */
 
576
  else if (LOCAL->dirty) unix_check (stream);
 
577
  stream->silent = silent;      /* restore old silence state */
 
578
  unix_abort (stream);          /* now punt the file and local data */
 
579
}
 
580
 
 
581
/* UNIX mail fetch message header
 
582
 * Accepts: MAIL stream
 
583
 *          message # to fetch
 
584
 *          pointer to returned header text length
 
585
 *          option flags
 
586
 * Returns: message header in RFC822 format
 
587
 */
 
588
 
 
589
                                /* lines to filter from header */
 
590
static STRINGLIST *unix_hlines = NIL;
 
591
 
 
592
char *unix_header (MAILSTREAM *stream,unsigned long msgno,
 
593
                   unsigned long *length,long flags)
 
594
{
 
595
  MESSAGECACHE *elt;
 
596
  unsigned char *s,*t,*tl;
 
597
  *length = 0;                  /* default to empty */
 
598
  if (flags & FT_UID) return "";/* UID call "impossible" */
 
599
  elt = mail_elt (stream,msgno);/* get cache */
 
600
  if (!unix_hlines) {           /* once only code */
 
601
    STRINGLIST *lines = unix_hlines = mail_newstringlist ();
 
602
    lines->text.size = strlen ((char *) (lines->text.data =
 
603
                                         (unsigned char *) "Status"));
 
604
    lines = lines->next = mail_newstringlist ();
 
605
    lines->text.size = strlen ((char *) (lines->text.data =
 
606
                                         (unsigned char *) "X-Status"));
 
607
    lines = lines->next = mail_newstringlist ();
 
608
    lines->text.size = strlen ((char *) (lines->text.data =
 
609
                                         (unsigned char *) "X-Keywords"));
 
610
    lines = lines->next = mail_newstringlist ();
 
611
    lines->text.size = strlen ((char *) (lines->text.data =
 
612
                                         (unsigned char *) "X-UID"));
 
613
    lines = lines->next = mail_newstringlist ();
 
614
    lines->text.size = strlen ((char *) (lines->text.data =
 
615
                                         (unsigned char *) "X-IMAP"));
 
616
    lines = lines->next = mail_newstringlist ();
 
617
    lines->text.size = strlen ((char *) (lines->text.data =
 
618
                                         (unsigned char *) "X-IMAPbase"));
 
619
  }
 
620
                                /* go to header position */
 
621
  lseek (LOCAL->fd,elt->private.special.offset +
 
622
         elt->private.msg.header.offset,L_SET);
 
623
 
 
624
  if (flags & FT_INTERNAL) {    /* initial data OK? */
 
625
    if (elt->private.msg.header.text.size > LOCAL->buflen) {
 
626
      fs_give ((void **) &LOCAL->buf);
 
627
      LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
 
628
                                     elt->private.msg.header.text.size) + 1);
 
629
    }
 
630
                                /* read message */
 
631
    read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
 
632
                                /* got text, tie off string */
 
633
    LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
 
634
                                /* squeeze out CRs (in case from PC) */
 
635
    for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++)
 
636
      if ((*t != '\r') || (t[1] != '\n')) *s++ = *t;
 
637
                                /* adjust length */
 
638
    LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0';
 
639
  }
 
640
  else {                        /* need to make a CRLF version */
 
641
    read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
 
642
          elt->private.msg.header.text.size);
 
643
                                /* tie off string, and convert to CRLF */
 
644
    s[elt->private.msg.header.text.size] = '\0';
 
645
    *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
 
646
                          elt->private.msg.header.text.size);
 
647
    fs_give ((void **) &s);     /* free readin buffer */
 
648
  }
 
649
  *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
 
650
  return (char *) LOCAL->buf;   /* return processed copy */
 
651
}
 
652
 
 
653
/* UNIX mail fetch message text
 
654
 * Accepts: MAIL stream
 
655
 *          message # to fetch
 
656
 *          pointer to returned stringstruct
 
657
 *          option flags
 
658
 * Returns: T on success, NIL if failure
 
659
 */
 
660
 
 
661
long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
 
662
{
 
663
  char *s;
 
664
  unsigned long i;
 
665
  MESSAGECACHE *elt;
 
666
                                /* UID call "impossible" */
 
667
  if (flags & FT_UID) return NIL;
 
668
  elt = mail_elt (stream,msgno);/* get cache element */
 
669
                                /* if message not seen */
 
670
  if (!(flags & FT_PEEK) && !elt->seen) {
 
671
                                /* mark message seen and dirty */
 
672
    elt->seen = elt->private.dirty = LOCAL->dirty = T;
 
673
    MM_FLAGS (stream,msgno);
 
674
  }
 
675
  s = unix_text_work (stream,elt,&i,flags);
 
676
  INIT (bs,mail_string,s,i);    /* set up stringstruct */
 
677
  return T;                     /* success */
 
678
}
 
679
 
 
680
/* UNIX mail fetch message text worker routine
 
681
 * Accepts: MAIL stream
 
682
 *          message cache element
 
683
 *          pointer to returned header text length
 
684
 *          option flags
 
685
 */
 
686
 
 
687
char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
 
688
                      unsigned long *length,long flags)
 
689
{
 
690
  FDDATA d;
 
691
  STRING bs;
 
692
  unsigned char *s,*t,*tl,tmp[CHUNKSIZE];
 
693
                                /* go to text position */
 
694
  lseek (LOCAL->fd,elt->private.special.offset +
 
695
         elt->private.msg.text.offset,L_SET);
 
696
  if (flags & FT_INTERNAL) {    /* initial data OK? */
 
697
    if (elt->private.msg.text.text.size > LOCAL->buflen) {
 
698
      fs_give ((void **) &LOCAL->buf);
 
699
      LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
 
700
                                     elt->private.msg.text.text.size) + 1);
 
701
    }
 
702
                                /* read message */
 
703
    read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
 
704
                                /* got text, tie off string */
 
705
    LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
 
706
                                /* squeeze out CRs (in case from PC) */
 
707
    for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t <= tl; t++)
 
708
      if ((*t != '\r') || (t[1] != '\n')) *s++ = *t;
 
709
                                /* adjust length */
 
710
    LOCAL->buf[*length = s - LOCAL->buf - 1] = '\0';
 
711
    return (char *) LOCAL->buf;
 
712
  }
 
713
 
 
714
                                /* have it cached already? */
 
715
  if (elt->private.uid != LOCAL->uid) {
 
716
                                /* not cached, cache it now */
 
717
    LOCAL->uid = elt->private.uid;
 
718
                                /* is buffer big enough? */
 
719
    if (elt->rfc822_size > LOCAL->text.size) {
 
720
      /* excessively conservative, but the right thing is too hard to do */
 
721
      fs_give ((void **) &LOCAL->text.data);
 
722
      LOCAL->text.data = (unsigned char *)
 
723
        fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
 
724
    }
 
725
    d.fd = LOCAL->fd;           /* yes, set up file descriptor */
 
726
    d.pos = elt->private.special.offset + elt->private.msg.text.offset;
 
727
    d.chunk = tmp;              /* initial buffer chunk */
 
728
    d.chunksize = CHUNKSIZE;    /* file chunk size */
 
729
    INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
 
730
    for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (CHR (&bs)) {
 
731
    case '\r':                  /* carriage return seen */
 
732
      *s++ = SNX (&bs);         /* copy it and any succeeding LF */
 
733
      if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
 
734
      break;
 
735
    case '\n':
 
736
      *s++ = '\r';              /* insert a CR */
 
737
    default:
 
738
      *s++ = SNX (&bs);         /* copy characters */
 
739
    }
 
740
    *s = '\0';                  /* tie off buffer */
 
741
                                /* calculate length of cached data */
 
742
    LOCAL->textlen = s - LOCAL->text.data;
 
743
  }
 
744
  *length = LOCAL->textlen;     /* return from cache */
 
745
  return (char *) LOCAL->text.data;
 
746
}
 
747
 
 
748
/* UNIX per-message modify flag
 
749
 * Accepts: MAIL stream
 
750
 *          message cache element
 
751
 */
 
752
 
 
753
void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
 
754
{
 
755
                                /* only after finishing */
 
756
  if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
 
757
}
 
758
 
 
759
 
 
760
/* UNIX mail ping mailbox
 
761
 * Accepts: MAIL stream
 
762
 * Returns: T if stream alive, else NIL
 
763
 */
 
764
 
 
765
long unix_ping (MAILSTREAM *stream)
 
766
{
 
767
  DOTLOCK lock;
 
768
  struct stat sbuf;
 
769
  long reparse;
 
770
                                /* big no-op if not readwrite */
 
771
  if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
 
772
    if (stream->rdonly) {       /* does he want to give up readwrite? */
 
773
                                /* checkpoint if we changed something */
 
774
      if (LOCAL->dirty) unix_check (stream);
 
775
      flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
 
776
      close (LOCAL->ld);        /* close the readwrite lock file */
 
777
      LOCAL->ld = -1;           /* no more readwrite lock fd */
 
778
      unlink (LOCAL->lname);    /* delete the readwrite lock file */
 
779
    }
 
780
    else {                      /* see if need to reparse */
 
781
      if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
 
782
                                /* get current mailbox size */
 
783
        if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
 
784
        else if (stat (stream->mailbox,&sbuf)) {
 
785
          sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
 
786
                   strerror (errno));
 
787
          MM_LOG (LOCAL->buf,ERROR);
 
788
          unix_abort (stream);
 
789
          return NIL;
 
790
        }
 
791
        reparse = (sbuf.st_size != LOCAL->filesize);
 
792
      }
 
793
                                /* parse if mailbox changed */
 
794
      if ((LOCAL->ddirty || reparse) && unix_parse (stream,&lock,LOCK_EX)) {
 
795
                                /* force checkpoint if double-dirty */
 
796
        if (LOCAL->ddirty) unix_rewrite (stream,NIL,&lock,NIL);
 
797
                                /* unlock mailbox */
 
798
        else unix_unlock (LOCAL->fd,stream,&lock);
 
799
        mail_unlock (stream);   /* and stream */
 
800
        MM_NOCRITICAL (stream); /* done with critical */
 
801
      }
 
802
    }
 
803
  }
 
804
  return LOCAL ? LONGT : NIL;   /* return if still alive */
 
805
}
 
806
 
 
807
/* UNIX mail check mailbox
 
808
 * Accepts: MAIL stream
 
809
 */
 
810
 
 
811
void unix_check (MAILSTREAM *stream)
 
812
{
 
813
  DOTLOCK lock;
 
814
                                /* parse and lock mailbox */
 
815
  if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
 
816
      unix_parse (stream,&lock,LOCK_EX)) {
 
817
                                /* any unsaved changes? */
 
818
    if (LOCAL->dirty && unix_rewrite (stream,NIL,&lock,NIL)) {
 
819
      if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
 
820
    }
 
821
                                /* no checkpoint needed, just unlock */
 
822
    else unix_unlock (LOCAL->fd,stream,&lock);
 
823
    mail_unlock (stream);       /* unlock the stream */
 
824
    MM_NOCRITICAL (stream);     /* done with critical */
 
825
  }
 
826
}
 
827
 
 
828
 
 
829
/* UNIX mail expunge mailbox
 
830
 * Accepts: MAIL stream
 
831
 *          sequence to expunge if non-NIL
 
832
 *          expunge options
 
833
 * Returns: T, always
 
834
 */
 
835
 
 
836
long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
 
837
{
 
838
  long ret;
 
839
  unsigned long i;
 
840
  DOTLOCK lock;
 
841
  char *msg = NIL;
 
842
                                /* parse and lock mailbox */
 
843
  if (ret = (sequence ? ((options & EX_UID) ?
 
844
                         mail_uid_sequence (stream,sequence) :
 
845
                         mail_sequence (stream,sequence)) : LONGT) &&
 
846
      LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
 
847
      unix_parse (stream,&lock,LOCK_EX)) {
 
848
                                /* check expunged messages if not dirty */
 
849
    for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
 
850
      MESSAGECACHE *elt = mail_elt (stream,i);
 
851
      if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
 
852
    }
 
853
    if (!LOCAL->dirty) {        /* not dirty and no expunged messages */
 
854
      unix_unlock (LOCAL->fd,stream,&lock);
 
855
      msg = "No messages deleted, so no update needed";
 
856
    }
 
857
    else if (unix_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
 
858
      if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
 
859
      else msg = "Mailbox checkpointed, but no messages expunged";
 
860
    }
 
861
                                /* rewrite failed */
 
862
    else unix_unlock (LOCAL->fd,stream,&lock);
 
863
    mail_unlock (stream);       /* unlock the stream */
 
864
    MM_NOCRITICAL (stream);     /* done with critical */
 
865
    if (msg && !stream->silent) MM_LOG (msg,NIL);
 
866
  }
 
867
  else if (!stream->silent) MM_LOG("Expunge ignored on readonly mailbox",WARN);
 
868
  return ret;
 
869
}
 
870
 
 
871
/* UNIX mail copy message(s)
 
872
 * Accepts: MAIL stream
 
873
 *          sequence
 
874
 *          destination mailbox
 
875
 *          copy options
 
876
 * Returns: T if copy successful, else NIL
 
877
 */
 
878
 
 
879
long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
 
880
{
 
881
  struct stat sbuf;
 
882
  int fd;
 
883
  char *s,file[MAILTMPLEN];
 
884
  DOTLOCK lock;
 
885
  time_t tp[2];
 
886
  unsigned long i,j;
 
887
  MESSAGECACHE *elt;
 
888
  long ret = T;
 
889
  mailproxycopy_t pc =
 
890
    (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
 
891
  copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
 
892
                              NIL : mail_parameters (NIL,GET_COPYUID,NIL));
 
893
  SEARCHSET *source = cu ? mail_newsearchset () : NIL;
 
894
  SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
 
895
  MAILSTREAM *tstream = NIL;
 
896
  DRIVER *d;
 
897
  for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
 
898
       (d && strcmp (d->name,"mbox") && !(d->flags & DR_DISABLE));
 
899
       d = d->next);            /* see if mbox driver active */
 
900
  if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
 
901
        mail_sequence (stream,sequence))) return NIL;
 
902
                                /* make sure destination is valid */
 
903
  if ((d && mbox_valid (mailbox) && (mailbox = "mbox")) ||
 
904
      unix_valid (mailbox) || !errno) {
 
905
                                /* try to open rewrite or at least sniff */
 
906
    if (!(tstream = mail_open_work (&unixdriver,NIL,mailbox,
 
907
                                    OP_SILENT|OP_NOKOD)) &&
 
908
        !(tstream = mail_open_work (&unixdriver,NIL,mailbox,
 
909
                                    OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))){
 
910
      sprintf (LOCAL->buf,"Unable to open mailbox for COPY: %.80s",mailbox);
 
911
      MM_LOG (LOCAL->buf,ERROR);
 
912
      return NIL;
 
913
    }
 
914
  }
 
915
  else switch (errno) {
 
916
  case ENOENT:                  /* no such file? */
 
917
    if (compare_cstring (mailbox,"INBOX")) {
 
918
      MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
 
919
      return NIL;
 
920
    }
 
921
    if (pc) return (*pc) (stream,sequence,mailbox,options);
 
922
    unix_create (NIL,"INBOX");/* create empty INBOX */
 
923
  case EINVAL:
 
924
    if (pc) return (*pc) (stream,sequence,mailbox,options);
 
925
    sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
 
926
    MM_LOG (LOCAL->buf,ERROR);
 
927
    return NIL;
 
928
  default:
 
929
    if (pc) return (*pc) (stream,sequence,mailbox,options);
 
930
    sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
 
931
    MM_LOG (LOCAL->buf,ERROR);
 
932
    return NIL;
 
933
  }
 
934
 
 
935
  LOCAL->buf[0] = '\0';
 
936
  MM_CRITICAL (stream);         /* go critical */
 
937
  if ((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
 
938
                       (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
 
939
                       &lock,LOCK_EX)) < 0) {
 
940
    MM_NOCRITICAL (stream);     /* done with critical */
 
941
    sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
 
942
    MM_LOG (LOCAL->buf,ERROR);/* log the error */
 
943
    return NIL;                 /* failed */
 
944
  }
 
945
  fstat (fd,&sbuf);             /* get current file size */
 
946
                                /* write all requested messages to mailbox */
 
947
  for (i = 1; ret && (i <= stream->nmsgs); i++)
 
948
    if ((elt = mail_elt (stream,i))->sequence) {
 
949
      lseek (LOCAL->fd,elt->private.special.offset,L_SET);
 
950
      read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
 
951
      if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
 
952
      else {                    /* internal header succeeded */
 
953
        s = unix_header (stream,i,&j,FT_INTERNAL);
 
954
                                /* header size, sans trailing newline */
 
955
        if (j && (s[j - 2] == '\n')) j--;
 
956
        if (write (fd,s,j) < 0) ret = NIL;
 
957
        else {                  /* message header succeeded */
 
958
          j = unix_xstatus (stream,LOCAL->buf,elt,
 
959
                            cu ? ++(tstream->uid_last) : NIL,cu ? LONGT : NIL);
 
960
          if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
 
961
          else {                /* message status succeeded */
 
962
            s = unix_text_work (stream,elt,&j,FT_INTERNAL);
 
963
            if ((write (fd,s,j) < 0) || (write (fd,"\n",1) < 0)) ret = NIL;
 
964
            else if (cu) {      /* need to pass back new UID? */
 
965
              mail_append_set (source,mail_uid (stream,i));
 
966
              mail_append_set (dest,tstream->uid_last);
 
967
            }
 
968
          }
 
969
        }
 
970
      }
 
971
    }
 
972
 
 
973
  if (!ret || fsync (fd)) {     /* force out the update */
 
974
    sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
 
975
    ftruncate (fd,sbuf.st_size);
 
976
    ret = NIL;
 
977
  }
 
978
                                /* force UIDVALIDITY assignment now */
 
979
  if (!tstream->uid_validity && !tstream->rdonly)
 
980
    tstream->uid_validity = time (0);
 
981
                                /* return sets if doing COPYUID */
 
982
  if (cu && ret && tstream->uid_validity)
 
983
    (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
 
984
  else {                        /* flush any sets we may have built */
 
985
    mail_free_searchset (&source);
 
986
    mail_free_searchset (&dest);
 
987
  }
 
988
  tp[1] = time (0);             /* set mtime to now */
 
989
  if (ret) tp[0] = tp[1] - 1;   /* set atime to now-1 if successful copy */
 
990
  else tp[0] =                  /* else preserve \Marked status */
 
991
         ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
 
992
         sbuf.st_atime : tp[1];
 
993
  utime (file,tp);              /* set the times */
 
994
  unix_unlock (fd,NIL,&lock);   /* unlock and close mailbox */
 
995
                                /* update last UID if we can */
 
996
  if (!tstream->rdonly) ((UNIXLOCAL *) tstream->local)->dirty = T;
 
997
  tstream = mail_close (tstream);
 
998
                                /* log the error */
 
999
  if (!ret) MM_LOG (LOCAL->buf,ERROR);
 
1000
                                /* delete if requested message */
 
1001
  else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
 
1002
    if ((elt = mail_elt (stream,i))->sequence)
 
1003
      elt->deleted = elt->private.dirty = LOCAL->dirty = T;
 
1004
  MM_NOCRITICAL (stream);       /* release critical */
 
1005
  return ret;
 
1006
}
 
1007
 
 
1008
/* UNIX mail append message from stringstruct
 
1009
 * Accepts: MAIL stream
 
1010
 *          destination mailbox
 
1011
 *          append callback
 
1012
 *          data for callback
 
1013
 * Returns: T if append successful, else NIL
 
1014
 */
 
1015
 
 
1016
#define BUFLEN 8*MAILTMPLEN
 
1017
 
 
1018
long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
 
1019
{
 
1020
  struct stat sbuf;
 
1021
  int fd;
 
1022
  unsigned long i;
 
1023
  char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
 
1024
  time_t tp[2];
 
1025
  FILE *sf,*df;
 
1026
  MESSAGECACHE elt;
 
1027
  DOTLOCK lock;
 
1028
  STRING *message;
 
1029
  unsigned long uidlocation = 0;
 
1030
  appenduid_t au = (appenduid_t)
 
1031
    (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
 
1032
     mail_parameters (NIL,GET_APPENDUID,NIL));
 
1033
  SEARCHSET *dst = au ? mail_newsearchset () : NIL;
 
1034
  long ret = LONGT;
 
1035
  MAILSTREAM *tstream = NIL;
 
1036
  if (!stream) {                /* stream specified? */
 
1037
    stream = &unixproto;        /* no, default stream to prototype */
 
1038
    for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
 
1039
      fs_give ((void **) &stream->user_flags[i]);
 
1040
    stream->kwd_create = T;     /* allow new flags */
 
1041
  }
 
1042
  if (unix_valid (mailbox)) {   /* make sure valid mailbox */
 
1043
    if (!(tstream = mail_open (NIL,mailbox,
 
1044
                               OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
 
1045
      sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
 
1046
      MM_LOG (tmp,ERROR);
 
1047
      return NIL;
 
1048
    }
 
1049
  }
 
1050
 
 
1051
  else switch (errno) {
 
1052
  case ENOENT:                  /* no such file? */
 
1053
    if (compare_cstring (mailbox,"INBOX")) {
 
1054
      MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
 
1055
      return NIL;
 
1056
    }
 
1057
    unix_create (NIL,"INBOX");  /* create empty INBOX */
 
1058
  case 0:                       /* merely empty file? */
 
1059
    tstream = stream;
 
1060
    break;
 
1061
  case EINVAL:
 
1062
    sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
 
1063
    MM_LOG (tmp,ERROR);
 
1064
    return NIL;
 
1065
  default:
 
1066
    sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
 
1067
    MM_LOG (tmp,ERROR);
 
1068
    return NIL;
 
1069
  }
 
1070
                                /* get first message */
 
1071
  if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
 
1072
  if (!(sf = tmpfile ())) {     /* must have scratch file */
 
1073
    sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
 
1074
    if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
 
1075
      sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
 
1076
      MM_LOG (tmp,ERROR);
 
1077
      return NIL;
 
1078
    }
 
1079
    unlink (tmp);
 
1080
  }
 
1081
 
 
1082
  do {                          /* parse date */
 
1083
    if (!date) rfc822_date (date = tmp);
 
1084
    if (!mail_parse_date (&elt,date)) {
 
1085
      sprintf (tmp,"Bad date in append: %.80s",date);
 
1086
      MM_LOG (tmp,ERROR);
 
1087
    }
 
1088
    else {                      /* user wants to suppress time zones? */
 
1089
      if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
 
1090
        time_t when = mail_longdate (&elt);
 
1091
        date = ctime (&when);   /* use traditional date */
 
1092
      }
 
1093
                                /* use POSIX-style date */
 
1094
      else date = mail_cdate (tmp,&elt);
 
1095
      if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
 
1096
      else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
 
1097
        sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
 
1098
        MM_LOG (tmp,ERROR);
 
1099
      }
 
1100
                                /* get next message */
 
1101
      else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
 
1102
    }
 
1103
    fclose (sf);                /* punt scratch file */
 
1104
    return NIL;                 /* give up */
 
1105
  } while (message);            /* until no more messages */
 
1106
  if (fflush (sf) || fstat (fileno (sf),&sbuf)) {
 
1107
    sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
 
1108
    MM_LOG (tmp,ERROR);
 
1109
    fclose (sf);                /* punt scratch file */
 
1110
    return NIL;                 /* give up */
 
1111
  }
 
1112
  i = sbuf.st_size;             /* size of scratch file */
 
1113
 
 
1114
                                /* close sniffing stream */
 
1115
  if (tstream != stream) tstream = mail_close (tstream);
 
1116
  MM_CRITICAL (stream);         /* go critical */
 
1117
                                /* try to open readwrite */
 
1118
  if (!(tstream = mail_open_work (&unixdriver,NIL,mailbox,
 
1119
                                  OP_SILENT|OP_NOKOD)) &&
 
1120
      !(tstream = mail_open_work (&unixdriver,NIL,mailbox,
 
1121
                                  OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
 
1122
    sprintf (tmp,"Unable to re-open mailbox for APPEND: %.80s",mailbox);
 
1123
    MM_LOG (tmp,ERROR);
 
1124
    return NIL;
 
1125
  }
 
1126
  if (((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
 
1127
                       (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
 
1128
                        &lock,LOCK_EX)) < 0) ||
 
1129
      !(df = fdopen (fd,"ab"))) {
 
1130
    MM_NOCRITICAL (stream);     /* done with critical */
 
1131
    sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
 
1132
    MM_LOG (tmp,ERROR);
 
1133
    return NIL;
 
1134
  }
 
1135
  fstat (fd,&sbuf);             /* get current file size */
 
1136
  rewind (sf);
 
1137
  tp[1] = time (0);             /* set mtime to now */
 
1138
                                /* write all messages */
 
1139
  if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
 
1140
      (fflush (df) == EOF) || fsync (fd)) {
 
1141
    sprintf (buf,"Message append failed: %s",strerror (errno));
 
1142
    MM_LOG (buf,ERROR);
 
1143
    ftruncate (fd,sbuf.st_size);
 
1144
    tp[0] =                     /* preserve \Marked status */
 
1145
      ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
 
1146
      sbuf.st_atime : tp[1];
 
1147
    ret = NIL;                  /* return error */
 
1148
  }
 
1149
  else tp[0] = tp[1] - 1;       /* set atime to now-1 if successful copy */
 
1150
  utime (file,tp);              /* set the times */
 
1151
  fclose (sf);                  /* done with scratch file */
 
1152
                                /* force UIDVALIDITY assignment now */
 
1153
  if (!tstream->uid_validity && !tstream->rdonly)
 
1154
    tstream->uid_validity = time (0);
 
1155
                                /* return sets if doing APPENDUID */
 
1156
  if (au && ret && tstream->uid_validity)
 
1157
    (*au) (mailbox,tstream->uid_validity,dst);
 
1158
  else mail_free_searchset (&dst);
 
1159
  unix_unlock (fd,NIL,&lock);   /* unlock and close mailbox */
 
1160
  fclose (df);                  /* note that unix_unlock() released the fd */
 
1161
                                /* update last UID if we can */
 
1162
  if (!tstream->rdonly) ((UNIXLOCAL *) tstream->local)->dirty = T;
 
1163
  tstream = mail_close (tstream);
 
1164
  MM_NOCRITICAL (stream);       /* release critical */
 
1165
  return ret;
 
1166
}
 
1167
 
 
1168
/* Collect and write single message to append scratch file
 
1169
 * Accepts: MAIL stream
 
1170
 *          scratch file
 
1171
 *          flags
 
1172
 *          message stringstruct
 
1173
 * Returns: NIL if write error, else T
 
1174
 */
 
1175
 
 
1176
int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
 
1177
                     STRING *msg)
 
1178
{
 
1179
  unsigned char *s,*t;
 
1180
  unsigned long uf;
 
1181
  long f = mail_parse_flags (stream,flags,&uf);
 
1182
                                /* write metadata, note date ends with NL */
 
1183
  if (fprintf (sf,"%ld %lu %lu %s",f,uf,SIZE (msg) + 1,date) < 0) return NIL;
 
1184
  while (SIZE (msg)) {          /* copy text to scratch file */
 
1185
    for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
 
1186
      if (!*s) *s = 0x80;       /* disallow NUL */
 
1187
                                /* write buffered text */
 
1188
    if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
 
1189
      SETPOS (msg,GETPOS (msg) + msg->cursize);
 
1190
    else return NIL;            /* failed */
 
1191
  }
 
1192
                                /* write trailing newline and return */
 
1193
  return (putc ('\n',sf) == EOF) ? NIL : T;
 
1194
}
 
1195
 
 
1196
/* Append messages from scratch file to mailbox
 
1197
 * Accepts: MAIL stream
 
1198
 *          source file
 
1199
 *          destination file
 
1200
 *          uidset to update if non-NIL
 
1201
 * Returns: T if success, NIL if failure
 
1202
 */
 
1203
 
 
1204
int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
 
1205
{
 
1206
  int ti,zn,c;
 
1207
  long f;
 
1208
  unsigned long i,j,uf;
 
1209
  char *x,tmp[MAILTMPLEN];
 
1210
  int hdrp = T;
 
1211
                                /* get message metadata line */
 
1212
  while (fgets (tmp,MAILTMPLEN,sf)) {
 
1213
    if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
 
1214
    f = strtol (tmp,&x,10);     /* get flags */
 
1215
    if (!((*x++ == ' ') && isdigit (*x))) return NIL;
 
1216
    uf = strtoul (x,&x,10);     /* get keywords */
 
1217
    if (!((*x++ == ' ') && isdigit (*x))) return NIL;
 
1218
    i = strtoul (x,&x,10);      /* get message size */
 
1219
    if ((*x++ != ' ') ||        /* build initial header */
 
1220
        (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
 
1221
        (f&fSEEN && (putc ('R',df) == EOF)) ||
 
1222
        (fputs ("\nX-Status: ",df) == EOF) ||
 
1223
        (f&fDELETED && (putc ('D',df) == EOF)) ||
 
1224
        (f&fFLAGGED && (putc ('F',df) == EOF)) ||
 
1225
        (f&fANSWERED && (putc ('A',df) == EOF)) ||
 
1226
        (f&fDRAFT && (putc ('T',df) == EOF)) ||
 
1227
        (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
 
1228
    while (uf)                  /* write user flags */
 
1229
      if (fprintf (df," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0)
 
1230
        return NIL;
 
1231
    if (set && (fprintf (df,"\nX-UID: %lu",++(stream->uid_last)) < 0))
 
1232
      return NIL;
 
1233
                                /* finish last metadata header */
 
1234
    if (putc ('\n',df) == EOF) return NIL;
 
1235
 
 
1236
    for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
 
1237
                                /* get read line length */
 
1238
      if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
 
1239
      i -= j;                   /* number of bytes left */
 
1240
                                /* squish out CRs (note also copies NUL) */
 
1241
      for (x = tmp; x = strchr (x,'\r'); --j) memmove (x,x+1,j-(x-tmp));
 
1242
      if (!j) continue;         /* do nothing if line emptied */
 
1243
                                /* start of line? */
 
1244
      if ((c == '\n')) switch (tmp[0]) {
 
1245
      case 'F':                 /* possible "From " (case counts here) */
 
1246
        if ((i > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
 
1247
            (tmp[3] == 'm') && (tmp[4] == ' ')) {
 
1248
          if (!unix_fromwidget) {
 
1249
            VALID (tmp,x,ti,zn);/* conditional, only write widget if */
 
1250
            if (!ti) break;     /*  it looks like a valid header */
 
1251
          }                     /* write the widget */
 
1252
          if (putc ('>',df) == EOF) return NIL;
 
1253
        }
 
1254
        break;
 
1255
      case 'S': case 's':       /* possible "Status:" */
 
1256
        if (hdrp && (i > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
 
1257
            ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
 
1258
            ((tmp[3] == 't') || (tmp[3] == 'T')) &&
 
1259
            ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
 
1260
            ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
 
1261
            (fputs ("X-Original-",df) == EOF)) return NIL;
 
1262
      case 'X': case 'x':       /* possible X-??? header */
 
1263
        if (hdrp && (tmp[1] == '-') &&
 
1264
                                /* possible X-UID: */
 
1265
            (((i > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
 
1266
              ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
 
1267
              ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
 
1268
                                /* possible X-IMAP: */
 
1269
             ((i > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
 
1270
              ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
 
1271
              ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
 
1272
              ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
 
1273
              ((tmp[6] == ':') ||
 
1274
                                /* or X-IMAPbase: */
 
1275
               ((i > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
 
1276
                ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
 
1277
                ((tmp[8] == 's') || (tmp[8] == 'S')) &&
 
1278
                ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
 
1279
                                /* possible X-Status: */
 
1280
             ((i > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
 
1281
              ((tmp[3] == 't') || (tmp[3] == 'T')) &&
 
1282
              ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
 
1283
              ((tmp[5] == 't') || (tmp[5] == 'T')) &&
 
1284
              ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
 
1285
              ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
 
1286
                                /* possible X-Keywords: */
 
1287
             ((i > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
 
1288
              ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
 
1289
              ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
 
1290
              ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
 
1291
              ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
 
1292
              ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
 
1293
              ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
 
1294
              ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
 
1295
            (fputs ("X-Original-",df) == EOF)) return NIL;
 
1296
      default:                  /* nothing to do */
 
1297
        break;
 
1298
      }
 
1299
                                /* just write the line */
 
1300
      if (fwrite (tmp,1,j,df) != j) return NIL;
 
1301
    }
 
1302
    if (i) return NIL;          /* didn't read entire message */
 
1303
                                /* update set */
 
1304
    mail_append_set (set,stream->uid_last);
 
1305
  }
 
1306
  return T;
 
1307
}
 
1308
 
 
1309
/* Internal routines */
 
1310
 
 
1311
 
 
1312
/* UNIX mail abort stream
 
1313
 * Accepts: MAIL stream
 
1314
 */
 
1315
 
 
1316
void unix_abort (MAILSTREAM *stream)
 
1317
{
 
1318
  if (LOCAL) {                  /* only if a file is open */
 
1319
    if (LOCAL->fd >= 0) close (LOCAL->fd);
 
1320
    if (LOCAL->ld >= 0) {       /* have a mailbox lock? */
 
1321
      flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
 
1322
      close (LOCAL->ld);        /* close the lock file */
 
1323
      unlink (LOCAL->lname);    /* and delete it */
 
1324
    }
 
1325
    if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
 
1326
                                /* free local text buffers */
 
1327
    if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
 
1328
    if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
 
1329
    if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
 
1330
    if (LOCAL->line) fs_give ((void **) &LOCAL->line);
 
1331
                                /* nuke the local data */
 
1332
    fs_give ((void **) &stream->local);
 
1333
    stream->dtb = NIL;          /* log out the DTB */
 
1334
  }
 
1335
}
 
1336
 
 
1337
/* UNIX open and lock mailbox
 
1338
 * Accepts: file name to open/lock
 
1339
 *          file open mode
 
1340
 *          destination buffer for lock file name
 
1341
 *          type of locking operation (LOCK_SH or LOCK_EX)
 
1342
 */
 
1343
 
 
1344
int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
 
1345
{
 
1346
  int fd;
 
1347
  blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
 
1348
  (*bn) (BLOCK_FILELOCK,NIL);
 
1349
                                /* try locking the easy way */
 
1350
  if (dotlock_lock (file,lock,-1)) {
 
1351
                                /* got dotlock file, easy open */
 
1352
    if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
 
1353
    else dotlock_unlock (lock); /* open failed, free the dotlock */
 
1354
  }
 
1355
                                /* no dot lock file, open file now */
 
1356
  else if ((fd = open (file,flags,mode)) >= 0) {
 
1357
                                /* try paranoid way to make a dot lock file */
 
1358
    if (dotlock_lock (file,lock,fd)) {
 
1359
      close (fd);               /* get fresh fd in case of timing race */
 
1360
      if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
 
1361
                                /* open failed, free the dotlock */
 
1362
      else dotlock_unlock (lock);
 
1363
    }
 
1364
    else flock (fd,op);         /* paranoid way failed, just flock() it */
 
1365
  }
 
1366
  (*bn) (BLOCK_NONE,NIL);
 
1367
  return fd;
 
1368
}
 
1369
 
 
1370
/* UNIX unlock and close mailbox
 
1371
 * Accepts: file descriptor
 
1372
 *          (optional) mailbox stream to check atime/mtime
 
1373
 *          (optional) lock file name
 
1374
 */
 
1375
 
 
1376
void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
 
1377
{
 
1378
  if (stream) {                 /* need to muck with times? */
 
1379
    struct stat sbuf;
 
1380
    time_t tp[2];
 
1381
    time_t now = time (0);
 
1382
    fstat (fd,&sbuf);           /* get file times */
 
1383
    if (LOCAL->ld >= 0) {       /* yes, readwrite session? */
 
1384
      tp[0] = now;              /* set atime to now */
 
1385
                                /* set mtime to (now - 1) if necessary */
 
1386
      tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
 
1387
    }
 
1388
    else if (stream->recent) {  /* readonly with recent messages */
 
1389
      if ((sbuf.st_atime >= sbuf.st_mtime) ||
 
1390
          (sbuf.st_atime >= sbuf.st_ctime))
 
1391
                                /* keep past mtime, whack back atime */
 
1392
        tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
 
1393
      else now = 0;             /* no time change needed */
 
1394
    }
 
1395
                                /* readonly with no recent messages */
 
1396
    else if ((sbuf.st_atime < sbuf.st_mtime) ||
 
1397
             (sbuf.st_atime < sbuf.st_ctime)) {
 
1398
      tp[0] = now;              /* set atime to now */
 
1399
                                /* set mtime to (now - 1) if necessary */
 
1400
      tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
 
1401
    }
 
1402
    else now = 0;               /* no time change needed */
 
1403
                                /* set the times, note change */
 
1404
    if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
 
1405
  }
 
1406
  flock (fd,LOCK_UN);           /* release flock'ers */
 
1407
  if (!stream) close (fd);      /* close the file if no stream */
 
1408
  dotlock_unlock (lock);        /* flush the lock file if any */
 
1409
}
 
1410
 
 
1411
/* UNIX mail parse and lock mailbox
 
1412
 * Accepts: MAIL stream
 
1413
 *          space to write lock file name
 
1414
 *          type of locking operation
 
1415
 * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
 
1416
 */
 
1417
 
 
1418
int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
 
1419
{
 
1420
  int zn;
 
1421
  unsigned long i,j,k,m;
 
1422
  unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
 
1423
  int ti = 0,retain = T;
 
1424
  unsigned long nmsgs = stream->nmsgs;
 
1425
  unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
 
1426
  unsigned long recent = stream->recent;
 
1427
  unsigned long oldnmsgs = stream->nmsgs;
 
1428
  short silent = stream->silent;
 
1429
  short pseudoseen = NIL;
 
1430
  struct stat sbuf;
 
1431
  STRING bs;
 
1432
  FDDATA d;
 
1433
  MESSAGECACHE *elt;
 
1434
  mail_lock (stream);           /* guard against recursion or pingers */
 
1435
                                /* toss out previous descriptor */
 
1436
  if (LOCAL->fd >= 0) close (LOCAL->fd);
 
1437
  MM_CRITICAL (stream);         /* open and lock mailbox (shared OK) */
 
1438
  if ((LOCAL->fd = unix_lock (stream->mailbox,(LOCAL->ld >= 0) ?
 
1439
                              O_RDWR : O_RDONLY,
 
1440
                              (int) mail_parameters(NIL,GET_MBXPROTECTION,NIL),
 
1441
                              lock,op)) < 0) {
 
1442
    sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
 
1443
    MM_LOG (tmp,ERROR);
 
1444
    unix_abort (stream);
 
1445
    mail_unlock (stream);
 
1446
    MM_NOCRITICAL (stream);     /* done with critical */
 
1447
    return NIL;
 
1448
  }
 
1449
  fstat (LOCAL->fd,&sbuf);      /* get status */
 
1450
                                /* validate change in size */
 
1451
  if (sbuf.st_size < LOCAL->filesize) {
 
1452
    sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
 
1453
             (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
 
1454
    MM_LOG (tmp,ERROR);         /* this is pretty bad */
 
1455
    unix_unlock (LOCAL->fd,stream,lock);
 
1456
    unix_abort (stream);
 
1457
    mail_unlock (stream);
 
1458
    MM_NOCRITICAL (stream);     /* done with critical */
 
1459
    return NIL;
 
1460
  }
 
1461
 
 
1462
                                /* new data? */
 
1463
  else if (i = sbuf.st_size - LOCAL->filesize) {
 
1464
    d.fd = LOCAL->fd;           /* yes, set up file descriptor */
 
1465
    d.pos = LOCAL->filesize;    /* get to that position in the file */
 
1466
    d.chunk = LOCAL->buf;       /* initial buffer chunk */
 
1467
    d.chunksize = CHUNKSIZE;    /* file chunk size */
 
1468
    INIT (&bs,fd_string,&d,i);  /* initialize stringstruct */
 
1469
                                /* skip leading whitespace for broken MTAs */
 
1470
    while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
 
1471
           (c == ' ') || (c == '\t')) SNX (&bs);
 
1472
    if (SIZE (&bs)) {           /* read new data */
 
1473
                                /* remember internal header position */
 
1474
      j = LOCAL->filesize + GETPOS (&bs);
 
1475
      s = unix_mbxline (stream,&bs,&i);
 
1476
      t = NIL,zn = 0;
 
1477
      if (i) VALID (s,t,ti,zn); /* see if valid From line */
 
1478
      if (!ti) {                /* someone pulled the rug from under us */
 
1479
        sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
 
1480
                 (char *) s);
 
1481
        MM_LOG (tmp,ERROR);
 
1482
        unix_unlock (LOCAL->fd,stream,lock);
 
1483
        unix_abort (stream);
 
1484
        mail_unlock (stream);
 
1485
                                /* done with critical */
 
1486
        MM_NOCRITICAL (stream);
 
1487
        return NIL;
 
1488
      }
 
1489
      stream->silent = T;       /* quell main program new message events */
 
1490
      do {                      /* found a message */
 
1491
                                /* instantiate first new message */
 
1492
        mail_exists (stream,++nmsgs);
 
1493
        (elt = mail_elt (stream,nmsgs))->valid = T;
 
1494
        recent++;               /* assume recent by default */
 
1495
        elt->recent = T;
 
1496
                                /* note position/size of internal header */
 
1497
        elt->private.special.offset = j;
 
1498
        elt->private.msg.header.offset = elt->private.special.text.size = i;
 
1499
 
 
1500
                                /* generate plausible IMAPish date string */
 
1501
        date[2] = date[6] = date[20] = '-'; date[11] = ' ';
 
1502
        date[14] = date[17] = ':';
 
1503
                                /* dd */
 
1504
        date[0] = t[ti - 2]; date[1] = t[ti - 1];
 
1505
                                /* mmm */
 
1506
        date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
 
1507
                                /* hh */
 
1508
        date[12] = t[ti + 1]; date[13] = t[ti + 2];
 
1509
                                /* mm */
 
1510
        date[15] = t[ti + 4]; date[16] = t[ti + 5];
 
1511
        if (t[ti += 6] == ':') {/* ss */
 
1512
          date[18] = t[++ti]; date[19] = t[++ti];
 
1513
          ti++;                 /* move to space */
 
1514
        }
 
1515
        else date[18] = date[19] = '0';
 
1516
                                /* yy -- advance over timezone if necessary */
 
1517
        if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
 
1518
        date[7] = t[ti + 1]; date[8] = t[ti + 2];
 
1519
        date[9] = t[ti + 3]; date[10] = t[ti + 4];
 
1520
                                /* zzz */
 
1521
        t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
 
1522
        date[21] = *t++; date[22] = *t++; date[23] = *t++;
 
1523
        if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
 
1524
        else {                  /* numeric time zone */
 
1525
          date[24] = *t++; date[25] = *t++;
 
1526
          date[26] = '\0'; date[20] = ' ';
 
1527
        }
 
1528
                                /* set internal date */
 
1529
        if (!mail_parse_date (elt,date)) {
 
1530
          sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
 
1531
          MM_LOG (tmp,WARN);
 
1532
        }
 
1533
 
 
1534
        do {                    /* look for message body */
 
1535
          s = t = unix_mbxline (stream,&bs,&i);
 
1536
          if (i) switch (*s) {  /* check header lines */
 
1537
          case 'X':             /* possible X-???: line */
 
1538
            if (s[1] == '-') {  /* must be immediately followed by hyphen */
 
1539
                                /* X-Status: becomes Status: in S case */
 
1540
              if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
 
1541
                  s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
 
1542
                                /* possible X-Keywords */
 
1543
              else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
 
1544
                       s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
 
1545
                       s[8] == 'd' && s[9] == 's' && s[10] == ':') {
 
1546
                SIZEDTEXT uf;
 
1547
                retain = NIL;   /* don't retain continuation */
 
1548
                s += 11;        /* flush leading whitespace */
 
1549
                while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
 
1550
                  while (*s == ' ') s++;
 
1551
                                /* find end of keyword */
 
1552
                  if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
 
1553
                                /* got a keyword? */
 
1554
                  if ((k = (u - s)) <= MAXUSERFLAG) {
 
1555
                    uf.data = (unsigned char *) s;
 
1556
                    uf.size = k;
 
1557
                    for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
 
1558
                      if (!compare_csizedtext (stream->user_flags[j],&uf)) {
 
1559
                        elt->user_flags |= ((long) 1) << j;
 
1560
                        break;
 
1561
                      }
 
1562
                  }
 
1563
                  s = u;        /* advance to next keyword */
 
1564
                }
 
1565
                break;
 
1566
              }
 
1567
 
 
1568
                                /* possible X-IMAP */
 
1569
              else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
 
1570
                       (s[5] == 'P') && ((m = (s[6] == ':')) ||
 
1571
                                         ((s[6] == 'b') && (s[7] == 'a') &&
 
1572
                                          (s[8] == 's') && (s[9] == 'e') &&
 
1573
                                          (s[10] == ':')))) {
 
1574
                retain = NIL;   /* don't retain continuation */
 
1575
                if ((nmsgs == 1) && !stream->uid_validity) {
 
1576
                                /* advance to data */
 
1577
                  s += m ? 7 : 11;
 
1578
                                /* flush whitespace */
 
1579
                  while (*s == ' ') s++;
 
1580
                  j = 0;        /* slurp UID validity */
 
1581
                                /* found a digit? */
 
1582
                  while (isdigit (*s)) {
 
1583
                    j *= 10;    /* yes, add it in */
 
1584
                    j += *s++ - '0';
 
1585
                  }
 
1586
                                /* flush whitespace */
 
1587
                  while (*s == ' ') s++;
 
1588
                                /* must have valid UID validity and UID last */
 
1589
                  if (j && isdigit (*s)) {
 
1590
                                /* pseudo-header seen if X-IMAP */
 
1591
                    if (m) pseudoseen = LOCAL->pseudo = T;
 
1592
                                /* save UID validity */
 
1593
                    stream->uid_validity = j;
 
1594
                    j = 0;      /* slurp UID last */
 
1595
                    while (isdigit (*s)) {
 
1596
                      j *= 10;  /* yes, add it in */
 
1597
                      j += *s++ - '0';
 
1598
                    }
 
1599
                                /* save UID last */
 
1600
                    stream->uid_last = j;
 
1601
                                /* process keywords */
 
1602
                    for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
 
1603
                         s = u,j++) {
 
1604
                                /* flush leading whitespace */
 
1605
                      while (*s == ' ') s++;
 
1606
                      u = strpbrk (s," \n\r");
 
1607
                                /* got a keyword? */
 
1608
                      if ((j < NUSERFLAGS) && ((k = (u - s)) <= MAXUSERFLAG)) {
 
1609
                        if (stream->user_flags[j])
 
1610
                          fs_give ((void **) &stream->user_flags[j]);
 
1611
                        stream->user_flags[j] = (char *) fs_get (k + 1);
 
1612
                        strncpy (stream->user_flags[j],s,k);
 
1613
                        stream->user_flags[j][k] = '\0';
 
1614
                      }
 
1615
                    }
 
1616
                  }
 
1617
                }
 
1618
                break;
 
1619
              }
 
1620
 
 
1621
                                /* possible X-UID */
 
1622
              else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
 
1623
                       s[5] == ':') {
 
1624
                retain = NIL;   /* don't retain continuation */
 
1625
                                /* only believe if have a UID validity */
 
1626
                if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
 
1627
                  s += 6;       /* advance to UID value */
 
1628
                                /* flush whitespace */
 
1629
                  while (*s == ' ') s++;
 
1630
                  j = 0;
 
1631
                                /* found a digit? */
 
1632
                  while (isdigit (*s)) {
 
1633
                    j *= 10;    /* yes, add it in */
 
1634
                    j += *s++ - '0';
 
1635
                  }
 
1636
                                /* flush remainder of line */
 
1637
                  while (*s != '\n') s++;
 
1638
                                /* make sure not duplicated */
 
1639
                  if (elt->private.uid)
 
1640
                    sprintf (tmp,"Message %lu UID %lu already has UID %lu",
 
1641
                             pseudoseen ? elt->msgno - 1 : elt->msgno,
 
1642
                             j,elt->private.uid);
 
1643
                                /* make sure UID doesn't go backwards */
 
1644
                  else if (j <= prevuid)
 
1645
                    sprintf (tmp,"Message %lu UID %lu less than %lu",
 
1646
                             pseudoseen ? elt->msgno - 1 : elt->msgno,
 
1647
                             j,prevuid + 1);
 
1648
#if 0   /* this is currently broken by UIDPLUS */
 
1649
                                /* or skip by mailbox's recorded last */
 
1650
                  else if (j > stream->uid_last)
 
1651
                    sprintf (tmp,"Message %lu UID %lu greater than last %lu",
 
1652
                             pseudoseen ? elt->msgno - 1 : elt->msgno,
 
1653
                             j,stream->uid_last);
 
1654
#endif
 
1655
                  else {        /* normal UID case */
 
1656
                    prevuid = elt->private.uid = j;
 
1657
#if 1   /* temporary kludge for UIDPLUS */
 
1658
                    if (prevuid > stream->uid_last) {
 
1659
                      stream->uid_last = prevuid;
 
1660
                      LOCAL->ddirty = LOCAL->dirty = T;
 
1661
                    }               
 
1662
#endif
 
1663
                    break;      /* exit this cruft */
 
1664
                  }
 
1665
                  MM_LOG (tmp,WARN);
 
1666
                                /* invalidate UID validity */
 
1667
                  stream->uid_validity = 0;
 
1668
                  elt->private.uid = 0;
 
1669
                }
 
1670
                break;
 
1671
              }
 
1672
            }
 
1673
                                /* otherwise fall into S case */
 
1674
 
 
1675
          case 'S':             /* possible Status: line */
 
1676
            if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
 
1677
                s[4] == 'u' && s[5] == 's' && s[6] == ':') {
 
1678
              retain = NIL;     /* don't retain continuation */
 
1679
              s += 6;           /* advance to status flags */
 
1680
              do switch (*s++) {/* parse flags */
 
1681
              case 'R':         /* message read */
 
1682
                elt->seen = T;
 
1683
                break;
 
1684
              case 'O':         /* message old */
 
1685
                if (elt->recent) {
 
1686
                  elt->recent = NIL;
 
1687
                  recent--;     /* it really wasn't recent */
 
1688
                }
 
1689
                break;
 
1690
              case 'D':         /* message deleted */
 
1691
                elt->deleted = T;
 
1692
                break;
 
1693
              case 'F':         /* message flagged */
 
1694
                elt->flagged = T;
 
1695
                break;
 
1696
              case 'A':         /* message answered */
 
1697
                elt->answered = T;
 
1698
                break;
 
1699
              case 'T':         /* message is a draft */
 
1700
                elt->draft = T;
 
1701
                break;
 
1702
              default:          /* some other crap */
 
1703
                break;
 
1704
              } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
 
1705
              break;            /* all done */
 
1706
            }
 
1707
                                /* otherwise fall into default case */
 
1708
 
 
1709
          default:              /* ordinary header line */
 
1710
            if ((*s == 'S') || (*s == 's') ||
 
1711
                (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
 
1712
              unsigned char *e,*v;
 
1713
                                /* must match what mail_filter() does */
 
1714
              for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
 
1715
                   (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
 
1716
                   ((c > ' ') || ((c != ' ') && (c != '\t') &&
 
1717
                                  (c != '\015') && (c != '\012')));
 
1718
                   *v++ = *u++);
 
1719
              *v = '\0';        /* tie off */
 
1720
                                /* matches internal header? */
 
1721
              if (!compare_cstring (tmp,"STATUS") ||
 
1722
                  !compare_cstring (tmp,"X-STATUS") ||
 
1723
                  !compare_cstring (tmp,"X-KEYWORDS") ||
 
1724
                  !compare_cstring (tmp,"X-UID") ||
 
1725
                  !compare_cstring (tmp,"X-IMAP") ||
 
1726
                  !compare_cstring (tmp,"X-IMAPBASE")) {
 
1727
                char err[MAILTMPLEN];
 
1728
                sprintf (err,"Discarding bogus %s header in message %lu",
 
1729
                         (char *) tmp,elt->msgno);
 
1730
                MM_LOG (err,WARN);
 
1731
                retain = NIL;   /* don't retain continuation */
 
1732
                break;          /* different case or something */
 
1733
              }
 
1734
            }
 
1735
                                /* retain or non-continuation? */
 
1736
            if (retain || ((*s != ' ') && (*s != '\t'))) {
 
1737
              retain = T;       /* retaining continuation now */
 
1738
                                /* line length in LF format newline */
 
1739
              k = i - (((i >= 2) && (s[i - 2] == '\r')) ? 1 : 0);
 
1740
                                /* "internal" header size */
 
1741
              elt->private.spare.data += k;
 
1742
                                /* message size */
 
1743
              elt->rfc822_size += k + 1;
 
1744
            }
 
1745
            else {
 
1746
              char err[MAILTMPLEN];
 
1747
              sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
 
1748
                      elt->msgno,(char *) s);
 
1749
              if (u = strpbrk (err,"\r\n")) *u = '\0';
 
1750
              MM_LOG (err,WARN);
 
1751
              break;            /* different case or something */
 
1752
            }
 
1753
            break;
 
1754
          }
 
1755
        } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
 
1756
                                /* "internal" header sans trailing newline */
 
1757
        if (i) elt->private.spare.data--;
 
1758
                                /* assign a UID if none found */
 
1759
        if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
 
1760
          prevuid = elt->private.uid = ++stream->uid_last;
 
1761
          elt->private.dirty = T;
 
1762
        }
 
1763
        else elt->private.dirty = elt->recent;
 
1764
 
 
1765
                                /* note size of header, location of text */
 
1766
        elt->private.msg.header.text.size = 
 
1767
          (elt->private.msg.text.offset =
 
1768
           (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
 
1769
             elt->private.special.text.size;
 
1770
        k = m = 0;              /* no previous line size yet */
 
1771
                                /* note current position */
 
1772
        j = LOCAL->filesize + GETPOS (&bs);
 
1773
        if (i) do {             /* look for next message */
 
1774
          s = unix_mbxline (stream,&bs,&i);
 
1775
          if (i) {              /* got new data? */
 
1776
            VALID (s,t,ti,zn);  /* yes, parse line */
 
1777
            if (!ti) {          /* not a header line, add it to message */
 
1778
              elt->rfc822_size += 
 
1779
                k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
 
1780
                                /* update current position */
 
1781
              j = LOCAL->filesize + GETPOS (&bs);
 
1782
            }
 
1783
          }
 
1784
        } while (i && !ti);     /* until found a header */
 
1785
        elt->private.msg.text.text.size = j -
 
1786
          (elt->private.special.offset + elt->private.msg.text.offset);
 
1787
        if (k == 2) {           /* last line was blank? */
 
1788
          elt->private.msg.text.text.size -= (m ? 1 : 2);
 
1789
          elt->rfc822_size -= 2;
 
1790
        }
 
1791
                                /* until end of buffer */
 
1792
      } while (!stream->sniff && i);
 
1793
      if (pseudoseen) {         /* flush pseudo-message if present */
 
1794
                                /* decrement recent count */
 
1795
        if (mail_elt (stream,1)->recent) recent--;
 
1796
                                /* and the exists count */
 
1797
        mail_exists (stream,nmsgs--);
 
1798
        mail_expunged(stream,1);/* fake an expunge of that message */
 
1799
      }
 
1800
                                /* need to start a new UID validity? */
 
1801
      if (!stream->uid_validity) {
 
1802
        stream->uid_validity = time (0);
 
1803
                                /* in case a whiner with no life */
 
1804
        if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
 
1805
          stream->uid_nosticky = T;
 
1806
        else if (nmsgs) {       /* don't bother if empty file */
 
1807
                                /* make dirty to restart UID epoch */
 
1808
          LOCAL->ddirty = LOCAL->dirty = T;
 
1809
                                /* need to rewrite msg 1 if not pseudo */
 
1810
          if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
 
1811
          MM_LOG ("Assigning new unique identifiers to all messages",NIL);
 
1812
        }
 
1813
      }
 
1814
      stream->nmsgs = oldnmsgs; /* whack it back down */
 
1815
      stream->silent = silent;  /* restore old silent setting */
 
1816
                                /* notify upper level of new mailbox sizes */
 
1817
      mail_exists (stream,nmsgs);
 
1818
      mail_recent (stream,recent);
 
1819
                                /* mark dirty so O flags are set */
 
1820
      if (recent) LOCAL->dirty = T;
 
1821
    }
 
1822
  }
 
1823
                                /* no change, don't babble if never got time */
 
1824
  else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
 
1825
    MM_LOG ("New mailbox modification time but apparently no changes",WARN);
 
1826
                                /* update parsed file size and time */
 
1827
  LOCAL->filesize = sbuf.st_size;
 
1828
  LOCAL->filetime = sbuf.st_mtime;
 
1829
  return T;                     /* return the winnage */
 
1830
}
 
1831
 
 
1832
/* UNIX read line from mailbox
 
1833
 * Accepts: mail stream
 
1834
 *          stringstruct
 
1835
 *          pointer to line size
 
1836
 * Returns: pointer to input line
 
1837
 */
 
1838
 
 
1839
char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
 
1840
{
 
1841
  unsigned long i,j,k,m;
 
1842
  char *s,*t,*te;
 
1843
  char *ret = "";
 
1844
                                /* flush old buffer */
 
1845
  if (LOCAL->line) fs_give ((void **) &LOCAL->line);
 
1846
                                /* if buffer needs refreshing */
 
1847
  if (!bs->cursize) SETPOS (bs,GETPOS (bs));
 
1848
  if (SIZE (bs)) {              /* find newline */
 
1849
                                /* end of fast scan */
 
1850
    te = (t = (s = bs->curpos) + bs->cursize) - 12;
 
1851
    while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
 
1852
                       (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
 
1853
                       (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
 
1854
                       (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
 
1855
      --s;                      /* back up */
 
1856
      break;                    /* exit loop */
 
1857
    }
 
1858
                                /* final character-at-a-time scan */
 
1859
    while ((s < t) && (*s != '\n')) ++s;
 
1860
                                /* difficult case if line spans buffer */
 
1861
    if ((i = s - bs->curpos) == bs->cursize) {
 
1862
                                /* have space in line buffer? */
 
1863
      if (i > LOCAL->linebuflen) {
 
1864
        fs_give ((void **) &LOCAL->linebuf);
 
1865
        LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
 
1866
      }
 
1867
                                /* remember what we have so far */
 
1868
      memcpy (LOCAL->linebuf,bs->curpos,i);
 
1869
                                /* load next buffer */
 
1870
      SETPOS (bs,k = GETPOS (bs) + i);
 
1871
                                /* end of fast scan */
 
1872
      te = (t = (s = bs->curpos) + bs->cursize) - 12;
 
1873
                                /* fast scan in overlap buffer */
 
1874
      while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
 
1875
                         (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
 
1876
                         (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
 
1877
                         (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
 
1878
        --s;                    /* back up */
 
1879
        break;                  /* exit loop */
 
1880
      }
 
1881
 
 
1882
                                /* final character-at-a-time scan */
 
1883
      while ((s < t) && (*s != '\n')) ++s;
 
1884
                                /* huge line? */
 
1885
      if ((j = s - bs->curpos) == bs->cursize) {
 
1886
        SETPOS (bs,GETPOS (bs) + j);
 
1887
                                /* look for end of line (s-l-o-w!!) */
 
1888
        for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
 
1889
        SETPOS (bs,k);          /* go back to where it started */
 
1890
      }
 
1891
                                /* got size of data, make buffer for return */
 
1892
      ret = LOCAL->line = (char *) fs_get (i + j + 2);
 
1893
                                /* copy first chunk */
 
1894
      memcpy (ret,LOCAL->linebuf,i);
 
1895
      while (j) {               /* copy remainder */
 
1896
        if (!bs->cursize) SETPOS (bs,GETPOS (bs));
 
1897
        memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
 
1898
        i += k;                 /* account for this much read in */
 
1899
        j -= k;
 
1900
        bs->curpos += k;        /* increment new position */
 
1901
        bs->cursize -= k;       /* eat that many bytes */
 
1902
      }
 
1903
      if (!bs->cursize) SETPOS (bs,GETPOS (bs));
 
1904
      if (SIZE (bs)) SNX (bs);  /* skip over newline if one seen */
 
1905
      ret[i++] = '\n';          /* make sure newline at end */
 
1906
      ret[i] = '\0';            /* makes debugging easier */
 
1907
    }
 
1908
    else {                      /* this is easy */
 
1909
      ret = bs->curpos;         /* string it at this position */
 
1910
      bs->curpos += ++i;        /* increment new position */
 
1911
      bs->cursize -= i;         /* eat that many bytes */
 
1912
    }
 
1913
    *size = i;                  /* return that to user */
 
1914
  }
 
1915
  else *size = 0;               /* end of data, return empty */
 
1916
  return ret;
 
1917
}
 
1918
 
 
1919
/* UNIX make pseudo-header
 
1920
 * Accepts: MAIL stream
 
1921
 *          buffer to write pseudo-header
 
1922
 * Returns: length of pseudo-header
 
1923
 */
 
1924
 
 
1925
unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
 
1926
{
 
1927
  int i;
 
1928
  char *s,tmp[MAILTMPLEN];
 
1929
  time_t now = time (0);
 
1930
  rfc822_fixed_date (tmp);
 
1931
  sprintf (hdr,"From %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
 
1932
           pseudo_from,ctime (&now),
 
1933
           tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
 
1934
           (unsigned long) now,mylocalhost (),stream->uid_validity,
 
1935
           stream->uid_last);
 
1936
  for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
 
1937
    if (stream->user_flags[i])
 
1938
      sprintf (s += strlen (s)," %s",stream->user_flags[i]);
 
1939
  sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
 
1940
  return strlen (hdr);          /* return header length */
 
1941
}
 
1942
 
 
1943
/* UNIX make status string
 
1944
 * Accepts: MAIL stream
 
1945
 *          destination string to write
 
1946
 *          message cache entry
 
1947
 *          UID to write if non-zero (else use elt->private.uid)
 
1948
 *          non-zero flag to write UID (.LT. 0 to write UID base info too)
 
1949
 * Returns: length of string
 
1950
 */
 
1951
 
 
1952
unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
 
1953
                            unsigned long uid,long flag)
 
1954
{
 
1955
  char *t,stack[64];
 
1956
  char *s = status;
 
1957
  unsigned long n;
 
1958
  int pad = 50;
 
1959
  int sticky = uid ? T : !stream->uid_nosticky;
 
1960
  /* This used to use sprintf(), but thanks to certain cretinous C libraries
 
1961
     with horribly slow implementations of sprintf() I had to change it to this
 
1962
     mess.  At least it should be fast. */
 
1963
  if ((flag < 0) && sticky) {   /* need to write X-IMAPbase: header? */
 
1964
    *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
 
1965
    *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
 
1966
    t = stack;
 
1967
    n = stream->uid_validity;   /* push UID validity digits on the stack */
 
1968
    do *t++ = (char) (n % 10) + '0';
 
1969
    while (n /= 10);
 
1970
                                /* pop UID validity digits from stack */
 
1971
    while (t > stack) *s++ = *--t;
 
1972
    *s++ = ' ';
 
1973
    n = stream->uid_last;       /* push UID last digits on the stack */
 
1974
    do *t++ = (char) (n % 10) + '0';
 
1975
    while (n /= 10);
 
1976
                                /* pop UID last digits from stack */
 
1977
    while (t > stack) *s++ = *--t;
 
1978
    for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
 
1979
      for (*s++ = ' '; *t; *s++ = *t++);
 
1980
    *s++ = '\n';
 
1981
    pad += 30;                  /* increased padding if have IMAPbase */
 
1982
  }
 
1983
  *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
 
1984
  *s++ = ':'; *s++ = ' ';
 
1985
  if (elt->seen) *s++ = 'R';
 
1986
  if (flag) *s++ = 'O';         /* only write O if have a UID */
 
1987
  *s++ = '\n';
 
1988
  *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
 
1989
  *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
 
1990
  if (elt->deleted) *s++ = 'D';
 
1991
  if (elt->flagged) *s++ = 'F';
 
1992
  if (elt->answered) *s++ = 'A';
 
1993
  if (elt->draft) *s++ = 'T';
 
1994
  *s++ = '\n';
 
1995
 
 
1996
  if (sticky) {                 /* only do this if UIDs sticky */
 
1997
    *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
 
1998
    *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
 
1999
    if (n = elt->user_flags) do {
 
2000
      *s++ = ' ';
 
2001
      for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
 
2002
    } while (n);
 
2003
    n = s - status;             /* get size of stuff so far */
 
2004
                                /* pad X-Keywords to make size constant */
 
2005
    if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
 
2006
    *s++ = '\n';
 
2007
    if (flag) {                 /* want to include UID? */
 
2008
      t = stack;
 
2009
                                /* push UID digits on the stack */
 
2010
      n = uid ? uid : elt->private.uid;
 
2011
      do *t++ = (char) (n % 10) + '0';
 
2012
      while (n /= 10);
 
2013
      *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
 
2014
      *s++ = ' ';
 
2015
                                /* pop UID from stack */
 
2016
      while (t > stack) *s++ = *--t;
 
2017
      *s++ = '\n';
 
2018
    }
 
2019
  }
 
2020
  *s++ = '\n'; *s = '\0';       /* end of extended message status */
 
2021
  return s - status;            /* return size of resulting string */
 
2022
}
 
2023
 
 
2024
/* Rewrite mailbox file
 
2025
 * Accepts: MAIL stream, must be critical and locked
 
2026
 *          return pointer to number of expunged messages if want expunge
 
2027
 *          lock file name
 
2028
 *          expunge sequence, not deleted flag
 
2029
 * Returns: T if success and mailbox unlocked, NIL if failure
 
2030
 */
 
2031
 
 
2032
#define OVERFLOWBUFLEN 8192     /* initial overflow buffer length */
 
2033
 
 
2034
long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
 
2035
                   long flags)
 
2036
{
 
2037
  MESSAGECACHE *elt;
 
2038
  UNIXFILE f;
 
2039
  char *s;
 
2040
  time_t tp[2];
 
2041
  long ret,flag;
 
2042
  unsigned long i,j;
 
2043
  unsigned long recent = stream->recent;
 
2044
  unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
 
2045
  if (nexp) *nexp = 0;          /* initially nothing expunged */
 
2046
                                /* calculate size of mailbox after rewrite */
 
2047
  for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
 
2048
    elt = mail_elt (stream,i);  /* get cache */
 
2049
    if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
 
2050
                                /* add RFC822 size of this message */
 
2051
      size += elt->private.special.text.size + elt->private.spare.data +
 
2052
        unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
 
2053
          elt->private.msg.text.text.size + 1;
 
2054
      flag = 1;                 /* only count X-IMAPbase once */
 
2055
    }
 
2056
  }
 
2057
                                /* no messages, has a life, and no pseudo */
 
2058
  if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
 
2059
    LOCAL->pseudo = T;          /* so make a pseudo-message now */
 
2060
    size = unix_pseudo (stream,LOCAL->buf);
 
2061
  }
 
2062
                                /* extend the file as necessary */
 
2063
  if (ret = unix_extend (stream,size)) {
 
2064
    /* Set up buffered I/O file structure
 
2065
     * curpos   current position being written through buffering
 
2066
     * filepos  current position being written physically to the disk
 
2067
     * bufpos   current position being written in the buffer
 
2068
     * protect  current maximum position that can be written to the disk
 
2069
     *          before buffering is forced
 
2070
     * The code tries to buffer so that that disk is written in multiples of
 
2071
     * OVERBLOWBUFLEN bytes.
 
2072
     */
 
2073
    f.stream = stream;          /* note mail stream */
 
2074
    f.curpos = f.filepos = 0;   /* start of file */
 
2075
    f.protect = stream->nmsgs ? /* initial protection pointer */
 
2076
    mail_elt (stream,1)->private.special.offset : 8192;
 
2077
    f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
 
2078
 
 
2079
    if (LOCAL->pseudo)          /* update pseudo-header */
 
2080
      unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
 
2081
                                /* loop through all messages */
 
2082
    for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
 
2083
      elt = mail_elt (stream,i);/* get cache */
 
2084
                                /* expunge this message? */
 
2085
      if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
 
2086
                                /* one less recent message */
 
2087
        if (elt->recent) --recent;
 
2088
        mail_expunged(stream,i);/* notify upper levels */
 
2089
        ++*nexp;                /* count up one more expunged message */
 
2090
      }
 
2091
      else {                    /* preserve this message */
 
2092
        i++;                    /* advance to next message */
 
2093
        if ((flag < 0) ||       /* need to rewrite message? */
 
2094
            elt->private.dirty || (f.curpos != elt->private.special.offset) ||
 
2095
            (elt->private.msg.header.text.size !=
 
2096
             (elt->private.spare.data +
 
2097
              unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
 
2098
          unsigned long newoffset = f.curpos;
 
2099
                                /* yes, seek to internal header */
 
2100
          lseek (LOCAL->fd,elt->private.special.offset,L_SET);
 
2101
          read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
 
2102
                                /* see if need to squeeze out a CR */
 
2103
          if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
 
2104
            LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
 
2105
            --size;             /* squeezed out a CR from PC */
 
2106
          }
 
2107
                                /* protection pointer moves to RFC822 header */
 
2108
          f.protect = elt->private.special.offset +
 
2109
            elt->private.msg.header.offset;
 
2110
                                /* write internal header */
 
2111
          unix_write (&f,LOCAL->buf,elt->private.special.text.size);
 
2112
                                /* get RFC822 header */
 
2113
          s = unix_header (stream,elt->msgno,&j,FT_INTERNAL);
 
2114
                                /* in case this got decremented */
 
2115
          elt->private.msg.header.offset = elt->private.special.text.size;
 
2116
                                /* header size, sans trailing newline */
 
2117
          if ((j < 2) || (s[j - 2] == '\n')) j--;
 
2118
          if (j != elt->private.spare.data) fatal ("header size inconsistent");
 
2119
                                /* protection pointer moves to RFC822 text */
 
2120
          f.protect = elt->private.special.offset +
 
2121
            elt->private.msg.text.offset;
 
2122
          unix_write (&f,s,j);  /* write RFC822 header */
 
2123
                                /* write status and UID */
 
2124
          unix_write (&f,LOCAL->buf,
 
2125
                      j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
 
2126
          flag = 1;             /* only write X-IMAPbase once */
 
2127
                                /* new file header size */
 
2128
          elt->private.msg.header.text.size = elt->private.spare.data + j;
 
2129
 
 
2130
                                /* did text move? */
 
2131
          if (f.curpos != f.protect) {
 
2132
                                /* get message text */
 
2133
            s = unix_text_work (stream,elt,&j,FT_INTERNAL);
 
2134
                                /* this can happen if CRs were squeezed */
 
2135
            if (j < elt->private.msg.text.text.size) {
 
2136
                                /* so fix up counts */
 
2137
              size -= elt->private.msg.text.text.size - j;
 
2138
              elt->private.msg.text.text.size = j;
 
2139
            }
 
2140
                                /* can't happen it says here */
 
2141
            else if (j > elt->private.msg.text.text.size)
 
2142
              fatal ("text size inconsistent");
 
2143
                                /* new text offset, status/UID may change it */
 
2144
            elt->private.msg.text.offset = f.curpos - newoffset;
 
2145
                                /* protection pointer moves to next message */
 
2146
            f.protect = (i <= stream->nmsgs) ?
 
2147
              mail_elt (stream,i)->private.special.offset : (f.curpos + j + 1);
 
2148
            unix_write (&f,s,j);/* write text */
 
2149
                                /* write trailing newline */
 
2150
            unix_write (&f,"\n",1);
 
2151
          }
 
2152
          else {                /* tie off header and status */
 
2153
            unix_write (&f,NIL,NIL);
 
2154
                                /* protection pointer moves to next message */
 
2155
            f.protect = (i <= stream->nmsgs) ?
 
2156
              mail_elt (stream,i)->private.special.offset : size;
 
2157
                                /* locate end of message text */
 
2158
            j = f.filepos + elt->private.msg.text.text.size;
 
2159
                                /* trailing newline already there? */
 
2160
            if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
 
2161
            else {              /* trailing newline missing, write it */
 
2162
              f.curpos = f.filepos = j;
 
2163
              unix_write (&f,"\n",1);
 
2164
            }
 
2165
          }
 
2166
                                /* new internal header offset */
 
2167
          elt->private.special.offset = newoffset;
 
2168
          elt->private.dirty =NIL;/* message is now clean */
 
2169
        }
 
2170
        else {                  /* no need to rewrite this message */
 
2171
                                /* tie off previous message if needed */
 
2172
          unix_write (&f,NIL,NIL);
 
2173
                                /* protection pointer moves to next message */
 
2174
          f.protect = (i <= stream->nmsgs) ?
 
2175
            mail_elt (stream,i)->private.special.offset : size;
 
2176
                                /* locate end of message text */
 
2177
          j = f.filepos + elt->private.special.text.size +
 
2178
            elt->private.msg.header.text.size +
 
2179
              elt->private.msg.text.text.size;
 
2180
                                /* trailing newline already there? */
 
2181
          if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
 
2182
          else {                /* trailing newline missing, write it */
 
2183
            f.curpos = f.filepos = j;
 
2184
            unix_write (&f,"\n",1);
 
2185
          }
 
2186
        }
 
2187
      }
 
2188
    }
 
2189
 
 
2190
    unix_write (&f,NIL,NIL);    /* tie off final message */
 
2191
    if (size != f.filepos) fatal ("file size inconsistent");
 
2192
    fs_give ((void **) &f.buf); /* free buffer */
 
2193
                                /* make sure tied off */
 
2194
    ftruncate (LOCAL->fd,LOCAL->filesize = size);
 
2195
    fsync (LOCAL->fd);          /* make sure the updates take */
 
2196
    if (size && (flag < 0)) fatal ("lost UID base information");
 
2197
                                /* no longer dirty */
 
2198
    LOCAL->ddirty = LOCAL->dirty = NIL;
 
2199
                                /* notify upper level of new mailbox sizes */
 
2200
    mail_exists (stream,stream->nmsgs);
 
2201
    mail_recent (stream,recent);
 
2202
                                /* set atime to now, mtime a second earlier */
 
2203
    tp[1] = (tp[0] = time (0)) - 1;
 
2204
                                /* set the times, note change */
 
2205
    if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
 
2206
    close (LOCAL->fd);          /* close and reopen file */
 
2207
    if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
 
2208
                           (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
 
2209
        < 0) {
 
2210
      sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
 
2211
      MM_LOG (LOCAL->buf,ERROR);
 
2212
      unix_abort (stream);
 
2213
    }
 
2214
    dotlock_unlock (lock);      /* flush the lock file */
 
2215
  }
 
2216
  return ret;                   /* return state from algorithm */
 
2217
}
 
2218
 
 
2219
/* Extend UNIX mailbox file
 
2220
 * Accepts: MAIL stream
 
2221
 *          new desired size
 
2222
 * Return: T if success, else NIL
 
2223
 */
 
2224
 
 
2225
long unix_extend (MAILSTREAM *stream,unsigned long size)
 
2226
{
 
2227
  unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
 
2228
  if (i) {                      /* does the mailbox need to grow? */
 
2229
    if (i > LOCAL->buflen) {    /* make sure have enough space */
 
2230
                                /* this user won the lottery all right */
 
2231
      fs_give ((void **) &LOCAL->buf);
 
2232
      LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
 
2233
    }
 
2234
    memset (LOCAL->buf,'\0',i); /* get a block of nulls */
 
2235
    while (T) {                 /* until write successful or punt */
 
2236
      lseek (LOCAL->fd,LOCAL->filesize,L_SET);
 
2237
      if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
 
2238
      else {
 
2239
        long e = errno;         /* note error before doing ftruncate */
 
2240
        ftruncate (LOCAL->fd,LOCAL->filesize);
 
2241
        if (MM_DISKERROR (stream,e,NIL)) {
 
2242
          fsync (LOCAL->fd);    /* user chose to punt */
 
2243
          sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
 
2244
          if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
 
2245
          return NIL;
 
2246
        }
 
2247
      }
 
2248
    }
 
2249
  }
 
2250
  return LONGT;
 
2251
}
 
2252
 
 
2253
/* Write data to buffered file
 
2254
 * Accepts: buffered file pointer
 
2255
 *          file data or NIL to indicate "flush buffer"
 
2256
 *          date size (ignored for "flush buffer")
 
2257
 * Does not return until success
 
2258
 */
 
2259
 
 
2260
void unix_write (UNIXFILE *f,char *buf,unsigned long size)
 
2261
{
 
2262
  unsigned long i,j,k;
 
2263
  if (buf) {                    /* doing buffered write? */
 
2264
    i = f->bufpos - f->buf;     /* yes, get size of current buffer data */
 
2265
                                /* yes, have space in current buffer chunk? */
 
2266
    if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
 
2267
                                /* yes, fill up buffer as much as we can */
 
2268
      memcpy (f->bufpos,buf,k = min (j,size));
 
2269
      f->bufpos += k;           /* new buffer position */
 
2270
      f->curpos += k;           /* new current position */
 
2271
      if (j -= k) return;       /* all done if still have buffer free space */
 
2272
      buf += k;                 /* full, get new unwritten data pointer */
 
2273
      size -= k;                /* new data size */
 
2274
      i += k;                   /* new buffer data size */
 
2275
    }
 
2276
    /* This chunk of the buffer is full.  See if can make some space by
 
2277
     * writing to the disk, if there's enough unprotected space to do so.
 
2278
     * Try to fill out any unaligned chunk, along with any subsequent full
 
2279
     * chunks that will fit in unprotected space.
 
2280
     */
 
2281
                                /* any unprotected space we can write to? */
 
2282
    if (j = min (i,f->protect - f->filepos)) {
 
2283
                                /* yes, filepos not at chunk boundary? */
 
2284
      if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
 
2285
        j -= k;                 /* yes, and can write out partial chunk */
 
2286
      else k = 0;               /* no partial chunk to write */
 
2287
                                /* if at least a chunk free, write that too */
 
2288
      if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
 
2289
      if (k) {                  /* write data if there is anything we can */
 
2290
        unix_phys_write (f,f->buf,k);
 
2291
                                /* slide buffer */
 
2292
        if (i -= k) memmove (f->buf,f->buf + k,i);
 
2293
        f->bufpos = f->buf + i; /* new end of buffer */
 
2294
      }
 
2295
    }
 
2296
 
 
2297
    /* Have flushed the buffer as best as possible.  All done if no more
 
2298
     * data to write.  Otherwise, if the buffer is empty AND if the unwritten
 
2299
     * data is larger than a chunk AND the unprotected space is also larger
 
2300
     * than a chunk, then write as many chunks as we can directly from the
 
2301
     * data.  Buffer the rest, expanding the buffer as needed.
 
2302
     */
 
2303
    if (size) {                 /* have more data that we need to buffer? */
 
2304
                                /* can write any of it to disk instead? */
 
2305
      if ((f->bufpos == f->buf) && 
 
2306
          ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
 
2307
                                /* write as much as we can right now */
 
2308
        unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
 
2309
        buf += j;               /* new data pointer */
 
2310
        size -= j;              /* new data size */
 
2311
        f->curpos += j;         /* advance current pointer */
 
2312
      }
 
2313
      if (size) {               /* still have data that we need to buffer? */
 
2314
                                /* yes, need to expand the buffer? */
 
2315
        if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
 
2316
                                /* note current position in buffer */
 
2317
          j = f->bufpos - f->buf;
 
2318
          i += OVERFLOWBUFLEN;  /* yes, grow another chunk */
 
2319
          fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
 
2320
                                /* in case buffer relocated */
 
2321
          f->bufpos = f->buf + j;
 
2322
        }
 
2323
                                /* buffer remaining data */
 
2324
        memcpy (f->bufpos,buf,size);
 
2325
        f->bufpos += size;      /* new end of buffer */
 
2326
        f->curpos += size;      /* advance current pointer */
 
2327
      }
 
2328
    }
 
2329
  }
 
2330
  else {                        /* flush buffer to disk */
 
2331
    unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
 
2332
    f->bufpos = f->buf;         /* reset buffer */
 
2333
                                /* update positions */
 
2334
    f->curpos = f->protect = f->filepos;
 
2335
  }
 
2336
}
 
2337
 
 
2338
/* Physical disk write
 
2339
 * Accepts: buffered file pointer
 
2340
 *          buffer address
 
2341
 *          buffer size
 
2342
 * Does not return until success
 
2343
 */
 
2344
 
 
2345
void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
 
2346
{
 
2347
  MAILSTREAM *stream = f->stream;
 
2348
                                /* write data at desired position */
 
2349
  while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
 
2350
                  (write (LOCAL->fd,buf,size) < 0))) {
 
2351
    int e;
 
2352
    char tmp[MAILTMPLEN];
 
2353
    sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
 
2354
    MM_LOG (tmp,ERROR);
 
2355
    MM_DISKERROR (NIL,e,T);     /* serious problem, must retry */
 
2356
  }
 
2357
  f->filepos += size;           /* update file position */
 
2358
}
 
2359
 
 
2360
/* MBOX mail routines */
 
2361
 
 
2362
 
 
2363
/* Driver dispatch used by MAIL */
 
2364
 
 
2365
DRIVER mboxdriver = {
 
2366
  "mbox",                       /* driver name */
 
2367
                                /* driver flags */
 
2368
  DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY,
 
2369
  (DRIVER *) NIL,               /* next driver */
 
2370
  mbox_valid,                   /* mailbox is valid for us */
 
2371
  unix_parameters,              /* manipulate parameters */
 
2372
  unix_scan,                    /* scan mailboxes */
 
2373
  unix_list,                    /* find mailboxes */
 
2374
  unix_lsub,                    /* find subscribed mailboxes */
 
2375
  NIL,                          /* subscribe to mailbox */
 
2376
  NIL,                          /* unsubscribe from mailbox */
 
2377
  mbox_create,                  /* create mailbox */
 
2378
  mbox_delete,                  /* delete mailbox */
 
2379
  mbox_rename,                  /* rename mailbox */
 
2380
  mbox_status,                  /* status of mailbox */
 
2381
  mbox_open,                    /* open mailbox */
 
2382
  unix_close,                   /* close mailbox */
 
2383
  NIL,                          /* fetch message "fast" attributes */
 
2384
  NIL,                          /* fetch message flags */
 
2385
  NIL,                          /* fetch overview */
 
2386
  NIL,                          /* fetch message structure */
 
2387
  unix_header,                  /* fetch message header */
 
2388
  unix_text,                    /* fetch message body */
 
2389
  NIL,                          /* fetch partial message text */
 
2390
  NIL,                          /* unique identifier */
 
2391
  NIL,                          /* message number */
 
2392
  NIL,                          /* modify flags */
 
2393
  unix_flagmsg,                 /* per-message modify flags */
 
2394
  NIL,                          /* search for message based on criteria */
 
2395
  NIL,                          /* sort messages */
 
2396
  NIL,                          /* thread messages */
 
2397
  mbox_ping,                    /* ping mailbox to see if still alive */
 
2398
  mbox_check,                   /* check for new messages */
 
2399
  mbox_expunge,                 /* expunge deleted messages */
 
2400
  unix_copy,                    /* copy messages to another mailbox */
 
2401
  mbox_append,                  /* append string message to mailbox */
 
2402
  NIL                           /* garbage collect stream */
 
2403
};
 
2404
 
 
2405
                                /* prototype stream */
 
2406
MAILSTREAM mboxproto = {&mboxdriver};
 
2407
 
 
2408
/* MBOX mail validate mailbox
 
2409
 * Accepts: mailbox name
 
2410
 * Returns: our driver if name is valid, NIL otherwise
 
2411
 */
 
2412
 
 
2413
DRIVER *mbox_valid (char *name)
 
2414
{
 
2415
                                /* only INBOX, mbox must exist */
 
2416
  if (!compare_cstring (name,"INBOX") && (unix_valid ("mbox") || !errno) &&
 
2417
      (unix_valid (sysinbox()) || !errno || (errno == ENOENT)))
 
2418
    return &mboxdriver;
 
2419
  return NIL;                   /* can't win (yet, anyway) */
 
2420
}
 
2421
 
 
2422
/* MBOX mail create mailbox
 
2423
 * Accepts: MAIL stream
 
2424
 *          mailbox name to create
 
2425
 * Returns: T on success, NIL on failure
 
2426
 */
 
2427
 
 
2428
long mbox_create (MAILSTREAM *stream,char *mailbox)
 
2429
{
 
2430
                                /* in case CREATEPROTO same-as-inbox */
 
2431
  return unix_create (NIL,compare_cstring (mailbox,"INBOX") ?
 
2432
                      mailbox : "mbox");
 
2433
}
 
2434
 
 
2435
 
 
2436
/* MBOX mail delete mailbox
 
2437
 * Accepts: MAIL stream
 
2438
 *          mailbox name to delete
 
2439
 * Returns: T on success, NIL on failure
 
2440
 */
 
2441
 
 
2442
long mbox_delete (MAILSTREAM *stream,char *mailbox)
 
2443
{
 
2444
  return mbox_rename (stream,mailbox,NIL);
 
2445
}
 
2446
 
 
2447
 
 
2448
/* MBOX mail rename mailbox
 
2449
 * Accepts: MAIL stream
 
2450
 *          old mailbox name
 
2451
 *          new mailbox name (or NIL for delete)
 
2452
 * Returns: T on success, NIL on failure
 
2453
 */
 
2454
 
 
2455
long mbox_rename (MAILSTREAM *stream,char *old,char *newname)
 
2456
{
 
2457
  char tmp[MAILTMPLEN];
 
2458
  long ret = unix_rename (stream,"~/mbox",newname);
 
2459
                                /* recreate file if renamed INBOX */
 
2460
  if (ret) unix_create (NIL,"mbox");
 
2461
  else MM_LOG (tmp,ERROR);      /* log error */
 
2462
  return ret;                   /* return success */
 
2463
}
 
2464
 
 
2465
/* MBOX Mail status
 
2466
 * Accepts: mail stream
 
2467
 *          mailbox name
 
2468
 *          status flags
 
2469
 * Returns: T on success, NIL on failure
 
2470
 */
 
2471
 
 
2472
long mbox_status (MAILSTREAM *stream,char *mbx,long flags)
 
2473
{
 
2474
  MAILSTATUS status;
 
2475
  unsigned long i;
 
2476
  MAILSTREAM *tstream = NIL;
 
2477
  MAILSTREAM *systream = NIL;
 
2478
                                /* make temporary stream (unless this mbx) */
 
2479
  if (!stream && !(stream = tstream =
 
2480
                   mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
 
2481
  status.flags = flags;         /* return status values */
 
2482
  status.messages = stream->nmsgs;
 
2483
  status.recent = stream->recent;
 
2484
  if (flags & SA_UNSEEN)        /* must search to get unseen messages */
 
2485
    for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
 
2486
      if (!mail_elt (stream,i)->seen) status.unseen++;
 
2487
  status.uidnext = stream->uid_last + 1;
 
2488
  status.uidvalidity = stream->uid_validity;
 
2489
  if (!status.recent &&         /* calculate post-snarf results */
 
2490
      (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
 
2491
    status.messages += systream->nmsgs;
 
2492
    status.recent += systream->recent;
 
2493
    if (flags & SA_UNSEEN)      /* must search to get unseen messages */
 
2494
      for (i = 1; i <= systream->nmsgs; i++)
 
2495
        if (!mail_elt (systream,i)->seen) status.unseen++;
 
2496
                                /* kludge but probably good enough */
 
2497
    status.uidnext += systream->nmsgs;
 
2498
  }
 
2499
  MM_STATUS(stream,mbx,&status);/* pass status to main program */
 
2500
  if (tstream) mail_close (tstream);
 
2501
  if (systream) mail_close (systream);
 
2502
  return T;                     /* success */
 
2503
}
 
2504
 
 
2505
/* MBOX mail open
 
2506
 * Accepts: stream to open
 
2507
 * Returns: stream on success, NIL on failure
 
2508
 */
 
2509
 
 
2510
MAILSTREAM *mbox_open (MAILSTREAM *stream)
 
2511
{
 
2512
  unsigned long i = 1;
 
2513
  unsigned long recent = 0;
 
2514
                                /* return prototype for OP_PROTOTYPE call */
 
2515
  if (!stream) return &mboxproto;
 
2516
                                /* change mailbox file name */
 
2517
  fs_give ((void **) &stream->mailbox);
 
2518
  stream->mailbox = cpystr ("mbox");
 
2519
                                /* open mailbox, snarf new mail */
 
2520
  if (!(unix_open (stream) && mbox_ping (stream))) return NIL;
 
2521
  stream->inbox = T;            /* mark that this is an INBOX */
 
2522
                                /* notify upper level of mailbox sizes */
 
2523
  mail_exists (stream,stream->nmsgs);
 
2524
  while (i <= stream->nmsgs) if (mail_elt (stream,i++)->recent) ++recent;
 
2525
  mail_recent (stream,recent);  /* including recent messages */
 
2526
  return stream;
 
2527
}
 
2528
 
 
2529
/* MBOX mail ping mailbox
 
2530
 * Accepts: MAIL stream
 
2531
 * Returns: T if stream alive, else NIL
 
2532
 * No-op for readonly files, since read/writer can expunge it from under us!
 
2533
 */
 
2534
 
 
2535
static int snarfed = 0;         /* number of snarfs */
 
2536
 
 
2537
long mbox_ping (MAILSTREAM *stream)
 
2538
{
 
2539
  int sfd;
 
2540
  unsigned long size;
 
2541
  struct stat sbuf;
 
2542
  char *s;
 
2543
  DOTLOCK lock,lockx;
 
2544
                                /* time to try snarf and sysinbox non-empty? */
 
2545
  if (LOCAL && !stream->rdonly && !stream->lock &&
 
2546
      (time (0) >= (LOCAL->lastsnarf +
 
2547
                    (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
 
2548
      !stat (sysinbox (),&sbuf) && sbuf.st_size) {
 
2549
                                /* yes, open and lock sysinbox */
 
2550
    if ((sfd = unix_lock (sysinbox (),O_RDWR,
 
2551
                          (int) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
 
2552
                          &lockx,LOCK_EX)) >= 0) {
 
2553
                                /* locked sysinbox in good format? */
 
2554
      if (fstat (sfd,&sbuf) || !(size = sbuf.st_size) ||
 
2555
          !unix_isvalid_fd (sfd)) {
 
2556
        sprintf (LOCAL->buf,"Mail drop %s is not in standard Unix format",
 
2557
                 sysinbox ());
 
2558
        MM_LOG (LOCAL->buf,ERROR);
 
2559
      }
 
2560
                                /* sysinbox good, parse and excl-lock mbox */
 
2561
      else if (unix_parse (stream,&lock,LOCK_EX)) {
 
2562
        lseek (sfd,0,L_SET);    /* read entire sysinbox into memory */
 
2563
        read (sfd,s = (char *) fs_get (size + 1),size);
 
2564
        s[size] = '\0';         /* tie it off */
 
2565
                                /* append to end of mbox */
 
2566
        lseek (LOCAL->fd,LOCAL->filesize,L_SET);
 
2567
 
 
2568
                                /* copy to mbox */
 
2569
        if ((write (LOCAL->fd,s,size) < 0) || fsync (LOCAL->fd)) {
 
2570
          sprintf (LOCAL->buf,"New mail move failed: %s",strerror (errno));
 
2571
          MM_LOG (LOCAL->buf,WARN);
 
2572
                                /* revert mbox to previous size */
 
2573
          ftruncate (LOCAL->fd,LOCAL->filesize);
 
2574
        }
 
2575
                                /* sysinbox better not have changed */
 
2576
        else if (fstat (sfd,&sbuf) || (size != sbuf.st_size)) {
 
2577
          sprintf (LOCAL->buf,"Mail drop %s lock failure, old=%lu now=%lu",
 
2578
                   sysinbox (),size,(unsigned long) sbuf.st_size);
 
2579
          MM_LOG (LOCAL->buf,ERROR);
 
2580
                                /* revert mbox to previous size */
 
2581
          ftruncate (LOCAL->fd,LOCAL->filesize);
 
2582
          /* Believe it or not, a Singaporean government system actually had
 
2583
           * symlinks from /var/mail/user to ~user/mbox.  To compound this
 
2584
           * error, they used an SVR4 system; BSD and OSF locks would have
 
2585
           * prevented it but not SVR4 locks.
 
2586
           */
 
2587
          if (!fstat (sfd,&sbuf) && (size == sbuf.st_size))
 
2588
            syslog (LOG_ALERT,"File %s and %s are the same file!",
 
2589
                    sysinbox (),stream->mailbox);
 
2590
        }
 
2591
        else {                  /* data copied OK */
 
2592
          ftruncate (sfd,0);    /* truncate sysinbox to zero bytes */
 
2593
          if (!snarfed++) {     /* have we snarfed before? */
 
2594
                                /* syslog if server, else mm_log() */
 
2595
            sprintf (LOCAL->buf,"Moved %lu bytes of new mail to %s from %s",
 
2596
                     size,stream->mailbox,sysinbox ());
 
2597
            if (strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
 
2598
                        "unknown"))
 
2599
              syslog (LOG_INFO,"%s host= %s",LOCAL->buf,tcp_clienthost ());
 
2600
            else MM_LOG (LOCAL->buf,WARN);
 
2601
          }
 
2602
        }
 
2603
                                /* done with sysinbox text */
 
2604
        fs_give ((void **) &s);
 
2605
                                /* all done with mbox */
 
2606
        unix_unlock (LOCAL->fd,stream,&lock);
 
2607
        mail_unlock (stream);   /* unlock the stream */
 
2608
                                /* done with critical */
 
2609
        MM_NOCRITICAL (stream);
 
2610
      }
 
2611
                                /* all done with sysinbox */
 
2612
      unix_unlock (sfd,NIL,&lockx);
 
2613
    }
 
2614
    LOCAL->lastsnarf = time (0);/* note time of last snarf */
 
2615
  }
 
2616
  return unix_ping (stream);    /* do the unix routine now */
 
2617
}
 
2618
 
 
2619
/* MBOX mail check mailbox
 
2620
 * Accepts: MAIL stream
 
2621
 */
 
2622
 
 
2623
void mbox_check (MAILSTREAM *stream)
 
2624
{
 
2625
                                /* do local ping, then do unix routine */
 
2626
  if (mbox_ping (stream)) unix_check (stream);
 
2627
}
 
2628
 
 
2629
 
 
2630
/* MBOX mail expunge mailbox
 
2631
 * Accepts: MAIL stream
 
2632
 *          sequence to expunge if non-NIL
 
2633
 *          expunge options
 
2634
 * Returns: T, always
 
2635
 */
 
2636
 
 
2637
long mbox_expunge (MAILSTREAM *stream,char *sequence,long options)
 
2638
{
 
2639
  long ret = unix_expunge (stream,sequence,options);
 
2640
  mbox_ping (stream);           /* do local ping */
 
2641
  return ret;
 
2642
}
 
2643
 
 
2644
 
 
2645
/* MBOX mail append message from stringstruct
 
2646
 * Accepts: MAIL stream
 
2647
 *          destination mailbox
 
2648
 *          append callback
 
2649
 *          data for callback
 
2650
 * Returns: T if append successful, else NIL
 
2651
 */
 
2652
 
 
2653
long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
 
2654
{
 
2655
  return unix_append (stream,"mbox",af,data);
 
2656
}