~ubuntu-branches/debian/squeeze/alpine/squeeze

« back to all changes in this revision

Viewing changes to imap/src/osdep/dos/bezrkdos.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:     Berkeley 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:        24 June 1992
 
26
 * Last Edited: 30 August 2006
 
27
 */
 
28
 
 
29
 
 
30
/* Dedication:
 
31
 *  This file is dedicated with affection to those Merry Marvels of Musical
 
32
 * Madness . . .
 
33
 *  ->  The Incomparable Leland Stanford Junior University Marching Band  <-
 
34
 * who entertain, awaken, and outrage Stanford fans in the fact of repeated
 
35
 * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have
 
36
 * HUSKY FEVER!!!].
 
37
 *
 
38
 */
 
39
 
 
40
#include <ctype.h>
 
41
#include <errno.h>
 
42
#include <fcntl.h>
 
43
#include "mail.h"
 
44
#include "osdep.h"
 
45
#include <time.h>
 
46
#include <sys\stat.h>
 
47
#include <dos.h>
 
48
#include "rfc822.h"
 
49
#include "dummy.h"
 
50
#include "misc.h"
 
51
#include "fdstring.h"
 
52
 
 
53
/* Berkeley I/O stream local data */
 
54
        
 
55
typedef struct bezerk_local {
 
56
  int fd;                       /* file descriptor for I/O */
 
57
  off_t filesize;               /* file size parsed */
 
58
  char *buf;                    /* temporary buffer */
 
59
} BEZERKLOCAL;
 
60
 
 
61
 
 
62
/* Convenient access to local data */
 
63
 
 
64
#define LOCAL ((BEZERKLOCAL *) stream->local)
 
65
 
 
66
/* Function prototypes */
 
67
 
 
68
DRIVER *bezerk_valid (char *name);
 
69
long bezerk_isvalid (char *name,char *tmp);
 
70
int bezerk_valid_line (char *s,char **rx,int *rzn);
 
71
void *bezerk_parameters (long function,void *value);
 
72
void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
 
73
void bezerk_list (MAILSTREAM *stream,char *ref,char *pat);
 
74
void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat);
 
75
long bezerk_create (MAILSTREAM *stream,char *mailbox);
 
76
long bezerk_delete (MAILSTREAM *stream,char *mailbox);
 
77
long bezerk_rename (MAILSTREAM *stream,char *old,char *newname);
 
78
MAILSTREAM *bezerk_open (MAILSTREAM *stream);
 
79
void bezerk_close (MAILSTREAM *stream,long options);
 
80
char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
 
81
                     unsigned long *length,long flags);
 
82
long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,
 
83
                  long flags);
 
84
long bezerk_ping (MAILSTREAM *stream);
 
85
void bezerk_check (MAILSTREAM *stream);
 
86
long bezerk_expunge (MAILSTREAM *stream,char *sequence,long options);
 
87
long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
 
88
                  long options);
 
89
long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
 
90
int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
 
91
                     STRING *msg);
 
92
void bezerk_gc (MAILSTREAM *stream,long gcflags);
 
93
char *bezerk_file (char *dst,char *name);
 
94
long bezerk_badname (char *tmp,char *s);
 
95
long bezerk_parse (MAILSTREAM *stream);
 
96
unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
 
97
                             unsigned long *size);
 
98
 
 
99
/* Berkeley mail routines */
 
100
 
 
101
 
 
102
/* Driver dispatch used by MAIL */
 
103
 
 
104
DRIVER bezerkdriver = {
 
105
  "bezerk",                     /* driver name */
 
106
                                /* driver flags */
 
107
  DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY,
 
108
  (DRIVER *) NIL,               /* next driver */
 
109
  bezerk_valid,                 /* mailbox is valid for us */
 
110
  bezerk_parameters,            /* manipulate parameters */
 
111
  bezerk_scan,                  /* scan mailboxes */
 
112
  bezerk_list,                  /* list mailboxes */
 
113
  bezerk_lsub,                  /* list subscribed mailboxes */
 
114
  NIL,                          /* subscribe to mailbox */
 
115
  NIL,                          /* unsubscribe from mailbox */
 
116
  bezerk_create,                /* create mailbox */
 
117
  bezerk_delete,                /* delete mailbox */
 
118
  bezerk_rename,                /* rename mailbox */
 
119
  mail_status_default,          /* status of mailbox */
 
120
  bezerk_open,                  /* open mailbox */
 
121
  bezerk_close,                 /* close mailbox */
 
122
  NIL,                          /* fetch message "fast" attributes */
 
123
  NIL,                          /* fetch message flags */
 
124
  NIL,                          /* fetch overview */
 
125
  NIL,                          /* fetch message envelopes */
 
126
  bezerk_header,                /* fetch message header */
 
127
  bezerk_text,                  /* fetch message text */
 
128
  NIL,                          /* fetch partial message text */
 
129
  NIL,                          /* unique identifier */
 
130
  NIL,                          /* message number */
 
131
  NIL,                          /* modify flags */
 
132
  NIL,                          /* per-message modify flags */
 
133
  NIL,                          /* search for message based on criteria */
 
134
  NIL,                          /* sort messages */
 
135
  NIL,                          /* thread messages */
 
136
  bezerk_ping,                  /* ping mailbox to see if still alive */
 
137
  bezerk_check,                 /* check for new messages */
 
138
  bezerk_expunge,               /* expunge deleted messages */
 
139
  bezerk_copy,                  /* copy messages to another mailbox */
 
140
  bezerk_append,                /* append string message to mailbox */
 
141
  NIL                           /* garbage collect stream */
 
142
};
 
143
 
 
144
                                /* prototype stream */
 
145
MAILSTREAM bezerkproto = {&bezerkdriver};
 
146
 
 
147
/* Berkeley mail validate mailbox
 
148
 * Accepts: mailbox name
 
149
 * Returns: our driver if name is valid, NIL otherwise
 
150
 */
 
151
 
 
152
DRIVER *bezerk_valid (char *name)
 
153
{
 
154
  char tmp[MAILTMPLEN];
 
155
  return bezerk_isvalid (name,tmp) ? &bezerkdriver : (DRIVER *) NIL;
 
156
}
 
157
 
 
158
 
 
159
/* Berkeley mail test for valid mailbox
 
160
 * Accepts: mailbox name
 
161
 * Returns: T if valid, NIL otherwise
 
162
 */
 
163
 
 
164
long bezerk_isvalid (char *name,char *tmp)
 
165
{
 
166
  int fd;
 
167
  long ret = NIL;
 
168
  struct stat sbuf;
 
169
  errno = EINVAL;               /* assume invalid argument */
 
170
                                /* if file, get its status */
 
171
  if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
 
172
    if (!sbuf.st_size)errno = 0;/* empty file */
 
173
    else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
 
174
      memset (tmp,'\0',MAILTMPLEN);
 
175
      errno = -1;               /* in case bezerk_valid_line fails */
 
176
      if (read (fd,tmp,MAILTMPLEN-1) >= 0)
 
177
        ret = bezerk_valid_line (tmp,NIL,NIL);
 
178
      close (fd);               /* close the file */
 
179
    }
 
180
  }
 
181
                                /* in case INBOX but not bezerk format */
 
182
  else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
 
183
           ((name[1] == 'N') || (name[1] == 'n')) &&
 
184
           ((name[2] == 'B') || (name[2] == 'b')) &&
 
185
           ((name[3] == 'O') || (name[3] == 'o')) &&
 
186
           ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
 
187
  return ret;                   /* return what we should */
 
188
}
 
189
 
 
190
/* Validate line
 
191
 * Accepts: pointer to candidate string to validate as a From header
 
192
 *          return pointer to end of date/time field
 
193
 *          return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
 
194
 *          return pointer to offset from t of time zone (if non-zero)
 
195
 * Returns: t,ti,zn set if valid From string, else ti is NIL
 
196
 */
 
197
 
 
198
int bezerk_valid_line (char *s,char **rx,int *rzn)
 
199
{
 
200
  char *x;
 
201
  int zn;
 
202
  int ti = 0;
 
203
                                /* line must begin with "From " */
 
204
  if ((*s != 'F') || (s[1] != 'r') || (s[2] != 'o') || (s[3] != 'm') ||
 
205
        (s[4] != ' ')) return NIL;
 
206
                                /* find end of line */
 
207
  for (x = s + 5; *x && *x != '\012'; x++);
 
208
  if (!x) return NIL;           /* end of line not found */
 
209
  if (x[-1] == '\015') x--;     /* ignore CR */
 
210
  if ((x - s < 27)) return NIL; /* line too short */
 
211
  if (x - s >= 41) {            /* possible search for " remote from " */
 
212
    for (zn = -1; x[zn] != ' '; zn--);
 
213
    if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') &&
 
214
        (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') &&
 
215
        (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') &&
 
216
        (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))
 
217
      x += zn - 12;
 
218
  }
 
219
  if (x[-5] == ' ') {           /* ends with year? */
 
220
                                /* no timezone? */
 
221
    if (x[-8] == ':') zn = 0,ti = -5;
 
222
                                /* three letter timezone? */
 
223
    else if (x[-9] == ' ') ti = zn = -9;
 
224
                                /* numeric timezone? */
 
225
    else if ((x[-11]==' ') && ((x[-10]=='+') || (x[-10]=='-'))) ti = zn = -11;
 
226
  }
 
227
  else if (x[-4] == ' ') {      /* no year and three leter timezone? */
 
228
    if (x[-9] == ' ') zn = -4,ti = -9;
 
229
  }
 
230
  else if (x[-6] == ' ') {      /* no year and numeric timezone? */
 
231
    if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-')))
 
232
      zn = -6,ti = -11;
 
233
  }
 
234
                                /* time must be www mmm dd hh:mm[:ss] */
 
235
  if (ti && !((x[ti - 3] == ':') &&
 
236
              (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') &&
 
237
              (x[ti - 3] == ' ') && (x[ti - 7] == ' ') &&
 
238
              (x[ti - 11] == ' '))) return NIL;
 
239
  if (rx) *rx = x;              /* set return values */
 
240
  if (rzn) *rzn = zn;
 
241
  return ti;
 
242
}
 
243
 
 
244
/* Berkeley manipulate driver parameters
 
245
 * Accepts: function code
 
246
 *          function-dependent value
 
247
 * Returns: function-dependent return value
 
248
 */
 
249
 
 
250
void *bezerk_parameters (long function,void *value)
 
251
{
 
252
  return NIL;
 
253
}
 
254
 
 
255
 
 
256
/* Berkeley mail scan mailboxes
 
257
 * Accepts: mail stream
 
258
 *          reference
 
259
 *          pattern to search
 
260
 *          string to scan
 
261
 */
 
262
 
 
263
void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
 
264
{
 
265
  if (stream) dummy_scan (NIL,ref,pat,contents);
 
266
}
 
267
 
 
268
 
 
269
/* Berkeley mail list mailboxes
 
270
 * Accepts: mail stream
 
271
 *          reference
 
272
 *          pattern to search
 
273
 */
 
274
 
 
275
void bezerk_list (MAILSTREAM *stream,char *ref,char *pat)
 
276
{
 
277
  if (stream) dummy_list (stream,ref,pat);
 
278
}
 
279
 
 
280
 
 
281
/* Berkeley mail list subscribed mailboxes
 
282
 * Accepts: mail stream
 
283
 *          reference
 
284
 *          pattern to search
 
285
 */
 
286
 
 
287
void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat)
 
288
{
 
289
  if (stream) dummy_lsub (stream,ref,pat);
 
290
}
 
291
 
 
292
/* Berkeley mail create mailbox
 
293
 * Accepts: MAIL stream
 
294
 *          mailbox name to create
 
295
 * Returns: T on success, NIL on failure
 
296
 */
 
297
 
 
298
long bezerk_create (MAILSTREAM *stream,char *mailbox)
 
299
{
 
300
  return dummy_create (stream,mailbox);
 
301
}
 
302
 
 
303
 
 
304
/* Berkeley mail delete mailbox
 
305
 * Accepts: MAIL stream
 
306
 *          mailbox name to delete
 
307
 * Returns: T on success, NIL on failure
 
308
 */
 
309
 
 
310
long bezerk_delete (MAILSTREAM *stream,char *mailbox)
 
311
{
 
312
  return dummy_delete (stream,mailbox);
 
313
}
 
314
 
 
315
 
 
316
/* Berkeley mail rename mailbox
 
317
 * Accepts: MAIL stream
 
318
 *          old mailbox name
 
319
 *          new mailbox name (or NIL for delete)
 
320
 * Returns: T on success, NIL on failure
 
321
 */
 
322
 
 
323
long bezerk_rename (MAILSTREAM *stream,char *old,char *newname)
 
324
{
 
325
  return dummy_rename (stream,old,newname);
 
326
}
 
327
 
 
328
/* Berkeley mail open
 
329
 * Accepts: stream to open
 
330
 * Returns: stream on success, NIL on failure
 
331
 */
 
332
 
 
333
MAILSTREAM *bezerk_open (MAILSTREAM *stream)
 
334
{
 
335
  long i;
 
336
  int fd;
 
337
  char *s;
 
338
  char tmp[MAILTMPLEN];
 
339
                                /* return prototype for OP_PROTOTYPE call */
 
340
  if (!stream) return &bezerkproto;
 
341
  if (stream->local) fatal ("bezerk recycle stream");
 
342
  if (!mailboxfile (tmp,stream->mailbox))
 
343
    return (MAILSTREAM *) bezerk_badname (tmp,stream->mailbox);
 
344
  if (((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0)) {
 
345
    sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
 
346
    mm_log (tmp,ERROR);
 
347
    return NIL;
 
348
  }
 
349
  stream->rdonly = T;           /* this driver is readonly */
 
350
  stream->local = fs_get (sizeof (BEZERKLOCAL));
 
351
                                /* canonicalize the stream mailbox name */
 
352
  fs_give ((void **) &stream->mailbox);
 
353
  if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
 
354
  stream->mailbox = cpystr (tmp);
 
355
  LOCAL->fd = fd;               /* note the file */
 
356
  LOCAL->filesize = 0;          /* initialize parsed file size */
 
357
  LOCAL->buf = NIL;             /* initially no local buffer */
 
358
  stream->sequence++;           /* bump sequence number */
 
359
  stream->uid_validity = time (0);
 
360
                                /* parse mailbox */
 
361
  stream->nmsgs = stream->recent = 0;
 
362
  if (!bezerk_ping (stream)) return NIL;
 
363
  if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
 
364
  stream->perm_seen = stream->perm_deleted =
 
365
    stream->perm_flagged = stream->perm_answered = stream->perm_draft = NIL;
 
366
  stream->perm_user_flags = NIL;
 
367
  return stream;                /* return stream to caller */
 
368
}
 
369
 
 
370
/* Berkeley mail close
 
371
 * Accepts: MAIL stream
 
372
 *          close options
 
373
 */
 
374
 
 
375
void bezerk_close (MAILSTREAM *stream,long options)
 
376
{
 
377
  if (stream && LOCAL) {        /* only if a file is open */
 
378
    int silent = stream->silent;
 
379
    stream->silent = T;
 
380
    if (options & CL_EXPUNGE) bezerk_expunge (stream,NIL,NIL);
 
381
    close (LOCAL->fd);          /* close the local file */
 
382
    if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
 
383
                                /* nuke the local data */
 
384
    fs_give ((void **) &stream->local);
 
385
    stream->dtb = NIL;          /* log out the DTB */
 
386
  }
 
387
}
 
388
 
 
389
/* Berkeley mail fetch message header
 
390
 * Accepts: MAIL stream
 
391
 *          message # to fetch
 
392
 *          pointer to returned header text length
 
393
 *          option flags
 
394
 * Returns: message header in RFC822 format
 
395
 */
 
396
 
 
397
char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
 
398
                     unsigned long *length,long flags)
 
399
{
 
400
  char tmp[MAILTMPLEN];
 
401
  *length = 0;                  /* default to empty */
 
402
  if (flags & FT_UID) return "";/* UID call "impossible" */
 
403
                                /* get to header position */
 
404
  lseek (LOCAL->fd,bezerk_hdrpos (stream,msgno,length),L_SET);
 
405
                                /* is buffer big enough? */
 
406
  if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
 
407
  LOCAL->buf = (char *) fs_get ((size_t) *length + 1);
 
408
  LOCAL->buf[*length] = '\0';   /* tie off string */
 
409
                                /* slurp the data */
 
410
  read (LOCAL->fd,LOCAL->buf,(size_t) *length);
 
411
  return LOCAL->buf;
 
412
}
 
413
 
 
414
 
 
415
/* Berkeley mail fetch message text (body only)
 
416
 * Accepts: MAIL stream
 
417
 *          message # to fetch
 
418
 *          pointer to returned header text length
 
419
 *          option flags
 
420
 * Returns: T, always
 
421
 */
 
422
 
 
423
long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
 
424
{
 
425
  MESSAGECACHE *elt;
 
426
  FDDATA d;
 
427
  unsigned long hdrsize,hdrpos;
 
428
                                /* UID call "impossible" */
 
429
  if (flags & FT_UID) return NIL;
 
430
  elt = mail_elt (stream,msgno);/* if message not seen */
 
431
                                /* mark message as seen */
 
432
  if (elt->seen && !(flags & FT_PEEK)) {
 
433
    elt->seen = T;
 
434
    mm_flags (stream,msgno);
 
435
  }
 
436
                                /* get location of text data */
 
437
  hdrpos = bezerk_hdrpos (stream,msgno,&hdrsize);
 
438
  d.fd = LOCAL->fd;             /* set initial stringstruct */
 
439
  d.pos = hdrpos + hdrsize;
 
440
                                /* flush old buffer */
 
441
  if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
 
442
  d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNKSIZE);
 
443
  INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize);
 
444
  return T;                     /* success */
 
445
}
 
446
 
 
447
/* Berkeley mail ping mailbox
 
448
 * Accepts: MAIL stream
 
449
 * Returns: T if stream still alive, NIL if not
 
450
 */
 
451
 
 
452
long bezerk_ping (MAILSTREAM *stream)
 
453
{
 
454
                                /* punt if stream no longer alive */
 
455
  if (!(stream && LOCAL)) return NIL;
 
456
                                /* parse mailbox, punt if parse dies */
 
457
  return (bezerk_parse (stream)) ? T : NIL;
 
458
}
 
459
 
 
460
 
 
461
/* Berkeley mail check mailbox (reparses status too)
 
462
 * Accepts: MAIL stream
 
463
 */
 
464
 
 
465
void bezerk_check (MAILSTREAM *stream)
 
466
{
 
467
  unsigned long i = 1;
 
468
  if (bezerk_ping (stream)) {   /* ping mailbox */
 
469
                                /* get new message status */
 
470
    while (i <= stream->nmsgs) mail_elt (stream,i++);
 
471
    mm_log ("Check completed",(long) NIL);
 
472
  }
 
473
}
 
474
 
 
475
/* Berkeley mail expunge mailbox
 
476
 * Accepts: MAIL stream
 
477
 *          sequence to expunge if non-NIL
 
478
 *          expunge options
 
479
 * Returns: T, always
 
480
 */
 
481
 
 
482
long bezerk_expunge (MAILSTREAM *stream,char *sequence,long options)
 
483
{
 
484
  if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",WARN);
 
485
  return LONGT;
 
486
}
 
487
 
 
488
/* Berkeley mail copy message(s)
 
489
 * Accepts: MAIL stream
 
490
 *          sequence
 
491
 *          destination mailbox
 
492
 *          copy options
 
493
 * Returns: T if success, NIL if failed
 
494
 */
 
495
 
 
496
long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
 
497
{
 
498
  char tmp[MAILTMPLEN];
 
499
  struct stat sbuf;
 
500
  MESSAGECACHE *elt;
 
501
  unsigned long i,j,k;
 
502
  int fd;
 
503
  mailproxycopy_t pc =
 
504
    (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
 
505
  if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
 
506
        mail_sequence (stream,sequence))) return NIL;
 
507
                                /* make sure valid mailbox */
 
508
  if (!bezerk_isvalid (mailbox,tmp) && errno) {
 
509
    if (errno == ENOENT)
 
510
      mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
 
511
                 (long) NIL);
 
512
    else if (pc) return (*pc) (stream,sequence,mailbox,options);
 
513
    else if (mailboxfile (tmp,mailbox)) {
 
514
      sprintf (tmp,"Not a Bezerk-format mailbox: %s",mailbox);
 
515
      mm_log (tmp,ERROR);
 
516
    }
 
517
    else bezerk_badname (tmp,mailbox);
 
518
    return NIL;
 
519
  }
 
520
                                /* open the destination */
 
521
  if (!mailboxfile (tmp,mailbox) ||
 
522
      (fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
 
523
                  S_IREAD|S_IWRITE)) < 0) {
 
524
    sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
 
525
    mm_log (tmp,ERROR);
 
526
    return NIL;
 
527
  }
 
528
 
 
529
  mm_critical (stream);         /* go critical */
 
530
  fstat (fd,&sbuf);             /* get current file size */
 
531
                                /* for each requested message */
 
532
  for (i = 1; i <= stream->nmsgs; i++)
 
533
    if ((elt = mail_elt (stream,i))->sequence) {
 
534
      lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET);
 
535
                                /* number of bytes to copy */
 
536
      j = elt->private.msg.full.offset + elt->rfc822_size;
 
537
      do {                      /* read from source position */
 
538
        k = min (j,(unsigned long) MAILTMPLEN);
 
539
        read (LOCAL->fd,tmp,(unsigned int) k);
 
540
        if (write (fd,tmp,(unsigned int) k) < 0) {
 
541
          sprintf (tmp,"Unable to write message: %s",strerror (errno));
 
542
          mm_log (tmp,ERROR);
 
543
          chsize (fd,sbuf.st_size);
 
544
          close (fd);           /* punt */
 
545
          mm_nocritical (stream);
 
546
          return NIL;
 
547
        }
 
548
      } while (j -= k);         /* until done */
 
549
    }
 
550
  close (fd);                   /* close the file */
 
551
  mm_nocritical (stream);       /* release critical */
 
552
                                /* delete all requested messages */
 
553
  if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
 
554
    if ((elt = mail_elt (stream,i))->sequence) elt->deleted = T;
 
555
  if (mail_parameters (NIL,GET_COPYUID,NIL))
 
556
    mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
 
557
  return T;
 
558
}
 
559
 
 
560
/* Berkeley mail append message from stringstruct
 
561
 * Accepts: MAIL stream
 
562
 *          destination mailbox
 
563
 *          append callback
 
564
 *          data for callback
 
565
 * Returns: T if append successful, else NIL
 
566
 */
 
567
 
 
568
#define BUFLEN MAILTMPLEN
 
569
 
 
570
long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
 
571
{
 
572
  struct stat sbuf;
 
573
  int fd;
 
574
  unsigned long i,j;
 
575
  char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
 
576
  FILE *sf,*df;
 
577
  MESSAGECACHE elt;
 
578
  STRING *message;
 
579
  long ret = LONGT;
 
580
                                /* default stream to prototype */
 
581
  if (!stream) stream = &bezerkproto;
 
582
                                /* make sure valid mailbox */
 
583
  if (!bezerk_isvalid (mailbox,tmp) && errno) {
 
584
    if (errno == ENOENT) {
 
585
      if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
 
586
          ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
 
587
          ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
 
588
          ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
 
589
          ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
 
590
        bezerk_create (NIL,"INBOX");
 
591
      else {
 
592
        mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
 
593
        return NIL;
 
594
      }
 
595
    }
 
596
    else if (mailboxfile (tmp,mailbox)) {
 
597
      sprintf (tmp,"Not a Bezerk-format mailbox: %.80ss",mailbox);
 
598
      mm_log (tmp,ERROR);
 
599
    }
 
600
    else bezerk_badname (tmp,mailbox);
 
601
    return NIL;
 
602
  }
 
603
  tzset ();                     /* initialize timezone stuff */
 
604
                                /* get first message */
 
605
  if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
 
606
  if (!(sf = tmpfile ())) {     /* must have scratch file */
 
607
    sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
 
608
    mm_log (tmp,ERROR);
 
609
  }
 
610
 
 
611
  do {                          /* parse date */
 
612
    if (!date) rfc822_date (date = tmp);
 
613
    if (!mail_parse_date (&elt,date)) {
 
614
      sprintf (tmp,"Bad date in append: %.80s",date);
 
615
      mm_log (tmp,ERROR);
 
616
    }
 
617
    else {                      /* user wants to suppress time zones? */
 
618
      if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
 
619
        time_t when = mail_longdate (&elt);
 
620
        date = ctime (&when);   /* use traditional date */
 
621
      }
 
622
                                /* use POSIX-style date */
 
623
      else date = mail_cdate (tmp,&elt);
 
624
      if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
 
625
      else if (!bezerk_append_msg (stream,sf,flags,date,message)) {
 
626
        sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
 
627
        mm_log (tmp,ERROR);
 
628
      }
 
629
                                /* get next message */
 
630
      else if ((*af) (stream,data,&flags,&date,&message)) continue;
 
631
    }
 
632
    fclose (sf);                /* punt scratch file */
 
633
    return NIL;                 /* give up */
 
634
  } while (message);            /* until no more messages */
 
635
  if (fflush (sf) || fstat (fileno (sf),&sbuf)) {
 
636
    sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
 
637
    mm_log (tmp,ERROR);
 
638
    fclose (sf);                /* punt scratch file */
 
639
    return NIL;                 /* give up */
 
640
  }
 
641
  i = sbuf.st_size;             /* size of scratch file */
 
642
 
 
643
  mm_critical (stream);         /* go critical */
 
644
                                /* open the destination */
 
645
  if (!mailboxfile (tmp,mailbox) || 
 
646
      ((fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
 
647
                   S_IREAD|S_IWRITE)) < 0) ||
 
648
      !(df = fdopen (fd,"ab"))) {
 
649
    mm_nocritical (stream);     /* done with critical */
 
650
    sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
 
651
    mm_log (tmp,ERROR);
 
652
    return NIL;
 
653
  }
 
654
  fstat (fd,&sbuf);             /* get current file size */
 
655
  while (i)                     /* until written all bytes */
 
656
    if ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) &&
 
657
        (fwrite (buf,1,j,df) == j)) i -= j;
 
658
  fclose (sf);                  /* done with scratch file */
 
659
                                /* make sure append wins */
 
660
  if (i || (fflush (df) == EOF)) {
 
661
    chsize (fd,sbuf.st_size);   /* revert file */
 
662
    close (fd);                 /* make sure fclose() doesn't corrupt us */
 
663
    sprintf (buf,"Message append failed: %s",strerror (errno));
 
664
    mm_log (buf,ERROR);
 
665
    ret = NIL;                  /* return error */
 
666
  }
 
667
  fclose (df);
 
668
  mm_nocritical (stream);       /* release critical */
 
669
  if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
 
670
    mm_log ("Can not return meaningful APPENDUID with this mailbox format",
 
671
            WARN);
 
672
  return ret;
 
673
}
 
674
 
 
675
/* Write single message to append scratch file
 
676
 * Accepts: MAIL stream
 
677
 *          scratch file
 
678
 *          flags
 
679
 *          message stringstruct
 
680
 * Returns: NIL if write error, else T
 
681
 */
 
682
 
 
683
int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
 
684
                     STRING *msg)
 
685
{
 
686
  int c;
 
687
  unsigned long i,uf;
 
688
  char tmp[MAILTMPLEN];
 
689
  long f = mail_parse_flags (stream,flags,&uf);
 
690
                                /* build initial header */
 
691
  if ((fprintf (sf,"From %s@%s %sStatus: ",
 
692
                myusername (),mylocalhost (),date) < 0) ||
 
693
      (f&fSEEN && (putc ('R',sf) == EOF)) ||
 
694
      (fputs ("\nX-Status: ",sf) == EOF) ||
 
695
      (f&fDELETED && (putc ('D',sf) == EOF)) ||
 
696
      (f&fFLAGGED && (putc ('F',sf) == EOF)) ||
 
697
      (f&fANSWERED && (putc ('A',sf) == EOF)) ||
 
698
      (f&fDRAFT && (putc ('T',sf) == EOF)) ||
 
699
      (fputs ("\nX-Keywords:",sf) == EOF)) return NIL;
 
700
  while (uf)                    /* write user flags */
 
701
    if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0)
 
702
      return NIL;
 
703
                                /* tie off flags */
 
704
  if (putc ('\n',sf) == EOF) return NIL;
 
705
  while (SIZE (msg)) {          /* copy text to scratch file */
 
706
                                /* possible delimiter if line starts with F */
 
707
    if ((c = 0xff & SNX (msg)) == 'F') {
 
708
                                /* copy line to buffer */
 
709
      for (i = 1,tmp[0] = c; SIZE (msg) && (c != '\n') && (i < MAILTMPLEN);)
 
710
        if (((c = 0xff & SNX (msg)) != '\r') || !(SIZE (msg)) ||
 
711
            (CHR (msg) != '\n')) tmp[i++] = c;
 
712
      if ((i > 4) && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') &&
 
713
          (tmp[4] == ' ')) {    /* possible "From " line? */
 
714
                                /* yes, see if need to write a widget */
 
715
        if (((c != '\n') || bezerk_valid_line (tmp,NIL,NIL)) &&
 
716
            (putc ('>',sf) == EOF)) return NIL;
 
717
      }
 
718
                                /* write buffered text */
 
719
      if (fwrite (tmp,1,i,sf) != i) return NIL;
 
720
      if (c == '\n') continue;  /* all done if got a complete line */
 
721
    }
 
722
                                /* copy line, toss out CR from CRLF */
 
723
    do if (((c == '\r') && SIZE (msg) && ((c = 0xff & SNX (msg)) != '\n') &&
 
724
            (putc ('\r',sf) == EOF)) || (putc (c,sf) == EOF)) return NIL;
 
725
    while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T));
 
726
  }
 
727
                                /* write trailing newline and return */
 
728
  return (putc ('\n',sf) == EOF) ? NIL : T;
 
729
}
 
730
 
 
731
 
 
732
/* Return bad file name error message
 
733
 * Accepts: temporary buffer
 
734
 *          file name
 
735
 * Returns: long NIL always
 
736
 */
 
737
 
 
738
long bezerk_badname (char *tmp,char *s)
 
739
{
 
740
  sprintf (tmp,"Invalid mailbox name: %s",s);
 
741
  mm_log (tmp,ERROR);
 
742
  return (long) NIL;
 
743
}
 
744
 
 
745
/* Parse mailbox
 
746
 * Accepts: MAIL stream
 
747
 * Returns: T if parse OK
 
748
 *          NIL if failure, stream aborted
 
749
 */
 
750
 
 
751
long bezerk_parse (MAILSTREAM *stream)
 
752
{
 
753
  struct stat sbuf;
 
754
  MESSAGECACHE *elt;
 
755
  char *s,*t,tmp[MAILTMPLEN + 1],*db,datemsg[100];
 
756
  long i;
 
757
  int j,ti,zn;
 
758
  long curpos = LOCAL->filesize;
 
759
  long nmsgs = stream->nmsgs;
 
760
  long recent = stream->recent;
 
761
  short silent = stream->silent;
 
762
  fstat (LOCAL->fd,&sbuf);      /* get status */
 
763
  if (sbuf.st_size < curpos) {  /* sanity check */
 
764
    sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
 
765
    mm_log (tmp,ERROR);
 
766
    bezerk_close (stream,NIL);
 
767
    return NIL;
 
768
  }
 
769
  stream->silent = T;           /* don't pass up mm_exists() events yet */
 
770
  db = datemsg + strlen (strcpy (datemsg,"Unparsable date: "));
 
771
                                /* while there is data to read */
 
772
  while (i = sbuf.st_size - curpos){
 
773
                                /* get to that position in the file */
 
774
    lseek (LOCAL->fd,curpos,SEEK_SET);
 
775
                                /* read first buffer's worth */
 
776
    read (LOCAL->fd,tmp,j = (int) min (i,(long) MAILTMPLEN));
 
777
    tmp[j] = '\0';              /* tie off buffer */
 
778
    if (!(ti = bezerk_valid_line (tmp,&t,&zn))) {
 
779
      mm_log ("Mailbox format invalidated (consult an expert), aborted",ERROR);
 
780
      bezerk_close (stream,NIL);
 
781
      return NIL;
 
782
    }
 
783
 
 
784
                                /* swell the cache */
 
785
    mail_exists (stream,++nmsgs);
 
786
                                /* instantiate an elt for this message */
 
787
    (elt = mail_elt (stream,nmsgs))->valid = T;
 
788
    elt->private.uid = ++stream->uid_last;
 
789
                                /* note file offset of header */
 
790
    elt->private.special.offset = curpos;
 
791
                                /* note offset of message */
 
792
    elt->private.msg.full.offset =
 
793
      (s = ((*t == '\015') ? (t + 2) : (t + 1))) - tmp;
 
794
                                /* generate plausable IMAPish date string */
 
795
    db[2] = db[6] = db[20] = '-'; db[11] = ' '; db[14] = db[17] = ':';
 
796
                                /* dd */
 
797
    db[0] = t[ti - 2]; db[1] = t[ti - 1];
 
798
                                /* mmm */
 
799
    db[3] = t[ti - 6]; db[4] = t[ti - 5]; db[5] = t[ti - 4];
 
800
                                /* hh */
 
801
    db[12] = t[ti + 1]; db[13] = t[ti + 2];
 
802
                                /* mm */
 
803
    db[15] = t[ti + 4]; db[16] = t[ti + 5];
 
804
    if (t[ti += 6] == ':') {    /* ss if present */
 
805
      db[18] = t[++ti]; db[19] = t[++ti];
 
806
      ti++;                     /* move to space */
 
807
    }
 
808
    else db[18] = db[19] = '0'; /* assume 0 seconds */
 
809
                                /* yy -- advance over timezone if necessary */
 
810
    if (++zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4);
 
811
    db[7] = t[ti]; db[8] = t[ti + 1]; db[9] = t[ti + 2]; db[10] = t[ti + 3];
 
812
    t = zn ? (t + zn) : "LCL";  /* zzz */
 
813
    db[21] = *t++; db[22] = *t++; db[23] = *t++;
 
814
    if ((db[21] != '+') && (db[21] != '-')) db[24] = '\0';
 
815
    else {                      /* numeric time zone */
 
816
      db[20] = ' '; db[24] = *t++; db[25] = *t++; db[26] = '\0';
 
817
    }
 
818
                                /* set internal date */
 
819
    if (!mail_parse_date (elt,db)) mm_log (datemsg,WARN);
 
820
 
 
821
    curpos += s - tmp;          /* advance position after header */
 
822
    t = strchr (s,'\012');      /* start of next line */
 
823
                                /* find start of next message */
 
824
    while (!(bezerk_valid_line (s,NIL,NIL))) {
 
825
      if (t) {                  /* have next line? */
 
826
        t++;                    /* advance to new line */
 
827
        curpos += t - s;        /* update position and size */
 
828
        elt->rfc822_size += ((t - s) + ((t[-2] == '\015') ? 0 : 1));
 
829
        s = t;                  /* move to next line */
 
830
        t = strchr (s,'\012');
 
831
      }
 
832
      else {                    /* try next buffer */
 
833
        j = strlen (s);         /* length of unread data in buffer */
 
834
        if ((i = sbuf.st_size - curpos) && (i != j)) {
 
835
                                /* get to that position in the file */
 
836
          lseek (LOCAL->fd,curpos,SEEK_SET);
 
837
                                /* read another buffer's worth */
 
838
          read (LOCAL->fd,s = tmp,j = (int) min (i,(long) MAILTMPLEN));
 
839
          tmp[j] = '\0';        /* tie off buffer */
 
840
          if (!(t = strchr (s,'\012'))) fatal ("Line too long in mailbox");
 
841
        }
 
842
        else {
 
843
          curpos += j;          /* last bit of data */
 
844
          elt->rfc822_size += j;
 
845
          break;
 
846
        }
 
847
      }
 
848
    }
 
849
  }
 
850
                                /* update parsed file size */
 
851
  LOCAL->filesize = sbuf.st_size;
 
852
  stream->silent = silent;      /* can pass up events now */
 
853
  mail_exists (stream,nmsgs);   /* notify upper level of new mailbox size */
 
854
  mail_recent (stream,recent);  /* and of change in recent messages */
 
855
  return T;                     /* return the winnage */
 
856
}
 
857
 
 
858
/* Berkeley locate header for a message
 
859
 * Accepts: MAIL stream
 
860
 *          message number
 
861
 *          pointer to returned header size
 
862
 * Returns: position of header in file
 
863
 */
 
864
 
 
865
unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
 
866
                             unsigned long *size)
 
867
{
 
868
  long siz;
 
869
  size_t i = 0;
 
870
  char c = '\0';
 
871
  char *s;
 
872
  char tmp[MAILTMPLEN];
 
873
  MESSAGECACHE *elt = mail_elt (stream,msgno);
 
874
  long pos = elt->private.special.offset + elt->private.msg.full.offset;
 
875
                                /* is size known? */
 
876
  if (!(*size = elt->private.msg.header.text.size)) {
 
877
                                /* get to header position */
 
878
    lseek (LOCAL->fd,pos,SEEK_SET);
 
879
                                /* search message for CRLF CRLF */
 
880
    for (siz = 1; siz <= elt->rfc822_size; siz++) {
 
881
      if (!i &&                 /* buffer empty? */
 
882
          (read (LOCAL->fd,s = tmp,
 
883
                 i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0))
 
884
        return pos;
 
885
      else i--;
 
886
                                /* two newline sequence? */
 
887
      if ((c == '\012') && (*s == '\012')) {
 
888
                                /* yes, note for later */
 
889
        elt->private.msg.header.text.size = (*size = siz);
 
890
        return pos;             /* return to caller */
 
891
      }
 
892
      else if ((c == '\012') && (*s == '\015')) {
 
893
                                /* yes, note for later */
 
894
        elt->private.msg.header.text.size = (*size = siz + 1);
 
895
        return pos;             /* return to caller */
 
896
      }
 
897
      else c = *s++;            /* next character */
 
898
    }
 
899
  }
 
900
  return pos;                   /* have position */
 
901
}