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

« back to all changes in this revision

Viewing changes to imap/src/osdep/os2/mtxnt.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:     MTX 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:        22 May 1990
 
26
 * Last Edited: 30 August 2006
 
27
 */
 
28
 
 
29
 
 
30
/*                              FILE TIME SEMANTICS
 
31
 *
 
32
 * The atime is the last read time of the file.
 
33
 * The mtime is the last flags update time of the file.
 
34
 * The ctime is the last write time of the file.
 
35
 */
 
36
 
 
37
#include <stdio.h>
 
38
#include <ctype.h>
 
39
#include <errno.h>
 
40
extern int errno;               /* just in case */
 
41
#include "mail.h"
 
42
#include "osdep.h"
 
43
#include <fcntl.h>
 
44
#include <time.h>
 
45
#include <sys/stat.h>
 
46
#include <sys/utime.h>
 
47
#include "misc.h"
 
48
#include "dummy.h"
 
49
#include "fdstring.h"
 
50
 
 
51
/* MTX I/O stream local data */
 
52
        
 
53
typedef struct mtx_local {
 
54
  unsigned int shouldcheck: 1;  /* if ping should do a check instead */
 
55
  unsigned int mustcheck: 1;    /* if ping must do a check instead */
 
56
  int fd;                       /* file descriptor for I/O */
 
57
  off_t filesize;               /* file size parsed */
 
58
  time_t filetime;              /* last file time */
 
59
  time_t lastsnarf;             /* last snarf time */
 
60
  unsigned char *buf;           /* temporary buffer */
 
61
  unsigned long buflen;         /* current size of temporary buffer */
 
62
} MTXLOCAL;
 
63
 
 
64
 
 
65
/* Convenient access to local data */
 
66
 
 
67
#define LOCAL ((MTXLOCAL *) stream->local)
 
68
 
 
69
 
 
70
/* Function prototypes */
 
71
 
 
72
DRIVER *mtx_valid (char *name);
 
73
int mtx_isvalid (char *name,char *file);
 
74
void *mtx_parameters (long function,void *value);
 
75
void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
 
76
void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
 
77
void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
 
78
long mtx_create (MAILSTREAM *stream,char *mailbox);
 
79
long mtx_delete (MAILSTREAM *stream,char *mailbox);
 
80
long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
 
81
long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
 
82
MAILSTREAM *mtx_open (MAILSTREAM *stream);
 
83
void mtx_close (MAILSTREAM *stream,long options);
 
84
void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
 
85
char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
 
86
                  unsigned long *length,long flags);
 
87
long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
 
88
void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
 
89
void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
 
90
long mtx_ping (MAILSTREAM *stream);
 
91
void mtx_check (MAILSTREAM *stream);
 
92
void mtx_snarf (MAILSTREAM *stream);
 
93
long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
 
94
long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
 
95
long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
 
96
 
 
97
long mtx_parse (MAILSTREAM *stream);
 
98
MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
 
99
void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
 
100
void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
 
101
unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
 
102
                          unsigned long *size);
 
103
 
 
104
 
 
105
/* MTX mail routines */
 
106
 
 
107
 
 
108
/* Driver dispatch used by MAIL */
 
109
 
 
110
DRIVER mtxdriver = {
 
111
  "mtx",                        /* driver name */
 
112
                                /* driver flags */
 
113
  DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY,
 
114
  (DRIVER *) NIL,               /* next driver */
 
115
  mtx_valid,                    /* mailbox is valid for us */
 
116
  mtx_parameters,               /* manipulate parameters */
 
117
  mtx_scan,                     /* scan mailboxes */
 
118
  mtx_list,                     /* list mailboxes */
 
119
  mtx_lsub,                     /* list subscribed mailboxes */
 
120
  NIL,                          /* subscribe to mailbox */
 
121
  NIL,                          /* unsubscribe from mailbox */
 
122
  mtx_create,                   /* create mailbox */
 
123
  mtx_delete,                   /* delete mailbox */
 
124
  mtx_rename,                   /* rename mailbox */
 
125
  mail_status_default,          /* status of mailbox */
 
126
  mtx_open,                     /* open mailbox */
 
127
  mtx_close,                    /* close mailbox */
 
128
  mtx_flags,                    /* fetch message "fast" attributes */
 
129
  mtx_flags,                    /* fetch message flags */
 
130
  NIL,                          /* fetch overview */
 
131
  NIL,                          /* fetch message envelopes */
 
132
  mtx_header,                   /* fetch message header */
 
133
  mtx_text,                     /* fetch message body */
 
134
  NIL,                          /* fetch partial message text */
 
135
  NIL,                          /* unique identifier */
 
136
  NIL,                          /* message number */
 
137
  mtx_flag,                     /* modify flags */
 
138
  mtx_flagmsg,                  /* per-message modify flags */
 
139
  NIL,                          /* search for message based on criteria */
 
140
  NIL,                          /* sort messages */
 
141
  NIL,                          /* thread messages */
 
142
  mtx_ping,                     /* ping mailbox to see if still alive */
 
143
  mtx_check,                    /* check for new messages */
 
144
  mtx_expunge,                  /* expunge deleted messages */
 
145
  mtx_copy,                     /* copy messages to another mailbox */
 
146
  mtx_append,                   /* append string message to mailbox */
 
147
  NIL                           /* garbage collect stream */
 
148
};
 
149
 
 
150
                                /* prototype stream */
 
151
MAILSTREAM mtxproto = {&mtxdriver};
 
152
 
 
153
/* MTX mail validate mailbox
 
154
 * Accepts: mailbox name
 
155
 * Returns: our driver if name is valid, NIL otherwise
 
156
 */
 
157
 
 
158
DRIVER *mtx_valid (char *name)
 
159
{
 
160
  char tmp[MAILTMPLEN];
 
161
  return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
 
162
}
 
163
 
 
164
 
 
165
/* MTX mail test for valid mailbox
 
166
 * Accepts: mailbox name
 
167
 *          buffer to return file name
 
168
 * Returns: T if valid, NIL otherwise
 
169
 */
 
170
 
 
171
int mtx_isvalid (char *name,char *file)
 
172
{
 
173
  int fd;
 
174
  int ret = NIL;
 
175
  char *s,tmp[MAILTMPLEN];
 
176
  struct stat sbuf;
 
177
  struct utimbuf times;
 
178
  errno = EINVAL;               /* assume invalid argument */
 
179
                                /* if file, get its status */
 
180
  if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
 
181
      ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
 
182
    if (!sbuf.st_size)errno = 0;/* empty file */
 
183
    else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
 
184
      memset (tmp,'\0',MAILTMPLEN);
 
185
      if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
 
186
          (s[1] == '\012')) {   /* valid format? */
 
187
        *s = '\0';              /* tie off header */
 
188
                                /* must begin with dd-mmm-yy" */
 
189
        ret = (((tmp[2] == '-' && tmp[6] == '-') ||
 
190
                (tmp[1] == '-' && tmp[5] == '-')) &&
 
191
               (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
 
192
      }
 
193
      else errno = -1;          /* bogus format */
 
194
      close (fd);               /* close the file */
 
195
                                /* \Marked status? */
 
196
      if (sbuf.st_ctime > sbuf.st_atime) {
 
197
                                /* preserve atime and mtime */
 
198
        times.actime = sbuf.st_atime;
 
199
        times.modtime = sbuf.st_mtime;
 
200
        utime (file,&times);    /* set the times */
 
201
      }
 
202
    }
 
203
  }
 
204
                                /* in case INBOX but not mtx format */
 
205
  else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
 
206
  return ret;                   /* return what we should */
 
207
}
 
208
 
 
209
 
 
210
/* MTX manipulate driver parameters
 
211
 * Accepts: function code
 
212
 *          function-dependent value
 
213
 * Returns: function-dependent return value
 
214
 */
 
215
 
 
216
void *mtx_parameters (long function,void *value)
 
217
{
 
218
  return NIL;
 
219
}
 
220
 
 
221
/* MTX mail scan mailboxes
 
222
 * Accepts: mail stream
 
223
 *          reference
 
224
 *          pattern to search
 
225
 *          string to scan
 
226
 */
 
227
 
 
228
void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
 
229
{
 
230
  if (stream) dummy_scan (NIL,ref,pat,contents);
 
231
}
 
232
 
 
233
 
 
234
/* MTX mail list mailboxes
 
235
 * Accepts: mail stream
 
236
 *          reference
 
237
 *          pattern to search
 
238
 */
 
239
 
 
240
void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
 
241
{
 
242
  if (stream) dummy_list (NIL,ref,pat);
 
243
}
 
244
 
 
245
 
 
246
/* MTX mail list subscribed mailboxes
 
247
 * Accepts: mail stream
 
248
 *          reference
 
249
 *          pattern to search
 
250
 */
 
251
 
 
252
void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
 
253
{
 
254
  if (stream) dummy_lsub (NIL,ref,pat);
 
255
}
 
256
 
 
257
/* MTX mail create mailbox
 
258
 * Accepts: MAIL stream
 
259
 *          mailbox name to create
 
260
 * Returns: T on success, NIL on failure
 
261
 */
 
262
 
 
263
long mtx_create (MAILSTREAM *stream,char *mailbox)
 
264
{
 
265
  char *s,mbx[MAILTMPLEN];
 
266
  if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
 
267
  sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
 
268
  mm_log (mbx,ERROR);
 
269
  return NIL;
 
270
}
 
271
 
 
272
 
 
273
/* MTX mail delete mailbox
 
274
 * Accepts: MAIL stream
 
275
 *          mailbox name to delete
 
276
 * Returns: T on success, NIL on failure
 
277
 */
 
278
 
 
279
long mtx_delete (MAILSTREAM *stream,char *mailbox)
 
280
{
 
281
  return mtx_rename (stream,mailbox,NIL);
 
282
}
 
283
 
 
284
/* MTX mail rename mailbox
 
285
 * Accepts: MAIL stream
 
286
 *          old mailbox name
 
287
 *          new mailbox name (or NIL for delete)
 
288
 * Returns: T on success, NIL on failure
 
289
 */
 
290
 
 
291
long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
 
292
{
 
293
  long ret = LONGT;
 
294
  char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
 
295
  int fd,ld;
 
296
  struct stat sbuf;
 
297
  if (!dummy_file (file,old) ||
 
298
      (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
 
299
                   ((s = strrchr (tmp,'\\')) && !s[1])))) {
 
300
    sprintf (tmp,newname ?
 
301
             "Can't rename mailbox %.80s to %.80s: invalid name" :
 
302
             "Can't delete mailbox %.80s: invalid name",
 
303
             old,newname);
 
304
    mm_log (tmp,ERROR);
 
305
    return NIL;
 
306
  }
 
307
  if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
 
308
    sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
 
309
    mm_log (tmp,ERROR);
 
310
    return NIL;
 
311
  }
 
312
                                /* get exclusive parse/append permission */
 
313
  if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
 
314
    mm_log ("Unable to lock rename mailbox",ERROR);
 
315
    return NIL;
 
316
  }
 
317
                                /* lock out other users */
 
318
  if (flock (fd,LOCK_EX|LOCK_NB)) {
 
319
    close (fd);                 /* couldn't lock, give up on it then */
 
320
    sprintf (tmp,"Mailbox %.80s is in use by another process",old);
 
321
    mm_log (tmp,ERROR);
 
322
    unlockfd (ld,lock);         /* release exclusive parse/append permission */
 
323
    return NIL;
 
324
  }
 
325
 
 
326
  if (newname) {                /* want rename? */
 
327
                                /* found superior to destination name? */
 
328
    if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
 
329
        ((tmp[1] != ':') || (s != tmp + 2))) {
 
330
      c = s[1];                 /* remember character after delimiter */
 
331
      *s = s[1] = '\0';         /* tie off name at delimiter */
 
332
                                /* name doesn't exist, create it */
 
333
      if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
 
334
        *s = '\\';              /* restore delimiter */
 
335
        if (!dummy_create (stream,tmp)) ret = NIL;
 
336
      }
 
337
      else *s = '\\';           /* restore delimiter */
 
338
      s[1] = c;                 /* restore character after delimiter */
 
339
    }
 
340
    flock (fd,LOCK_UN);         /* release lock on the file */
 
341
    close (fd);                 /* pacify NTFS */
 
342
                                /* rename the file */
 
343
    if (ret && rename (file,tmp)) {
 
344
      sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
 
345
               strerror (errno));
 
346
      mm_log (tmp,ERROR);
 
347
      ret = NIL;                /* set failure */
 
348
    }
 
349
  }
 
350
  else {
 
351
    flock (fd,LOCK_UN);         /* release lock on the file */
 
352
    close (fd);                 /* pacify NTFS */
 
353
    if (unlink (file)) {
 
354
      sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
 
355
      mm_log (tmp,ERROR);
 
356
      ret = NIL;                /* set failure */
 
357
    }
 
358
  }
 
359
  unlockfd (ld,lock);           /* release exclusive parse/append permission */
 
360
  return ret;                   /* return success */
 
361
}
 
362
 
 
363
/* MTX mail open
 
364
 * Accepts: stream to open
 
365
 * Returns: stream on success, NIL on failure
 
366
 */
 
367
 
 
368
MAILSTREAM *mtx_open (MAILSTREAM *stream)
 
369
{
 
370
  int fd,ld;
 
371
  char tmp[MAILTMPLEN];
 
372
                                /* return prototype for OP_PROTOTYPE call */
 
373
  if (!stream) return &mtxproto;
 
374
  if (stream->local) fatal ("mtx recycle stream");
 
375
                                /* canonicalize the mailbox name */
 
376
  if (!dummy_file (tmp,stream->mailbox)) {
 
377
    sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
 
378
    mm_log (tmp,ERROR);
 
379
  }
 
380
  if (stream->rdonly ||
 
381
      (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
 
382
    if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
 
383
      sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
 
384
      mm_log (tmp,ERROR);
 
385
      return NIL;
 
386
    }
 
387
    else if (!stream->rdonly) { /* got it, but readonly */
 
388
      mm_log ("Can't get write access to mailbox, access is readonly",WARN);
 
389
      stream->rdonly = T;
 
390
    }
 
391
  }
 
392
  stream->local = fs_get (sizeof (MTXLOCAL));
 
393
  LOCAL->fd = fd;               /* bind the file */
 
394
  LOCAL->buf = (char *) fs_get (CHUNKSIZE);
 
395
  LOCAL->buflen = CHUNKSIZE - 1;
 
396
                                /* note if an INBOX or not */
 
397
  stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
 
398
  fs_give ((void **) &stream->mailbox);
 
399
  stream->mailbox = cpystr (tmp);
 
400
                                /* get shared parse permission */
 
401
  if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
 
402
    mm_log ("Unable to lock open mailbox",ERROR);
 
403
    return NIL;
 
404
  }
 
405
  flock (LOCAL->fd,LOCK_SH);    /* lock the file */
 
406
  unlockfd (ld,tmp);            /* release shared parse permission */
 
407
  LOCAL->filesize = 0;          /* initialize parsed file size */
 
408
  LOCAL->filetime = 0;          /* time not set up yet */
 
409
  LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
 
410
  stream->sequence++;           /* bump sequence number */
 
411
  stream->uid_validity = (unsigned long) time (0);
 
412
                                /* parse mailbox */
 
413
  stream->nmsgs = stream->recent = 0;
 
414
  if (mtx_ping (stream) && !stream->nmsgs)
 
415
    mm_log ("Mailbox is empty",(long) NIL);
 
416
  if (!LOCAL) return NIL;       /* failure if stream died */
 
417
  stream->perm_seen = stream->perm_deleted =
 
418
    stream->perm_flagged = stream->perm_answered = stream->perm_draft =
 
419
      stream->rdonly ? NIL : T;
 
420
  stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
 
421
  return stream;                /* return stream to caller */
 
422
}
 
423
 
 
424
/* MTX mail close
 
425
 * Accepts: MAIL stream
 
426
 *          close options
 
427
 */
 
428
 
 
429
void mtx_close (MAILSTREAM *stream,long options)
 
430
{
 
431
  if (stream && LOCAL) {        /* only if a file is open */
 
432
    int silent = stream->silent;
 
433
    stream->silent = T;         /* note this stream is dying */
 
434
    if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
 
435
    stream->silent = silent;    /* restore previous status */
 
436
    flock (LOCAL->fd,LOCK_UN);  /* unlock local file */
 
437
    close (LOCAL->fd);          /* close the local file */
 
438
                                /* free local text buffer */
 
439
    if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
 
440
                                /* nuke the local data */
 
441
    fs_give ((void **) &stream->local);
 
442
    stream->dtb = NIL;          /* log out the DTB */
 
443
  }
 
444
}
 
445
 
 
446
 
 
447
/* MTX mail fetch flags
 
448
 * Accepts: MAIL stream
 
449
 *          sequence
 
450
 *          option flags
 
451
 * Sniffs at file to see if some other process changed the flags
 
452
 */
 
453
 
 
454
void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
 
455
{
 
456
  unsigned long i;
 
457
  if (mtx_ping (stream) &&      /* ping mailbox, get new status for messages */
 
458
      ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
 
459
       mail_sequence (stream,sequence)))
 
460
    for (i = 1; i <= stream->nmsgs; i++) 
 
461
      if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
 
462
}
 
463
 
 
464
/* MTX mail fetch message header
 
465
 * Accepts: MAIL stream
 
466
 *          message # to fetch
 
467
 *          pointer to returned header text length
 
468
 *          option flags
 
469
 * Returns: message header in RFC822 format
 
470
 */
 
471
 
 
472
char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
 
473
                  long flags)
 
474
{
 
475
  *length = 0;                  /* default to empty */
 
476
  if (flags & FT_UID) return "";/* UID call "impossible" */
 
477
                                /* get to header position */
 
478
  lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
 
479
                                /* is buffer big enough? */
 
480
  if (*length > LOCAL->buflen) {
 
481
    fs_give ((void **) &LOCAL->buf);
 
482
    LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
 
483
  }
 
484
  LOCAL->buf[*length] = '\0';   /* tie off string */
 
485
                                /* slurp the data */
 
486
  read (LOCAL->fd,LOCAL->buf,*length);
 
487
  return LOCAL->buf;
 
488
}
 
489
 
 
490
/* MTX mail fetch message text (body only)
 
491
 * Accepts: MAIL stream
 
492
 *          message # to fetch
 
493
 *          pointer to returned header text length
 
494
 *          option flags
 
495
 * Returns: T, always
 
496
 */
 
497
 
 
498
long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
 
499
{
 
500
  FDDATA d;
 
501
  unsigned long i,j;
 
502
  MESSAGECACHE *elt;
 
503
                                /* UID call "impossible" */
 
504
  if (flags & FT_UID) return NIL;
 
505
  elt = mtx_elt (stream,msgno); /* get message status */
 
506
                                /* if message not seen */
 
507
  if (!(flags & FT_PEEK) && !elt->seen) {
 
508
    elt->seen = T;              /* mark message as seen */
 
509
                                /* recalculate status */
 
510
    mtx_update_status (stream,msgno,NIL);
 
511
    mm_flags (stream,msgno);
 
512
  }
 
513
                                /* find header position */
 
514
  i = mtx_hdrpos (stream,msgno,&j);
 
515
  d.fd = LOCAL->fd;             /* set up file descriptor */
 
516
  d.pos = i + j;
 
517
  d.chunk = LOCAL->buf; /* initial buffer chunk */
 
518
  d.chunksize = CHUNKSIZE;
 
519
  INIT (bs,fd_string,&d,elt->rfc822_size - j);
 
520
  return T;                     /* success */
 
521
}
 
522
 
 
523
/* MTX mail modify flags
 
524
 * Accepts: MAIL stream
 
525
 *          sequence
 
526
 *          flag(s)
 
527
 *          option flags
 
528
 */
 
529
 
 
530
void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
 
531
{
 
532
  struct utimbuf times;
 
533
  struct stat sbuf;
 
534
  if (!stream->rdonly) {        /* make sure the update takes */
 
535
    fsync (LOCAL->fd);
 
536
    fstat (LOCAL->fd,&sbuf);    /* get current write time */
 
537
    times.modtime = LOCAL->filetime = sbuf.st_mtime;
 
538
    times.actime = time (0);    /* make sure read comes after all that */
 
539
    utime (stream->mailbox,&times);
 
540
  }
 
541
}
 
542
 
 
543
 
 
544
/* MTX mail per-message modify flags
 
545
 * Accepts: MAIL stream
 
546
 *          message cache element
 
547
 */
 
548
 
 
549
void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
 
550
{
 
551
  struct stat sbuf;
 
552
                                /* maybe need to do a checkpoint? */
 
553
  if (LOCAL->filetime && !LOCAL->shouldcheck) {
 
554
    fstat (LOCAL->fd,&sbuf);    /* get current write time */
 
555
    if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
 
556
    LOCAL->filetime = 0;        /* don't do this test for any other messages */
 
557
  }
 
558
                                /* recalculate status */
 
559
  mtx_update_status (stream,elt->msgno,NIL);
 
560
}
 
561
 
 
562
/* MTX mail ping mailbox
 
563
 * Accepts: MAIL stream
 
564
 * Returns: T if stream still alive, NIL if not
 
565
 */
 
566
 
 
567
long mtx_ping (MAILSTREAM *stream)
 
568
{
 
569
  unsigned long i = 1;
 
570
  long r = T;
 
571
  int ld;
 
572
  char lock[MAILTMPLEN];
 
573
  struct stat sbuf;
 
574
  if (stream && LOCAL) {        /* only if stream already open */
 
575
    fstat (LOCAL->fd,&sbuf);    /* get current file poop */
 
576
    if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
 
577
        (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
 
578
                                /* check for changed message status */
 
579
    if (LOCAL->mustcheck || LOCAL->shouldcheck) {
 
580
      LOCAL->filetime = sbuf.st_mtime;
 
581
      if (LOCAL->shouldcheck)   /* babble when we do this unilaterally */
 
582
        mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
 
583
      while (i <= stream->nmsgs) mtx_elt (stream,i++);
 
584
      LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
 
585
    }
 
586
                                /* get shared parse/append permission */
 
587
    if ((sbuf.st_size != LOCAL->filesize) &&
 
588
        ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
 
589
                                /* parse resulting mailbox */
 
590
      r = (mtx_parse (stream)) ? T : NIL;
 
591
      unlockfd (ld,lock);       /* release shared parse/append permission */
 
592
    }
 
593
  }
 
594
  return r;                     /* return result of the parse */
 
595
}
 
596
 
 
597
 
 
598
/* MTX mail check mailbox (reparses status too)
 
599
 * Accepts: MAIL stream
 
600
 */
 
601
 
 
602
void mtx_check (MAILSTREAM *stream)
 
603
{
 
604
                                /* mark that a check is desired */
 
605
  if (LOCAL) LOCAL->mustcheck = T;
 
606
  if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
 
607
}
 
608
 
 
609
/* MTX mail expunge mailbox
 
610
 *          sequence to expunge if non-NIL
 
611
 *          expunge options
 
612
 * Returns: T, always
 
613
 */
 
614
 
 
615
long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
 
616
{
 
617
  long ret;
 
618
  struct utimbuf times;
 
619
  struct stat sbuf;
 
620
  off_t pos = 0;
 
621
  int ld;
 
622
  unsigned long i = 1;
 
623
  unsigned long j,k,m,recent;
 
624
  unsigned long n = 0;
 
625
  unsigned long delta = 0;
 
626
  char lock[MAILTMPLEN];
 
627
  MESSAGECACHE *elt;
 
628
  if (!(ret = (sequence ? ((options & EX_UID) ?
 
629
                           mail_uid_sequence (stream,sequence) :
 
630
                           mail_sequence (stream,sequence)) : LONGT) &&
 
631
        mtx_ping (stream)));    /* parse sequence if given, ping stream */
 
632
  else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
 
633
  else {
 
634
    if (LOCAL->filetime && !LOCAL->shouldcheck) {
 
635
      fstat (LOCAL->fd,&sbuf);  /* get current write time */
 
636
      if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
 
637
    }
 
638
                                /* get exclusive parse/append permission */
 
639
    if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
 
640
      mm_log ("Unable to lock expunge mailbox",ERROR);
 
641
                                /* make sure see any newly-arrived messages */
 
642
    else if (!mtx_parse (stream));
 
643
                                /* get exclusive access */
 
644
    else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
 
645
      flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
 
646
      mm_log ("Can't expunge because mailbox is in use by another process",
 
647
              ERROR);
 
648
      unlockfd (ld,lock);       /* release exclusive parse/append permission */
 
649
    }
 
650
 
 
651
    else {
 
652
      mm_critical (stream);     /* go critical */
 
653
      recent = stream->recent;  /* get recent now that pinged and locked */
 
654
                                /* for each message */
 
655
      while (i <= stream->nmsgs) {
 
656
                                /* get cache element */
 
657
        elt = mtx_elt (stream,i);
 
658
                                /* number of bytes to smash or preserve */
 
659
        k = elt->private.special.text.size + elt->rfc822_size;
 
660
                                /* if need to expunge this message */
 
661
        if (elt->deleted && (sequence ? elt->sequence : T)) {
 
662
                                /* if recent, note one less recent message */
 
663
          if (elt->recent) --recent;
 
664
          delta += k;           /* number of bytes to delete */
 
665
                                /* notify upper levels */
 
666
          mail_expunged (stream,i);
 
667
          n++;                  /* count up one more expunged message */
 
668
        }
 
669
        else if (i++ && delta) {/* preserved message */
 
670
                                /* first byte to preserve */
 
671
          j = elt->private.special.offset;
 
672
          do {                  /* read from source position */
 
673
            m = min (k,LOCAL->buflen);
 
674
            lseek (LOCAL->fd,j,L_SET);
 
675
            read (LOCAL->fd,LOCAL->buf,m);
 
676
            pos = j - delta;    /* write to destination position */
 
677
            while (T) {
 
678
              lseek (LOCAL->fd,pos,L_SET);
 
679
              if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
 
680
              mm_notify (stream,strerror (errno),WARN);
 
681
              mm_diskerror (stream,errno,T);
 
682
            }
 
683
            pos += m;           /* new position */
 
684
            j += m;             /* next chunk, perhaps */
 
685
          } while (k -= m);     /* until done */
 
686
                                /* note the new address of this text */
 
687
          elt->private.special.offset -= delta;
 
688
        }
 
689
                                /* preserved but no deleted messages */
 
690
        else pos = elt->private.special.offset + k;
 
691
      }
 
692
      if (n) {                  /* truncate file after last message */
 
693
        if (pos != (LOCAL->filesize -= delta)) {
 
694
          sprintf (LOCAL->buf,
 
695
                   "Calculated size mismatch %lu != %lu, delta = %lu",
 
696
                   (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
 
697
          mm_log (LOCAL->buf,WARN);
 
698
          LOCAL->filesize = pos;/* fix it then */
 
699
        }
 
700
        ftruncate (LOCAL->fd,LOCAL->filesize);
 
701
        sprintf (LOCAL->buf,"Expunged %lu messages",n);
 
702
                                /* output the news */
 
703
        mm_log (LOCAL->buf,(long) NIL);
 
704
      }
 
705
      else mm_log ("No messages deleted, so no update needed",(long) NIL);
 
706
      fsync (LOCAL->fd);        /* force disk update */
 
707
      fstat (LOCAL->fd,&sbuf);  /* get new write time */
 
708
      times.modtime = LOCAL->filetime = sbuf.st_mtime;
 
709
      times.actime = time (0);  /* reset atime to now */
 
710
      utime (stream->mailbox,&times);
 
711
      mm_nocritical (stream);   /* release critical */
 
712
                                /* notify upper level of new mailbox size */
 
713
      mail_exists (stream,stream->nmsgs);
 
714
      mail_recent (stream,recent);
 
715
      flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
 
716
      unlockfd (ld,lock);       /* release exclusive parse/append permission */
 
717
    }
 
718
  }
 
719
  return ret;
 
720
}
 
721
 
 
722
/* MTX mail copy message(s)
 
723
 * Accepts: MAIL stream
 
724
 *          sequence
 
725
 *          destination mailbox
 
726
 *          copy options
 
727
 * Returns: T if success, NIL if failed
 
728
 */
 
729
 
 
730
long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
 
731
{
 
732
  struct stat sbuf;
 
733
  struct utimbuf times;
 
734
  MESSAGECACHE *elt;
 
735
  unsigned long i,j,k;
 
736
  long ret = LONGT;
 
737
  int fd,ld;
 
738
  char file[MAILTMPLEN],lock[MAILTMPLEN];
 
739
  mailproxycopy_t pc =
 
740
    (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
 
741
                                /* make sure valid mailbox */
 
742
  if (!mtx_isvalid (mailbox,file)) switch (errno) {
 
743
  case ENOENT:                  /* no such file? */
 
744
    mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
 
745
    return NIL;
 
746
  case 0:                       /* merely empty file? */
 
747
    break;
 
748
  case EINVAL:
 
749
    if (pc) return (*pc) (stream,sequence,mailbox,options);
 
750
    sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
 
751
    mm_log (LOCAL->buf,ERROR);
 
752
    return NIL;
 
753
  default:
 
754
    if (pc) return (*pc) (stream,sequence,mailbox,options);
 
755
    sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
 
756
    mm_log (LOCAL->buf,ERROR);
 
757
    return NIL;
 
758
  }
 
759
  if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
 
760
        mail_sequence (stream,sequence))) return NIL;
 
761
                                /* got file? */
 
762
  if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
 
763
    sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
 
764
    mm_log (LOCAL->buf,ERROR);
 
765
    return NIL;
 
766
  }
 
767
  mm_critical (stream);         /* go critical */
 
768
                                /* get exclusive parse/append permission */
 
769
  if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
 
770
    mm_log ("Unable to lock copy mailbox",ERROR);
 
771
    mm_nocritical (stream);
 
772
    return NIL;
 
773
  }
 
774
  fstat (fd,&sbuf);             /* get current file size */
 
775
  lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
 
776
 
 
777
                                /* for each requested message */
 
778
  for (i = 1; ret && (i <= stream->nmsgs); i++) 
 
779
    if ((elt = mail_elt (stream,i))->sequence) {
 
780
      lseek (LOCAL->fd,elt->private.special.offset,L_SET);
 
781
                                /* number of bytes to copy */
 
782
      k = elt->private.special.text.size + elt->rfc822_size;
 
783
      do {                      /* read from source position */
 
784
        j = min (k,LOCAL->buflen);
 
785
        read (LOCAL->fd,LOCAL->buf,j);
 
786
        if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
 
787
      } while (ret && (k -= j));/* until done */
 
788
    }
 
789
                                /* make sure all the updates take */
 
790
  if (!(ret && (ret = !fsync (fd)))) {
 
791
    sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
 
792
    mm_log (LOCAL->buf,ERROR);
 
793
    ftruncate (fd,sbuf.st_size);
 
794
  }
 
795
                                /* set atime to now-1 if successful copy */
 
796
  if (ret) times.actime = time (0) - 1;
 
797
                                /* else preserved \Marked status */
 
798
  else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
 
799
         sbuf.st_atime : time (0);
 
800
  times.modtime = sbuf.st_mtime;/* preserve mtime */
 
801
  utime (file,&times);          /* set the times */
 
802
  unlockfd (ld,lock);           /* release exclusive parse/append permission */
 
803
  close (fd);                   /* close the file */
 
804
  mm_nocritical (stream);       /* release critical */
 
805
                                /* delete all requested messages */
 
806
  if (ret && (options & CP_MOVE)) {
 
807
    for (i = 1; i <= stream->nmsgs; i++)
 
808
      if ((elt = mtx_elt (stream,i))->sequence) {
 
809
        elt->deleted = T;       /* mark message deleted */
 
810
                                /* recalculate status */
 
811
        mtx_update_status (stream,i,NIL);
 
812
      }
 
813
    if (!stream->rdonly) {      /* make sure the update takes */
 
814
      fsync (LOCAL->fd);
 
815
      fstat (LOCAL->fd,&sbuf);  /* get current write time */
 
816
      times.modtime = LOCAL->filetime = sbuf.st_mtime;
 
817
      times.actime = time (0);  /* make sure atime remains greater */
 
818
      utime (stream->mailbox,&times);
 
819
    }
 
820
  }
 
821
  if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
 
822
    mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
 
823
  return ret;
 
824
}
 
825
 
 
826
/* MTX mail append message from stringstruct
 
827
 * Accepts: MAIL stream
 
828
 *          destination mailbox
 
829
 *          append callback
 
830
 *          data for callback
 
831
 * Returns: T if append successful, else NIL
 
832
 */
 
833
 
 
834
long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
 
835
{
 
836
  struct stat sbuf;
 
837
  int fd,ld,c;
 
838
  char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
 
839
  struct utimbuf times;
 
840
  FILE *df;
 
841
  MESSAGECACHE elt;
 
842
  long f;
 
843
  unsigned long i,uf;
 
844
  STRING *message;
 
845
  long ret = LONGT;
 
846
                                /* default stream to prototype */
 
847
  if (!stream) stream = &mtxproto;
 
848
                                /* make sure valid mailbox */
 
849
  if (!mtx_isvalid (mailbox,file)) switch (errno) {
 
850
  case ENOENT:                  /* no such file? */
 
851
    if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX");
 
852
    else {
 
853
      mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
 
854
      return NIL;
 
855
    }
 
856
                                /* falls through */
 
857
  case 0:                       /* merely empty file? */
 
858
    break;
 
859
  case EINVAL:
 
860
    sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
 
861
    mm_log (tmp,ERROR);
 
862
    return NIL;
 
863
  default:
 
864
    sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
 
865
    mm_log (tmp,ERROR);
 
866
    return NIL;
 
867
  }
 
868
                                /* get first message */
 
869
  if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
 
870
 
 
871
                                /* open destination mailbox */
 
872
  if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
 
873
       < 0) || !(df = fdopen (fd,"ab"))) {
 
874
    sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
 
875
    mm_log (tmp,ERROR);
 
876
    return NIL;
 
877
  }
 
878
                                /* get parse/append permission */
 
879
  if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
 
880
    mm_log ("Unable to lock append mailbox",ERROR);
 
881
    close (fd);
 
882
    return NIL;
 
883
  }
 
884
  mm_critical (stream);         /* go critical */
 
885
  fstat (fd,&sbuf);             /* get current file size */
 
886
  errno = 0;
 
887
  do {                          /* parse flags */
 
888
    if (!SIZE (message)) {      /* guard against zero-length */
 
889
      mm_log ("Append of zero-length message",ERROR);
 
890
      ret = NIL;
 
891
      break;
 
892
    }
 
893
    f = mail_parse_flags (stream,flags,&i);
 
894
                                /* reverse bits (dontcha wish we had CIRC?) */
 
895
    for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
 
896
    if (date) {                 /* parse date if given */
 
897
      if (!mail_parse_date (&elt,date)) {
 
898
        sprintf (tmp,"Bad date in append: %.80s",date);
 
899
        mm_log (tmp,ERROR);
 
900
        ret = NIL;              /* mark failure */
 
901
        break;
 
902
      }
 
903
      mail_date (tmp,&elt);     /* write preseved date */
 
904
    }
 
905
    else internal_date (tmp);   /* get current date in IMAP format */
 
906
                                /* write header */
 
907
    if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
 
908
                 (unsigned long) f) < 0) ret = NIL;
 
909
    else {                      /* write message */
 
910
      if (i) do c = 0xff & SNX (message);
 
911
      while ((putc (c,df) != EOF) && --i);
 
912
                                /* get next message */
 
913
      if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
 
914
    }
 
915
  } while (ret && message);
 
916
                                /* if error... */
 
917
  if (!ret || (fflush (df) == EOF)) {
 
918
    ftruncate (fd,sbuf.st_size);/* revert file */
 
919
    close (fd);                 /* make sure fclose() doesn't corrupt us */
 
920
    if (errno) {
 
921
      sprintf (tmp,"Message append failed: %s",strerror (errno));
 
922
      mm_log (tmp,ERROR);
 
923
    }
 
924
    ret = NIL;
 
925
  }
 
926
  if (ret) times.actime = time (0) - 1;
 
927
                                /* else preserved \Marked status */
 
928
  else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
 
929
         sbuf.st_atime : time (0);
 
930
  times.modtime = sbuf.st_mtime;/* preserve mtime */
 
931
  utime (file,&times);          /* set the times */
 
932
  fclose (df);                  /* close the file */
 
933
  unlockfd (ld,lock);           /* release exclusive parse/append permission */
 
934
  mm_nocritical (stream);       /* release critical */
 
935
  if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
 
936
    mm_log ("Can not return meaningful APPENDUID with this mailbox format",
 
937
            WARN);
 
938
  return ret;
 
939
}
 
940
 
 
941
/* Internal routines */
 
942
 
 
943
 
 
944
/* MTX mail parse mailbox
 
945
 * Accepts: MAIL stream
 
946
 * Returns: T if parse OK
 
947
 *          NIL if failure, stream aborted
 
948
 */
 
949
 
 
950
long mtx_parse (MAILSTREAM *stream)
 
951
{
 
952
  struct stat sbuf;
 
953
  MESSAGECACHE *elt = NIL;
 
954
  unsigned char c,*s,*t,*x;
 
955
  char tmp[MAILTMPLEN];
 
956
  unsigned long i,j;
 
957
  long curpos = LOCAL->filesize;
 
958
  long nmsgs = stream->nmsgs;
 
959
  long recent = stream->recent;
 
960
  short added = NIL;
 
961
  short silent = stream->silent;
 
962
  fstat (LOCAL->fd,&sbuf);      /* get status */
 
963
  if (sbuf.st_size < curpos) {  /* sanity check */
 
964
    sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
 
965
    mm_log (tmp,ERROR);
 
966
    mtx_close (stream,NIL);
 
967
    return NIL;
 
968
  }
 
969
  stream->silent = T;           /* don't pass up mm_exists() events yet */
 
970
  while (sbuf.st_size - curpos){/* while there is stuff to parse */
 
971
                                /* get to that position in the file */
 
972
    lseek (LOCAL->fd,curpos,L_SET);
 
973
    if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
 
974
      sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
 
975
               (unsigned long) curpos,(unsigned long) sbuf.st_size,
 
976
               i ? strerror (errno) : "no data read");
 
977
      mm_log (tmp,ERROR);
 
978
      mtx_close (stream,NIL);
 
979
      return NIL;
 
980
    }
 
981
    LOCAL->buf[i] = '\0';       /* tie off buffer just in case */
 
982
    if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
 
983
      sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
 
984
               (unsigned long) curpos,i,(char *) LOCAL->buf);
 
985
      mm_log (tmp,ERROR);
 
986
      mtx_close (stream,NIL);
 
987
      return NIL;
 
988
    }
 
989
    *s = '\0';                  /* tie off header line */
 
990
    i = (s + 2) - LOCAL->buf;   /* note start of text offset */
 
991
    if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
 
992
      sprintf (tmp,"Unable to parse internal header at %lu: %s",
 
993
               (unsigned long) curpos,(char *) LOCAL->buf);
 
994
      mm_log (tmp,ERROR);
 
995
      mtx_close (stream,NIL);
 
996
      return NIL;
 
997
    }
 
998
    *s++ = '\0'; *t++ = '\0';   /* tie off fields */
 
999
 
 
1000
    added = T;                  /* note that a new message was added */
 
1001
                                /* swell the cache */
 
1002
    mail_exists (stream,++nmsgs);
 
1003
                                /* instantiate an elt for this message */
 
1004
    (elt = mail_elt (stream,nmsgs))->valid = T;
 
1005
    elt->private.uid = ++stream->uid_last;
 
1006
                                /* note file offset of header */
 
1007
    elt->private.special.offset = curpos;
 
1008
                                /* in case error */
 
1009
    elt->private.special.text.size = 0;
 
1010
                                /* header size not known yet */
 
1011
    elt->private.msg.header.text.size = 0;
 
1012
    x = s;                      /* parse the header components */
 
1013
    if (mail_parse_date (elt,LOCAL->buf) &&
 
1014
        (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
 
1015
        isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
 
1016
        isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
 
1017
        isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
 
1018
        isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
 
1019
      elt->private.special.text.size = i;
 
1020
    else {                      /* oops */
 
1021
      sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
 
1022
               curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
 
1023
      mm_log (tmp,ERROR);
 
1024
      mtx_close (stream,NIL);
 
1025
      return NIL;
 
1026
    }
 
1027
                                /* make sure didn't run off end of file */
 
1028
    if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
 
1029
      sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
 
1030
               elt->private.special.offset,(unsigned long) curpos,
 
1031
               (unsigned long) sbuf.st_size);
 
1032
      mm_log (tmp,ERROR);
 
1033
      mtx_close (stream,NIL);
 
1034
      return NIL;
 
1035
    }
 
1036
    c = t[10];                  /* remember first system flags byte */
 
1037
    t[10] = '\0';               /* tie off flags */
 
1038
    j = strtoul (t,NIL,8);      /* get user flags value */
 
1039
    t[10] = c;                  /* restore first system flags byte */
 
1040
                                /* set up all valid user flags (reversed!) */
 
1041
    while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
 
1042
                  stream->user_flags[i]) elt->user_flags |= 1 << i;
 
1043
                                /* calculate system flags */
 
1044
    if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
 
1045
    if (j & fDELETED) elt->deleted = T;
 
1046
    if (j & fFLAGGED) elt->flagged = T;
 
1047
    if (j & fANSWERED) elt->answered = T;
 
1048
    if (j & fDRAFT) elt->draft = T;
 
1049
    if (!(j & fOLD)) {          /* newly arrived message? */
 
1050
      elt->recent = T;
 
1051
      recent++;                 /* count up a new recent message */
 
1052
                                /* mark it as old */
 
1053
      mtx_update_status (stream,nmsgs,NIL);
 
1054
    }
 
1055
  }
 
1056
  fsync (LOCAL->fd);            /* make sure all the fOLD flags take */
 
1057
                                /* update parsed file size and time */
 
1058
  LOCAL->filesize = sbuf.st_size;
 
1059
  fstat (LOCAL->fd,&sbuf);      /* get status again to ensure time is right */
 
1060
  LOCAL->filetime = sbuf.st_mtime;
 
1061
  if (added && !stream->rdonly){/* make sure atime updated */
 
1062
    struct utimbuf times;
 
1063
    times.actime = time (0);
 
1064
    times.modtime = LOCAL->filetime;
 
1065
    utime (stream->mailbox,&times);
 
1066
  }
 
1067
  stream->silent = silent;      /* can pass up events now */
 
1068
  mail_exists (stream,nmsgs);   /* notify upper level of new mailbox size */
 
1069
  mail_recent (stream,recent);  /* and of change in recent messages */
 
1070
  return LONGT;                 /* return the winnage */
 
1071
}
 
1072
 
 
1073
/* MTX get cache element with status updating from file
 
1074
 * Accepts: MAIL stream
 
1075
 *          message number
 
1076
 * Returns: cache element
 
1077
 */
 
1078
 
 
1079
MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
 
1080
{
 
1081
  MESSAGECACHE *elt = mail_elt (stream,msgno);
 
1082
  struct {                      /* old flags */
 
1083
    unsigned int seen : 1;
 
1084
    unsigned int deleted : 1;
 
1085
    unsigned int flagged : 1;
 
1086
    unsigned int answered : 1;
 
1087
    unsigned int draft : 1;
 
1088
    unsigned long user_flags;
 
1089
  } old;
 
1090
  old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
 
1091
  old.answered = elt->answered; old.draft = elt->draft;
 
1092
  old.user_flags = elt->user_flags;
 
1093
  mtx_read_flags (stream,elt);
 
1094
  if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
 
1095
      (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
 
1096
      (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
 
1097
    mm_flags (stream,msgno);    /* let top level know */
 
1098
  return elt;
 
1099
}
 
1100
 
 
1101
/* MTX read flags from file
 
1102
 * Accepts: MAIL stream
 
1103
 * Returns: cache element
 
1104
 */
 
1105
 
 
1106
void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
 
1107
{
 
1108
  unsigned long i,j;
 
1109
                                /* noop if readonly and have valid flags */
 
1110
  if (stream->rdonly && elt->valid) return;
 
1111
                                /* set the seek pointer */
 
1112
  lseek (LOCAL->fd,(off_t) elt->private.special.offset +
 
1113
         elt->private.special.text.size - 14,L_SET);
 
1114
                                /* read the new flags */
 
1115
  if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
 
1116
    sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
 
1117
    fatal (LOCAL->buf);
 
1118
  }
 
1119
                                /* calculate system flags */
 
1120
  i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
 
1121
  elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
 
1122
  elt->flagged = i & fFLAGGED ? T : NIL;
 
1123
  elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
 
1124
  LOCAL->buf[10] = '\0';        /* tie off flags */
 
1125
  j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
 
1126
                                /* set up all valid user flags (reversed!) */
 
1127
  while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
 
1128
                stream->user_flags[i]) elt->user_flags |= 1 << i;
 
1129
  elt->valid = T;               /* have valid flags now */
 
1130
}
 
1131
 
 
1132
/* MTX update status string
 
1133
 * Accepts: MAIL stream
 
1134
 *          message number
 
1135
 *          flag saying whether or not to sync
 
1136
 */
 
1137
 
 
1138
void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
 
1139
{
 
1140
  struct utimbuf times;
 
1141
  struct stat sbuf;
 
1142
  MESSAGECACHE *elt = mail_elt (stream,msgno);
 
1143
  unsigned long j,k = 0;
 
1144
                                /* readonly */
 
1145
  if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
 
1146
  else {                        /* readwrite */
 
1147
    j = elt->user_flags;        /* get user flags */
 
1148
                                /* reverse bits (dontcha wish we had CIRC?) */
 
1149
    while (j) k |= 1 << (29 - find_rightmost_bit (&j));
 
1150
                                /* print new flag string */
 
1151
    sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
 
1152
             (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
 
1153
              (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
 
1154
              (fDRAFT * elt->draft)));
 
1155
    while (T) {                 /* get to that place in the file */
 
1156
      lseek (LOCAL->fd,(off_t) elt->private.special.offset +
 
1157
             elt->private.special.text.size - 14,L_SET);
 
1158
                                /* write new flags */
 
1159
      if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
 
1160
      mm_notify (stream,strerror (errno),WARN);
 
1161
      mm_diskerror (stream,errno,T);
 
1162
    }
 
1163
    if (syncflag) {             /* sync if requested */
 
1164
      fsync (LOCAL->fd);
 
1165
      fstat (LOCAL->fd,&sbuf);  /* get new write time */
 
1166
      times.modtime = LOCAL->filetime = sbuf.st_mtime;
 
1167
      times.actime = time (0);  /* make sure read is later */
 
1168
      utime (stream->mailbox,&times);
 
1169
    }
 
1170
  }
 
1171
}
 
1172
 
 
1173
/* MTX locate header for a message
 
1174
 * Accepts: MAIL stream
 
1175
 *          message number
 
1176
 *          pointer to returned header size
 
1177
 * Returns: position of header in file
 
1178
 */
 
1179
 
 
1180
unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
 
1181
                          unsigned long *size)
 
1182
{
 
1183
  unsigned long siz;
 
1184
  long i = 0;
 
1185
  int q = 0;
 
1186
  char *s,tmp[MAILTMPLEN];
 
1187
  MESSAGECACHE *elt = mtx_elt (stream,msgno);
 
1188
  unsigned long ret = elt->private.special.offset +
 
1189
    elt->private.special.text.size;
 
1190
                                /* is header size known? */
 
1191
  if (!(*size = elt->private.msg.header.text.size)) {
 
1192
    lseek (LOCAL->fd,ret,L_SET);/* get to header position */
 
1193
                                /* search message for CRLF CRLF */
 
1194
    for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
 
1195
                                /* read another buffer as necessary */
 
1196
      if ((--i <= 0) &&         /* buffer empty? */
 
1197
          (read (LOCAL->fd,s = tmp,
 
1198
                 i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
 
1199
        return ret;             /* I/O error? */
 
1200
      switch (q) {              /* sniff at buffer */
 
1201
      case 0:                   /* first character */
 
1202
        q = (*s++ == '\015') ? 1 : 0;
 
1203
        break;
 
1204
      case 1:                   /* second character */
 
1205
        q = (*s++ == '\012') ? 2 : 0;
 
1206
        break;
 
1207
      case 2:                   /* third character */
 
1208
        q = (*s++ == '\015') ? 3 : 0;
 
1209
        break;
 
1210
      case 3:                   /* fourth character */
 
1211
        if (*s++ == '\012') {   /* have the sequence? */
 
1212
                                /* yes, note for later */
 
1213
          elt->private.msg.header.text.size = *size = siz;
 
1214
          return ret;
 
1215
        }
 
1216
        q = 0;                  /* lost... */
 
1217
        break;
 
1218
      }
 
1219
    }
 
1220
                                /* header consumes entire message */
 
1221
    elt->private.msg.header.text.size = *size = elt->rfc822_size;
 
1222
  }
 
1223
  return ret;
 
1224
}