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

« back to all changes in this revision

Viewing changes to imap/src/c-client/rfc822.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:     RFC 2822 and MIME 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:        27 July 1988
 
26
 * Last Edited: 6 December 2006
 
27
 *
 
28
 * This original version of this file is
 
29
 * Copyright 1988 Stanford University
 
30
 * and was developed in the Symbolic Systems Resources Group of the Knowledge
 
31
 * Systems Laboratory at Stanford University in 1987-88, and was funded by the
 
32
 * Biomedical Research Technology Program of the NationalInstitutes of Health
 
33
 * under grant number RR-00785.
 
34
 */
 
35
 
 
36
 
 
37
#include <ctype.h>
 
38
#include <stdio.h>
 
39
#include <time.h>
 
40
#include "c-client.h"
 
41
 
 
42
 
 
43
/* Support for deprecated features in earlier specifications.  Note that this
 
44
 * module follows RFC 2822, and all use of "rfc822" in function names is
 
45
 * for compatibility.  Only the code identified by the conditionals below
 
46
 * follows the earlier documents.
 
47
 */
 
48
 
 
49
#define RFC733 1                /* parse "at" */
 
50
#define RFC822 0                /* generate A-D-L (MUST be 0 for 2822) */
 
51
 
 
52
/* RFC-822 static data */
 
53
 
 
54
#define RFC822CONT "    "       /* RFC 2822 continuation */
 
55
 
 
56
                                /* should have been "Remailed-" */
 
57
#define RESENTPREFIX "ReSent-"
 
58
static char *resentprefix = RESENTPREFIX;
 
59
                                /* syntax error host string */
 
60
static const char *errhst = ERRHOST;
 
61
 
 
62
 
 
63
/* Body formats constant strings, must match definitions in mail.h */
 
64
 
 
65
char *body_types[TYPEMAX+1] = {
 
66
  "TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO",
 
67
  "MODEL", "X-UNKNOWN"
 
68
};
 
69
 
 
70
 
 
71
char *body_encodings[ENCMAX+1] = {
 
72
  "7BIT", "8BIT", "BINARY", "BASE64", "QUOTED-PRINTABLE", "X-UNKNOWN"
 
73
};
 
74
 
 
75
 
 
76
/* Token delimiting special characters */
 
77
 
 
78
                                /* RFC 2822 specials */
 
79
const char *specials = " ()<>@,;:\\\"[].\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
 
80
                                /* RFC 2822 phrase specials (no space) */
 
81
const char *rspecials = "()<>@,;:\\\"[].\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
 
82
                                /* RFC 2822 dot-atom specials (no dot) */
 
83
const char *wspecials = " ()<>@,;:\\\"[]\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
 
84
                                /* RFC 2045 MIME body token specials */
 
85
const char *tspecials = " ()<>@,;:\\\"[]/?=\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
 
86
 
 
87
/* Subtype defaulting (a no-no, but regretably necessary...)
 
88
 * Accepts: type code
 
89
 * Returns: default subtype name
 
90
 */
 
91
 
 
92
char *rfc822_default_subtype (unsigned short type)
 
93
{
 
94
  switch (type) {
 
95
  case TYPETEXT:                /* default is TEXT/PLAIN */
 
96
    return "PLAIN";
 
97
  case TYPEMULTIPART:           /* default is MULTIPART/MIXED */
 
98
    return "MIXED";
 
99
  case TYPEMESSAGE:             /* default is MESSAGE/RFC822 */
 
100
    return "RFC822";
 
101
  case TYPEAPPLICATION:         /* default is APPLICATION/OCTET-STREAM */
 
102
    return "OCTET-STREAM";
 
103
  case TYPEAUDIO:               /* default is AUDIO/BASIC */
 
104
    return "BASIC";
 
105
  default:                      /* others have no default subtype */
 
106
    return "UNKNOWN";
 
107
  }
 
108
}
 
109
 
 
110
/* RFC 2822 parsing routines */
 
111
 
 
112
 
 
113
/* Parse an RFC 2822 message
 
114
 * Accepts: pointer to return envelope
 
115
 *          pointer to return body
 
116
 *          pointer to header
 
117
 *          header byte count
 
118
 *          pointer to body stringstruct
 
119
 *          pointer to local host name
 
120
 *          recursion depth
 
121
 *          source driver flags
 
122
 */
 
123
 
 
124
void rfc822_parse_msg_full (ENVELOPE **en,BODY **bdy,char *s,unsigned long i,
 
125
                            STRING *bs,char *host,unsigned long depth,
 
126
                            unsigned long flags)
 
127
{
 
128
  char c,*t,*d;
 
129
  char *tmp = (char *) fs_get ((size_t) i + 100);
 
130
  ENVELOPE *env = (*en = mail_newenvelope ());
 
131
  BODY *body = bdy ? (*bdy = mail_newbody ()) : NIL;
 
132
  long MIMEp = -1;              /* flag that MIME semantics are in effect */
 
133
  parseline_t pl = (parseline_t) mail_parameters (NIL,GET_PARSELINE,NIL);
 
134
  if (!host) host = BADHOST;    /* make sure that host is non-null */
 
135
  while (i && *s != '\n') {     /* until end of header */
 
136
    t = tmp;                    /* initialize buffer pointer */
 
137
    c = ' ';                    /* and previous character */
 
138
    while (i && c) {            /* collect text until logical end of line */
 
139
      switch (c = *s++) {       /* slurp a character */
 
140
      case '\015':              /* return, possible end of logical line */
 
141
        if (*s == '\n') break;  /* ignore if LF follows */
 
142
      case '\012':              /* LF, possible end of logical line */
 
143
                                /* tie off unless next line starts with WS */
 
144
        if (*s != ' ' && *s != '\t') *t++ = c = '\0';
 
145
        break;
 
146
      case '\t':                /* tab */
 
147
        *t++ = ' ';             /* coerce to space */
 
148
        break;
 
149
      default:                  /* all other characters */
 
150
        *t++ = c;               /* insert the character into the line */
 
151
        break;
 
152
      }
 
153
      if (!--i) *t++ = '\0';    /* see if end of header */
 
154
    }
 
155
 
 
156
                                /* find header item type */
 
157
    if (t = d = strchr (tmp,':')) {
 
158
      *d++ = '\0';              /* tie off header item, point at its data */
 
159
      while (*d == ' ') d++;    /* flush whitespace */
 
160
      while ((tmp < t--) && (*t == ' ')) *t = '\0';
 
161
      ucase (tmp);              /* coerce to uppercase */
 
162
                                /* external callback */
 
163
      if (pl) (*pl) (env,tmp,d,host);
 
164
      switch (*tmp) {           /* dispatch based on first character */
 
165
      case '>':                 /* possible >From: */
 
166
        if (!strcmp (tmp+1,"FROM")) rfc822_parse_adrlist (&env->from,d,host);
 
167
        break;
 
168
      case 'B':                 /* possible bcc: */
 
169
        if (!strcmp (tmp+1,"CC")) rfc822_parse_adrlist (&env->bcc,d,host);
 
170
        break;
 
171
      case 'C':                 /* possible cc: or Content-<mumble>*/
 
172
        if (!strcmp (tmp+1,"C")) rfc822_parse_adrlist (&env->cc,d,host);
 
173
        else if ((tmp[1] == 'O') && (tmp[2] == 'N') && (tmp[3] == 'T') &&
 
174
                 (tmp[4] == 'E') && (tmp[5] == 'N') && (tmp[6] == 'T') &&
 
175
                 (tmp[7] == '-') && body)
 
176
          switch (MIMEp) {
 
177
          case -1:              /* unknown if MIME or not */
 
178
            if (!(MIMEp =       /* see if MIME-Version header exists */
 
179
                  search ((unsigned char *) s-1,i,
 
180
                          (unsigned char *)"\012MIME-Version",(long) 13))) {
 
181
#if 1
 
182
              /* This is a disgusting kludge, and most of the messages which
 
183
               * benefit from it are spam.
 
184
               */
 
185
              if (!strcmp (tmp+8,"TRANSFER-ENCODING") ||
 
186
                  (!strcmp (tmp+8,"TYPE") && strchr (d,'/'))) {
 
187
                MM_LOG ("Warning: MIME header encountered in non-MIME message",
 
188
                        PARSE);
 
189
                MIMEp = 1;      /* declare MIME now */
 
190
              }
 
191
              else
 
192
#endif
 
193
                break;          /* non-MIME message */
 
194
            }
 
195
          case T:               /* definitely MIME */
 
196
            rfc822_parse_content_header (body,tmp+8,d);
 
197
          }
 
198
        break;
 
199
      case 'D':                 /* possible Date: */
 
200
        if (!env->date && !strcmp (tmp+1,"ATE")) env->date = cpystr (d);
 
201
        break;
 
202
      case 'F':                 /* possible From: */
 
203
        if (!strcmp (tmp+1,"ROM")) rfc822_parse_adrlist (&env->from,d,host);
 
204
        else if (!strcmp (tmp+1,"OLLOWUP-TO")) {
 
205
          t = env->followup_to = (char *) fs_get (1 + strlen (d));
 
206
          while (c = *d++) if (c != ' ') *t++ = c;
 
207
          *t++ = '\0';
 
208
        }
 
209
        break;
 
210
      case 'I':                 /* possible In-Reply-To: */
 
211
        if (!env->in_reply_to && !strcmp (tmp+1,"N-REPLY-TO"))
 
212
          env->in_reply_to = cpystr (d);
 
213
        break;
 
214
 
 
215
      case 'M':                 /* possible Message-ID: or MIME-Version: */
 
216
        if (!env->message_id && !strcmp (tmp+1,"ESSAGE-ID"))
 
217
          env->message_id = cpystr (d);
 
218
        else if (!strcmp (tmp+1,"IME-VERSION")) {
 
219
                                /* tie off at end of phrase */
 
220
          if (t = rfc822_parse_phrase (d)) *t = '\0';
 
221
          rfc822_skipws (&d);   /* skip whitespace */
 
222
                                /* known version? */
 
223
          if (strcmp (d,"1.0") && strcmp (d,"RFC-XXXX"))
 
224
            MM_LOG ("Warning: message has unknown MIME version",PARSE);
 
225
          MIMEp = T;            /* note that we are MIME */
 
226
        }
 
227
        break;
 
228
      case 'N':                 /* possible Newsgroups: */
 
229
        if (!env->newsgroups && !strcmp (tmp+1,"EWSGROUPS")) {
 
230
          t = env->newsgroups = (char *) fs_get (1 + strlen (d));
 
231
          while (c = *d++) if (c != ' ') *t++ = c;
 
232
          *t++ = '\0';
 
233
        }
 
234
        break;
 
235
      case 'R':                 /* possible Reply-To: */
 
236
        if (!strcmp (tmp+1,"EPLY-TO"))
 
237
          rfc822_parse_adrlist (&env->reply_to,d,host);
 
238
        else if (!env->references && !strcmp (tmp+1,"EFERENCES"))
 
239
          env->references = cpystr (d);
 
240
        break;
 
241
      case 'S':                 /* possible Subject: or Sender: */
 
242
        if (!env->subject && !strcmp (tmp+1,"UBJECT"))
 
243
          env->subject = cpystr (d);
 
244
        else if (!strcmp (tmp+1,"ENDER"))
 
245
          rfc822_parse_adrlist (&env->sender,d,host);
 
246
        break;
 
247
      case 'T':                 /* possible To: */
 
248
        if (!strcmp (tmp+1,"O")) rfc822_parse_adrlist (&env->to,d,host);
 
249
        break;
 
250
      default:
 
251
        break;
 
252
      }
 
253
    }
 
254
  }
 
255
  fs_give ((void **) &tmp);     /* done with scratch buffer */
 
256
                                /* default Sender: and Reply-To: to From: */
 
257
  if (!env->sender) env->sender = rfc822_cpy_adr (env->from);
 
258
  if (!env->reply_to) env->reply_to = rfc822_cpy_adr (env->from);
 
259
                                /* now parse the body */
 
260
  if (body) rfc822_parse_content (body,bs,host,depth,flags);
 
261
}
 
262
 
 
263
/* Parse a message body content
 
264
 * Accepts: pointer to body structure
 
265
 *          body string
 
266
 *          pointer to local host name
 
267
 *          recursion depth
 
268
 *          source driver flags
 
269
 */
 
270
 
 
271
void rfc822_parse_content (BODY *body,STRING *bs,char *h,unsigned long depth,
 
272
                           unsigned long flags)
 
273
{
 
274
  char c,c1,*s,*s1;
 
275
  int f;
 
276
  unsigned long i,j,k,m;
 
277
  PARAMETER *param;
 
278
  PART *part = NIL;
 
279
  if (depth > MAXMIMEDEPTH) {   /* excessively deep recursion? */
 
280
    body->type = TYPETEXT;      /* yes, probably a malicious MIMEgram */
 
281
    MM_LOG ("Ignoring excessively deep MIME recursion",PARSE);
 
282
  }
 
283
  if (!body->subtype)           /* default subtype if still unknown */
 
284
    body->subtype = cpystr (rfc822_default_subtype (body->type));
 
285
                                /* note offset and sizes */
 
286
  body->contents.offset = GETPOS (bs);
 
287
                                /* note internal body size in all cases */
 
288
  body->size.bytes = body->contents.text.size = i = SIZE (bs);
 
289
  if (!(flags & DR_CRLF)) body->size.bytes = strcrlflen (bs);
 
290
  switch (body->type) {         /* see if anything else special to do */
 
291
  case TYPETEXT:                /* text content */
 
292
    if (!body->parameter) {     /* no parameters set */
 
293
      body->parameter = mail_newbody_parameter ();
 
294
      body->parameter->attribute = cpystr ("CHARSET");
 
295
      while (i--) {             /* count lines and guess charset */
 
296
        c = SNX (bs);           /* get current character */
 
297
                                /* charset still unknown? */
 
298
        if (!body->parameter->value) {
 
299
          if ((c == I2C_ESC) && (i && i--) && ((c = SNX (bs)) == I2C_MULTI) &&
 
300
              (i && i--) && (((c = SNX (bs)) == I2CS_94x94_JIS_NEW) ||
 
301
                             (c == I2CS_94x94_JIS_OLD)))
 
302
            body->parameter->value = cpystr ("ISO-2022-JP");
 
303
          else if (c & 0x80) body->parameter->value = cpystr ("X-UNKNOWN");
 
304
        }
 
305
        if (c == '\n') body->size.lines++;
 
306
      }
 
307
                                /* otherwise assume US-ASCII */
 
308
      if (!body->parameter->value) body->parameter->value = cpystr("US-ASCII");
 
309
    }
 
310
                                /* just count lines */
 
311
    else while (i--) if ((SNX (bs)) == '\n') body->size.lines++;
 
312
    break;
 
313
 
 
314
  case TYPEMESSAGE:             /* encapsulated message */
 
315
                                /* encapsulated RFC-822 message? */
 
316
    if (!strcmp (body->subtype,"RFC822")) {
 
317
      body->nested.msg = mail_newmsg ();
 
318
      switch (body->encoding) { /* make sure valid encoding */
 
319
      case ENC7BIT:             /* these are valid nested encodings */
 
320
      case ENC8BIT:
 
321
      case ENCBINARY:
 
322
        break;
 
323
      default:
 
324
        MM_LOG ("Ignoring nested encoding of message contents",PARSE);
 
325
      }
 
326
                                /* hunt for blank line */
 
327
      for (c = '\012',j = 0; (i > j) && ((c != '\012') || (CHR(bs) != '\012'));
 
328
           j++) if ((c1 = SNX (bs)) != '\015') c = c1;
 
329
      if (i > j) {              /* unless no more text */
 
330
        c1 = SNX (bs);          /* body starts here */
 
331
        j++;                    /* advance count */
 
332
      }
 
333
                                /* note body text offset and header size */
 
334
      body->nested.msg->header.text.size = j;
 
335
      body->nested.msg->text.text.size = body->contents.text.size - j;
 
336
      body->nested.msg->text.offset = GETPOS (bs);
 
337
      body->nested.msg->full.offset = body->nested.msg->header.offset =
 
338
        body->contents.offset;
 
339
      body->nested.msg->full.text.size = body->contents.text.size;
 
340
                                /* copy header string */
 
341
      SETPOS (bs,body->contents.offset);
 
342
      s = (char *) fs_get ((size_t) j + 1);
 
343
      for (s1 = s,k = j; k--; *s1++ = SNX (bs));
 
344
      s[j] = '\0';              /* tie off string (not really necessary) */
 
345
                                /* now parse the body */
 
346
      rfc822_parse_msg_full (&body->nested.msg->env,&body->nested.msg->body,s,
 
347
                             j,bs,h,depth+1,flags);
 
348
      fs_give ((void **) &s);   /* free header string */
 
349
                                /* restore position */
 
350
      SETPOS (bs,body->contents.offset);
 
351
    }
 
352
                                /* count number of lines */
 
353
    while (i--) if (SNX (bs) == '\n') body->size.lines++;
 
354
    break;
 
355
  case TYPEMULTIPART:           /* multiple parts */
 
356
    switch (body->encoding) {   /* make sure valid encoding */
 
357
    case ENC7BIT:               /* these are valid nested encodings */
 
358
    case ENC8BIT:
 
359
    case ENCBINARY:
 
360
      break;
 
361
    default:
 
362
      MM_LOG ("Ignoring nested encoding of multipart contents",PARSE);
 
363
    }
 
364
                                /* remember if digest */
 
365
    f = !strcmp (body->subtype,"DIGEST");
 
366
                                /* find cookie */
 
367
    for (s1 = NIL,param = body->parameter; param && !s1; param = param->next)
 
368
      if (!strcmp (param->attribute,"BOUNDARY")) s1 = param->value;
 
369
    if (!s1) s1 = "-";          /* yucky default */
 
370
    j = strlen (s1) + 2;        /* length of cookie and header */
 
371
    c = '\012';                 /* initially at beginning of line */
 
372
 
 
373
    while (i > j) {             /* examine data */
 
374
      if (m = GETPOS (bs)) m--; /* get position in front of character */
 
375
      switch (c) {              /* examine each line */
 
376
      case '\015':              /* handle CRLF form */
 
377
        if (CHR (bs) == '\012'){/* following LF? */
 
378
          c = SNX (bs); i--;    /* yes, slurp it */
 
379
        }
 
380
      case '\012':              /* at start of a line, start with -- ? */
 
381
        if (!(i && i-- && ((c = SNX (bs)) == '-') && i-- &&
 
382
              ((c = SNX (bs)) == '-'))) break;
 
383
                                /* see if cookie matches */
 
384
        if (k = j - 2) for (s = s1; i-- && *s++ == (c = SNX (bs)) && --k;);
 
385
        if (k) break;           /* strings didn't match if non-zero */
 
386
                                /* terminating delimiter? */
 
387
        if ((c = ((i && i--) ? (SNX (bs)) : '\012')) == '-') {
 
388
          if ((i && i--) && ((c = SNX (bs)) == '-') &&
 
389
              ((i && i--) ? (((c = SNX (bs)) == '\015') || (c=='\012')):T)) {
 
390
                                /* if have a final part calculate its size */
 
391
            if (part) part->body.mime.text.size =
 
392
              (m > part->body.mime.offset) ? (m - part->body.mime.offset) :0;
 
393
            part = NIL; i = 1;  /* terminate scan */
 
394
          }
 
395
          break;
 
396
        }
 
397
                                /* swallow trailing whitespace */
 
398
        while ((c == ' ') || (c == '\t'))
 
399
          c = ((i && i--) ? (SNX (bs)) : '\012');
 
400
        switch (c) {            /* need newline after all of it */
 
401
        case '\015':            /* handle CRLF form */
 
402
          if (i && CHR (bs) == '\012') {
 
403
            c = SNX (bs); i--;/* yes, slurp it */
 
404
          }
 
405
        case '\012':            /* new line */
 
406
          if (part) {           /* calculate size of previous */
 
407
            part->body.mime.text.size =
 
408
              (m > part->body.mime.offset) ? (m-part->body.mime.offset) : 0;
 
409
            /* instantiate next */
 
410
            part = part->next = mail_newbody_part ();
 
411
          }                     /* otherwise start new list */
 
412
          else part = body->nested.part = mail_newbody_part ();
 
413
                                /* digest has a different default */
 
414
          if (f) part->body.type = TYPEMESSAGE;
 
415
                                /* note offset from main body */
 
416
          part->body.mime.offset = GETPOS (bs);
 
417
          break;
 
418
        default:                /* whatever it was it wasn't valid */
 
419
          break;
 
420
        }
 
421
        break;
 
422
      default:                  /* not at a line */
 
423
        c = SNX (bs); i--;      /* get next character */
 
424
        break;
 
425
      }
 
426
    }
 
427
 
 
428
                                /* calculate size of any final part */
 
429
    if (part) part->body.mime.text.size = i +
 
430
      ((GETPOS(bs) > part->body.mime.offset) ?
 
431
       (GETPOS(bs) - part->body.mime.offset) : 0);
 
432
                                /* make a scratch buffer */
 
433
    s1 = (char *) fs_get ((size_t) (k = MAILTMPLEN));
 
434
                                /* in case empty multipart */
 
435
    if (!body->nested.part) body->nested.part = mail_newbody_part ();
 
436
                                /* parse non-empty body parts */
 
437
    for (part = body->nested.part; part; part = part->next) {
 
438
                                /* part non-empty (header and/or content)? */
 
439
      if (i = part->body.mime.text.size) {
 
440
                                /* move to that part of the body */
 
441
        SETPOS (bs,part->body.mime.offset);
 
442
                                /* until end of header */
 
443
        while (i && ((c = CHR (bs)) != '\015') && (c != '\012')) {
 
444
                                /* collect text until logical end of line */
 
445
          for (j = 0,c = ' '; c; ) {
 
446
                                /* make sure buffer big enough */
 
447
            if (j > (k - 10)) fs_resize ((void **) &s1,k += MAILTMPLEN);
 
448
            switch (c1 = SNX (bs)) {
 
449
            case '\015':        /* return */
 
450
              if (i && (CHR (bs) == '\012')) {
 
451
                c1 = SNX (bs);  /* eat any LF following */
 
452
                i--;
 
453
              }
 
454
            case '\012':        /* newline, possible end of logical line */
 
455
                                /* tie off unless continuation */
 
456
              if (!i || ((CHR (bs) != ' ') && (CHR (bs) != '\t')))
 
457
                s1[j] = c = '\0';
 
458
              break;
 
459
            case '\t':          /* tab */
 
460
            case ' ':           /* insert whitespace if not already there */
 
461
              if (c != ' ') s1[j++] = c = ' ';
 
462
              break;
 
463
            default:            /* all other characters */
 
464
              s1[j++] = c = c1; /* insert the character into the line */
 
465
              break;
 
466
            }
 
467
                                /* end of data ties off the header */
 
468
            if (!i || !--i) s1[j++] = c = '\0';
 
469
          }
 
470
 
 
471
                                /* find header item type */
 
472
          if (((s1[0] == 'C') || (s1[0] == 'c')) &&
 
473
              ((s1[1] == 'O') || (s1[1] == 'o')) &&
 
474
              ((s1[2] == 'N') || (s1[2] == 'n')) &&
 
475
              ((s1[3] == 'T') || (s1[3] == 't')) &&
 
476
              ((s1[4] == 'E') || (s1[4] == 'e')) &&
 
477
              ((s1[5] == 'N') || (s1[5] == 'n')) &&
 
478
              ((s1[6] == 'T') || (s1[6] == 't')) &&
 
479
              (s1[7] == '-') && (s = strchr (s1+8,':'))) {
 
480
                                /* tie off and flush whitespace */
 
481
            for (*s++ = '\0'; *s == ' '; s++);
 
482
                                /* parse the header */
 
483
            rfc822_parse_content_header (&part->body,ucase (s1+8),s);
 
484
          }
 
485
        }
 
486
                                /* skip header trailing (CR)LF */
 
487
        if (i && (CHR (bs) =='\015')) {i--; c1 = SNX (bs);}
 
488
        if (i && (CHR (bs) =='\012')) {i--; c1 = SNX (bs);}
 
489
        j = bs->size;           /* save upper level size */
 
490
                                /* set offset for next level, fake size to i */
 
491
        bs->size = GETPOS (bs) + i;
 
492
        part->body.mime.text.size -= i;
 
493
                                /* now parse it */
 
494
        rfc822_parse_content (&part->body,bs,h,depth+1,flags);
 
495
        bs->size = j;           /* restore current level size */
 
496
      }
 
497
      else {                    /* zero-length part, use default subtype */
 
498
        part->body.subtype = cpystr (rfc822_default_subtype (part->body.type));
 
499
                                /* see if anything else special to do */
 
500
        switch (part->body.type) {
 
501
        case TYPETEXT:          /* text content */
 
502
                                /* default parameters */
 
503
          if (!part->body.parameter) {
 
504
            part->body.parameter = mail_newbody_parameter ();
 
505
            part->body.parameter->attribute = cpystr ("CHARSET");
 
506
            part->body.parameter->value = cpystr ("US-ASCII");
 
507
          }
 
508
          break;
 
509
        case TYPEMESSAGE:       /* encapsulated message in digest */
 
510
          part->body.nested.msg = mail_newmsg ();
 
511
          break;
 
512
        default:
 
513
          break;
 
514
        }
 
515
      }
 
516
    }
 
517
    fs_give ((void **) &s1);    /* finished with scratch buffer */
 
518
    break;
 
519
  default:                      /* nothing special to do in any other case */
 
520
    break;
 
521
  }
 
522
}
 
523
 
 
524
/* Parse RFC 2822 body content header
 
525
 * Accepts: body to write to
 
526
 *          possible content name
 
527
 *          remainder of header
 
528
 */
 
529
 
 
530
void rfc822_parse_content_header (BODY *body,char *name,char *s)
 
531
{
 
532
  char c,*t;
 
533
  long i;
 
534
  STRINGLIST *stl;
 
535
  rfc822_skipws (&s);           /* skip leading comments */
 
536
                                /* flush whitespace */
 
537
  if (t = strchr (name,' ')) *t = '\0';
 
538
  switch (*name) {              /* see what kind of content */
 
539
  case 'I':                     /* possible Content-ID */
 
540
    if (!(strcmp (name+1,"D") || body->id)) body->id = cpystr (s);
 
541
    break;
 
542
  case 'D':                     /* possible Content-Description */
 
543
    if (!(strcmp (name+1,"ESCRIPTION") || body->description))
 
544
      body->description = cpystr (s);
 
545
    if (!(strcmp (name+1,"ISPOSITION") || body->disposition.type)) {
 
546
                                /* get type word */
 
547
      if (!(name = rfc822_parse_word (s,tspecials))) break;
 
548
      c = *name;                /* remember delimiter */
 
549
      *name = '\0';             /* tie off type */
 
550
      body->disposition.type = ucase (cpystr (s));
 
551
      *name = c;                /* restore delimiter */
 
552
      rfc822_skipws (&name);    /* skip whitespace */
 
553
      rfc822_parse_parameter (&body->disposition.parameter,name);
 
554
    }
 
555
    break;
 
556
  case 'L':                     /* possible Content-Language */
 
557
    if (!(strcmp (name+1,"ANGUAGE") || body->language)) {
 
558
      stl = NIL;                /* process languages */
 
559
      while (s && (name = rfc822_parse_word (s,tspecials))) {
 
560
        c = *name;              /* save delimiter */
 
561
        *name = '\0';           /* tie off subtype */
 
562
        if (stl) stl = stl->next = mail_newstringlist ();
 
563
        else stl = body->language = mail_newstringlist ();
 
564
        stl->text.data = (unsigned char *) ucase (cpystr (s));
 
565
        stl->text.size = strlen ((char *) stl->text.data);
 
566
        *name = c;              /* restore delimiter */
 
567
        rfc822_skipws (&name);  /* skip whitespace */
 
568
        if (*name == ',') {     /* any more languages? */
 
569
          s = ++name;           /* advance to it them */
 
570
          rfc822_skipws (&s);
 
571
        }
 
572
        else s = NIL;           /* bogus or end of list */
 
573
      }
 
574
    }
 
575
    else if (!(strcmp (name+1,"OCATION") || body->location))
 
576
      body->location = cpystr (s);
 
577
    break;
 
578
  case 'M':                     /* possible Content-MD5 */
 
579
    if (!(strcmp (name+1,"D5") || body->md5)) body->md5 = cpystr (s);
 
580
    break;
 
581
 
 
582
  case 'T':                     /* possible Content-Type/Transfer-Encoding */
 
583
    if (!(strcmp (name+1,"YPE") || body->subtype || body->parameter)) {
 
584
                                /* get type word */
 
585
      if (!(name = rfc822_parse_word (s,tspecials))) break;
 
586
      c = *name;                /* remember delimiter */
 
587
      *name = '\0';             /* tie off type */
 
588
      s = ucase (rfc822_cpy(s));/* search for body type */
 
589
      for (i = 0; (i <= TYPEMAX) && body_types[i] &&
 
590
             strcmp (s,body_types[i]); i++);
 
591
                                /* record body type index */
 
592
      body->type = (i <= TYPEMAX) ? (unsigned short) i : TYPEOTHER;
 
593
                                /* and name if new type */
 
594
      if (body_types[body->type]) fs_give ((void **) &s);
 
595
      else body_types[body->type] = s;
 
596
      *name = c;                /* restore delimiter */
 
597
      rfc822_skipws (&name);    /* skip whitespace */
 
598
      if ((*name == '/') &&     /* subtype? */
 
599
          (name = rfc822_parse_word ((s = ++name),tspecials))) {
 
600
        c = *name;              /* save delimiter */
 
601
        *name = '\0';           /* tie off subtype */
 
602
        rfc822_skipws (&s);     /* copy subtype */
 
603
        if (s) body->subtype = ucase (rfc822_cpy (s));
 
604
        *name = c;              /* restore delimiter */
 
605
        rfc822_skipws (&name);  /* skip whitespace */
 
606
      }
 
607
      else if (!name) {         /* no subtype, was a subtype delimiter? */
 
608
        name = s;               /* barf, restore pointer */
 
609
        rfc822_skipws (&name);  /* skip leading whitespace */
 
610
      }
 
611
      rfc822_parse_parameter (&body->parameter,name);
 
612
    }
 
613
    else if (!strcmp (name+1,"RANSFER-ENCODING")) {
 
614
      if (!(name = rfc822_parse_word (s,tspecials))) break;
 
615
      *name = '\0';             /* tie off encoding */
 
616
      s = ucase (rfc822_cpy(s));/* search for body encoding */
 
617
      for (i = 0; (i <= ENCMAX) && body_encodings[i] &&
 
618
           strcmp (s,body_encodings[i]); i++);
 
619
                                /* record body encoding index */
 
620
      body->encoding = (i <= ENCMAX) ? (unsigned short) i : ENCOTHER;
 
621
                                /* and name if new encoding */
 
622
      if (body_encodings[body->encoding]) fs_give ((void **) &s);
 
623
      else body_encodings[body->encoding] = ucase (cpystr (s));
 
624
    }
 
625
    break;
 
626
  default:                      /* otherwise unknown */
 
627
    break;
 
628
  }
 
629
}
 
630
 
 
631
/* Parse RFC 2822 body parameter list
 
632
 * Accepts: parameter list to write to
 
633
 *          text of list
 
634
 */
 
635
 
 
636
void rfc822_parse_parameter (PARAMETER **par,char *text)
 
637
{
 
638
  char c,*s,tmp[MAILTMPLEN];
 
639
  PARAMETER *param = NIL;
 
640
                                /* parameter list? */
 
641
  while (text && (*text == ';') &&
 
642
         (text = rfc822_parse_word ((s = ++text),tspecials))) {
 
643
    c = *text;                  /* remember delimiter */
 
644
    *text = '\0';               /* tie off attribute name */
 
645
    rfc822_skipws (&s);         /* skip leading attribute whitespace */
 
646
    if (!*s) *text = c;         /* must have an attribute name */
 
647
    else {                      /* instantiate a new parameter */
 
648
      if (*par) param = param->next = mail_newbody_parameter ();
 
649
      else param = *par = mail_newbody_parameter ();
 
650
      param->attribute = ucase (cpystr (s));
 
651
      *text = c;                /* restore delimiter */
 
652
      rfc822_skipws (&text);    /* skip whitespace before equal sign */
 
653
      if ((*text == '=') &&     /* make sure have value */
 
654
          (text = rfc822_parse_word ((s = ++text),tspecials))) {
 
655
        c = *text;              /* remember delimiter */
 
656
        *text = '\0';           /* tie off value */
 
657
        rfc822_skipws (&s);     /* skip leading value whitespace */
 
658
        if (*s) param->value = rfc822_cpy (s);
 
659
        *text = c;              /* restore delimiter */
 
660
        rfc822_skipws (&text);
 
661
      }
 
662
      if (!param->value) {      /* value not found? */
 
663
        param->value = cpystr ("MISSING_PARAMETER_VALUE");
 
664
        sprintf (tmp,"Missing parameter value: %.80s",param->attribute);
 
665
        MM_LOG (tmp,PARSE);
 
666
      }
 
667
    }
 
668
  }
 
669
                                /* string not present */
 
670
  if (!text) MM_LOG ("Missing parameter",PARSE);
 
671
  else if (*text) {             /* must be end of poop */
 
672
    sprintf (tmp,"Unexpected characters at end of parameters: %.80s",text);
 
673
    MM_LOG (tmp,PARSE);
 
674
  }
 
675
}
 
676
 
 
677
/* Parse RFC 2822 address list
 
678
 * Accepts: address list to write to
 
679
 *          input string
 
680
 *          default host name
 
681
 */
 
682
 
 
683
void rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host)
 
684
{
 
685
  int c;
 
686
  char *s,tmp[MAILTMPLEN];
 
687
  ADDRESS *last = *lst;
 
688
  ADDRESS *adr;
 
689
  if (!string) return;          /* no string */
 
690
  rfc822_skipws (&string);      /* skip leading WS */
 
691
  if (!*string) return;         /* empty string */
 
692
                                /* run to tail of list */
 
693
  if (last) while (last->next) last = last->next;
 
694
  while (string) {              /* loop until string exhausted */
 
695
    while (*string == ',') {    /* RFC 822 allowed null addresses!! */
 
696
      ++string;                 /* skip the comma */
 
697
      rfc822_skipws (&string);  /* and any leading WS */
 
698
    }
 
699
    if (!*string) string = NIL; /* punt if ran out of string */
 
700
                                /* got an address? */
 
701
    else if (adr = rfc822_parse_address (lst,last,&string,host,0)) {
 
702
      last = adr;               /* new tail address */
 
703
      if (string) {             /* analyze what follows */
 
704
        rfc822_skipws (&string);
 
705
        switch (c = *(unsigned char *) string) {
 
706
        case ',':               /* comma? */
 
707
          ++string;             /* then another address follows */
 
708
          break;
 
709
        default:
 
710
          s = isalnum (c) ? "Must use comma to separate addresses: %.80s" :
 
711
            "Unexpected characters at end of address: %.80s";
 
712
          sprintf (tmp,s,string);
 
713
          MM_LOG (tmp,PARSE);
 
714
          last = last->next = mail_newaddr ();
 
715
          last->mailbox = cpystr ("UNEXPECTED_DATA_AFTER_ADDRESS");
 
716
          last->host = cpystr (errhst);
 
717
                                /* falls through */
 
718
        case '\0':              /* null-specified address? */
 
719
          string = NIL;         /* punt remainder of parse */
 
720
          break;
 
721
        }
 
722
      }
 
723
    }
 
724
    else if (string) {          /* bad mailbox */
 
725
      rfc822_skipws (&string);  /* skip WS */
 
726
      if (!*string) strcpy (tmp,"Missing address after comma");
 
727
      else sprintf (tmp,"Invalid mailbox list: %.80s",string);
 
728
      MM_LOG (tmp,PARSE);
 
729
      string = NIL;
 
730
      (adr = mail_newaddr ())->mailbox = cpystr ("INVALID_ADDRESS");
 
731
      adr->host = cpystr (errhst);
 
732
      if (last) last = last->next = adr;
 
733
      else *lst = last = adr;
 
734
      break;
 
735
    }
 
736
  }
 
737
}
 
738
 
 
739
/* Parse RFC 2822 address
 
740
 * Accepts: address list to write to
 
741
 *          tail of address list
 
742
 *          pointer to input string
 
743
 *          default host name
 
744
 *          group nesting depth
 
745
 * Returns: new list tail
 
746
 */
 
747
 
 
748
ADDRESS *rfc822_parse_address (ADDRESS **lst,ADDRESS *last,char **string,
 
749
                               char *defaulthost,unsigned long depth)
 
750
{
 
751
  ADDRESS *adr;
 
752
  if (!*string) return NIL;     /* no string */
 
753
  rfc822_skipws (string);       /* skip leading WS */
 
754
  if (!**string) return NIL;    /* empty string */
 
755
  if (adr = rfc822_parse_group (lst,last,string,defaulthost,depth)) last = adr;
 
756
                                /* got an address? */
 
757
  else if (adr = rfc822_parse_mailbox (string,defaulthost)) {
 
758
    if (!*lst) *lst = adr;      /* yes, first time through? */
 
759
    else last->next = adr;      /* no, append to the list */
 
760
                                /* set for subsequent linking */
 
761
    for (last = adr; last->next; last = last->next);
 
762
  }
 
763
  else if (*string) return NIL;
 
764
  return last;
 
765
}
 
766
 
 
767
/* Parse RFC 2822 group
 
768
 * Accepts: address list to write to
 
769
 *          pointer to tail of address list
 
770
 *          pointer to input string
 
771
 *          default host name
 
772
 *          group nesting depth
 
773
 */
 
774
 
 
775
ADDRESS *rfc822_parse_group (ADDRESS **lst,ADDRESS *last,char **string,
 
776
                             char *defaulthost,unsigned long depth)
 
777
{
 
778
  char tmp[MAILTMPLEN];
 
779
  char *p,*s;
 
780
  ADDRESS *adr;
 
781
  if (depth > MAXGROUPDEPTH) {  /* excessively deep recursion? */
 
782
    MM_LOG ("Ignoring excessively deep group recursion",PARSE);
 
783
    return NIL;                 /* probably abusive */
 
784
  }
 
785
  if (!*string) return NIL;     /* no string */
 
786
  rfc822_skipws (string);       /* skip leading WS */
 
787
  if (!**string ||              /* trailing whitespace or not group */
 
788
      ((*(p = *string) != ':') && !(p = rfc822_parse_phrase (*string))))
 
789
    return NIL;
 
790
  s = p;                        /* end of candidate phrase */
 
791
  rfc822_skipws (&s);           /* find delimiter */
 
792
  if (*s != ':') return NIL;    /* not really a group */
 
793
  *p = '\0';                    /* tie off group name */
 
794
  p = ++s;                      /* continue after the delimiter */
 
795
  rfc822_skipws (&p);           /* skip subsequent whitespace */
 
796
                                /* write as address */
 
797
  (adr = mail_newaddr ())->mailbox = rfc822_cpy (*string);
 
798
  if (!*lst) *lst = adr;        /* first time through? */
 
799
  else last->next = adr;        /* no, append to the list */
 
800
  last = adr;                   /* set for subsequent linking */
 
801
  *string = p;                  /* continue after this point */
 
802
  while (*string && **string && (**string != ';')) {
 
803
    if (adr = rfc822_parse_address (lst,last,string,defaulthost,depth+1)) {
 
804
      last = adr;               /* new tail address */
 
805
      if (*string) {            /* anything more? */
 
806
        rfc822_skipws (string); /* skip whitespace */
 
807
        switch (**string) {     /* see what follows */
 
808
        case ',':               /* another address? */
 
809
          ++*string;            /* yes, skip past the comma */
 
810
        case ';':               /* end of group? */
 
811
        case '\0':              /* end of string */
 
812
          break;
 
813
        default:
 
814
          sprintf (tmp,"Unexpected characters after address in group: %.80s",
 
815
                   *string);
 
816
          MM_LOG (tmp,PARSE);
 
817
          *string = NIL;        /* cancel remainder of parse */
 
818
          last = last->next = mail_newaddr ();
 
819
          last->mailbox = cpystr ("UNEXPECTED_DATA_AFTER_ADDRESS_IN_GROUP");
 
820
          last->host = cpystr (errhst);
 
821
        }
 
822
      }
 
823
    }
 
824
    else {                      /* bogon */
 
825
      sprintf (tmp,"Invalid group mailbox list: %.80s",*string);
 
826
      MM_LOG (tmp,PARSE);
 
827
      *string = NIL;            /* cancel remainder of parse */
 
828
      (adr = mail_newaddr ())->mailbox = cpystr ("INVALID_ADDRESS_IN_GROUP");
 
829
      adr->host = cpystr (errhst);
 
830
      last = last->next = adr;
 
831
    }
 
832
  }
 
833
  if (*string) {                /* skip close delimiter */
 
834
    if (**string == ';') ++*string;
 
835
    rfc822_skipws (string);
 
836
  }
 
837
                                /* append end of address mark to the list */
 
838
  last->next = (adr = mail_newaddr ());
 
839
  last = adr;                   /* set for subsequent linking */
 
840
  return last;                  /* return the tail */
 
841
}
 
842
 
 
843
/* Parse RFC 2822 mailbox
 
844
 * Accepts: pointer to string pointer
 
845
 *          default host
 
846
 * Returns: address list
 
847
 *
 
848
 * Updates string pointer
 
849
 */
 
850
 
 
851
ADDRESS *rfc822_parse_mailbox (char **string,char *defaulthost)
 
852
{
 
853
  ADDRESS *adr = NIL;
 
854
  char *s,*end;
 
855
  parsephrase_t pp = (parsephrase_t) mail_parameters (NIL,GET_PARSEPHRASE,NIL);
 
856
  if (!*string) return NIL;     /* no string */
 
857
  rfc822_skipws (string);       /* flush leading whitespace */
 
858
  if (!**string) return NIL;    /* empty string */
 
859
  if (*(s = *string) == '<')    /* note start, handle case of phraseless RA */
 
860
    adr = rfc822_parse_routeaddr (s,string,defaulthost);
 
861
                                /* otherwise, expect at least one word */
 
862
  else if (end = rfc822_parse_phrase (s)) {
 
863
    if ((adr = rfc822_parse_routeaddr (end,string,defaulthost))) {
 
864
                                /* phrase is a personal name */
 
865
      if (adr->personal) fs_give ((void **) &adr->personal);
 
866
      *end = '\0';              /* tie off phrase */
 
867
      adr->personal = rfc822_cpy (s);
 
868
    }
 
869
                                /* call external phraseparser if phrase only */
 
870
    else if (pp && rfc822_phraseonly (end) &&
 
871
             (adr = (*pp) (s,end,defaulthost))) {
 
872
      *string = end;            /* update parse pointer */
 
873
      rfc822_skipws (string);   /* skip WS in the normal way */
 
874
    }
 
875
    else adr = rfc822_parse_addrspec (s,string,defaulthost);
 
876
  }
 
877
  return adr;                   /* return the address */
 
878
}
 
879
 
 
880
 
 
881
/* Check if address is a phrase only
 
882
 * Accepts: pointer to end of phrase
 
883
 * Returns: T if phrase only, else NIL;
 
884
 */
 
885
 
 
886
long rfc822_phraseonly (char *end)
 
887
{
 
888
  while (*end == ' ') ++end;    /* call rfc822_skipws() instead?? */
 
889
  switch (*end) {
 
890
  case '\0': case ',': case ';':
 
891
    return LONGT;               /* is a phrase only */
 
892
  }
 
893
  return NIL;                   /* something other than phase is here */
 
894
}
 
895
 
 
896
/* Parse RFC 2822 route-address
 
897
 * Accepts: string pointer
 
898
 *          pointer to string pointer to update
 
899
 * Returns: address
 
900
 *
 
901
 * Updates string pointer
 
902
 */
 
903
 
 
904
ADDRESS *rfc822_parse_routeaddr (char *string,char **ret,char *defaulthost)
 
905
{
 
906
  char tmp[MAILTMPLEN];
 
907
  ADDRESS *adr;
 
908
  char *s,*t,*adl;
 
909
  size_t adllen,i;
 
910
  if (!string) return NIL;
 
911
  rfc822_skipws (&string);      /* flush leading whitespace */
 
912
                                /* must start with open broket */
 
913
  if (*string != '<') return NIL;
 
914
  t = ++string;                 /* see if A-D-L there */
 
915
  rfc822_skipws (&t);           /* flush leading whitespace */
 
916
  for (adl = NIL,adllen = 0;    /* parse possible A-D-L */
 
917
       (*t == '@') && (s = rfc822_parse_domain (t+1,&t));) {
 
918
    i = strlen (s) + 2;         /* @ plus domain plus delimiter or NUL */
 
919
    if (adl) {                  /* have existing A-D-L? */
 
920
      fs_resize ((void **) &adl,adllen + i);
 
921
      sprintf (adl + adllen - 1,",@%s",s);
 
922
    }
 
923
                                /* write initial A-D-L */
 
924
    else sprintf (adl = (char *) fs_get (i),"@%s",s);
 
925
    adllen += i;                /* new A-D-L length */
 
926
    fs_give ((void **) &s);     /* don't need domain any more */
 
927
    rfc822_skipws (&t);         /* skip WS */
 
928
    if (*t != ',') break;       /* put if not comma */
 
929
    t++;                        /* skip the comma */
 
930
    rfc822_skipws (&t);         /* skip WS */
 
931
  }
 
932
  if (adl) {                    /* got an A-D-L? */
 
933
    if (*t != ':') {            /* make sure syntax good */
 
934
      sprintf (tmp,"Unterminated at-domain-list: %.80s%.80s",adl,t);
 
935
      MM_LOG (tmp,PARSE);
 
936
    }
 
937
    else string = ++t;          /* continue parse from this point */
 
938
  }
 
939
 
 
940
                                /* parse address spec */
 
941
  if (!(adr = rfc822_parse_addrspec (string,ret,defaulthost))) {
 
942
    if (adl) fs_give ((void **) &adl);
 
943
    return NIL;
 
944
  }
 
945
  if (adl) adr->adl = adl;      /* have an A-D-L? */
 
946
  if (*ret) if (**ret == '>') { /* make sure terminated OK */
 
947
    ++*ret;                     /* skip past the broket */
 
948
    rfc822_skipws (ret);        /* flush trailing WS */
 
949
    if (!**ret) *ret = NIL;     /* wipe pointer if at end of string */
 
950
    return adr;                 /* return the address */
 
951
  }
 
952
  sprintf (tmp,"Unterminated mailbox: %.80s@%.80s",adr->mailbox,
 
953
           *adr->host == '@' ? "<null>" : adr->host);
 
954
  MM_LOG (tmp,PARSE);
 
955
  adr->next = mail_newaddr ();
 
956
  adr->next->mailbox = cpystr ("MISSING_MAILBOX_TERMINATOR");
 
957
  adr->next->host = cpystr (errhst);
 
958
  return adr;                   /* return the address */
 
959
}
 
960
 
 
961
/* Parse RFC 2822 address-spec
 
962
 * Accepts: string pointer
 
963
 *          pointer to string pointer to update
 
964
 *          default host
 
965
 * Returns: address
 
966
 *
 
967
 * Updates string pointer
 
968
 */
 
969
 
 
970
ADDRESS *rfc822_parse_addrspec (char *string,char **ret,char *defaulthost)
 
971
{
 
972
  ADDRESS *adr;
 
973
  char c,*s,*t,*v,*end;
 
974
  if (!string) return NIL;      /* no string */
 
975
  rfc822_skipws (&string);      /* flush leading whitespace */
 
976
  if (!*string) return NIL;     /* empty string */
 
977
                                /* find end of mailbox */
 
978
  if (!(t = rfc822_parse_word (string,wspecials))) return NIL;
 
979
  adr = mail_newaddr ();        /* create address block */
 
980
  c = *t;                       /* remember delimiter */
 
981
  *t = '\0';                    /* tie off mailbox */
 
982
                                /* copy mailbox */
 
983
  adr->mailbox = rfc822_cpy (string);
 
984
  *t = c;                       /* restore delimiter */
 
985
  end = t;                      /* remember end of mailbox */
 
986
  rfc822_skipws (&t);           /* skip whitespace */
 
987
  while (*t == '.') {           /* some cretin taking RFC 822 too seriously? */
 
988
    string = ++t;               /* skip past the dot and any WS */
 
989
    rfc822_skipws (&string);
 
990
                                /* get next word of mailbox */
 
991
    if (t = rfc822_parse_word (string,wspecials)) {
 
992
      end = t;                  /* remember new end of mailbox */
 
993
      c = *t;                   /* remember delimiter */
 
994
      *t = '\0';                /* tie off word */
 
995
      s = rfc822_cpy (string);  /* copy successor part */
 
996
      *t = c;                   /* restore delimiter */
 
997
                                /* build new mailbox */
 
998
      sprintf (v = (char *) fs_get (strlen (adr->mailbox) + strlen (s) + 2),
 
999
               "%s.%s",adr->mailbox,s);
 
1000
      fs_give ((void **) &adr->mailbox);
 
1001
      adr->mailbox = v;         /* new host name */
 
1002
      rfc822_skipws (&t);       /* skip WS after mailbox */
 
1003
    }
 
1004
    else {                      /* barf */
 
1005
      MM_LOG ("Invalid mailbox part after .",PARSE);
 
1006
      break;
 
1007
    }
 
1008
  }
 
1009
  t = end;                      /* remember delimiter in case no host */
 
1010
 
 
1011
  rfc822_skipws (&end);         /* sniff ahead at what follows */
 
1012
#if RFC733                      /* RFC 733 used "at" instead of "@" */
 
1013
  if (((*end == 'a') || (*end == 'A')) &&
 
1014
      ((end[1] == 't') || (end[1] == 'T')) &&
 
1015
      ((end[2] == ' ') || (end[2] == '\t') || (end[2] == '\015') ||
 
1016
       (end[2] == '\012') || (end[2] == '(')))
 
1017
    *++end = '@';
 
1018
#endif
 
1019
  if (*end != '@') end = t;     /* host name missing */
 
1020
                                /* otherwise parse host name */
 
1021
  else if (!(adr->host = rfc822_parse_domain (++end,&end)))
 
1022
    adr->host = cpystr (errhst);
 
1023
                                /* default host if missing */
 
1024
  if (!adr->host) adr->host = cpystr (defaulthost);
 
1025
                                /* try person name in comments if missing */
 
1026
  if (end && !(adr->personal && *adr->personal)) {
 
1027
    while (*end == ' ') ++end;  /* see if we can find a person name here */
 
1028
    if ((*end == '(') && (s = rfc822_skip_comment (&end,LONGT)) && strlen (s))
 
1029
      adr->personal = rfc822_cpy (s);
 
1030
    rfc822_skipws (&end);       /* skip any other WS in the normal way */
 
1031
  }
 
1032
                                /* set return to end pointer */
 
1033
  *ret = (end && *end) ? end : NIL;
 
1034
  return adr;                   /* return the address we got */
 
1035
}
 
1036
 
 
1037
/* Parse RFC 2822 domain
 
1038
 * Accepts: string pointer
 
1039
 *          pointer to return end of domain
 
1040
 * Returns: domain name or NIL if failure
 
1041
 */
 
1042
 
 
1043
char *rfc822_parse_domain (char *string,char **end)
 
1044
{
 
1045
  char *ret = NIL;
 
1046
  char c,*s,*t,*v;
 
1047
  rfc822_skipws (&string);      /* skip whitespace */
 
1048
  if (*string == '[') {         /* domain literal? */
 
1049
    if (!(*end = rfc822_parse_word (string + 1,"]\\")))
 
1050
      MM_LOG ("Empty domain literal",PARSE);
 
1051
    else if (**end != ']') MM_LOG ("Unterminated domain literal",PARSE);
 
1052
    else {
 
1053
      size_t len = ++*end - string;
 
1054
      strncpy (ret = (char *) fs_get (len + 1),string,len);
 
1055
      ret[len] = '\0';          /* tie off literal */
 
1056
    }
 
1057
  }
 
1058
                                /* search for end of host */
 
1059
  else if (t = rfc822_parse_word (string,wspecials)) {
 
1060
    c = *t;                     /* remember delimiter */
 
1061
    *t = '\0';                  /* tie off host */
 
1062
    ret = rfc822_cpy (string);  /* copy host */
 
1063
    *t = c;                     /* restore delimiter */
 
1064
    *end = t;                   /* remember end of domain */
 
1065
    rfc822_skipws (&t);         /* skip WS after host */
 
1066
    while (*t == '.') {         /* some cretin taking RFC 822 too seriously? */
 
1067
      string = ++t;             /* skip past the dot and any WS */
 
1068
      rfc822_skipws (&string);
 
1069
      if (string = rfc822_parse_domain (string,&t)) {
 
1070
        *end = t;               /* remember new end of domain */
 
1071
        c = *t;                 /* remember delimiter */
 
1072
        *t = '\0';              /* tie off host */
 
1073
        s = rfc822_cpy (string);/* copy successor part */
 
1074
        *t = c;                 /* restore delimiter */
 
1075
                                /* build new domain */
 
1076
        sprintf (v = (char *) fs_get (strlen (ret) + strlen (s) + 2),
 
1077
                 "%s.%s",ret,s);
 
1078
        fs_give ((void **) &ret);
 
1079
        ret = v;                /* new host name */
 
1080
        rfc822_skipws (&t);     /* skip WS after domain */
 
1081
      }
 
1082
      else {                    /* barf */
 
1083
        MM_LOG ("Invalid domain part after .",PARSE);
 
1084
        break;
 
1085
      }
 
1086
    }
 
1087
  }
 
1088
  else MM_LOG ("Missing or invalid host name after @",PARSE);
 
1089
  return ret;
 
1090
}
 
1091
 
 
1092
/* Parse RFC 2822 phrase
 
1093
 * Accepts: string pointer
 
1094
 * Returns: pointer to end of phrase
 
1095
 */
 
1096
 
 
1097
char *rfc822_parse_phrase (char *s)
 
1098
{
 
1099
  char *curpos;
 
1100
  if (!s) return NIL;           /* no-op if no string */
 
1101
                                /* find first word of phrase */
 
1102
  curpos = rfc822_parse_word (s,NIL);
 
1103
  if (!curpos) return NIL;      /* no words means no phrase */
 
1104
  if (!*curpos) return curpos;  /* check if string ends with word */
 
1105
  s = curpos;                   /* sniff past the end of this word and WS */
 
1106
  rfc822_skipws (&s);           /* skip whitespace */
 
1107
                                /* recurse to see if any more */
 
1108
  return (s = rfc822_parse_phrase (s)) ? s : curpos;
 
1109
}
 
1110
 
 
1111
/* Parse RFC 2822 word
 
1112
 * Accepts: string pointer
 
1113
 *          delimiter (or NIL for phrase word parsing)
 
1114
 * Returns: pointer to end of word
 
1115
 */
 
1116
 
 
1117
char *rfc822_parse_word (char *s,const char *delimiters)
 
1118
{
 
1119
  char *st,*str;
 
1120
  if (!s) return NIL;           /* no string */
 
1121
  rfc822_skipws (&s);           /* flush leading whitespace */
 
1122
  if (!*s) return NIL;          /* empty string */
 
1123
  str = s;                      /* hunt pointer for strpbrk */
 
1124
  while (T) {                   /* look for delimiter, return if none */
 
1125
    if (!(st = strpbrk (str,delimiters ? delimiters : wspecials)))
 
1126
      return str + strlen (str);
 
1127
                                /* ESC in phrase */
 
1128
    if (!delimiters && (*st == I2C_ESC)) {
 
1129
      str = ++st;               /* always skip past ESC */
 
1130
      switch (*st) {            /* special hack for RFC 1468 (ISO-2022-JP) */
 
1131
      case I2C_MULTI:           /* multi byte sequence */
 
1132
        switch (*++st) {
 
1133
        case I2CS_94x94_JIS_OLD:/* old JIS (1978) */
 
1134
        case I2CS_94x94_JIS_NEW:/* new JIS (1983) */
 
1135
          str = ++st;           /* skip past the shift to JIS */
 
1136
          while (st = strchr (st,I2C_ESC))
 
1137
            if ((*++st == I2C_G0_94) && ((st[1] == I2CS_94_ASCII) ||
 
1138
                                         (st[1] == I2CS_94_JIS_ROMAN) ||
 
1139
                                         (st[1] == I2CS_94_JIS_BUGROM))) {
 
1140
              str = st += 2;    /* skip past the shift back to ASCII */
 
1141
              break;
 
1142
            }
 
1143
                                /* eats entire text if no shift back */
 
1144
          if (!st || !*st) return str + strlen (str);
 
1145
        }
 
1146
        break;
 
1147
      case I2C_G0_94:           /* single byte sequence */
 
1148
        switch (st[1]) {
 
1149
        case I2CS_94_ASCII:     /* shift to ASCII */
 
1150
        case I2CS_94_JIS_ROMAN: /* shift to JIS-Roman */
 
1151
        case I2CS_94_JIS_BUGROM:/* old buggy definition of JIS-Roman */
 
1152
          str = st + 2;         /* skip past the shift */
 
1153
          break;
 
1154
        }
 
1155
      }
 
1156
    }
 
1157
 
 
1158
    else switch (*st) {         /* dispatch based on delimiter */
 
1159
    case '"':                   /* quoted string */
 
1160
                                /* look for close quote */
 
1161
      while (*++st != '"') switch (*st) {
 
1162
      case '\0':                /* unbalanced quoted string */
 
1163
        return NIL;             /* sick sick sick */
 
1164
      case '\\':                /* quoted character */
 
1165
        if (!*++st) return NIL; /* skip the next character */
 
1166
      default:                  /* ordinary character */
 
1167
        break;                  /* no special action */
 
1168
      }
 
1169
      str = ++st;               /* continue parse */
 
1170
      break;
 
1171
    case '\\':                  /* quoted character */
 
1172
      /* This is wrong; a quoted-pair can not be part of a word.  However,
 
1173
       * domain-literal is parsed as a word and quoted-pairs can be used
 
1174
       * *there*.  Either way, it's pretty pathological.
 
1175
       */
 
1176
      if (st[1]) {              /* not on NUL though... */
 
1177
        str = st + 2;           /* skip quoted character and go on */
 
1178
        break;
 
1179
      }
 
1180
    default:                    /* found a word delimiter */
 
1181
      return (st == s) ? NIL : st;
 
1182
    }
 
1183
  }
 
1184
}
 
1185
 
 
1186
/* Copy an RFC 2822 format string
 
1187
 * Accepts: string
 
1188
 * Returns: copy of string
 
1189
 */
 
1190
 
 
1191
char *rfc822_cpy (char *src)
 
1192
{
 
1193
                                /* copy and unquote */
 
1194
  return rfc822_quote (cpystr (src));
 
1195
}
 
1196
 
 
1197
 
 
1198
/* Unquote an RFC 2822 format string
 
1199
 * Accepts: string
 
1200
 * Returns: string
 
1201
 */
 
1202
 
 
1203
char *rfc822_quote (char *src)
 
1204
{
 
1205
  char *ret = src;
 
1206
  if (strpbrk (src,"\\\"")) {   /* any quoting in string? */
 
1207
    char *dst = ret;
 
1208
    while (*src) {              /* copy string */
 
1209
      if (*src == '\"') src++;  /* skip double quote entirely */
 
1210
      else {
 
1211
        if (*src == '\\') src++;/* skip over single quote, copy next always */
 
1212
        *dst++ = *src++;        /* copy character */
 
1213
      }
 
1214
    }
 
1215
    *dst = '\0';                /* tie off string */
 
1216
  }
 
1217
  return ret;                   /* return our string */
 
1218
}
 
1219
 
 
1220
 
 
1221
/* Copy address list
 
1222
 * Accepts: address list
 
1223
 * Returns: address list
 
1224
 */
 
1225
 
 
1226
ADDRESS *rfc822_cpy_adr (ADDRESS *adr)
 
1227
{
 
1228
  ADDRESS *dadr;
 
1229
  ADDRESS *ret = NIL;
 
1230
  ADDRESS *prev = NIL;
 
1231
  while (adr) {                 /* loop while there's still an MAP adr */
 
1232
    dadr = mail_newaddr ();     /* instantiate a new address */
 
1233
    if (!ret) ret = dadr;       /* note return */
 
1234
    if (prev) prev->next = dadr;/* tie on to the end of any previous */
 
1235
    dadr->personal = cpystr (adr->personal);
 
1236
    dadr->adl = cpystr (adr->adl);
 
1237
    dadr->mailbox = cpystr (adr->mailbox);
 
1238
    dadr->host = cpystr (adr->host);
 
1239
    prev = dadr;                /* this is now the previous */
 
1240
    adr = adr->next;            /* go to next address in list */
 
1241
  }
 
1242
  return (ret);                 /* return the MTP address list */
 
1243
}
 
1244
 
 
1245
/* Skips RFC 2822 whitespace
 
1246
 * Accepts: pointer to string pointer
 
1247
 */
 
1248
 
 
1249
void rfc822_skipws (char **s)
 
1250
{
 
1251
  while (T) switch (**s) {
 
1252
  case ' ': case '\t': case '\015': case '\012':
 
1253
    ++*s;                       /* skip all forms of LWSP */
 
1254
    break;
 
1255
  case '(':                     /* start of comment */
 
1256
    if (rfc822_skip_comment (s,(long) NIL)) break;
 
1257
  default:
 
1258
    return;                     /* end of whitespace */
 
1259
  }
 
1260
}
 
1261
 
 
1262
 
 
1263
/* Skips RFC 2822 comment
 
1264
 * Accepts: pointer to string pointer
 
1265
 *          trim flag
 
1266
 * Returns: pointer to first non-blank character of comment
 
1267
 */
 
1268
 
 
1269
char *rfc822_skip_comment (char **s,long trim)
 
1270
{
 
1271
  char *ret,tmp[MAILTMPLEN];
 
1272
  char *s1 = *s;
 
1273
  char *t = NIL;
 
1274
                                /* skip past whitespace */
 
1275
  for (ret = ++s1; *ret == ' '; ret++);
 
1276
  do switch (*s1) {             /* get character of comment */
 
1277
  case '(':                     /* nested comment? */
 
1278
    if (!rfc822_skip_comment (&s1,(long) NIL)) return NIL;
 
1279
    t = --s1;                   /* last significant char at end of comment */
 
1280
    break;
 
1281
  case ')':                     /* end of comment? */
 
1282
    *s = ++s1;                  /* skip past end of comment */
 
1283
    if (trim) {                 /* if level 0, must trim */
 
1284
      if (t) t[1] = '\0';       /* tie off comment string */
 
1285
      else *ret = '\0';         /* empty comment */
 
1286
    }
 
1287
    return ret;
 
1288
  case '\\':                    /* quote next character? */
 
1289
    if (*++s1) {                /* next character non-null? */
 
1290
      t = s1;                   /* update last significant character pointer */
 
1291
      break;                    /* all OK */
 
1292
    }
 
1293
  case '\0':                    /* end of string */
 
1294
    sprintf (tmp,"Unterminated comment: %.80s",*s);
 
1295
    MM_LOG (tmp,PARSE);
 
1296
    **s = '\0';                 /* nuke duplicate messages in case reparse */
 
1297
    return NIL;                 /* this is wierd if it happens */
 
1298
  case ' ':                     /* whitespace isn't significant */
 
1299
    break;
 
1300
  default:                      /* random character */
 
1301
    t = s1;                     /* update last significant character pointer */
 
1302
    break;
 
1303
  } while (s1++);
 
1304
  return NIL;                   /* impossible, but pacify lint et al */
 
1305
}
 
1306
 
 
1307
/* Buffered output routines */
 
1308
 
 
1309
 
 
1310
/* Output character to buffer
 
1311
 * Accepts: buffer
 
1312
 *          character to write
 
1313
 * Returns: T if success, NIL if error
 
1314
 */
 
1315
 
 
1316
static long rfc822_output_char (RFC822BUFFER *buf,int c)
 
1317
{
 
1318
  *buf->cur++ = c;              /* add character, soutr buffer if full */
 
1319
  return (buf->cur == buf->end) ? rfc822_output_flush (buf) : LONGT;
 
1320
}
 
1321
 
 
1322
 
 
1323
/* Output data to buffer
 
1324
 * Accepts: buffer
 
1325
 *          data to write
 
1326
 *          size of data
 
1327
 * Returns: T if success, NIL if error
 
1328
 */
 
1329
 
 
1330
static long rfc822_output_data (RFC822BUFFER *buf,char *string,long len)
 
1331
{
 
1332
  while (len) {                 /* until request satified */
 
1333
    long i;
 
1334
    if (i = min (len,buf->end - buf->cur)) {
 
1335
      memcpy (buf->cur,string,i);
 
1336
      buf->cur += i;            /* blat data */
 
1337
      string += i;
 
1338
      len -= i;
 
1339
    }
 
1340
                                /* soutr buffer now if full */
 
1341
    if (len && !rfc822_output_flush (buf)) return NIL;
 
1342
  }
 
1343
  return LONGT;
 
1344
}
 
1345
 
 
1346
/* Output string to buffer
 
1347
 * Accepts: buffer
 
1348
 *          string to write
 
1349
 * Returns: T if success, NIL if error
 
1350
 */
 
1351
 
 
1352
static long rfc822_output_string (RFC822BUFFER *buf,char *string)
 
1353
{
 
1354
  return rfc822_output_data (buf,string,strlen (string));
 
1355
}
 
1356
 
 
1357
 
 
1358
/* Flush buffer
 
1359
 * Accepts: buffer
 
1360
 *          I/O routine
 
1361
 *          stream for I/O routine
 
1362
 * Returns: T if success, NIL if error
 
1363
 */
 
1364
 
 
1365
long rfc822_output_flush (RFC822BUFFER *buf)
 
1366
{
 
1367
  *buf->cur = '\0';             /* tie off buffer at this point */
 
1368
  return (*buf->f) (buf->s,buf->cur = buf->beg);
 
1369
}
 
1370
 
 
1371
/* Message writing routines */
 
1372
 
 
1373
 
 
1374
/* Output RFC 822 message
 
1375
 * Accepts: temporary buffer as a SIZEDTEXT
 
1376
 *          envelope
 
1377
 *          body
 
1378
 *          I/O routine
 
1379
 *          stream for I/O routine
 
1380
 *          non-zero if 8-bit output desired
 
1381
 * Returns: T if successful, NIL if failure
 
1382
 *
 
1383
 * This routine always uses standard specials for phrases and does not write
 
1384
 * bcc entries, since it is called from the SMTP and NNTP routines.  If you
 
1385
 * need to do something different you need to arm an rfc822outfull_t and/or
 
1386
 * rfc822out_t function.
 
1387
 */
 
1388
 
 
1389
long rfc822_output_full (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,long ok8)
 
1390
{
 
1391
  rfc822outfull_t r822of =
 
1392
    (rfc822outfull_t) mail_parameters (NIL,GET_RFC822OUTPUTFULL,NIL);
 
1393
  rfc822out_t r822o = (rfc822out_t) mail_parameters (NIL,GET_RFC822OUTPUT,NIL);
 
1394
                                /* call external RFC 2822 output generator */
 
1395
  if (r822of) return (*r822of) (buf,env,body,ok8);
 
1396
  else if (r822o) return (*r822o) (buf->cur,env,body,buf->f,buf->s,ok8);
 
1397
                                /* encode body as necessary */
 
1398
  if (ok8) rfc822_encode_body_8bit (env,body);
 
1399
  else rfc822_encode_body_7bit (env,body);
 
1400
                                /* output header and body */
 
1401
  return rfc822_output_header (buf,env,body,NIL,NIL) &&
 
1402
    rfc822_output_text (buf,body) && rfc822_output_flush (buf);
 
1403
}
 
1404
 
 
1405
/* Output RFC 822 header
 
1406
 * Accepts: buffer
 
1407
 *          envelope
 
1408
 *          body
 
1409
 *          non-standard specials to be used for phrases if non-NIL
 
1410
 *          flags (non-zero to include bcc
 
1411
 * Returns: T if success, NIL if failure
 
1412
 */
 
1413
 
 
1414
long rfc822_output_header (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,
 
1415
                           const char *specials,long flags)
 
1416
{
 
1417
  long i = env->remail ? strlen (env->remail) : 0;
 
1418
  return                        /* write header */
 
1419
    (!i ||                    /* snip extra CRLF from remail header */
 
1420
     rfc822_output_data (buf,env->remail,
 
1421
                         ((i > 4) && (env->remail[i-4] == '\015')) ?
 
1422
                         i - 2 : i)) &&
 
1423
    rfc822_output_header_line (buf,"Newsgroups",i,env->newsgroups) &&
 
1424
    rfc822_output_header_line (buf,"Date",i,env->date) &&
 
1425
    rfc822_output_address_line (buf,"From",i,env->from,specials) &&
 
1426
    rfc822_output_address_line (buf,"Sender",i,env->sender,specials) &&
 
1427
    rfc822_output_address_line (buf,"Reply-To",i,env->reply_to,specials) &&
 
1428
    rfc822_output_header_line (buf,"Subject",i,env->subject) &&
 
1429
    ((env->bcc && !(env->to || env->cc)) ?
 
1430
     rfc822_output_string (buf,"To: undisclosed recipients: ;\015\012") :
 
1431
     LONGT) &&
 
1432
    rfc822_output_address_line (buf,"To",i,env->to,specials) &&
 
1433
    rfc822_output_address_line (buf,"cc",i,env->cc,specials) &&
 
1434
    (flags ? rfc822_output_address_line (buf,"bcc",i,env->bcc,specials) : T) &&
 
1435
    rfc822_output_header_line (buf,"In-Reply-To",i,env->in_reply_to) &&
 
1436
    rfc822_output_header_line (buf,"Message-ID",i,env->message_id) &&
 
1437
    rfc822_output_header_line (buf,"Followup-to",i,env->followup_to) &&
 
1438
    rfc822_output_header_line (buf,"References",i,env->references) &&
 
1439
    (env->remail || !body ||
 
1440
     (rfc822_output_string (buf,"MIME-Version: 1.0\015\012") &&
 
1441
      rfc822_output_body_header (buf,body))) &&
 
1442
                                /* write terminating blank line */
 
1443
    rfc822_output_string (buf,"\015\012");
 
1444
}
 
1445
 
 
1446
/* Output RFC 2822 header text line
 
1447
 * Accepts: buffer
 
1448
 *          pointer to header type
 
1449
 *          non-NIL if resending
 
1450
 *          pointer to text
 
1451
 * Returns: T if success, NIL if failure
 
1452
 */
 
1453
 
 
1454
long rfc822_output_header_line (RFC822BUFFER *buf,char *type,long resent,
 
1455
                                char *text)
 
1456
{
 
1457
  return !text ||
 
1458
    ((resent ? rfc822_output_string (buf,resentprefix) : LONGT) &&
 
1459
     rfc822_output_string (buf,type) && rfc822_output_string (buf,": ") &&
 
1460
     rfc822_output_string (buf,text) && rfc822_output_string (buf,"\015\012"));
 
1461
}
 
1462
 
 
1463
 
 
1464
/* Output RFC 2822 header address line
 
1465
 * Accepts: buffer
 
1466
 *          pointer to header type
 
1467
 *          non-NIL if resending
 
1468
 *          address(s) to interpret
 
1469
 *          non-standard specials to be used for phrases if non-NIL
 
1470
 * Returns: T if success, NIL if failure
 
1471
 */
 
1472
 
 
1473
long rfc822_output_address_line (RFC822BUFFER *buf,char *type,long resent,
 
1474
                                 ADDRESS *adr,const char *specials)
 
1475
{
 
1476
  long pretty = strlen (type);
 
1477
  return !adr ||
 
1478
    ((resent ? rfc822_output_string (buf,resentprefix) : LONGT) &&
 
1479
     rfc822_output_data (buf,type,pretty) && rfc822_output_string (buf,": ") &&
 
1480
     rfc822_output_address_list (buf,adr,
 
1481
                                 resent ? pretty + sizeof (RESENTPREFIX) - 1 :
 
1482
                                 pretty,specials) &&
 
1483
     rfc822_output_string (buf,"\015\012"));
 
1484
}
 
1485
 
 
1486
/* Output RFC 2822 address list
 
1487
 * Accepts: buffer
 
1488
 *          pointer to address list
 
1489
 *          non-zero if pretty-printing
 
1490
 *          non-standard specials to be used for phrases if non-NIL
 
1491
 * Returns: T if success, NIL if failure
 
1492
 */
 
1493
 
 
1494
long rfc822_output_address_list (RFC822BUFFER *buf,ADDRESS *adr,long pretty,
 
1495
                                 const char *specials)
 
1496
{
 
1497
  long n;
 
1498
                                /* default to rspecials */
 
1499
  if (!specials) specials = rspecials;
 
1500
  for (n = 0; adr; adr = adr->next) {
 
1501
    char *base = buf->cur;
 
1502
    if (adr->host) {            /* ordinary address? */
 
1503
      if (!(pretty && n)) {     /* suppress if pretty and in group */
 
1504
        if (                    /* use phrase <route-addr> if phrase */
 
1505
#if RFC822
 
1506
            adr->adl ||         /* or A-D-L */
 
1507
#endif
 
1508
            (adr->personal && *adr->personal)) {
 
1509
          if (!((adr->personal ? rfc822_output_cat (buf,adr->personal,
 
1510
                                                    rspecials) : LONGT) &&
 
1511
                rfc822_output_string (buf," <") &&
 
1512
                rfc822_output_address (buf,adr) &&
 
1513
                rfc822_output_string (buf,">"))) return NIL;
 
1514
        }
 
1515
        else if (!rfc822_output_address (buf,adr)) return NIL;
 
1516
        if (adr->next && adr->next->mailbox &&
 
1517
            !rfc822_output_string (buf,", ")) return NIL;
 
1518
      }
 
1519
    }
 
1520
    else if (adr->mailbox) {    /* start of group? */
 
1521
                                /* yes, write group */
 
1522
      if (!(rfc822_output_cat (buf,adr->mailbox,rspecials) &&
 
1523
            rfc822_output_string (buf,": "))) return NIL;
 
1524
      ++n;                      /* in a group now */
 
1525
    }
 
1526
    else if (n) {               /* must be end of group (but be paranoid) */
 
1527
      if (!rfc822_output_char (buf,';') ||
 
1528
          ((!--n && adr->next && adr->next->mailbox) &&
 
1529
           !rfc822_output_string (buf,", "))) return NIL;
 
1530
    }
 
1531
    if (pretty &&               /* pretty printing? */
 
1532
        ((pretty += ((buf->cur > base) ? buf->cur - base :
 
1533
                     (buf->end - base) + (buf->cur - buf->beg))) >= 78)) {
 
1534
      if (!(rfc822_output_string (buf,"\015\012") &&
 
1535
            rfc822_output_string (buf,RFC822CONT))) return NIL;
 
1536
      base = buf->cur;  /* update base for pretty printing */
 
1537
      pretty = sizeof (RFC822CONT) - 1;
 
1538
    }
 
1539
  }
 
1540
  return LONGT;
 
1541
}
 
1542
 
 
1543
/* Write RFC 2822 route-address to string
 
1544
 * Accepts: buffer
 
1545
 *          pointer to single address
 
1546
 * Returns: T if success, NIL if failure
 
1547
 */
 
1548
 
 
1549
long rfc822_output_address (RFC822BUFFER *buf,ADDRESS *adr)
 
1550
{
 
1551
  return !adr || !adr->host ||
 
1552
    (
 
1553
#if RFC822                      /* old code with A-D-L support */
 
1554
     (!adr->adl || (rfc822_output_string (buf,adr->adl) &&
 
1555
                    rfc822_output_char (buf,':'))) &&
 
1556
#endif
 
1557
     rfc822_output_cat (buf,adr->mailbox,NIL) &&
 
1558
     ((*adr->host == '@') ||    /* unless null host (HIGHLY discouraged!) */
 
1559
      (rfc822_output_char (buf,'@') &&
 
1560
       rfc822_output_cat (buf,adr->host,NIL))));
 
1561
}
 
1562
 
 
1563
 
 
1564
/* Output RFC 2822 string with concatenation
 
1565
 * Accepts: buffer
 
1566
 *          string to concatenate
 
1567
 *          list of special characters or NIL for dot-atom format
 
1568
 * Returns: T if success, NIL if failure
 
1569
 */
 
1570
 
 
1571
long rfc822_output_cat (RFC822BUFFER *buf,char *src,const char *specials)
 
1572
{
 
1573
  char *s;
 
1574
  if (!*src ||                  /* empty string or any specials present? */
 
1575
      (specials ? (T && strpbrk (src,specials)) :
 
1576
       (strpbrk (src,wspecials) || (*src == '.') || strstr (src,"..") ||
 
1577
        (src[strlen (src) - 1] == '.')))) {
 
1578
                                /* yes, write as quoted string*/
 
1579
    if (!rfc822_output_char (buf,'"')) return NIL;
 
1580
                                /* embedded quote characters? */
 
1581
    for (; s = strpbrk (src,"\\\""); src = s + 1) {
 
1582
                                /* yes, insert quoting */
 
1583
      if (!(rfc822_output_data (buf,src,s-src) &&
 
1584
            rfc822_output_char (buf,'\\') &&
 
1585
            rfc822_output_char (buf,*s))) return NIL;
 
1586
    }
 
1587
                                /* return string and trailing quote*/
 
1588
    return rfc822_output_string (buf,src) && rfc822_output_char (buf,'"');
 
1589
  }
 
1590
                                /* easy case */
 
1591
  return rfc822_output_string (buf,src);
 
1592
}
 
1593
 
 
1594
/* Output MIME parameter list
 
1595
 * Accepts: buffer
 
1596
 *          parameter list
 
1597
 * Returns: T if success, NIL if failure
 
1598
 */
 
1599
 
 
1600
long rfc822_output_parameter (RFC822BUFFER *buf,PARAMETER *param)
 
1601
{
 
1602
  while (param) {
 
1603
    if (rfc822_output_string (buf,"; ") &&
 
1604
        rfc822_output_string (buf,param->attribute) &&
 
1605
        rfc822_output_char (buf,'=') &&
 
1606
        rfc822_output_cat (buf,param->value,tspecials)) param = param->next;
 
1607
    else return NIL;
 
1608
  }
 
1609
  return LONGT;
 
1610
}
 
1611
 
 
1612
 
 
1613
/* Output RFC 2822 stringlist
 
1614
 * Accepts: buffer
 
1615
 *          stringlist
 
1616
 * Returns: T if success, NIL if failure
 
1617
 */
 
1618
 
 
1619
long rfc822_output_stringlist (RFC822BUFFER *buf,STRINGLIST *stl)
 
1620
{
 
1621
  while (stl)
 
1622
    if (!rfc822_output_cat (buf,(char *) stl->text.data,tspecials) ||
 
1623
        ((stl = stl->next) && !rfc822_output_string (buf,", ")))
 
1624
      return NIL;
 
1625
  return LONGT;
 
1626
}
 
1627
 
 
1628
/* Output body content header
 
1629
 * Accepts: buffer
 
1630
 *          body to interpret
 
1631
 * Returns: T if success, NIL if failure
 
1632
 */
 
1633
 
 
1634
long rfc822_output_body_header (RFC822BUFFER *buf,BODY *body)
 
1635
{
 
1636
  return                        /* type and subtype*/
 
1637
    rfc822_output_string (buf,"Content-Type: ") &&
 
1638
    rfc822_output_string (buf,body_types[body->type]) &&
 
1639
    rfc822_output_char (buf,'/') &&
 
1640
    rfc822_output_string (buf,body->subtype ? body->subtype :
 
1641
                          rfc822_default_subtype (body->type)) &&
 
1642
                                /* parameters (w/ US-ASCII default */
 
1643
    (body->parameter ? rfc822_output_parameter (buf,body->parameter) :
 
1644
     ((body->type != TYPETEXT) ||
 
1645
      (rfc822_output_string (buf,"; CHARSET=") &&
 
1646
       rfc822_output_string (buf,(body->encoding == ENC7BIT) ?
 
1647
                             "US-ASCII" : "X-UNKNOWN")))) &&
 
1648
    (!body->encoding ||     /* note: 7BIT never output as encoding! */
 
1649
     (rfc822_output_string (buf,"\015\012Content-Transfer-Encoding: ") &&
 
1650
      rfc822_output_string (buf,body_encodings[body->encoding]))) &&
 
1651
    (!body->id ||               /* identification */
 
1652
     (rfc822_output_string (buf,"\015\012Content-ID: ") &&
 
1653
      rfc822_output_string (buf,body->id))) &&
 
1654
    (!body->description ||      /* description */
 
1655
     (rfc822_output_string (buf,"\015\012Content-Description: ") &&
 
1656
      rfc822_output_string (buf,body->description))) &&
 
1657
    (!body->md5 ||              /* MD5 checksum */
 
1658
     (rfc822_output_string (buf,"\015\012Content-MD5: ") &&
 
1659
      rfc822_output_string (buf,body->md5))) &&
 
1660
    (!body->language ||         /* language */
 
1661
     (rfc822_output_string (buf,"\015\012Content-Language: ") &&
 
1662
      rfc822_output_stringlist (buf,body->language))) &&
 
1663
    (!body->location ||         /* location */
 
1664
     (rfc822_output_string (buf,"\015\012Content-Location: ") &&
 
1665
      rfc822_output_string (buf,body->location))) &&
 
1666
    (!body->disposition.type || /* disposition */
 
1667
     (rfc822_output_string (buf,"\015\012Content-Disposition: ") &&
 
1668
      rfc822_output_string (buf,body->disposition.type) &&
 
1669
      rfc822_output_parameter (buf,body->disposition.parameter))) &&
 
1670
    rfc822_output_string (buf,"\015\012");
 
1671
}
 
1672
 
 
1673
/* Encode a body for 7BIT transmittal
 
1674
 * Accepts: envelope
 
1675
 *          body
 
1676
 */
 
1677
 
 
1678
void rfc822_encode_body_7bit (ENVELOPE *env,BODY *body)
 
1679
{
 
1680
  void *f;
 
1681
  PART *part;
 
1682
  PARAMETER **param;
 
1683
  if (body) switch (body->type) {
 
1684
  case TYPEMULTIPART:           /* multi-part */
 
1685
    for (param = &body->parameter;
 
1686
         *param && strcmp ((*param)->attribute,"BOUNDARY");
 
1687
         param = &(*param)->next);
 
1688
    if (!*param) {              /* cookie not set up yet? */
 
1689
      char tmp[MAILTMPLEN];     /* make cookie not in BASE64 or QUOTEPRINT*/
 
1690
      sprintf (tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
 
1691
               (unsigned long) random (),(unsigned long) time (0),
 
1692
               (unsigned long) getpid ());
 
1693
      (*param) = mail_newbody_parameter ();
 
1694
      (*param)->attribute = cpystr ("BOUNDARY");
 
1695
      (*param)->value = cpystr (tmp);
 
1696
    }
 
1697
    part = body->nested.part;   /* encode body parts */
 
1698
    do rfc822_encode_body_7bit (env,&part->body);
 
1699
    while (part = part->next);  /* until done */
 
1700
    break;
 
1701
  case TYPEMESSAGE:             /* encapsulated message */
 
1702
    switch (body->encoding) {
 
1703
    case ENC7BIT:
 
1704
      break;
 
1705
    case ENC8BIT:
 
1706
      MM_LOG ("8-bit included message in 7-bit message body",PARSE);
 
1707
      break;
 
1708
    case ENCBINARY:
 
1709
      MM_LOG ("Binary included message in 7-bit message body",PARSE);
 
1710
      break;
 
1711
    default:
 
1712
      fatal ("Invalid rfc822_encode_body_7bit message encoding");
 
1713
    }
 
1714
    break;                      /* can't change encoding */
 
1715
  default:                      /* all else has some encoding */
 
1716
    switch (body->encoding) {
 
1717
    case ENC8BIT:               /* encode 8BIT into QUOTED-PRINTABLE */
 
1718
                                /* remember old 8-bit contents */
 
1719
      f = (void *) body->contents.text.data;
 
1720
      body->contents.text.data =
 
1721
        rfc822_8bit (body->contents.text.data,
 
1722
                     body->contents.text.size,&body->contents.text.size);
 
1723
      body->encoding = ENCQUOTEDPRINTABLE;
 
1724
      fs_give (&f);             /* flush old binary contents */
 
1725
      break;
 
1726
    case ENCBINARY:             /* encode binary into BASE64 */
 
1727
                                /* remember old binary contents */
 
1728
      f = (void *) body->contents.text.data;
 
1729
      body->contents.text.data =
 
1730
        rfc822_binary ((void *) body->contents.text.data,
 
1731
                       body->contents.text.size,&body->contents.text.size);
 
1732
      body->encoding = ENCBASE64;
 
1733
      fs_give (&f);             /* flush old binary contents */
 
1734
    default:                    /* otherwise OK */
 
1735
      break;
 
1736
    }
 
1737
    break;
 
1738
  }
 
1739
}
 
1740
 
 
1741
/* Encode a body for 8BIT transmittal
 
1742
 * Accepts: envelope
 
1743
 *          body
 
1744
 */
 
1745
 
 
1746
void rfc822_encode_body_8bit (ENVELOPE *env,BODY *body)
 
1747
{
 
1748
  void *f;
 
1749
  PART *part;
 
1750
  PARAMETER **param;
 
1751
  if (body) switch (body->type) {
 
1752
  case TYPEMULTIPART:           /* multi-part */
 
1753
    for (param = &body->parameter;
 
1754
         *param && strcmp ((*param)->attribute,"BOUNDARY");
 
1755
         param = &(*param)->next);
 
1756
    if (!*param) {              /* cookie not set up yet? */
 
1757
      char tmp[MAILTMPLEN];     /* make cookie not in BASE64 or QUOTEPRINT*/
 
1758
      sprintf (tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
 
1759
               (unsigned long) random (),(unsigned long) time (0),
 
1760
               (unsigned long) getpid ());
 
1761
      (*param) = mail_newbody_parameter ();
 
1762
      (*param)->attribute = cpystr ("BOUNDARY");
 
1763
      (*param)->value = cpystr (tmp);
 
1764
    }
 
1765
    part = body->nested.part;   /* encode body parts */
 
1766
    do rfc822_encode_body_8bit (env,&part->body);
 
1767
    while (part = part->next);  /* until done */
 
1768
    break;
 
1769
  case TYPEMESSAGE:             /* encapsulated message */
 
1770
    switch (body->encoding) {
 
1771
    case ENC7BIT:
 
1772
    case ENC8BIT:
 
1773
      break;
 
1774
    case ENCBINARY:
 
1775
      MM_LOG ("Binary included message in 8-bit message body",PARSE);
 
1776
      break;
 
1777
    default:
 
1778
      fatal ("Invalid rfc822_encode_body_7bit message encoding");
 
1779
    }
 
1780
    break;                      /* can't change encoding */
 
1781
  default:                      /* other type, encode binary into BASE64 */
 
1782
    if (body->encoding == ENCBINARY) {
 
1783
                                /* remember old binary contents */
 
1784
      f = (void *) body->contents.text.data;
 
1785
      body->contents.text.data =
 
1786
        rfc822_binary ((void *) body->contents.text.data,
 
1787
                       body->contents.text.size,&body->contents.text.size);
 
1788
      body->encoding = ENCBASE64;
 
1789
      fs_give (&f);             /* flush old binary contents */
 
1790
    }
 
1791
    break;
 
1792
  }
 
1793
}
 
1794
 
 
1795
/* Output RFC 822 text
 
1796
 * Accepts: buffer
 
1797
 *          body
 
1798
 * Returns: T if successful, NIL if failure
 
1799
 */
 
1800
 
 
1801
long rfc822_output_text (RFC822BUFFER *buf,BODY *body)
 
1802
{
 
1803
                                /* MULTIPART gets special handling */
 
1804
  if (body->type == TYPEMULTIPART) {
 
1805
    char *cookie,tmp[MAILTMPLEN];
 
1806
    PARAMETER *param;
 
1807
    PART *part;
 
1808
                                /* find cookie */
 
1809
    for (param = body->parameter; param && strcmp (param->attribute,"BOUNDARY");
 
1810
         param = param->next);
 
1811
    if (param) cookie = param->value;
 
1812
    else {                /* make cookie not in BASE64 or QUOTEPRINT*/
 
1813
      sprintf (cookie = tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
 
1814
               (unsigned long) random (),(unsigned long) time (0),
 
1815
               (unsigned long) getpid ());
 
1816
      (param = mail_newbody_parameter ())->attribute = cpystr ("BOUNDARY");
 
1817
      param->value = cpystr (tmp);
 
1818
      param->next = body->parameter;
 
1819
      body->parameter = param;
 
1820
    }
 
1821
                                /* output each part */
 
1822
    for (part = body->nested.part; part; part = part->next)
 
1823
      if (!(rfc822_output_string (buf,"--") &&
 
1824
            rfc822_output_string (buf,cookie) &&
 
1825
            rfc822_output_string (buf,"\015\012") &&
 
1826
            rfc822_output_body_header (buf,&part->body) &&
 
1827
            rfc822_output_string (buf,"\015\012") &&
 
1828
            rfc822_output_text (buf,&part->body))) return NIL;
 
1829
                                /* output trailing cookie */
 
1830
    return rfc822_output_string (buf,"--") &&
 
1831
      rfc822_output_string (buf,cookie) &&
 
1832
      rfc822_output_string (buf,"--\015\012");
 
1833
  }
 
1834
                                /* output segment and trailing CRLF */
 
1835
  return (!body->contents.text.data ||
 
1836
          rfc822_output_string (buf,(char *) body->contents.text.data)) &&
 
1837
    rfc822_output_string (buf,"\015\012");
 
1838
}
 
1839
 
 
1840
/* Body contents encoding/decoding routines */
 
1841
 
 
1842
 
 
1843
/* Convert BASE64 contents to binary
 
1844
 * Accepts: source
 
1845
 *          length of source
 
1846
 *          pointer to return destination length
 
1847
 * Returns: destination as binary or NIL if error
 
1848
 */
 
1849
 
 
1850
#define WSP 0176                /* NUL, TAB, LF, FF, CR, SPC */
 
1851
#define JNK 0177
 
1852
#define PAD 0100
 
1853
 
 
1854
void *rfc822_base64 (unsigned char *src,unsigned long srcl,unsigned long *len)
 
1855
{
 
1856
  char c,*s,tmp[MAILTMPLEN];
 
1857
  void *ret = fs_get ((size_t) ((*len = 4 + ((srcl * 3) / 4))) + 1);
 
1858
  char *d = (char *) ret;
 
1859
  int e;
 
1860
  static char decode[256] = {
 
1861
   WSP,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,WSP,WSP,JNK,WSP,WSP,JNK,JNK,
 
1862
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
 
1863
   WSP,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,076,JNK,JNK,JNK,077,
 
1864
   064,065,066,067,070,071,072,073,074,075,JNK,JNK,JNK,PAD,JNK,JNK,
 
1865
   JNK,000,001,002,003,004,005,006,007,010,011,012,013,014,015,016,
 
1866
   017,020,021,022,023,024,025,026,027,030,031,JNK,JNK,JNK,JNK,JNK,
 
1867
   JNK,032,033,034,035,036,037,040,041,042,043,044,045,046,047,050,
 
1868
   051,052,053,054,055,056,057,060,061,062,063,JNK,JNK,JNK,JNK,JNK,
 
1869
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
 
1870
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
 
1871
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
 
1872
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
 
1873
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
 
1874
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
 
1875
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
 
1876
   JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK
 
1877
  };
 
1878
                                /* initialize block */
 
1879
  memset (ret,0,((size_t) *len) + 1);
 
1880
  *len = 0;                     /* in case we return an error */
 
1881
 
 
1882
                                /* simple-minded decode */
 
1883
  for (e = 0; srcl--; ) switch (c = decode[*src++]) {
 
1884
  default:                      /* valid BASE64 data character */
 
1885
    switch (e++) {              /* install based on quantum position */
 
1886
    case 0:
 
1887
      *d = c << 2;              /* byte 1: high 6 bits */
 
1888
      break;
 
1889
    case 1:
 
1890
      *d++ |= c >> 4;           /* byte 1: low 2 bits */
 
1891
      *d = c << 4;              /* byte 2: high 4 bits */
 
1892
      break;
 
1893
    case 2:
 
1894
      *d++ |= c >> 2;           /* byte 2: low 4 bits */
 
1895
      *d = c << 6;              /* byte 3: high 2 bits */
 
1896
      break;
 
1897
    case 3:
 
1898
      *d++ |= c;                /* byte 3: low 6 bits */
 
1899
      e = 0;                    /* reinitialize mechanism */
 
1900
      break;
 
1901
    }
 
1902
    break;
 
1903
  case WSP:                     /* whitespace */
 
1904
    break;
 
1905
  case PAD:                     /* padding */
 
1906
    switch (e++) {              /* check quantum position */
 
1907
    case 3:                     /* one = is good enough in quantum 3 */
 
1908
                                /* make sure no data characters in remainder */
 
1909
      for (; srcl; --srcl) switch (decode[*src++]) {
 
1910
                                /* ignore space, junk and extraneous padding */
 
1911
      case WSP: case JNK: case PAD:
 
1912
        break;
 
1913
      default:                  /* valid BASE64 data character */
 
1914
        /* This indicates bad MIME.  One way that it can be caused is if
 
1915
           a single-section message was BASE64 encoded and then something
 
1916
           (e.g. a mailing list processor) appended text.  The problem is
 
1917
           that in 1 out of 3 cases, there is no padding and hence no way
 
1918
           to detect the end of the data.  Consequently, prudent software
 
1919
           will always encapsulate a BASE64 segment inside a MULTIPART.
 
1920
           */
 
1921
        sprintf (tmp,"Possible data truncation in rfc822_base64(): %.80s",
 
1922
                 (char *) src - 1);
 
1923
        if (s = strpbrk (tmp,"\015\012")) *s = NIL;
 
1924
        mm_log (tmp,PARSE);
 
1925
        srcl = 1;               /* don't issue any more messages */
 
1926
        break;
 
1927
      }
 
1928
      break;
 
1929
    case 2:                     /* expect a second = in quantum 2 */
 
1930
      if (srcl && (*src == '=')) break;
 
1931
    default:                    /* impossible quantum position */
 
1932
      fs_give (&ret);
 
1933
      return NIL;
 
1934
    }
 
1935
    break;
 
1936
  case JNK:                     /* junk character */
 
1937
    fs_give (&ret);
 
1938
    return NIL;
 
1939
  }
 
1940
  *len = d - (char *) ret;      /* calculate data length */
 
1941
  *d = '\0';                    /* NUL terminate just in case */
 
1942
  return ret;                   /* return the string */
 
1943
}
 
1944
 
 
1945
/* Convert binary contents to BASE64
 
1946
 * Accepts: source
 
1947
 *          length of source
 
1948
 *          pointer to return destination length
 
1949
 * Returns: destination as BASE64
 
1950
 */
 
1951
 
 
1952
unsigned char *rfc822_binary (void *src,unsigned long srcl,unsigned long *len)
 
1953
{
 
1954
  unsigned char *ret,*d;
 
1955
  unsigned char *s = (unsigned char *) src;
 
1956
  char *v = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
1957
  unsigned long i = ((srcl + 2) / 3) * 4;
 
1958
  *len = i += 2 * ((i / 60) + 1);
 
1959
  d = ret = (unsigned char *) fs_get ((size_t) ++i);
 
1960
                                /* process tuplets */
 
1961
  for (i = 0; srcl >= 3; s += 3, srcl -= 3) {
 
1962
    *d++ = v[s[0] >> 2];        /* byte 1: high 6 bits (1) */
 
1963
                                /* byte 2: low 2 bits (1), high 4 bits (2) */
 
1964
    *d++ = v[((s[0] << 4) + (s[1] >> 4)) & 0x3f];
 
1965
                                /* byte 3: low 4 bits (2), high 2 bits (3) */
 
1966
    *d++ = v[((s[1] << 2) + (s[2] >> 6)) & 0x3f];
 
1967
    *d++ = v[s[2] & 0x3f];      /* byte 4: low 6 bits (3) */
 
1968
    if ((++i) == 15) {          /* output 60 characters? */
 
1969
      i = 0;                    /* restart line break count, insert CRLF */
 
1970
      *d++ = '\015'; *d++ = '\012';
 
1971
    }
 
1972
  }
 
1973
  if (srcl) {
 
1974
    *d++ = v[s[0] >> 2];        /* byte 1: high 6 bits (1) */
 
1975
                                /* byte 2: low 2 bits (1), high 4 bits (2) */
 
1976
    *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
 
1977
                                /* byte 3: low 4 bits (2), high 2 bits (3) */
 
1978
    *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) : 0)) & 0x3f] : '=';
 
1979
                                /* byte 4: low 6 bits (3) */
 
1980
    *d++ = srcl ? v[s[2] & 0x3f] : '=';
 
1981
    if (srcl) srcl--;           /* count third character if processed */
 
1982
    if ((++i) == 15) {          /* output 60 characters? */
 
1983
      i = 0;                    /* restart line break count, insert CRLF */
 
1984
      *d++ = '\015'; *d++ = '\012';
 
1985
    }
 
1986
  }
 
1987
  *d++ = '\015'; *d++ = '\012'; /* insert final CRLF */
 
1988
  *d = '\0';                    /* tie off string */
 
1989
  if (((unsigned long) (d - ret)) != *len) fatal ("rfc822_binary logic flaw");
 
1990
  return ret;                   /* return the resulting string */
 
1991
}
 
1992
 
 
1993
/* Convert QUOTED-PRINTABLE contents to 8BIT
 
1994
 * Accepts: source
 
1995
 *          length of source
 
1996
 *          pointer to return destination length
 
1997
 * Returns: destination as 8-bit text or NIL if error
 
1998
 */
 
1999
 
 
2000
unsigned char *rfc822_qprint (unsigned char *src,unsigned long srcl,
 
2001
                              unsigned long *len)
 
2002
{
 
2003
  char tmp[MAILTMPLEN];
 
2004
  unsigned int bogon = NIL;
 
2005
  unsigned char *ret = (unsigned char *) fs_get ((size_t) srcl + 1);
 
2006
  unsigned char *d = ret;
 
2007
  unsigned char *t = d;
 
2008
  unsigned char *s = src;
 
2009
  unsigned char c,e;
 
2010
  *len = 0;                     /* in case we return an error */
 
2011
                                /* until run out of characters */
 
2012
  while (((unsigned long) (s - src)) < srcl) {
 
2013
    switch (c = *s++) {         /* what type of character is it? */
 
2014
    case '=':                   /* quoting character */
 
2015
      if (((unsigned long) (s - src)) < srcl) switch (c = *s++) {
 
2016
      case '\0':                /* end of data */
 
2017
        s--;                    /* back up pointer */
 
2018
        break;
 
2019
      case '\015':              /* non-significant line break */
 
2020
        if ((((unsigned long) (s - src)) < srcl) && (*s == '\012')) s++;
 
2021
      case '\012':              /* bare LF */
 
2022
        t = d;                  /* accept any leading spaces */
 
2023
        break;
 
2024
      default:                  /* two hex digits then */
 
2025
        if (!(isxdigit (c) && (((unsigned long) (s - src)) < srcl) &&
 
2026
              (e = *s++) && isxdigit (e))) {
 
2027
          /* This indicates bad MIME.  One way that it can be caused is if
 
2028
             a single-section message was QUOTED-PRINTABLE encoded and then
 
2029
             something (e.g. a mailing list processor) appended text.  The
 
2030
             problem is that there is no way to determine where the encoded
 
2031
             data ended and the appended crud began.  Consequently, prudent
 
2032
             software will always encapsulate a QUOTED-PRINTABLE segment
 
2033
             inside a MULTIPART.
 
2034
           */
 
2035
          if (!bogon++) {       /* only do this once */
 
2036
            sprintf (tmp,"Invalid quoted-printable sequence: =%.80s",
 
2037
                   (char *) s - 1);
 
2038
            mm_log (tmp,PARSE);
 
2039
          }
 
2040
          *d++ = '=';           /* treat = as ordinary character */
 
2041
          *d++ = c;             /* and the character following */
 
2042
          t = d;                /* note point of non-space */
 
2043
          break;
 
2044
        }
 
2045
        *d++ = hex2byte (c,e);  /* merge the two hex digits */
 
2046
        t = d;                  /* note point of non-space */
 
2047
        break;
 
2048
      }
 
2049
      break;
 
2050
    case ' ':                   /* space, possibly bogus */
 
2051
      *d++ = c;                 /* stash the space but don't update s */
 
2052
      break;
 
2053
    case '\015':                /* end of line */
 
2054
    case '\012':                /* bare LF */
 
2055
      d = t;                    /* slide back to last non-space, drop in */
 
2056
    default:
 
2057
      *d++ = c;                 /* stash the character */
 
2058
      t = d;                    /* note point of non-space */
 
2059
    }      
 
2060
  }
 
2061
  *d = '\0';                    /* tie off results */
 
2062
  *len = d - ret;               /* calculate length */
 
2063
  return ret;                   /* return the string */
 
2064
}
 
2065
 
 
2066
/* Convert 8BIT contents to QUOTED-PRINTABLE
 
2067
 * Accepts: source
 
2068
 *          length of source
 
2069
 *          pointer to return destination length
 
2070
 * Returns: destination as quoted-printable text
 
2071
 */
 
2072
 
 
2073
#define MAXL (size_t) 75        /* 76th position only used by continuation = */
 
2074
 
 
2075
unsigned char *rfc822_8bit (unsigned char *src,unsigned long srcl,
 
2076
                            unsigned long *len)
 
2077
{
 
2078
  unsigned long lp = 0;
 
2079
  unsigned char *ret = (unsigned char *)
 
2080
    fs_get ((size_t) (3*srcl + 3*(((3*srcl)/MAXL) + 1)));
 
2081
  unsigned char *d = ret;
 
2082
  char *hex = "0123456789ABCDEF";
 
2083
  unsigned char c;
 
2084
  while (srcl--) {              /* for each character */
 
2085
                                /* true line break? */
 
2086
    if (((c = *src++) == '\015') && (*src == '\012') && srcl) {
 
2087
      *d++ = '\015'; *d++ = *src++; srcl--;
 
2088
      lp = 0;                   /* reset line count */
 
2089
    }
 
2090
    else {                      /* not a line break */
 
2091
                                /* quoting required? */
 
2092
      if (iscntrl (c) || (c == 0x7f) || (c & 0x80) || (c == '=') ||
 
2093
          ((c == ' ') && (*src == '\015'))) {
 
2094
        if ((lp += 3) > MAXL) { /* yes, would line overflow? */
 
2095
          *d++ = '='; *d++ = '\015'; *d++ = '\012';
 
2096
          lp = 3;               /* set line count */
 
2097
        }
 
2098
        *d++ = '=';             /* quote character */
 
2099
        *d++ = hex[c >> 4];     /* high order 4 bits */
 
2100
        *d++ = hex[c & 0xf];    /* low order 4 bits */
 
2101
      }
 
2102
      else {                    /* ordinary character */
 
2103
        if ((++lp) > MAXL) {    /* would line overflow? */
 
2104
          *d++ = '='; *d++ = '\015'; *d++ = '\012';
 
2105
          lp = 1;               /* set line count */
 
2106
        }
 
2107
        *d++ = c;               /* ordinary character */
 
2108
      }
 
2109
    }
 
2110
  }
 
2111
  *d = '\0';                    /* tie off destination */
 
2112
  *len = d - ret;               /* calculate true size */
 
2113
                                /* try to give some space back */
 
2114
  fs_resize ((void **) &ret,(size_t) *len + 1);
 
2115
  return ret;
 
2116
}
 
2117
 
 
2118
/* Legacy Routines */
 
2119
 
 
2120
/*
 
2121
 * WARNING: These routines are for compatibility with old software only.
 
2122
 *
 
2123
 * Their use in new software is to be avoided.
 
2124
 *
 
2125
 * These interfaces do not provide satisfactory buffer checking.  In
 
2126
 * versions of c-client prior to imap-2005, they did not provide any
 
2127
 * buffer checking at all.
 
2128
 *
 
2129
 * As a half-hearted attempt, these new compatability functions for the
 
2130
 * legacy interfaces limit what they write to size SENDBUFLEN and will
 
2131
 * fatal() if more than that is written.  However, that isn't good enough
 
2132
 * since several of these functions *append* to the buffer, and return an
 
2133
 * updated pointer.  Consequently, there is no way of knowing what the
 
2134
 * actual available space is in the buffer, yet the function will still
 
2135
 * write up to SENDBUFLEN bytes even if there is much less space actually
 
2136
 * available.  The result is a buffer overflow.
 
2137
 *
 
2138
 * You won't get a buffer overflow if you never attempt to append using
 
2139
 * these interfaces, but you can get the fatal() if it tries to write
 
2140
 * more than SENDBUFLEN bytes.
 
2141
 *
 
2142
 * To avoid this problem, use the corresponding rfc822_output_???()
 
2143
 * functions instead, e.g., rfc822_output_address() instead of
 
2144
 * rfc822_address().
 
2145
 *
 
2146
 */
 
2147
 
 
2148
/* Flush routine, only called if overflow
 
2149
 * Accepts: stream
 
2150
 *          string to output
 
2151
 * Returns: never
 
2152
 */
 
2153
 
 
2154
static long rfc822_legacy_soutr (void *stream,char *string)
 
2155
{
 
2156
  fatal ("rfc822.c legacy routine buffer overflow");
 
2157
  return NIL;
 
2158
}
 
2159
 
 
2160
/* Legacy write RFC 2822 header from message structure
 
2161
 * Accepts: scratch buffer to write into
 
2162
 *          message envelope
 
2163
 *          message body
 
2164
 */
 
2165
 
 
2166
void rfc822_header (char *header,ENVELOPE *env,BODY *body)
 
2167
{
 
2168
  RFC822BUFFER buf;
 
2169
                                /* write at start of buffer */
 
2170
  buf.end = (buf.beg = buf.cur = header) + SENDBUFLEN - 1;
 
2171
  buf.f = rfc822_legacy_soutr;
 
2172
  buf.s = NIL;
 
2173
  rfc822_output_header (&buf,env,body,NIL,NIL);
 
2174
  *buf.cur = '\0';              /* tie off buffer */
 
2175
}
 
2176
 
 
2177
 
 
2178
/* Legacy write RFC 2822 text from header line
 
2179
 * Accepts: pointer to destination string pointer
 
2180
 *          pointer to header type
 
2181
 *          message to interpret
 
2182
 *          pointer to text
 
2183
 */
 
2184
 
 
2185
void rfc822_header_line (char **header,char *type,ENVELOPE *env,char *text)
 
2186
{
 
2187
  RFC822BUFFER buf;
 
2188
                                /* append to buffer */
 
2189
  buf.end = (buf.beg = buf.cur = *header + strlen (*header)) + SENDBUFLEN - 1;
 
2190
  buf.f = rfc822_legacy_soutr;
 
2191
  buf.s = NIL;
 
2192
  rfc822_output_header_line (&buf,type,env->remail ? LONGT : NIL,text);
 
2193
  *(*header = buf.cur) = '\0';  /* tie off buffer */
 
2194
}
 
2195
 
 
2196
/* Legacy write RFC 2822 address from header line
 
2197
 * Accepts: pointer to destination string pointer
 
2198
 *          pointer to header type
 
2199
 *          message to interpret
 
2200
 *          address to interpret
 
2201
 */
 
2202
 
 
2203
void rfc822_address_line (char **header,char *type,ENVELOPE *env,ADDRESS *adr)
 
2204
{
 
2205
  RFC822BUFFER buf;
 
2206
                                /* append to buffer */
 
2207
  buf.end = (buf.beg = buf.cur = *header + strlen (*header)) + SENDBUFLEN - 1;
 
2208
  buf.f = rfc822_legacy_soutr;
 
2209
  buf.s = NIL;
 
2210
  rfc822_output_address_line (&buf,type,env->remail ? LONGT : NIL,adr,NIL);
 
2211
  *(*header = buf.cur) = '\0';  /* tie off buffer */
 
2212
}
 
2213
 
 
2214
 
 
2215
/* Legacy write RFC 2822 address list
 
2216
 * Accepts: pointer to destination string
 
2217
 *          address to interpret
 
2218
 *          header base if pretty-printing
 
2219
 * Returns: end of destination string
 
2220
 */
 
2221
 
 
2222
char *rfc822_write_address_full (char *dest,ADDRESS *adr,char *base)
 
2223
{
 
2224
  RFC822BUFFER buf;
 
2225
                                /* append to buffer */
 
2226
  buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
 
2227
  buf.f = rfc822_legacy_soutr;
 
2228
  buf.s = NIL;
 
2229
  rfc822_output_address_list (&buf,adr,base ? dest - base : 0,NIL);
 
2230
  *buf.cur = '\0';              /* tie off buffer */
 
2231
  return buf.cur;
 
2232
}
 
2233
 
 
2234
 
 
2235
/* Legacy write RFC 2822 route-address to string
 
2236
 * Accepts: pointer to destination string
 
2237
 *          address to interpret
 
2238
 */
 
2239
 
 
2240
void rfc822_address (char *dest,ADDRESS *adr)
 
2241
{
 
2242
  RFC822BUFFER buf;
 
2243
                                /* append to buffer */
 
2244
  buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
 
2245
  buf.f = rfc822_legacy_soutr;
 
2246
  buf.s = NIL;
 
2247
  rfc822_output_address (&buf,adr);
 
2248
  *buf.cur = '\0';              /* tie off buffer */
 
2249
}
 
2250
 
 
2251
/* Concatenate RFC 2822 string
 
2252
 * Accepts: pointer to destination string
 
2253
 *          pointer to string to concatenate
 
2254
 *          list of special characters or NIL for dot-atom format
 
2255
 */
 
2256
 
 
2257
void rfc822_cat (char *dest,char *src,const char *specials)
 
2258
{
 
2259
  RFC822BUFFER buf;
 
2260
                                /* append to buffer */
 
2261
  buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
 
2262
  buf.f = rfc822_legacy_soutr;
 
2263
  buf.s = NIL;
 
2264
  rfc822_output_cat (&buf,src,specials);
 
2265
  *buf.cur = '\0';              /* tie off buffer */
 
2266
}
 
2267
 
 
2268
 
 
2269
/* Legacy write body content header
 
2270
 * Accepts: pointer to destination string pointer
 
2271
 *          pointer to body to interpret
 
2272
 */
 
2273
 
 
2274
void rfc822_write_body_header (char **dst,BODY *body)
 
2275
{
 
2276
  RFC822BUFFER buf;
 
2277
                                /* append to buffer */
 
2278
  buf.end = (buf.beg = buf.cur = *dst + strlen (*dst)) + SENDBUFLEN - 1;
 
2279
  buf.f = rfc822_legacy_soutr;
 
2280
  buf.s = NIL;
 
2281
  rfc822_output_body_header (&buf,body);
 
2282
  *(*dst = buf.cur) = '\0';     /* tie off buffer */
 
2283
}
 
2284
 
 
2285
/* Legacy output RFC 822 message
 
2286
 * Accepts: temporary buffer
 
2287
 *          envelope
 
2288
 *          body
 
2289
 *          I/O routine
 
2290
 *          stream for I/O routine
 
2291
 *          non-zero if 8-bit output desired
 
2292
 * Returns: T if successful, NIL if failure
 
2293
 */
 
2294
 
 
2295
long rfc822_output (char *t,ENVELOPE *env,BODY *body,soutr_t f,void *s,
 
2296
                    long ok8bit)
 
2297
{
 
2298
  long ret;
 
2299
  rfc822out_t r822o = (rfc822out_t) mail_parameters (NIL,GET_RFC822OUTPUT,NIL);
 
2300
                                /* call external RFC 2822 output generator */
 
2301
  if (r822o) ret = (*r822o) (t,env,body,f,s,ok8bit);
 
2302
  else {                        /* output generator not armed */
 
2303
    RFC822BUFFER buf;           /* use our own buffer rather than trust */
 
2304
    char tmp[SENDBUFLEN+1];     /*  client to give us a big enough one */
 
2305
    buf.f = f;
 
2306
    buf.s = s;
 
2307
    buf.end = (buf.beg = buf.cur = t) + SENDBUFLEN - 1;
 
2308
    tmp[SENDBUFLEN] = '\0';     /* must have additional guard byte */
 
2309
    ret = rfc822_output_full (&buf,env,body,ok8bit);
 
2310
  }
 
2311
  return ret;
 
2312
}
 
2313
 
 
2314
 
 
2315
/* Legacy output RFC 822 body
 
2316
 * Accepts: body
 
2317
 *          I/O routine
 
2318
 *          stream for I/O routine
 
2319
 * Returns: T if successful, NIL if failure
 
2320
 */
 
2321
 
 
2322
long rfc822_output_body (BODY *body,soutr_t f,void *s)
 
2323
{
 
2324
  RFC822BUFFER buf;
 
2325
  char tmp[SENDBUFLEN+1];
 
2326
  buf.f = f;
 
2327
  buf.s = s;
 
2328
  buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN;
 
2329
  tmp[SENDBUFLEN] = '\0';       /* must have additional guard byte */
 
2330
  return rfc822_output_text (&buf,body) && rfc822_output_flush (&buf);
 
2331
}