1 | |
---|
2 | /* |
---|
3 | * mhbuildsbr.c -- routines to expand/translate MIME composition files |
---|
4 | * |
---|
5 | * $Id: mhbuildsbr.c,v 1.2 2002-05-17 21:32:05 rbasch Exp $ |
---|
6 | */ |
---|
7 | |
---|
8 | /* |
---|
9 | * This code was originally part of mhn.c. I split it into |
---|
10 | * a separate program (mhbuild.c) and then later split it |
---|
11 | * again (mhbuildsbr.c). But the code still has some of |
---|
12 | * the mhn.c code in it. This program needs additional |
---|
13 | * streamlining and removal of unneeded code. |
---|
14 | */ |
---|
15 | |
---|
16 | #include <h/mh.h> |
---|
17 | #include <fcntl.h> |
---|
18 | #include <h/signals.h> |
---|
19 | #include <h/md5.h> |
---|
20 | #include <errno.h> |
---|
21 | #include <signal.h> |
---|
22 | #include <zotnet/mts/mts.h> |
---|
23 | #include <zotnet/tws/tws.h> |
---|
24 | #include <h/mime.h> |
---|
25 | #include <h/mhparse.h> |
---|
26 | |
---|
27 | #ifdef HAVE_SYS_WAIT_H |
---|
28 | # include <sys/wait.h> |
---|
29 | #endif |
---|
30 | |
---|
31 | |
---|
32 | extern int errno; |
---|
33 | |
---|
34 | extern int debugsw; |
---|
35 | extern int verbosw; |
---|
36 | |
---|
37 | extern int ebcdicsw; |
---|
38 | extern int listsw; |
---|
39 | extern int rfc934sw; |
---|
40 | |
---|
41 | extern int endian; /* mhmisc.c */ |
---|
42 | |
---|
43 | /* cache policies */ |
---|
44 | extern int rcachesw; /* mhcachesbr.c */ |
---|
45 | extern int wcachesw; /* mhcachesbr.c */ |
---|
46 | |
---|
47 | int checksw = 0; /* Add Content-MD5 field */ |
---|
48 | |
---|
49 | /* |
---|
50 | * Directory to place tmp files. This must |
---|
51 | * be set before these routines are called. |
---|
52 | */ |
---|
53 | char *tmp; |
---|
54 | |
---|
55 | pid_t xpid = 0; |
---|
56 | |
---|
57 | static char prefix[] = "----- =_aaaaaaaaaa"; |
---|
58 | |
---|
59 | /* |
---|
60 | * Structure for mapping types to their internal flags |
---|
61 | */ |
---|
62 | struct k2v { |
---|
63 | char *kv_key; |
---|
64 | int kv_value; |
---|
65 | }; |
---|
66 | |
---|
67 | /* |
---|
68 | * Structures for TEXT messages |
---|
69 | */ |
---|
70 | static struct k2v SubText[] = { |
---|
71 | { "plain", TEXT_PLAIN }, |
---|
72 | { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */ |
---|
73 | { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */ |
---|
74 | { NULL, TEXT_UNKNOWN } /* this one must be last! */ |
---|
75 | }; |
---|
76 | |
---|
77 | static struct k2v Charset[] = { |
---|
78 | { "us-ascii", CHARSET_USASCII }, |
---|
79 | { "iso-8859-1", CHARSET_LATIN }, |
---|
80 | { NULL, CHARSET_UNKNOWN } /* this one must be last! */ |
---|
81 | }; |
---|
82 | |
---|
83 | /* |
---|
84 | * Structures for MULTIPART messages |
---|
85 | */ |
---|
86 | static struct k2v SubMultiPart[] = { |
---|
87 | { "mixed", MULTI_MIXED }, |
---|
88 | { "alternative", MULTI_ALTERNATE }, |
---|
89 | { "digest", MULTI_DIGEST }, |
---|
90 | { "parallel", MULTI_PARALLEL }, |
---|
91 | { NULL, MULTI_UNKNOWN } /* this one must be last! */ |
---|
92 | }; |
---|
93 | |
---|
94 | /* |
---|
95 | * Structures for MESSAGE messages |
---|
96 | */ |
---|
97 | static struct k2v SubMessage[] = { |
---|
98 | { "rfc822", MESSAGE_RFC822 }, |
---|
99 | { "partial", MESSAGE_PARTIAL }, |
---|
100 | { "external-body", MESSAGE_EXTERNAL }, |
---|
101 | { NULL, MESSAGE_UNKNOWN } /* this one must be last! */ |
---|
102 | }; |
---|
103 | |
---|
104 | /* |
---|
105 | * Structure for APPLICATION messages |
---|
106 | */ |
---|
107 | static struct k2v SubApplication[] = { |
---|
108 | { "octet-stream", APPLICATION_OCTETS }, |
---|
109 | { "postscript", APPLICATION_POSTSCRIPT }, |
---|
110 | { NULL, APPLICATION_UNKNOWN } /* this one must be last! */ |
---|
111 | }; |
---|
112 | |
---|
113 | |
---|
114 | /* mhmisc.c */ |
---|
115 | int make_intermediates (char *); |
---|
116 | void content_error (char *, CT, char *, ...); |
---|
117 | |
---|
118 | /* mhcachesbr.c */ |
---|
119 | int find_cache (CT, int, int *, char *, char *, int); |
---|
120 | |
---|
121 | /* ftpsbr.c */ |
---|
122 | int ftp_get (char *, char *, char *, char *, char *, char *, int, int); |
---|
123 | |
---|
124 | /* mhfree.c */ |
---|
125 | void free_content (CT); |
---|
126 | void free_ctinfo (CT); |
---|
127 | void free_encoding (CT, int); |
---|
128 | |
---|
129 | /* |
---|
130 | * prototypes |
---|
131 | */ |
---|
132 | CT build_mime (char *); |
---|
133 | int pidcheck (int); |
---|
134 | |
---|
135 | /* |
---|
136 | * static prototypes |
---|
137 | */ |
---|
138 | static CT get_content (FILE *, char *, int); |
---|
139 | static int add_header (CT, char *, char *); |
---|
140 | static int get_ctinfo (char *, CT, int); |
---|
141 | static int get_comment (CT, char **, int); |
---|
142 | static int InitGeneric (CT); |
---|
143 | static int InitText (CT); |
---|
144 | static int InitMultiPart (CT); |
---|
145 | static void reverse_parts (CT); |
---|
146 | static int InitMessage (CT); |
---|
147 | static int params_external (CT, int); |
---|
148 | static int InitApplication (CT); |
---|
149 | static int init_decoded_content (CT); |
---|
150 | static int init_encoding (CT, OpenCEFunc); |
---|
151 | static void close_encoding (CT); |
---|
152 | static unsigned long size_encoding (CT); |
---|
153 | static int InitBase64 (CT); |
---|
154 | static int openBase64 (CT, char **); |
---|
155 | static int InitQuoted (CT); |
---|
156 | static int openQuoted (CT, char **); |
---|
157 | static int Init7Bit (CT); |
---|
158 | static int open7Bit (CT, char **); |
---|
159 | static int openExternal (CT, CT, CE, char **, int *); |
---|
160 | static int InitFile (CT); |
---|
161 | static int openFile (CT, char **); |
---|
162 | static int InitFTP (CT); |
---|
163 | static int openFTP (CT, char **); |
---|
164 | static int InitMail (CT); |
---|
165 | static int openMail (CT, char **); |
---|
166 | static char *fgetstr (char *, int, FILE *); |
---|
167 | static int user_content (FILE *, char *, char *, CT *); |
---|
168 | static void set_id (CT, int); |
---|
169 | static int compose_content (CT); |
---|
170 | static int scan_content (CT); |
---|
171 | static int build_headers (CT); |
---|
172 | static char *calculate_digest (CT, int); |
---|
173 | static int readDigest (CT, char *); |
---|
174 | |
---|
175 | /* |
---|
176 | * Structures for mapping (content) types to |
---|
177 | * the functions to handle them. |
---|
178 | */ |
---|
179 | struct str2init { |
---|
180 | char *si_key; |
---|
181 | int si_val; |
---|
182 | InitFunc si_init; |
---|
183 | }; |
---|
184 | |
---|
185 | static struct str2init str2cts[] = { |
---|
186 | { "application", CT_APPLICATION, InitApplication }, |
---|
187 | { "audio", CT_AUDIO, InitGeneric }, |
---|
188 | { "image", CT_IMAGE, InitGeneric }, |
---|
189 | { "message", CT_MESSAGE, InitMessage }, |
---|
190 | { "multipart", CT_MULTIPART, InitMultiPart }, |
---|
191 | { "text", CT_TEXT, InitText }, |
---|
192 | { "video", CT_VIDEO, InitGeneric }, |
---|
193 | { NULL, CT_EXTENSION, NULL }, /* these two must be last! */ |
---|
194 | { NULL, CT_UNKNOWN, NULL }, |
---|
195 | }; |
---|
196 | |
---|
197 | static struct str2init str2ces[] = { |
---|
198 | { "base64", CE_BASE64, InitBase64 }, |
---|
199 | { "quoted-printable", CE_QUOTED, InitQuoted }, |
---|
200 | { "8bit", CE_8BIT, Init7Bit }, |
---|
201 | { "7bit", CE_7BIT, Init7Bit }, |
---|
202 | { "binary", CE_BINARY, NULL }, |
---|
203 | { NULL, CE_EXTENSION, NULL }, /* these two must be last! */ |
---|
204 | { NULL, CE_UNKNOWN, NULL }, |
---|
205 | }; |
---|
206 | |
---|
207 | /* |
---|
208 | * NOTE WELL: si_key MUST NOT have value of NOTOK |
---|
209 | * |
---|
210 | * si_key is 1 if access method is anonymous. |
---|
211 | */ |
---|
212 | static struct str2init str2methods[] = { |
---|
213 | { "afs", 1, InitFile }, |
---|
214 | { "anon-ftp", 1, InitFTP }, |
---|
215 | { "ftp", 0, InitFTP }, |
---|
216 | { "local-file", 0, InitFile }, |
---|
217 | { "mail-server", 0, InitMail }, |
---|
218 | { NULL, 0, NULL } |
---|
219 | }; |
---|
220 | |
---|
221 | |
---|
222 | int |
---|
223 | pidcheck (int status) |
---|
224 | { |
---|
225 | if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT) |
---|
226 | return status; |
---|
227 | |
---|
228 | fflush (stdout); |
---|
229 | fflush (stderr); |
---|
230 | done (1); |
---|
231 | /* NOTREACHED */ |
---|
232 | } |
---|
233 | |
---|
234 | |
---|
235 | /* |
---|
236 | * Main routine for translating composition file |
---|
237 | * into valid MIME message. It translates the draft |
---|
238 | * into a content structure (actually a tree of content |
---|
239 | * structures). This message then can be manipulated |
---|
240 | * in various ways, including being output via |
---|
241 | * output_message(). |
---|
242 | */ |
---|
243 | |
---|
244 | CT |
---|
245 | build_mime (char *infile) |
---|
246 | { |
---|
247 | int compnum, state; |
---|
248 | char buf[BUFSIZ], name[NAMESZ]; |
---|
249 | char *cp, *np, *vp; |
---|
250 | struct multipart *m; |
---|
251 | struct part **pp; |
---|
252 | CT ct; |
---|
253 | FILE *in; |
---|
254 | |
---|
255 | umask (~m_gmprot ()); |
---|
256 | |
---|
257 | /* open the composition draft */ |
---|
258 | if ((in = fopen (infile, "r")) == NULL) |
---|
259 | adios (infile, "unable to open for reading"); |
---|
260 | |
---|
261 | /* |
---|
262 | * Allocate space for primary (outside) content |
---|
263 | */ |
---|
264 | if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL) |
---|
265 | adios (NULL, "out of memory"); |
---|
266 | |
---|
267 | /* |
---|
268 | * Allocate structure for handling decoded content |
---|
269 | * for this part. We don't really need this, but |
---|
270 | * allocate it to remain consistent. |
---|
271 | */ |
---|
272 | init_decoded_content (ct); |
---|
273 | |
---|
274 | /* |
---|
275 | * Parse some of the header fields in the composition |
---|
276 | * draft into the linked list of header fields for |
---|
277 | * the new MIME message. |
---|
278 | */ |
---|
279 | for (compnum = 1, state = FLD;;) { |
---|
280 | switch (state = m_getfld (state, name, buf, sizeof(buf), in)) { |
---|
281 | case FLD: |
---|
282 | case FLDPLUS: |
---|
283 | case FLDEOF: |
---|
284 | compnum++; |
---|
285 | |
---|
286 | /* abort if draft has Mime-Version header field */ |
---|
287 | if (!strcasecmp (name, VRSN_FIELD)) |
---|
288 | adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD); |
---|
289 | |
---|
290 | /* abort if draft has Content-Transfer-Encoding header field */ |
---|
291 | if (!strcasecmp (name, ENCODING_FIELD)) |
---|
292 | adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD); |
---|
293 | |
---|
294 | /* ignore any Content-Type fields in the header */ |
---|
295 | if (!strcasecmp (name, TYPE_FIELD)) { |
---|
296 | while (state == FLDPLUS) |
---|
297 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
298 | goto finish_field; |
---|
299 | } |
---|
300 | |
---|
301 | /* get copies of the buffers */ |
---|
302 | np = add (name, NULL); |
---|
303 | vp = add (buf, NULL); |
---|
304 | |
---|
305 | /* if necessary, get rest of field */ |
---|
306 | while (state == FLDPLUS) { |
---|
307 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
308 | vp = add (buf, vp); /* add to previous value */ |
---|
309 | } |
---|
310 | |
---|
311 | /* Now add the header data to the list */ |
---|
312 | add_header (ct, np, vp); |
---|
313 | |
---|
314 | finish_field: |
---|
315 | /* if this wasn't the last header field, then continue */ |
---|
316 | if (state != FLDEOF) |
---|
317 | continue; |
---|
318 | /* else fall... */ |
---|
319 | |
---|
320 | case FILEEOF: |
---|
321 | adios (NULL, "draft has empty body -- no directives!"); |
---|
322 | /* NOTREACHED */ |
---|
323 | |
---|
324 | case BODY: |
---|
325 | case BODYEOF: |
---|
326 | fseek (in, (long) (-strlen (buf)), SEEK_CUR); |
---|
327 | break; |
---|
328 | |
---|
329 | case LENERR: |
---|
330 | case FMTERR: |
---|
331 | adios (NULL, "message format error in component #%d", compnum); |
---|
332 | |
---|
333 | default: |
---|
334 | adios (NULL, "getfld() returned %d", state); |
---|
335 | } |
---|
336 | break; |
---|
337 | } |
---|
338 | |
---|
339 | /* |
---|
340 | * Now add the MIME-Version header field |
---|
341 | * to the list of header fields. |
---|
342 | */ |
---|
343 | np = add (VRSN_FIELD, NULL); |
---|
344 | vp = concat (" ", VRSN_VALUE, "\n", NULL); |
---|
345 | add_header (ct, np, vp); |
---|
346 | |
---|
347 | /* |
---|
348 | * We initally assume we will find multiple contents in the |
---|
349 | * draft. So create a multipart/mixed content to hold everything. |
---|
350 | * We can remove this later, if it is not needed. |
---|
351 | */ |
---|
352 | if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK) |
---|
353 | done (1); |
---|
354 | ct->c_type = CT_MULTIPART; |
---|
355 | ct->c_subtype = MULTI_MIXED; |
---|
356 | ct->c_file = add (infile, NULL); |
---|
357 | |
---|
358 | if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL) |
---|
359 | adios (NULL, "out of memory"); |
---|
360 | ct->c_ctparams = (void *) m; |
---|
361 | pp = &m->mp_parts; |
---|
362 | |
---|
363 | /* |
---|
364 | * read and parse the composition file |
---|
365 | * and the directives it contains. |
---|
366 | */ |
---|
367 | while (fgetstr (buf, sizeof(buf) - 1, in)) { |
---|
368 | struct part *part; |
---|
369 | CT p; |
---|
370 | |
---|
371 | if (user_content (in, infile, buf, &p) == DONE) { |
---|
372 | admonish (NULL, "ignoring spurious #end"); |
---|
373 | continue; |
---|
374 | } |
---|
375 | if (!p) |
---|
376 | continue; |
---|
377 | |
---|
378 | if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL) |
---|
379 | adios (NULL, "out of memory"); |
---|
380 | *pp = part; |
---|
381 | pp = &part->mp_next; |
---|
382 | part->mp_part = p; |
---|
383 | } |
---|
384 | |
---|
385 | /* |
---|
386 | * close the composition draft since |
---|
387 | * it's not needed any longer. |
---|
388 | */ |
---|
389 | fclose (in); |
---|
390 | |
---|
391 | /* check if any contents were found */ |
---|
392 | if (!m->mp_parts) |
---|
393 | adios (NULL, "no content directives found"); |
---|
394 | |
---|
395 | /* |
---|
396 | * If only one content was found, then remove and |
---|
397 | * free the outer multipart content. |
---|
398 | */ |
---|
399 | if (!m->mp_parts->mp_next) { |
---|
400 | CT p; |
---|
401 | |
---|
402 | p = m->mp_parts->mp_part; |
---|
403 | m->mp_parts->mp_part = NULL; |
---|
404 | |
---|
405 | /* move header fields */ |
---|
406 | p->c_first_hf = ct->c_first_hf; |
---|
407 | p->c_last_hf = ct->c_last_hf; |
---|
408 | ct->c_first_hf = NULL; |
---|
409 | ct->c_last_hf = NULL; |
---|
410 | |
---|
411 | free_content (ct); |
---|
412 | ct = p; |
---|
413 | } else { |
---|
414 | set_id (ct, 1); |
---|
415 | } |
---|
416 | |
---|
417 | /* |
---|
418 | * Fill out, or expand directives. Parse and execute |
---|
419 | * commands specified by profile composition strings. |
---|
420 | */ |
---|
421 | compose_content (ct); |
---|
422 | |
---|
423 | if ((cp = strchr(prefix, 'a')) == NULL) |
---|
424 | adios (NULL, "internal error(4)"); |
---|
425 | |
---|
426 | /* |
---|
427 | * Scan the contents. Choose a transfer encoding, and |
---|
428 | * check if prefix for multipart boundary clashes with |
---|
429 | * any of the contents. |
---|
430 | */ |
---|
431 | while (scan_content (ct) == NOTOK) { |
---|
432 | if (*cp < 'z') { |
---|
433 | (*cp)++; |
---|
434 | } else { |
---|
435 | if (*++cp == 0) |
---|
436 | adios (NULL, "giving up trying to find a unique delimiter string"); |
---|
437 | else |
---|
438 | (*cp)++; |
---|
439 | } |
---|
440 | } |
---|
441 | |
---|
442 | /* Build the rest of the header field structures */ |
---|
443 | build_headers (ct); |
---|
444 | |
---|
445 | return ct; |
---|
446 | } |
---|
447 | |
---|
448 | |
---|
449 | /* |
---|
450 | * Main routine for reading/parsing the headers |
---|
451 | * of a message content. |
---|
452 | * |
---|
453 | * toplevel = 1 # we are at the top level of the message |
---|
454 | * toplevel = 0 # we are inside message type or multipart type |
---|
455 | * # other than multipart/digest |
---|
456 | * toplevel = -1 # we are inside multipart/digest |
---|
457 | */ |
---|
458 | |
---|
459 | static CT |
---|
460 | get_content (FILE *in, char *file, int toplevel) |
---|
461 | { |
---|
462 | int compnum, state; |
---|
463 | char buf[BUFSIZ], name[NAMESZ]; |
---|
464 | CT ct; |
---|
465 | |
---|
466 | if (!(ct = (CT) calloc (1, sizeof(*ct)))) |
---|
467 | adios (NULL, "out of memory"); |
---|
468 | |
---|
469 | ct->c_fp = in; |
---|
470 | ct->c_file = add (file, NULL); |
---|
471 | ct->c_begin = ftell (ct->c_fp) + 1; |
---|
472 | |
---|
473 | /* |
---|
474 | * Read the content headers |
---|
475 | */ |
---|
476 | for (compnum = 1, state = FLD;;) { |
---|
477 | switch (state = m_getfld (state, name, buf, sizeof(buf), in)) { |
---|
478 | case FLD: |
---|
479 | case FLDPLUS: |
---|
480 | case FLDEOF: |
---|
481 | compnum++; |
---|
482 | |
---|
483 | /* Get MIME-Version field */ |
---|
484 | if (!strcasecmp (name, VRSN_FIELD)) { |
---|
485 | int ucmp; |
---|
486 | char c, *cp, *dp; |
---|
487 | |
---|
488 | cp = add (buf, NULL); |
---|
489 | while (state == FLDPLUS) { |
---|
490 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
491 | cp = add (buf, cp); |
---|
492 | } |
---|
493 | |
---|
494 | if (ct->c_vrsn) { |
---|
495 | advise (NULL, "message %s has multiple %s: fields (%s)", |
---|
496 | ct->c_file, VRSN_FIELD, dp = trimcpy (cp)); |
---|
497 | free (dp); |
---|
498 | free (cp); |
---|
499 | goto out; |
---|
500 | } |
---|
501 | |
---|
502 | ct->c_vrsn = cp; |
---|
503 | while (isspace (*cp)) |
---|
504 | cp++; |
---|
505 | for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n')) |
---|
506 | *dp++ = ' '; |
---|
507 | for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) |
---|
508 | if (!isspace (*dp)) |
---|
509 | break; |
---|
510 | *++dp = '\0'; |
---|
511 | if (debugsw) |
---|
512 | fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp); |
---|
513 | |
---|
514 | if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) |
---|
515 | goto out; |
---|
516 | |
---|
517 | for (dp = cp; istoken (*dp); dp++) |
---|
518 | continue; |
---|
519 | c = *dp, *dp = '\0'; |
---|
520 | ucmp = !strcasecmp (cp, VRSN_VALUE); |
---|
521 | *dp = c; |
---|
522 | if (!ucmp) |
---|
523 | admonish (NULL, |
---|
524 | "message %s has unknown value for %s: field (%s)", |
---|
525 | ct->c_file, VRSN_FIELD, cp); |
---|
526 | goto got_header; |
---|
527 | } |
---|
528 | |
---|
529 | /* Get Content-Type field */ |
---|
530 | if (!strcasecmp (name, TYPE_FIELD)) { |
---|
531 | char *cp; |
---|
532 | struct str2init *s2i; |
---|
533 | CI ci = &ct->c_ctinfo; |
---|
534 | |
---|
535 | cp = add (buf, NULL); |
---|
536 | while (state == FLDPLUS) { |
---|
537 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
538 | cp = add (buf, cp); |
---|
539 | } |
---|
540 | |
---|
541 | /* Check if we've already seen a Content-Type header */ |
---|
542 | if (ct->c_ctline) { |
---|
543 | char *dp = trimcpy (cp); |
---|
544 | |
---|
545 | advise (NULL, "message %s has multiple %s: fields (%s)", |
---|
546 | ct->c_file, TYPE_FIELD, dp); |
---|
547 | free (dp); |
---|
548 | free (cp); |
---|
549 | goto out; |
---|
550 | } |
---|
551 | |
---|
552 | /* Parse the Content-Type field */ |
---|
553 | if (get_ctinfo (cp, ct, 0) == NOTOK) |
---|
554 | goto out; |
---|
555 | |
---|
556 | /* |
---|
557 | * Set the Init function and the internal |
---|
558 | * flag for this content type. |
---|
559 | */ |
---|
560 | for (s2i = str2cts; s2i->si_key; s2i++) |
---|
561 | if (!strcasecmp (ci->ci_type, s2i->si_key)) |
---|
562 | break; |
---|
563 | if (!s2i->si_key && !uprf (ci->ci_type, "X-")) |
---|
564 | s2i++; |
---|
565 | ct->c_type = s2i->si_val; |
---|
566 | ct->c_ctinitfnx = s2i->si_init; |
---|
567 | goto got_header; |
---|
568 | } |
---|
569 | |
---|
570 | /* Get Content-Transfer-Encoding field */ |
---|
571 | if (!strcasecmp (name, ENCODING_FIELD)) { |
---|
572 | char *cp, *dp; |
---|
573 | char c; |
---|
574 | struct str2init *s2i; |
---|
575 | |
---|
576 | cp = add (buf, NULL); |
---|
577 | while (state == FLDPLUS) { |
---|
578 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
579 | cp = add (buf, cp); |
---|
580 | } |
---|
581 | |
---|
582 | /* |
---|
583 | * Check if we've already seen the |
---|
584 | * Content-Transfer-Encoding field |
---|
585 | */ |
---|
586 | if (ct->c_celine) { |
---|
587 | advise (NULL, "message %s has multiple %s: fields (%s)", |
---|
588 | ct->c_file, ENCODING_FIELD, dp = trimcpy (cp)); |
---|
589 | free (dp); |
---|
590 | free (cp); |
---|
591 | goto out; |
---|
592 | } |
---|
593 | |
---|
594 | ct->c_celine = cp; /* Save copy of this field */ |
---|
595 | while (isspace (*cp)) |
---|
596 | cp++; |
---|
597 | for (dp = cp; istoken (*dp); dp++) |
---|
598 | continue; |
---|
599 | c = *dp; |
---|
600 | *dp = '\0'; |
---|
601 | |
---|
602 | /* |
---|
603 | * Find the internal flag and Init function |
---|
604 | * for this transfer encoding. |
---|
605 | */ |
---|
606 | for (s2i = str2ces; s2i->si_key; s2i++) |
---|
607 | if (!strcasecmp (cp, s2i->si_key)) |
---|
608 | break; |
---|
609 | if (!s2i->si_key && !uprf (cp, "X-")) |
---|
610 | s2i++; |
---|
611 | *dp = c; |
---|
612 | ct->c_encoding = s2i->si_val; |
---|
613 | |
---|
614 | /* Call the Init function for this encoding */ |
---|
615 | if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK) |
---|
616 | goto out; |
---|
617 | goto got_header; |
---|
618 | } |
---|
619 | |
---|
620 | /* Get Content-ID field */ |
---|
621 | if (!strcasecmp (name, ID_FIELD)) { |
---|
622 | ct->c_id = add (buf, ct->c_id); |
---|
623 | while (state == FLDPLUS) { |
---|
624 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
625 | ct->c_id = add (buf, ct->c_id); |
---|
626 | } |
---|
627 | goto got_header; |
---|
628 | } |
---|
629 | |
---|
630 | /* Get Content-Description field */ |
---|
631 | if (!strcasecmp (name, DESCR_FIELD)) { |
---|
632 | ct->c_descr = add (buf, ct->c_descr); |
---|
633 | while (state == FLDPLUS) { |
---|
634 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
635 | ct->c_descr = add (buf, ct->c_descr); |
---|
636 | } |
---|
637 | goto got_header; |
---|
638 | } |
---|
639 | |
---|
640 | /* Get Content-MD5 field */ |
---|
641 | if (!strcasecmp (name, MD5_FIELD)) { |
---|
642 | char *cp, *dp, *ep; |
---|
643 | |
---|
644 | cp = add (buf, NULL); |
---|
645 | while (state == FLDPLUS) { |
---|
646 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
647 | cp = add (buf, cp); |
---|
648 | } |
---|
649 | |
---|
650 | if (!checksw) { |
---|
651 | free (cp); |
---|
652 | goto got_header; |
---|
653 | } |
---|
654 | |
---|
655 | if (ct->c_digested) { |
---|
656 | advise (NULL, "message %s has multiple %s: fields (%s)", |
---|
657 | ct->c_file, MD5_FIELD, dp = trimcpy (cp)); |
---|
658 | free (dp); |
---|
659 | free (cp); |
---|
660 | goto out; |
---|
661 | } |
---|
662 | |
---|
663 | ep = cp; |
---|
664 | while (isspace (*cp)) |
---|
665 | cp++; |
---|
666 | for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n')) |
---|
667 | *dp++ = ' '; |
---|
668 | for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) |
---|
669 | if (!isspace (*dp)) |
---|
670 | break; |
---|
671 | *++dp = '\0'; |
---|
672 | if (debugsw) |
---|
673 | fprintf (stderr, "%s: %s\n", MD5_FIELD, cp); |
---|
674 | |
---|
675 | if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) { |
---|
676 | free (ep); |
---|
677 | goto out; |
---|
678 | } |
---|
679 | |
---|
680 | for (dp = cp; *dp && !isspace (*dp); dp++) |
---|
681 | continue; |
---|
682 | *dp = '\0'; |
---|
683 | |
---|
684 | readDigest (ct, cp); |
---|
685 | free (ep); |
---|
686 | ct->c_digested++; |
---|
687 | goto got_header; |
---|
688 | } |
---|
689 | |
---|
690 | #if 0 |
---|
691 | if (uprf (name, XXX_FIELD_PRF)) |
---|
692 | advise (NULL, "unknown field (%s) in message %s", |
---|
693 | name, ct->c_file); |
---|
694 | /* and fall... */ |
---|
695 | #endif |
---|
696 | |
---|
697 | while (state == FLDPLUS) |
---|
698 | state = m_getfld (state, name, buf, sizeof(buf), in); |
---|
699 | |
---|
700 | got_header: |
---|
701 | if (state != FLDEOF) { |
---|
702 | ct->c_begin = ftell (in) + 1; |
---|
703 | continue; |
---|
704 | } |
---|
705 | /* else fall... */ |
---|
706 | |
---|
707 | case BODY: |
---|
708 | case BODYEOF: |
---|
709 | ct->c_begin = ftell (in) - strlen (buf); |
---|
710 | break; |
---|
711 | |
---|
712 | case FILEEOF: |
---|
713 | ct->c_begin = ftell (in); |
---|
714 | break; |
---|
715 | |
---|
716 | case LENERR: |
---|
717 | case FMTERR: |
---|
718 | adios (NULL, "message format error in component #%d", compnum); |
---|
719 | |
---|
720 | default: |
---|
721 | adios (NULL, "getfld() returned %d", state); |
---|
722 | } |
---|
723 | break; |
---|
724 | } |
---|
725 | |
---|
726 | /* |
---|
727 | * Check if we saw a Content-Type field. |
---|
728 | * If not, then assign a default value for |
---|
729 | * it, and the Init function. |
---|
730 | */ |
---|
731 | if (!ct->c_ctline) { |
---|
732 | /* |
---|
733 | * If we are inside a multipart/digest message, |
---|
734 | * so default type is message/rfc822 |
---|
735 | */ |
---|
736 | if (toplevel < 0) { |
---|
737 | if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK) |
---|
738 | goto out; |
---|
739 | ct->c_type = CT_MESSAGE; |
---|
740 | ct->c_ctinitfnx = InitMessage; |
---|
741 | } else { |
---|
742 | /* |
---|
743 | * Else default type is text/plain |
---|
744 | */ |
---|
745 | if (get_ctinfo ("text/plain", ct, 0) == NOTOK) |
---|
746 | goto out; |
---|
747 | ct->c_type = CT_TEXT; |
---|
748 | ct->c_ctinitfnx = InitText; |
---|
749 | } |
---|
750 | } |
---|
751 | |
---|
752 | /* Use default Transfer-Encoding, if necessary */ |
---|
753 | if (!ct->c_celine) { |
---|
754 | ct->c_encoding = CE_7BIT; |
---|
755 | Init7Bit (ct); |
---|
756 | } |
---|
757 | |
---|
758 | return ct; |
---|
759 | |
---|
760 | out: |
---|
761 | free_content (ct); |
---|
762 | return NULL; |
---|
763 | } |
---|
764 | |
---|
765 | |
---|
766 | /* |
---|
767 | * small routine to add header field to list |
---|
768 | */ |
---|
769 | |
---|
770 | static int |
---|
771 | add_header (CT ct, char *name, char *value) |
---|
772 | { |
---|
773 | HF hp; |
---|
774 | |
---|
775 | /* allocate header field structure */ |
---|
776 | if (!(hp = malloc (sizeof(*hp)))) |
---|
777 | adios (NULL, "out of memory"); |
---|
778 | |
---|
779 | /* link data into header structure */ |
---|
780 | hp->name = name; |
---|
781 | hp->value = value; |
---|
782 | hp->next = NULL; |
---|
783 | |
---|
784 | /* link header structure into the list */ |
---|
785 | if (ct->c_first_hf == NULL) { |
---|
786 | ct->c_first_hf = hp; /* this is the first */ |
---|
787 | ct->c_last_hf = hp; |
---|
788 | } else { |
---|
789 | ct->c_last_hf->next = hp; /* add it to the end */ |
---|
790 | ct->c_last_hf = hp; |
---|
791 | } |
---|
792 | |
---|
793 | return 0; |
---|
794 | } |
---|
795 | |
---|
796 | |
---|
797 | /* |
---|
798 | * Used to parse both: |
---|
799 | * 1) Content-Type line |
---|
800 | * 2) composition directives |
---|
801 | * |
---|
802 | * and fills in the information of the CTinfo structure. |
---|
803 | */ |
---|
804 | |
---|
805 | static int |
---|
806 | get_ctinfo (char *cp, CT ct, int magic) |
---|
807 | { |
---|
808 | int i; |
---|
809 | char *dp, **ap, **ep; |
---|
810 | char c; |
---|
811 | CI ci; |
---|
812 | |
---|
813 | ci = &ct->c_ctinfo; |
---|
814 | i = strlen (invo_name) + 2; |
---|
815 | |
---|
816 | /* store copy of Content-Type line */ |
---|
817 | cp = ct->c_ctline = add (cp, NULL); |
---|
818 | |
---|
819 | while (isspace (*cp)) /* trim leading spaces */ |
---|
820 | cp++; |
---|
821 | |
---|
822 | /* change newlines to spaces */ |
---|
823 | for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n')) |
---|
824 | *dp++ = ' '; |
---|
825 | |
---|
826 | /* trim trailing spaces */ |
---|
827 | for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) |
---|
828 | if (!isspace (*dp)) |
---|
829 | break; |
---|
830 | *++dp = '\0'; |
---|
831 | |
---|
832 | if (debugsw) |
---|
833 | fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp); |
---|
834 | |
---|
835 | if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) |
---|
836 | return NOTOK; |
---|
837 | |
---|
838 | for (dp = cp; istoken (*dp); dp++) |
---|
839 | continue; |
---|
840 | c = *dp, *dp = '\0'; |
---|
841 | ci->ci_type = add (cp, NULL); /* store content type */ |
---|
842 | *dp = c, cp = dp; |
---|
843 | |
---|
844 | if (!*ci->ci_type) { |
---|
845 | advise (NULL, "invalid %s: field in message %s (empty type)", |
---|
846 | TYPE_FIELD, ct->c_file); |
---|
847 | return NOTOK; |
---|
848 | } |
---|
849 | |
---|
850 | /* down case the content type string */ |
---|
851 | for (dp = ci->ci_type; *dp; dp++) |
---|
852 | if (isalpha(*dp) && isupper (*dp)) |
---|
853 | *dp = tolower (*dp); |
---|
854 | |
---|
855 | while (isspace (*cp)) |
---|
856 | cp++; |
---|
857 | |
---|
858 | if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) |
---|
859 | return NOTOK; |
---|
860 | |
---|
861 | if (*cp != '/') { |
---|
862 | if (!magic) |
---|
863 | ci->ci_subtype = add ("", NULL); |
---|
864 | goto magic_skip; |
---|
865 | } |
---|
866 | |
---|
867 | cp++; |
---|
868 | while (isspace (*cp)) |
---|
869 | cp++; |
---|
870 | |
---|
871 | if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) |
---|
872 | return NOTOK; |
---|
873 | |
---|
874 | for (dp = cp; istoken (*dp); dp++) |
---|
875 | continue; |
---|
876 | c = *dp, *dp = '\0'; |
---|
877 | ci->ci_subtype = add (cp, NULL); /* store the content subtype */ |
---|
878 | *dp = c, cp = dp; |
---|
879 | |
---|
880 | if (!*ci->ci_subtype) { |
---|
881 | advise (NULL, |
---|
882 | "invalid %s: field in message %s (empty subtype for \"%s\")", |
---|
883 | TYPE_FIELD, ct->c_file, ci->ci_type); |
---|
884 | return NOTOK; |
---|
885 | } |
---|
886 | |
---|
887 | /* down case the content subtype string */ |
---|
888 | for (dp = ci->ci_subtype; *dp; dp++) |
---|
889 | if (isalpha(*dp) && isupper (*dp)) |
---|
890 | *dp = tolower (*dp); |
---|
891 | |
---|
892 | magic_skip: |
---|
893 | while (isspace (*cp)) |
---|
894 | cp++; |
---|
895 | |
---|
896 | if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) |
---|
897 | return NOTOK; |
---|
898 | |
---|
899 | /* |
---|
900 | * Parse attribute/value pairs given with Content-Type |
---|
901 | */ |
---|
902 | ep = (ap = ci->ci_attrs) + NPARMS; |
---|
903 | while (*cp == ';') { |
---|
904 | char *vp, *up; |
---|
905 | |
---|
906 | if (ap >= ep) { |
---|
907 | advise (NULL, |
---|
908 | "too many parameters in message %s's %s: field (%d max)", |
---|
909 | ct->c_file, TYPE_FIELD, NPARMS); |
---|
910 | return NOTOK; |
---|
911 | } |
---|
912 | |
---|
913 | cp++; |
---|
914 | while (isspace (*cp)) |
---|
915 | cp++; |
---|
916 | |
---|
917 | if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) |
---|
918 | return NOTOK; |
---|
919 | |
---|
920 | if (*cp == 0) { |
---|
921 | advise (NULL, |
---|
922 | "extraneous trailing ';' in message %s's %s: parameter list", |
---|
923 | ct->c_file, TYPE_FIELD); |
---|
924 | return OK; |
---|
925 | } |
---|
926 | |
---|
927 | /* down case the attribute name */ |
---|
928 | for (dp = cp; istoken (*dp); dp++) |
---|
929 | if (isalpha(*dp) && isupper (*dp)) |
---|
930 | *dp = tolower (*dp); |
---|
931 | |
---|
932 | for (up = dp; isspace (*dp); ) |
---|
933 | dp++; |
---|
934 | if (dp == cp || *dp != '=') { |
---|
935 | advise (NULL, |
---|
936 | "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)", |
---|
937 | ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp); |
---|
938 | return NOTOK; |
---|
939 | } |
---|
940 | |
---|
941 | vp = (*ap = add (cp, NULL)) + (up - cp); |
---|
942 | *vp = '\0'; |
---|
943 | for (dp++; isspace (*dp); ) |
---|
944 | dp++; |
---|
945 | |
---|
946 | /* now add the attribute value */ |
---|
947 | ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp); |
---|
948 | |
---|
949 | if (*dp == '"') { |
---|
950 | for (cp = ++dp, dp = vp;;) { |
---|
951 | switch (c = *cp++) { |
---|
952 | case '\0': |
---|
953 | bad_quote: |
---|
954 | advise (NULL, |
---|
955 | "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", |
---|
956 | ct->c_file, TYPE_FIELD, i, i, "", *ap); |
---|
957 | return NOTOK; |
---|
958 | |
---|
959 | case '\\': |
---|
960 | *dp++ = c; |
---|
961 | if ((c = *cp++) == '\0') |
---|
962 | goto bad_quote; |
---|
963 | /* else fall... */ |
---|
964 | |
---|
965 | default: |
---|
966 | *dp++ = c; |
---|
967 | continue; |
---|
968 | |
---|
969 | case '"': |
---|
970 | *dp = '\0'; |
---|
971 | break; |
---|
972 | } |
---|
973 | break; |
---|
974 | } |
---|
975 | } else { |
---|
976 | for (cp = dp, dp = vp; istoken (*cp); cp++, dp++) |
---|
977 | continue; |
---|
978 | *dp = '\0'; |
---|
979 | } |
---|
980 | if (!*vp) { |
---|
981 | advise (NULL, |
---|
982 | "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", |
---|
983 | ct->c_file, TYPE_FIELD, i, i, "", *ap); |
---|
984 | return NOTOK; |
---|
985 | } |
---|
986 | ap++; |
---|
987 | |
---|
988 | while (isspace (*cp)) |
---|
989 | cp++; |
---|
990 | |
---|
991 | if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) |
---|
992 | return NOTOK; |
---|
993 | } |
---|
994 | |
---|
995 | /* |
---|
996 | * Get any <Content-Id> given in buffer |
---|
997 | */ |
---|
998 | if (magic && *cp == '<') { |
---|
999 | if (ct->c_id) { |
---|
1000 | free (ct->c_id); |
---|
1001 | ct->c_id = NULL; |
---|
1002 | } |
---|
1003 | if (!(dp = strchr(ct->c_id = ++cp, '>'))) { |
---|
1004 | advise (NULL, "invalid ID in message %s", ct->c_file); |
---|
1005 | return NOTOK; |
---|
1006 | } |
---|
1007 | c = *dp; |
---|
1008 | *dp = '\0'; |
---|
1009 | if (*ct->c_id) |
---|
1010 | ct->c_id = concat ("<", ct->c_id, ">\n", NULL); |
---|
1011 | else |
---|
1012 | ct->c_id = NULL; |
---|
1013 | *dp++ = c; |
---|
1014 | cp = dp; |
---|
1015 | |
---|
1016 | while (isspace (*cp)) |
---|
1017 | cp++; |
---|
1018 | } |
---|
1019 | |
---|
1020 | /* |
---|
1021 | * Get any [Content-Description] given in buffer. |
---|
1022 | */ |
---|
1023 | if (magic && *cp == '[') { |
---|
1024 | ct->c_descr = ++cp; |
---|
1025 | for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) |
---|
1026 | if (*dp == ']') |
---|
1027 | break; |
---|
1028 | if (dp < cp) { |
---|
1029 | advise (NULL, "invalid description in message %s", ct->c_file); |
---|
1030 | ct->c_descr = NULL; |
---|
1031 | return NOTOK; |
---|
1032 | } |
---|
1033 | |
---|
1034 | c = *dp; |
---|
1035 | *dp = '\0'; |
---|
1036 | if (*ct->c_descr) |
---|
1037 | ct->c_descr = concat (ct->c_descr, "\n", NULL); |
---|
1038 | else |
---|
1039 | ct->c_descr = NULL; |
---|
1040 | *dp++ = c; |
---|
1041 | cp = dp; |
---|
1042 | |
---|
1043 | while (isspace (*cp)) |
---|
1044 | cp++; |
---|
1045 | } |
---|
1046 | |
---|
1047 | /* |
---|
1048 | * Check if anything is left over |
---|
1049 | */ |
---|
1050 | if (*cp) { |
---|
1051 | if (magic) |
---|
1052 | ci->ci_magic = add (cp, NULL); |
---|
1053 | else |
---|
1054 | advise (NULL, |
---|
1055 | "extraneous information in message %s's %s: field\n%*.*s(%s)", |
---|
1056 | ct->c_file, TYPE_FIELD, i, i, "", cp); |
---|
1057 | } |
---|
1058 | |
---|
1059 | return OK; |
---|
1060 | } |
---|
1061 | |
---|
1062 | |
---|
1063 | static int |
---|
1064 | get_comment (CT ct, char **ap, int istype) |
---|
1065 | { |
---|
1066 | int i; |
---|
1067 | char *bp, *cp; |
---|
1068 | char c, buffer[BUFSIZ], *dp; |
---|
1069 | CI ci; |
---|
1070 | |
---|
1071 | ci = &ct->c_ctinfo; |
---|
1072 | cp = *ap; |
---|
1073 | bp = buffer; |
---|
1074 | cp++; |
---|
1075 | |
---|
1076 | for (i = 0;;) { |
---|
1077 | switch (c = *cp++) { |
---|
1078 | case '\0': |
---|
1079 | invalid: |
---|
1080 | advise (NULL, "invalid comment in message %s's %s: field", |
---|
1081 | ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD); |
---|
1082 | return NOTOK; |
---|
1083 | |
---|
1084 | case '\\': |
---|
1085 | *bp++ = c; |
---|
1086 | if ((c = *cp++) == '\0') |
---|
1087 | goto invalid; |
---|
1088 | *bp++ = c; |
---|
1089 | continue; |
---|
1090 | |
---|
1091 | case '(': |
---|
1092 | i++; |
---|
1093 | /* and fall... */ |
---|
1094 | default: |
---|
1095 | *bp++ = c; |
---|
1096 | continue; |
---|
1097 | |
---|
1098 | case ')': |
---|
1099 | if (--i < 0) |
---|
1100 | break; |
---|
1101 | *bp++ = c; |
---|
1102 | continue; |
---|
1103 | } |
---|
1104 | break; |
---|
1105 | } |
---|
1106 | *bp = '\0'; |
---|
1107 | |
---|
1108 | if (istype) { |
---|
1109 | if ((dp = ci->ci_comment)) { |
---|
1110 | ci->ci_comment = concat (dp, " ", buffer, NULL); |
---|
1111 | free (dp); |
---|
1112 | } else { |
---|
1113 | ci->ci_comment = add (buffer, NULL); |
---|
1114 | } |
---|
1115 | } |
---|
1116 | |
---|
1117 | while (isspace (*cp)) |
---|
1118 | cp++; |
---|
1119 | |
---|
1120 | *ap = cp; |
---|
1121 | return OK; |
---|
1122 | } |
---|
1123 | |
---|
1124 | |
---|
1125 | /* |
---|
1126 | * CONTENTS |
---|
1127 | * |
---|
1128 | * Handles content types audio, image, and video. |
---|
1129 | * There's not much to do right here. |
---|
1130 | */ |
---|
1131 | |
---|
1132 | static int |
---|
1133 | InitGeneric (CT ct) |
---|
1134 | { |
---|
1135 | return OK; /* not much to do here */ |
---|
1136 | } |
---|
1137 | |
---|
1138 | |
---|
1139 | /* |
---|
1140 | * TEXT |
---|
1141 | */ |
---|
1142 | |
---|
1143 | static int |
---|
1144 | InitText (CT ct) |
---|
1145 | { |
---|
1146 | char **ap, **ep; |
---|
1147 | struct k2v *kv; |
---|
1148 | struct text *t; |
---|
1149 | CI ci = &ct->c_ctinfo; |
---|
1150 | |
---|
1151 | /* check for missing subtype */ |
---|
1152 | if (!*ci->ci_subtype) |
---|
1153 | ci->ci_subtype = add ("plain", ci->ci_subtype); |
---|
1154 | |
---|
1155 | /* match subtype */ |
---|
1156 | for (kv = SubText; kv->kv_key; kv++) |
---|
1157 | if (!strcasecmp (ci->ci_subtype, kv->kv_key)) |
---|
1158 | break; |
---|
1159 | ct->c_subtype = kv->kv_value; |
---|
1160 | |
---|
1161 | /* allocate text character set structure */ |
---|
1162 | if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL) |
---|
1163 | adios (NULL, "out of memory"); |
---|
1164 | ct->c_ctparams = (void *) t; |
---|
1165 | |
---|
1166 | /* initially mark character set as unspecified */ |
---|
1167 | t->tx_charset = CHARSET_UNSPECIFIED; |
---|
1168 | |
---|
1169 | /* scan for charset parameter */ |
---|
1170 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) |
---|
1171 | if (!strcasecmp (*ap, "charset")) |
---|
1172 | break; |
---|
1173 | |
---|
1174 | /* check if content specified a character set */ |
---|
1175 | if (*ap) { |
---|
1176 | /* match character set or set to CHARSET_UNKNOWN */ |
---|
1177 | for (kv = Charset; kv->kv_key; kv++) |
---|
1178 | if (!strcasecmp (*ep, kv->kv_key)) |
---|
1179 | break; |
---|
1180 | t->tx_charset = kv->kv_value; |
---|
1181 | } |
---|
1182 | |
---|
1183 | return OK; |
---|
1184 | } |
---|
1185 | |
---|
1186 | |
---|
1187 | /* |
---|
1188 | * MULTIPART |
---|
1189 | */ |
---|
1190 | |
---|
1191 | static int |
---|
1192 | InitMultiPart (CT ct) |
---|
1193 | { |
---|
1194 | int inout; |
---|
1195 | long last, pos; |
---|
1196 | char *cp, *dp, **ap, **ep; |
---|
1197 | char *bp, buffer[BUFSIZ]; |
---|
1198 | struct multipart *m; |
---|
1199 | struct k2v *kv; |
---|
1200 | struct part *part, **next; |
---|
1201 | CI ci = &ct->c_ctinfo; |
---|
1202 | CT p; |
---|
1203 | FILE *fp; |
---|
1204 | |
---|
1205 | /* |
---|
1206 | * The encoding for multipart messages must be either |
---|
1207 | * 7bit, 8bit, or binary (per RFC2045). |
---|
1208 | */ |
---|
1209 | if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT |
---|
1210 | && ct->c_encoding != CE_BINARY) { |
---|
1211 | admonish (NULL, |
---|
1212 | "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary", |
---|
1213 | ci->ci_type, ci->ci_subtype, ct->c_file); |
---|
1214 | return NOTOK; |
---|
1215 | } |
---|
1216 | |
---|
1217 | /* match subtype */ |
---|
1218 | for (kv = SubMultiPart; kv->kv_key; kv++) |
---|
1219 | if (!strcasecmp (ci->ci_subtype, kv->kv_key)) |
---|
1220 | break; |
---|
1221 | ct->c_subtype = kv->kv_value; |
---|
1222 | |
---|
1223 | /* |
---|
1224 | * Check for "boundary" parameter, which is |
---|
1225 | * required for multipart messages. |
---|
1226 | */ |
---|
1227 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { |
---|
1228 | if (!strcasecmp (*ap, "boundary")) { |
---|
1229 | bp = *ep; |
---|
1230 | break; |
---|
1231 | } |
---|
1232 | } |
---|
1233 | |
---|
1234 | /* complain if boundary parameter is missing */ |
---|
1235 | if (!*ap) { |
---|
1236 | advise (NULL, |
---|
1237 | "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field", |
---|
1238 | ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD); |
---|
1239 | return NOTOK; |
---|
1240 | } |
---|
1241 | |
---|
1242 | /* allocate primary structure for multipart info */ |
---|
1243 | if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL) |
---|
1244 | adios (NULL, "out of memory"); |
---|
1245 | ct->c_ctparams = (void *) m; |
---|
1246 | |
---|
1247 | /* check if boundary parameter contains only whitespace characters */ |
---|
1248 | for (cp = bp; isspace (*cp); cp++) |
---|
1249 | continue; |
---|
1250 | if (!*cp) { |
---|
1251 | advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field", |
---|
1252 | ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD); |
---|
1253 | return NOTOK; |
---|
1254 | } |
---|
1255 | |
---|
1256 | /* remove trailing whitespace from boundary parameter */ |
---|
1257 | for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--) |
---|
1258 | if (!isspace (*dp)) |
---|
1259 | break; |
---|
1260 | *++dp = '\0'; |
---|
1261 | |
---|
1262 | /* record boundary separators */ |
---|
1263 | m->mp_start = concat (bp, "\n", NULL); |
---|
1264 | m->mp_stop = concat (bp, "--\n", NULL); |
---|
1265 | |
---|
1266 | if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { |
---|
1267 | advise (ct->c_file, "unable to open for reading"); |
---|
1268 | return NOTOK; |
---|
1269 | } |
---|
1270 | |
---|
1271 | fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET); |
---|
1272 | last = ct->c_end; |
---|
1273 | next = &m->mp_parts; |
---|
1274 | part = NULL; |
---|
1275 | inout = 1; |
---|
1276 | |
---|
1277 | while (fgets (buffer, sizeof(buffer) - 1, fp)) { |
---|
1278 | if (pos > last) |
---|
1279 | break; |
---|
1280 | |
---|
1281 | pos += strlen (buffer); |
---|
1282 | if (buffer[0] != '-' || buffer[1] != '-') |
---|
1283 | continue; |
---|
1284 | if (inout) { |
---|
1285 | if (strcmp (buffer + 2, m->mp_start)) |
---|
1286 | continue; |
---|
1287 | next_part: |
---|
1288 | if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL) |
---|
1289 | adios (NULL, "out of memory"); |
---|
1290 | *next = part; |
---|
1291 | next = &part->mp_next; |
---|
1292 | |
---|
1293 | if (!(p = get_content (fp, ct->c_file, |
---|
1294 | rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) { |
---|
1295 | fclose (ct->c_fp); |
---|
1296 | ct->c_fp = NULL; |
---|
1297 | return NOTOK; |
---|
1298 | } |
---|
1299 | p->c_fp = NULL; |
---|
1300 | part->mp_part = p; |
---|
1301 | pos = p->c_begin; |
---|
1302 | fseek (fp, pos, SEEK_SET); |
---|
1303 | inout = 0; |
---|
1304 | } else { |
---|
1305 | if (strcmp (buffer + 2, m->mp_start) == 0) { |
---|
1306 | inout = 1; |
---|
1307 | end_part: |
---|
1308 | p = part->mp_part; |
---|
1309 | p->c_end = ftell(fp) - (strlen(buffer) + 1); |
---|
1310 | if (p->c_end < p->c_begin) |
---|
1311 | p->c_begin = p->c_end; |
---|
1312 | if (inout) |
---|
1313 | goto next_part; |
---|
1314 | goto last_part; |
---|
1315 | } else { |
---|
1316 | if (strcmp (buffer + 2, m->mp_stop) == 0) |
---|
1317 | goto end_part; |
---|
1318 | } |
---|
1319 | } |
---|
1320 | } |
---|
1321 | |
---|
1322 | advise (NULL, "bogus multipart content in message %s", ct->c_file); |
---|
1323 | if (!inout && part) { |
---|
1324 | p = part->mp_part; |
---|
1325 | p->c_end = ct->c_end; |
---|
1326 | |
---|
1327 | if (p->c_begin >= p->c_end) { |
---|
1328 | for (next = &m->mp_parts; *next != part; |
---|
1329 | next = &((*next)->mp_next)) |
---|
1330 | continue; |
---|
1331 | *next = NULL; |
---|
1332 | free_content (p); |
---|
1333 | free ((char *) part); |
---|
1334 | } |
---|
1335 | } |
---|
1336 | |
---|
1337 | last_part: |
---|
1338 | /* reverse the order of the parts for multipart/alternative */ |
---|
1339 | if (ct->c_subtype == MULTI_ALTERNATE) |
---|
1340 | reverse_parts (ct); |
---|
1341 | |
---|
1342 | /* |
---|
1343 | * label all subparts with part number, and |
---|
1344 | * then initialize the content of the subpart. |
---|
1345 | */ |
---|
1346 | { |
---|
1347 | int partnum; |
---|
1348 | char *pp; |
---|
1349 | char partnam[BUFSIZ]; |
---|
1350 | |
---|
1351 | if (ct->c_partno) { |
---|
1352 | snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno); |
---|
1353 | pp = partnam + strlen (partnam); |
---|
1354 | } else { |
---|
1355 | pp = partnam; |
---|
1356 | } |
---|
1357 | |
---|
1358 | for (part = m->mp_parts, partnum = 1; part; |
---|
1359 | part = part->mp_next, partnum++) { |
---|
1360 | p = part->mp_part; |
---|
1361 | |
---|
1362 | sprintf (pp, "%d", partnum); |
---|
1363 | p->c_partno = add (partnam, NULL); |
---|
1364 | |
---|
1365 | /* initialize the content of the subparts */ |
---|
1366 | if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) { |
---|
1367 | fclose (ct->c_fp); |
---|
1368 | ct->c_fp = NULL; |
---|
1369 | return NOTOK; |
---|
1370 | } |
---|
1371 | } |
---|
1372 | } |
---|
1373 | |
---|
1374 | fclose (ct->c_fp); |
---|
1375 | ct->c_fp = NULL; |
---|
1376 | return OK; |
---|
1377 | } |
---|
1378 | |
---|
1379 | |
---|
1380 | /* |
---|
1381 | * reverse the order of the parts of a multipart |
---|
1382 | */ |
---|
1383 | |
---|
1384 | static void |
---|
1385 | reverse_parts (CT ct) |
---|
1386 | { |
---|
1387 | int i; |
---|
1388 | struct multipart *m; |
---|
1389 | struct part **base, **bmp, **next, *part; |
---|
1390 | |
---|
1391 | m = (struct multipart *) ct->c_ctparams; |
---|
1392 | |
---|
1393 | /* if only one part, just return */ |
---|
1394 | if (!m->mp_parts || !m->mp_parts->mp_next) |
---|
1395 | return; |
---|
1396 | |
---|
1397 | /* count number of parts */ |
---|
1398 | i = 0; |
---|
1399 | for (part = m->mp_parts; part; part = part->mp_next) |
---|
1400 | i++; |
---|
1401 | |
---|
1402 | /* allocate array of pointers to the parts */ |
---|
1403 | if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base)))) |
---|
1404 | adios (NULL, "out of memory"); |
---|
1405 | bmp = base; |
---|
1406 | |
---|
1407 | /* point at all the parts */ |
---|
1408 | for (part = m->mp_parts; part; part = part->mp_next) |
---|
1409 | *bmp++ = part; |
---|
1410 | *bmp = NULL; |
---|
1411 | |
---|
1412 | /* reverse the order of the parts */ |
---|
1413 | next = &m->mp_parts; |
---|
1414 | for (bmp--; bmp >= base; bmp--) { |
---|
1415 | part = *bmp; |
---|
1416 | *next = part; |
---|
1417 | next = &part->mp_next; |
---|
1418 | } |
---|
1419 | *next = NULL; |
---|
1420 | |
---|
1421 | /* free array of pointers */ |
---|
1422 | free ((char *) base); |
---|
1423 | } |
---|
1424 | |
---|
1425 | |
---|
1426 | /* |
---|
1427 | * MESSAGE |
---|
1428 | */ |
---|
1429 | |
---|
1430 | static int |
---|
1431 | InitMessage (CT ct) |
---|
1432 | { |
---|
1433 | struct k2v *kv; |
---|
1434 | CI ci = &ct->c_ctinfo; |
---|
1435 | |
---|
1436 | if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) { |
---|
1437 | admonish (NULL, |
---|
1438 | "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit", |
---|
1439 | ci->ci_type, ci->ci_subtype, ct->c_file); |
---|
1440 | return NOTOK; |
---|
1441 | } |
---|
1442 | |
---|
1443 | /* check for missing subtype */ |
---|
1444 | if (!*ci->ci_subtype) |
---|
1445 | ci->ci_subtype = add ("rfc822", ci->ci_subtype); |
---|
1446 | |
---|
1447 | /* match subtype */ |
---|
1448 | for (kv = SubMessage; kv->kv_key; kv++) |
---|
1449 | if (!strcasecmp (ci->ci_subtype, kv->kv_key)) |
---|
1450 | break; |
---|
1451 | ct->c_subtype = kv->kv_value; |
---|
1452 | |
---|
1453 | switch (ct->c_subtype) { |
---|
1454 | case MESSAGE_RFC822: |
---|
1455 | break; |
---|
1456 | |
---|
1457 | case MESSAGE_PARTIAL: |
---|
1458 | { |
---|
1459 | char **ap, **ep; |
---|
1460 | struct partial *p; |
---|
1461 | |
---|
1462 | if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL) |
---|
1463 | adios (NULL, "out of memory"); |
---|
1464 | ct->c_ctparams = (void *) p; |
---|
1465 | |
---|
1466 | /* scan for parameters "id", "number", and "total" */ |
---|
1467 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { |
---|
1468 | if (!strcasecmp (*ap, "id")) { |
---|
1469 | p->pm_partid = add (*ep, NULL); |
---|
1470 | continue; |
---|
1471 | } |
---|
1472 | if (!strcasecmp (*ap, "number")) { |
---|
1473 | if (sscanf (*ep, "%d", &p->pm_partno) != 1 |
---|
1474 | || p->pm_partno < 1) { |
---|
1475 | invalid_param: |
---|
1476 | advise (NULL, |
---|
1477 | "invalid %s parameter for \"%s/%s\" type in message %s's %s field", |
---|
1478 | *ap, ci->ci_type, ci->ci_subtype, |
---|
1479 | ct->c_file, TYPE_FIELD); |
---|
1480 | return NOTOK; |
---|
1481 | } |
---|
1482 | continue; |
---|
1483 | } |
---|
1484 | if (!strcasecmp (*ap, "total")) { |
---|
1485 | if (sscanf (*ep, "%d", &p->pm_maxno) != 1 |
---|
1486 | || p->pm_maxno < 1) |
---|
1487 | goto invalid_param; |
---|
1488 | continue; |
---|
1489 | } |
---|
1490 | } |
---|
1491 | |
---|
1492 | if (!p->pm_partid |
---|
1493 | || !p->pm_partno |
---|
1494 | || (p->pm_maxno && p->pm_partno > p->pm_maxno)) { |
---|
1495 | advise (NULL, |
---|
1496 | "invalid parameters for \"%s/%s\" type in message %s's %s field", |
---|
1497 | ci->ci_type, ci->ci_subtype, |
---|
1498 | ct->c_file, TYPE_FIELD); |
---|
1499 | return NOTOK; |
---|
1500 | } |
---|
1501 | } |
---|
1502 | break; |
---|
1503 | |
---|
1504 | case MESSAGE_EXTERNAL: |
---|
1505 | { |
---|
1506 | int exresult; |
---|
1507 | struct exbody *e; |
---|
1508 | CT p; |
---|
1509 | FILE *fp; |
---|
1510 | |
---|
1511 | if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL) |
---|
1512 | adios (NULL, "out of memory"); |
---|
1513 | ct->c_ctparams = (void *) e; |
---|
1514 | |
---|
1515 | if (!ct->c_fp |
---|
1516 | && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { |
---|
1517 | advise (ct->c_file, "unable to open for reading"); |
---|
1518 | return NOTOK; |
---|
1519 | } |
---|
1520 | |
---|
1521 | fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET); |
---|
1522 | |
---|
1523 | if (!(p = get_content (fp, ct->c_file, 0))) { |
---|
1524 | fclose (ct->c_fp); |
---|
1525 | ct->c_fp = NULL; |
---|
1526 | return NOTOK; |
---|
1527 | } |
---|
1528 | |
---|
1529 | e->eb_parent = ct; |
---|
1530 | e->eb_content = p; |
---|
1531 | p->c_ctexbody = e; |
---|
1532 | if ((exresult = params_external (ct, 0)) != NOTOK |
---|
1533 | && p->c_ceopenfnx == openMail) { |
---|
1534 | int cc, size; |
---|
1535 | char *bp; |
---|
1536 | |
---|
1537 | if ((size = ct->c_end - p->c_begin) <= 0) { |
---|
1538 | if (!e->eb_subject) |
---|
1539 | content_error (NULL, ct, |
---|
1540 | "empty body for access-type=mail-server"); |
---|
1541 | goto no_body; |
---|
1542 | } |
---|
1543 | |
---|
1544 | if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL) |
---|
1545 | adios (NULL, "out of memory"); |
---|
1546 | fseek (p->c_fp, p->c_begin, SEEK_SET); |
---|
1547 | while (size > 0) |
---|
1548 | switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) { |
---|
1549 | case NOTOK: |
---|
1550 | adios ("failed", "fread"); |
---|
1551 | |
---|
1552 | case OK: |
---|
1553 | adios (NULL, "unexpected EOF from fread"); |
---|
1554 | |
---|
1555 | default: |
---|
1556 | bp += cc, size -= cc; |
---|
1557 | break; |
---|
1558 | } |
---|
1559 | *bp = 0; |
---|
1560 | } |
---|
1561 | no_body: |
---|
1562 | p->c_fp = NULL; |
---|
1563 | p->c_end = p->c_begin; |
---|
1564 | |
---|
1565 | fclose (ct->c_fp); |
---|
1566 | ct->c_fp = NULL; |
---|
1567 | |
---|
1568 | if (exresult == NOTOK) |
---|
1569 | return NOTOK; |
---|
1570 | if (e->eb_flags == NOTOK) |
---|
1571 | return OK; |
---|
1572 | |
---|
1573 | switch (p->c_type) { |
---|
1574 | case CT_MULTIPART: |
---|
1575 | break; |
---|
1576 | |
---|
1577 | case CT_MESSAGE: |
---|
1578 | if (p->c_subtype != MESSAGE_RFC822) |
---|
1579 | break; |
---|
1580 | /* else fall... */ |
---|
1581 | default: |
---|
1582 | e->eb_partno = ct->c_partno; |
---|
1583 | if (p->c_ctinitfnx) |
---|
1584 | (*p->c_ctinitfnx) (p); |
---|
1585 | break; |
---|
1586 | } |
---|
1587 | } |
---|
1588 | break; |
---|
1589 | |
---|
1590 | default: |
---|
1591 | break; |
---|
1592 | } |
---|
1593 | |
---|
1594 | return OK; |
---|
1595 | } |
---|
1596 | |
---|
1597 | |
---|
1598 | static int |
---|
1599 | params_external (CT ct, int composing) |
---|
1600 | { |
---|
1601 | char **ap, **ep; |
---|
1602 | struct exbody *e = (struct exbody *) ct->c_ctparams; |
---|
1603 | CI ci = &ct->c_ctinfo; |
---|
1604 | |
---|
1605 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { |
---|
1606 | if (!strcasecmp (*ap, "access-type")) { |
---|
1607 | struct str2init *s2i; |
---|
1608 | CT p = e->eb_content; |
---|
1609 | |
---|
1610 | for (s2i = str2methods; s2i->si_key; s2i++) |
---|
1611 | if (!strcasecmp (*ep, s2i->si_key)) |
---|
1612 | break; |
---|
1613 | |
---|
1614 | if (!s2i->si_key) { |
---|
1615 | e->eb_access = *ep; |
---|
1616 | e->eb_flags = NOTOK; |
---|
1617 | p->c_encoding = CE_EXTERNAL; |
---|
1618 | continue; |
---|
1619 | } |
---|
1620 | e->eb_access = s2i->si_key; |
---|
1621 | e->eb_flags = s2i->si_val; |
---|
1622 | p->c_encoding = CE_EXTERNAL; |
---|
1623 | |
---|
1624 | /* Call the Init function for this external type */ |
---|
1625 | if ((*s2i->si_init)(p) == NOTOK) |
---|
1626 | return NOTOK; |
---|
1627 | continue; |
---|
1628 | } |
---|
1629 | if (!strcasecmp (*ap, "name")) { |
---|
1630 | e->eb_name = *ep; |
---|
1631 | continue; |
---|
1632 | } |
---|
1633 | if (!strcasecmp (*ap, "permission")) { |
---|
1634 | e->eb_permission = *ep; |
---|
1635 | continue; |
---|
1636 | } |
---|
1637 | if (!strcasecmp (*ap, "site")) { |
---|
1638 | e->eb_site = *ep; |
---|
1639 | continue; |
---|
1640 | } |
---|
1641 | if (!strcasecmp (*ap, "directory")) { |
---|
1642 | e->eb_dir = *ep; |
---|
1643 | continue; |
---|
1644 | } |
---|
1645 | if (!strcasecmp (*ap, "mode")) { |
---|
1646 | e->eb_mode = *ep; |
---|
1647 | continue; |
---|
1648 | } |
---|
1649 | if (!strcasecmp (*ap, "size")) { |
---|
1650 | sscanf (*ep, "%lu", &e->eb_size); |
---|
1651 | continue; |
---|
1652 | } |
---|
1653 | if (!strcasecmp (*ap, "server")) { |
---|
1654 | e->eb_server = *ep; |
---|
1655 | continue; |
---|
1656 | } |
---|
1657 | if (!strcasecmp (*ap, "subject")) { |
---|
1658 | e->eb_subject = *ep; |
---|
1659 | continue; |
---|
1660 | } |
---|
1661 | if (composing && !strcasecmp (*ap, "body")) { |
---|
1662 | e->eb_body = getcpy (*ep); |
---|
1663 | continue; |
---|
1664 | } |
---|
1665 | } |
---|
1666 | |
---|
1667 | if (!e->eb_access) { |
---|
1668 | advise (NULL, |
---|
1669 | "invalid parameters for \"%s/%s\" type in message %s's %s field", |
---|
1670 | ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD); |
---|
1671 | return NOTOK; |
---|
1672 | } |
---|
1673 | |
---|
1674 | return OK; |
---|
1675 | } |
---|
1676 | |
---|
1677 | |
---|
1678 | /* |
---|
1679 | * APPLICATION |
---|
1680 | */ |
---|
1681 | |
---|
1682 | static int |
---|
1683 | InitApplication (CT ct) |
---|
1684 | { |
---|
1685 | struct k2v *kv; |
---|
1686 | CI ci = &ct->c_ctinfo; |
---|
1687 | |
---|
1688 | /* match subtype */ |
---|
1689 | for (kv = SubApplication; kv->kv_key; kv++) |
---|
1690 | if (!strcasecmp (ci->ci_subtype, kv->kv_key)) |
---|
1691 | break; |
---|
1692 | ct->c_subtype = kv->kv_value; |
---|
1693 | |
---|
1694 | return OK; |
---|
1695 | } |
---|
1696 | |
---|
1697 | |
---|
1698 | /* |
---|
1699 | * Set up structures for placing unencoded |
---|
1700 | * content when building parts. |
---|
1701 | */ |
---|
1702 | |
---|
1703 | static int |
---|
1704 | init_decoded_content (CT ct) |
---|
1705 | { |
---|
1706 | CE ce; |
---|
1707 | |
---|
1708 | if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL) |
---|
1709 | adios (NULL, "out of memory"); |
---|
1710 | |
---|
1711 | ct->c_cefile = ce; |
---|
1712 | ct->c_ceopenfnx = open7Bit; /* since unencoded */ |
---|
1713 | ct->c_ceclosefnx = close_encoding; |
---|
1714 | ct->c_cesizefnx = NULL; /* since unencoded */ |
---|
1715 | |
---|
1716 | return OK; |
---|
1717 | } |
---|
1718 | |
---|
1719 | |
---|
1720 | /* |
---|
1721 | * TRANSFER ENCODINGS |
---|
1722 | */ |
---|
1723 | |
---|
1724 | static int |
---|
1725 | init_encoding (CT ct, OpenCEFunc openfnx) |
---|
1726 | { |
---|
1727 | CE ce; |
---|
1728 | |
---|
1729 | if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL) |
---|
1730 | adios (NULL, "out of memory"); |
---|
1731 | |
---|
1732 | ct->c_cefile = ce; |
---|
1733 | ct->c_ceopenfnx = openfnx; |
---|
1734 | ct->c_ceclosefnx = close_encoding; |
---|
1735 | ct->c_cesizefnx = size_encoding; |
---|
1736 | |
---|
1737 | return OK; |
---|
1738 | } |
---|
1739 | |
---|
1740 | |
---|
1741 | static void |
---|
1742 | close_encoding (CT ct) |
---|
1743 | { |
---|
1744 | CE ce; |
---|
1745 | |
---|
1746 | if (!(ce = ct->c_cefile)) |
---|
1747 | return; |
---|
1748 | |
---|
1749 | if (ce->ce_fp) { |
---|
1750 | fclose (ce->ce_fp); |
---|
1751 | ce->ce_fp = NULL; |
---|
1752 | } |
---|
1753 | } |
---|
1754 | |
---|
1755 | |
---|
1756 | static unsigned long |
---|
1757 | size_encoding (CT ct) |
---|
1758 | { |
---|
1759 | int fd; |
---|
1760 | unsigned long size; |
---|
1761 | char *file; |
---|
1762 | CE ce; |
---|
1763 | struct stat st; |
---|
1764 | |
---|
1765 | if (!(ce = ct->c_cefile)) |
---|
1766 | return (ct->c_end - ct->c_begin); |
---|
1767 | |
---|
1768 | if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK) |
---|
1769 | return (long) st.st_size; |
---|
1770 | |
---|
1771 | if (ce->ce_file) { |
---|
1772 | if (stat (ce->ce_file, &st) != NOTOK) |
---|
1773 | return (long) st.st_size; |
---|
1774 | else |
---|
1775 | return 0L; |
---|
1776 | } |
---|
1777 | |
---|
1778 | if (ct->c_encoding == CE_EXTERNAL) |
---|
1779 | return (ct->c_end - ct->c_begin); |
---|
1780 | |
---|
1781 | file = NULL; |
---|
1782 | if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK) |
---|
1783 | return (ct->c_end - ct->c_begin); |
---|
1784 | |
---|
1785 | if (fstat (fd, &st) != NOTOK) |
---|
1786 | size = (long) st.st_size; |
---|
1787 | else |
---|
1788 | size = 0L; |
---|
1789 | |
---|
1790 | (*ct->c_ceclosefnx) (ct); |
---|
1791 | return size; |
---|
1792 | } |
---|
1793 | |
---|
1794 | |
---|
1795 | /* |
---|
1796 | * BASE64 |
---|
1797 | */ |
---|
1798 | |
---|
1799 | static unsigned char b642nib[0x80] = { |
---|
1800 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
---|
1801 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
---|
1802 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
---|
1803 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
---|
1804 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
---|
1805 | 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, |
---|
1806 | 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, |
---|
1807 | 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
---|
1808 | 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, |
---|
1809 | 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, |
---|
1810 | 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, |
---|
1811 | 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, |
---|
1812 | 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, |
---|
1813 | 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, |
---|
1814 | 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, |
---|
1815 | 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff |
---|
1816 | }; |
---|
1817 | |
---|
1818 | |
---|
1819 | static int |
---|
1820 | InitBase64 (CT ct) |
---|
1821 | { |
---|
1822 | return init_encoding (ct, openBase64); |
---|
1823 | } |
---|
1824 | |
---|
1825 | |
---|
1826 | static int |
---|
1827 | openBase64 (CT ct, char **file) |
---|
1828 | { |
---|
1829 | int bitno, cc, digested; |
---|
1830 | int fd, len, skip; |
---|
1831 | unsigned long bits; |
---|
1832 | unsigned char value, *b, *b1, *b2, *b3; |
---|
1833 | char *cp, *ep, buffer[BUFSIZ]; |
---|
1834 | CE ce; |
---|
1835 | MD5_CTX mdContext; |
---|
1836 | |
---|
1837 | b = (unsigned char *) &bits; |
---|
1838 | b1 = &b[endian > 0 ? 1 : 2]; |
---|
1839 | b2 = &b[endian > 0 ? 2 : 1]; |
---|
1840 | b3 = &b[endian > 0 ? 3 : 0]; |
---|
1841 | |
---|
1842 | ce = ct->c_cefile; |
---|
1843 | if (ce->ce_fp) { |
---|
1844 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
1845 | goto ready_to_go; |
---|
1846 | } |
---|
1847 | |
---|
1848 | if (ce->ce_file) { |
---|
1849 | if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) { |
---|
1850 | content_error (ce->ce_file, ct, "unable to fopen for reading"); |
---|
1851 | return NOTOK; |
---|
1852 | } |
---|
1853 | goto ready_to_go; |
---|
1854 | } |
---|
1855 | |
---|
1856 | if (*file == NULL) { |
---|
1857 | ce->ce_file = add (m_scratch ("", tmp), NULL); |
---|
1858 | ce->ce_unlink = 1; |
---|
1859 | } else { |
---|
1860 | ce->ce_file = add (*file, NULL); |
---|
1861 | ce->ce_unlink = 0; |
---|
1862 | } |
---|
1863 | |
---|
1864 | if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { |
---|
1865 | content_error (ce->ce_file, ct, "unable to fopen for reading/writing"); |
---|
1866 | return NOTOK; |
---|
1867 | } |
---|
1868 | |
---|
1869 | if ((len = ct->c_end - ct->c_begin) < 0) |
---|
1870 | adios (NULL, "internal error(1)"); |
---|
1871 | |
---|
1872 | if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { |
---|
1873 | content_error (ct->c_file, ct, "unable to open for reading"); |
---|
1874 | return NOTOK; |
---|
1875 | } |
---|
1876 | |
---|
1877 | if ((digested = ct->c_digested)) |
---|
1878 | MD5Init (&mdContext); |
---|
1879 | |
---|
1880 | bitno = 18; |
---|
1881 | bits = 0L; |
---|
1882 | skip = 0; |
---|
1883 | |
---|
1884 | lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET); |
---|
1885 | while (len > 0) { |
---|
1886 | switch (cc = read (fd, buffer, sizeof(buffer) - 1)) { |
---|
1887 | case NOTOK: |
---|
1888 | content_error (ct->c_file, ct, "error reading from"); |
---|
1889 | goto clean_up; |
---|
1890 | |
---|
1891 | case OK: |
---|
1892 | content_error (NULL, ct, "premature eof"); |
---|
1893 | goto clean_up; |
---|
1894 | |
---|
1895 | default: |
---|
1896 | if (cc > len) |
---|
1897 | cc = len; |
---|
1898 | len -= cc; |
---|
1899 | |
---|
1900 | for (ep = (cp = buffer) + cc; cp < ep; cp++) { |
---|
1901 | switch (*cp) { |
---|
1902 | default: |
---|
1903 | if (isspace (*cp)) |
---|
1904 | break; |
---|
1905 | if (skip || (*cp & 0x80) |
---|
1906 | || (value = b642nib[*cp & 0x7f]) > 0x3f) { |
---|
1907 | if (debugsw) { |
---|
1908 | fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n", |
---|
1909 | *cp, |
---|
1910 | (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)), |
---|
1911 | skip); |
---|
1912 | } |
---|
1913 | content_error (NULL, ct, |
---|
1914 | "invalid BASE64 encoding -- continuing"); |
---|
1915 | continue; |
---|
1916 | } |
---|
1917 | |
---|
1918 | bits |= value << bitno; |
---|
1919 | test_end: |
---|
1920 | if ((bitno -= 6) < 0) { |
---|
1921 | putc ((char) *b1, ce->ce_fp); |
---|
1922 | if (digested) |
---|
1923 | MD5Update (&mdContext, b1, 1); |
---|
1924 | if (skip < 2) { |
---|
1925 | putc ((char) *b2, ce->ce_fp); |
---|
1926 | if (digested) |
---|
1927 | MD5Update (&mdContext, b2, 1); |
---|
1928 | if (skip < 1) { |
---|
1929 | putc ((char) *b3, ce->ce_fp); |
---|
1930 | if (digested) |
---|
1931 | MD5Update (&mdContext, b3, 1); |
---|
1932 | } |
---|
1933 | } |
---|
1934 | |
---|
1935 | if (ferror (ce->ce_fp)) { |
---|
1936 | content_error (ce->ce_file, ct, |
---|
1937 | "error writing to"); |
---|
1938 | goto clean_up; |
---|
1939 | } |
---|
1940 | bitno = 18, bits = 0L, skip = 0; |
---|
1941 | } |
---|
1942 | break; |
---|
1943 | |
---|
1944 | case '=': |
---|
1945 | if (++skip > 3) |
---|
1946 | goto self_delimiting; |
---|
1947 | goto test_end; |
---|
1948 | } |
---|
1949 | } |
---|
1950 | } |
---|
1951 | } |
---|
1952 | |
---|
1953 | if (bitno != 18) { |
---|
1954 | if (debugsw) |
---|
1955 | fprintf (stderr, "premature ending (bitno %d)\n", bitno); |
---|
1956 | |
---|
1957 | content_error (NULL, ct, "invalid BASE64 encoding"); |
---|
1958 | goto clean_up; |
---|
1959 | } |
---|
1960 | |
---|
1961 | self_delimiting: |
---|
1962 | fseek (ct->c_fp, 0L, SEEK_SET); |
---|
1963 | |
---|
1964 | if (fflush (ce->ce_fp)) { |
---|
1965 | content_error (ce->ce_file, ct, "error writing to"); |
---|
1966 | goto clean_up; |
---|
1967 | } |
---|
1968 | |
---|
1969 | if (digested) { |
---|
1970 | unsigned char digest[16]; |
---|
1971 | |
---|
1972 | MD5Final (digest, &mdContext); |
---|
1973 | if (memcmp((char *) digest, (char *) ct->c_digest, |
---|
1974 | sizeof(digest) / sizeof(digest[0]))) |
---|
1975 | content_error (NULL, ct, |
---|
1976 | "content integrity suspect (digest mismatch) -- continuing"); |
---|
1977 | else |
---|
1978 | if (debugsw) |
---|
1979 | fprintf (stderr, "content integrity confirmed\n"); |
---|
1980 | } |
---|
1981 | |
---|
1982 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
1983 | |
---|
1984 | ready_to_go: |
---|
1985 | *file = ce->ce_file; |
---|
1986 | return fileno (ce->ce_fp); |
---|
1987 | |
---|
1988 | clean_up: |
---|
1989 | free_encoding (ct, 0); |
---|
1990 | return NOTOK; |
---|
1991 | } |
---|
1992 | |
---|
1993 | |
---|
1994 | /* |
---|
1995 | * QUOTED PRINTABLE |
---|
1996 | */ |
---|
1997 | |
---|
1998 | static char hex2nib[0x80] = { |
---|
1999 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2000 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2001 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2002 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2003 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2004 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2005 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
---|
2006 | 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2007 | 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, |
---|
2008 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2009 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2010 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2011 | 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, |
---|
2012 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2013 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
2014 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
---|
2015 | }; |
---|
2016 | |
---|
2017 | |
---|
2018 | static int |
---|
2019 | InitQuoted (CT ct) |
---|
2020 | { |
---|
2021 | return init_encoding (ct, openQuoted); |
---|
2022 | } |
---|
2023 | |
---|
2024 | |
---|
2025 | static int |
---|
2026 | openQuoted (CT ct, char **file) |
---|
2027 | { |
---|
2028 | int cc, digested, len, quoted; |
---|
2029 | char *cp, *ep; |
---|
2030 | char buffer[BUFSIZ]; |
---|
2031 | unsigned char mask; |
---|
2032 | CE ce; |
---|
2033 | MD5_CTX mdContext; |
---|
2034 | |
---|
2035 | ce = ct->c_cefile; |
---|
2036 | if (ce->ce_fp) { |
---|
2037 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
2038 | goto ready_to_go; |
---|
2039 | } |
---|
2040 | |
---|
2041 | if (ce->ce_file) { |
---|
2042 | if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) { |
---|
2043 | content_error (ce->ce_file, ct, "unable to fopen for reading"); |
---|
2044 | return NOTOK; |
---|
2045 | } |
---|
2046 | goto ready_to_go; |
---|
2047 | } |
---|
2048 | |
---|
2049 | if (*file == NULL) { |
---|
2050 | ce->ce_file = add (m_scratch ("", tmp), NULL); |
---|
2051 | ce->ce_unlink = 1; |
---|
2052 | } else { |
---|
2053 | ce->ce_file = add (*file, NULL); |
---|
2054 | ce->ce_unlink = 0; |
---|
2055 | } |
---|
2056 | |
---|
2057 | if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { |
---|
2058 | content_error (ce->ce_file, ct, "unable to fopen for reading/writing"); |
---|
2059 | return NOTOK; |
---|
2060 | } |
---|
2061 | |
---|
2062 | if ((len = ct->c_end - ct->c_begin) < 0) |
---|
2063 | adios (NULL, "internal error(2)"); |
---|
2064 | |
---|
2065 | if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { |
---|
2066 | content_error (ct->c_file, ct, "unable to open for reading"); |
---|
2067 | return NOTOK; |
---|
2068 | } |
---|
2069 | |
---|
2070 | if ((digested = ct->c_digested)) |
---|
2071 | MD5Init (&mdContext); |
---|
2072 | |
---|
2073 | quoted = 0; |
---|
2074 | #ifdef lint |
---|
2075 | mask = 0; |
---|
2076 | #endif |
---|
2077 | |
---|
2078 | fseek (ct->c_fp, ct->c_begin, SEEK_SET); |
---|
2079 | while (len > 0) { |
---|
2080 | char *dp; |
---|
2081 | |
---|
2082 | if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) { |
---|
2083 | content_error (NULL, ct, "premature eof"); |
---|
2084 | goto clean_up; |
---|
2085 | } |
---|
2086 | |
---|
2087 | if ((cc = strlen (buffer)) > len) |
---|
2088 | cc = len; |
---|
2089 | len -= cc; |
---|
2090 | |
---|
2091 | for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--) |
---|
2092 | if (!isspace (*ep)) |
---|
2093 | break; |
---|
2094 | *++ep = '\n', ep++; |
---|
2095 | |
---|
2096 | for (; cp < ep; cp++) { |
---|
2097 | if (quoted) { |
---|
2098 | if (quoted > 1) { |
---|
2099 | if (!isxdigit (*cp)) { |
---|
2100 | invalid_hex: |
---|
2101 | dp = "expecting hexidecimal-digit"; |
---|
2102 | goto invalid_encoding; |
---|
2103 | } |
---|
2104 | mask <<= 4; |
---|
2105 | mask |= hex2nib[*cp & 0x7f]; |
---|
2106 | putc (mask, ce->ce_fp); |
---|
2107 | if (digested) |
---|
2108 | MD5Update (&mdContext, &mask, 1); |
---|
2109 | } else { |
---|
2110 | switch (*cp) { |
---|
2111 | case ':': |
---|
2112 | putc (*cp, ce->ce_fp); |
---|
2113 | if (digested) |
---|
2114 | MD5Update (&mdContext, (unsigned char *) ":", 1); |
---|
2115 | break; |
---|
2116 | |
---|
2117 | default: |
---|
2118 | if (!isxdigit (*cp)) |
---|
2119 | goto invalid_hex; |
---|
2120 | mask = hex2nib[*cp & 0x7f]; |
---|
2121 | quoted = 2; |
---|
2122 | continue; |
---|
2123 | } |
---|
2124 | } |
---|
2125 | |
---|
2126 | if (ferror (ce->ce_fp)) { |
---|
2127 | content_error (ce->ce_file, ct, "error writing to"); |
---|
2128 | goto clean_up; |
---|
2129 | } |
---|
2130 | quoted = 0; |
---|
2131 | continue; |
---|
2132 | } |
---|
2133 | |
---|
2134 | switch (*cp) { |
---|
2135 | default: |
---|
2136 | if (*cp < '!' || *cp > '~') { |
---|
2137 | int i; |
---|
2138 | dp = "expecting character in range [!..~]"; |
---|
2139 | |
---|
2140 | invalid_encoding: |
---|
2141 | i = strlen (invo_name) + 2; |
---|
2142 | content_error (NULL, ct, |
---|
2143 | "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x", |
---|
2144 | dp, i, i, "", *cp); |
---|
2145 | goto clean_up; |
---|
2146 | } |
---|
2147 | /* and fall...*/ |
---|
2148 | case ' ': |
---|
2149 | case '\t': |
---|
2150 | case '\n': |
---|
2151 | putc (*cp, ce->ce_fp); |
---|
2152 | if (digested) { |
---|
2153 | if (*cp == '\n') |
---|
2154 | MD5Update (&mdContext, (unsigned char *) "\r\n",2); |
---|
2155 | else |
---|
2156 | MD5Update (&mdContext, (unsigned char *) cp, 1); |
---|
2157 | } |
---|
2158 | if (ferror (ce->ce_fp)) { |
---|
2159 | content_error (ce->ce_file, ct, "error writing to"); |
---|
2160 | goto clean_up; |
---|
2161 | } |
---|
2162 | break; |
---|
2163 | |
---|
2164 | case '=': |
---|
2165 | if (*++cp != '\n') { |
---|
2166 | quoted = 1; |
---|
2167 | cp--; |
---|
2168 | } |
---|
2169 | break; |
---|
2170 | } |
---|
2171 | } |
---|
2172 | } |
---|
2173 | if (quoted) { |
---|
2174 | content_error (NULL, ct, |
---|
2175 | "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting"); |
---|
2176 | goto clean_up; |
---|
2177 | } |
---|
2178 | |
---|
2179 | fseek (ct->c_fp, 0L, SEEK_SET); |
---|
2180 | |
---|
2181 | if (fflush (ce->ce_fp)) { |
---|
2182 | content_error (ce->ce_file, ct, "error writing to"); |
---|
2183 | goto clean_up; |
---|
2184 | } |
---|
2185 | |
---|
2186 | if (digested) { |
---|
2187 | unsigned char digest[16]; |
---|
2188 | |
---|
2189 | MD5Final (digest, &mdContext); |
---|
2190 | if (memcmp((char *) digest, (char *) ct->c_digest, |
---|
2191 | sizeof(digest) / sizeof(digest[0]))) |
---|
2192 | content_error (NULL, ct, |
---|
2193 | "content integrity suspect (digest mismatch) -- continuing"); |
---|
2194 | else |
---|
2195 | if (debugsw) |
---|
2196 | fprintf (stderr, "content integrity confirmed\n"); |
---|
2197 | } |
---|
2198 | |
---|
2199 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
2200 | |
---|
2201 | ready_to_go: |
---|
2202 | *file = ce->ce_file; |
---|
2203 | return fileno (ce->ce_fp); |
---|
2204 | |
---|
2205 | clean_up: |
---|
2206 | free_encoding (ct, 0); |
---|
2207 | return NOTOK; |
---|
2208 | } |
---|
2209 | |
---|
2210 | |
---|
2211 | /* |
---|
2212 | * 7BIT |
---|
2213 | */ |
---|
2214 | |
---|
2215 | static int |
---|
2216 | Init7Bit (CT ct) |
---|
2217 | { |
---|
2218 | if (init_encoding (ct, open7Bit) == NOTOK) |
---|
2219 | return NOTOK; |
---|
2220 | |
---|
2221 | ct->c_cesizefnx = NULL; /* no need to decode for real size */ |
---|
2222 | return OK; |
---|
2223 | } |
---|
2224 | |
---|
2225 | |
---|
2226 | static int |
---|
2227 | open7Bit (CT ct, char **file) |
---|
2228 | { |
---|
2229 | int cc, fd, len; |
---|
2230 | char buffer[BUFSIZ]; |
---|
2231 | CE ce; |
---|
2232 | |
---|
2233 | ce = ct->c_cefile; |
---|
2234 | if (ce->ce_fp) { |
---|
2235 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
2236 | goto ready_to_go; |
---|
2237 | } |
---|
2238 | |
---|
2239 | if (ce->ce_file) { |
---|
2240 | if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) { |
---|
2241 | content_error (ce->ce_file, ct, "unable to fopen for reading"); |
---|
2242 | return NOTOK; |
---|
2243 | } |
---|
2244 | goto ready_to_go; |
---|
2245 | } |
---|
2246 | |
---|
2247 | if (*file == NULL) { |
---|
2248 | ce->ce_file = add (m_scratch ("", tmp), NULL); |
---|
2249 | ce->ce_unlink = 1; |
---|
2250 | } else { |
---|
2251 | ce->ce_file = add (*file, NULL); |
---|
2252 | ce->ce_unlink = 0; |
---|
2253 | } |
---|
2254 | |
---|
2255 | if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { |
---|
2256 | content_error (ce->ce_file, ct, "unable to fopen for reading/writing"); |
---|
2257 | return NOTOK; |
---|
2258 | } |
---|
2259 | |
---|
2260 | if (ct->c_type == CT_MULTIPART) { |
---|
2261 | char **ap, **ep; |
---|
2262 | CI ci = &ct->c_ctinfo; |
---|
2263 | |
---|
2264 | len = 0; |
---|
2265 | fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype); |
---|
2266 | len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type) |
---|
2267 | + 1 + strlen (ci->ci_subtype); |
---|
2268 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { |
---|
2269 | putc (';', ce->ce_fp); |
---|
2270 | len++; |
---|
2271 | |
---|
2272 | snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep); |
---|
2273 | |
---|
2274 | if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) { |
---|
2275 | fputs ("\n\t", ce->ce_fp); |
---|
2276 | len = 8; |
---|
2277 | } else { |
---|
2278 | putc (' ', ce->ce_fp); |
---|
2279 | len++; |
---|
2280 | } |
---|
2281 | fprintf (ce->ce_fp, "%s", buffer); |
---|
2282 | len += cc; |
---|
2283 | } |
---|
2284 | |
---|
2285 | if (ci->ci_comment) { |
---|
2286 | if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) { |
---|
2287 | fputs ("\n\t", ce->ce_fp); |
---|
2288 | len = 8; |
---|
2289 | } |
---|
2290 | else { |
---|
2291 | putc (' ', ce->ce_fp); |
---|
2292 | len++; |
---|
2293 | } |
---|
2294 | fprintf (ce->ce_fp, "(%s)", ci->ci_comment); |
---|
2295 | len += cc; |
---|
2296 | } |
---|
2297 | fprintf (ce->ce_fp, "\n"); |
---|
2298 | if (ct->c_id) |
---|
2299 | fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id); |
---|
2300 | if (ct->c_descr) |
---|
2301 | fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr); |
---|
2302 | fprintf (ce->ce_fp, "\n"); |
---|
2303 | } |
---|
2304 | |
---|
2305 | if ((len = ct->c_end - ct->c_begin) < 0) |
---|
2306 | adios (NULL, "internal error(3)"); |
---|
2307 | |
---|
2308 | if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { |
---|
2309 | content_error (ct->c_file, ct, "unable to open for reading"); |
---|
2310 | return NOTOK; |
---|
2311 | } |
---|
2312 | |
---|
2313 | lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET); |
---|
2314 | while (len > 0) |
---|
2315 | switch (cc = read (fd, buffer, sizeof(buffer) - 1)) { |
---|
2316 | case NOTOK: |
---|
2317 | content_error (ct->c_file, ct, "error reading from"); |
---|
2318 | goto clean_up; |
---|
2319 | |
---|
2320 | case OK: |
---|
2321 | content_error (NULL, ct, "premature eof"); |
---|
2322 | goto clean_up; |
---|
2323 | |
---|
2324 | default: |
---|
2325 | if (cc > len) |
---|
2326 | cc = len; |
---|
2327 | len -= cc; |
---|
2328 | |
---|
2329 | fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp); |
---|
2330 | if (ferror (ce->ce_fp)) { |
---|
2331 | content_error (ce->ce_file, ct, "error writing to"); |
---|
2332 | goto clean_up; |
---|
2333 | } |
---|
2334 | } |
---|
2335 | |
---|
2336 | fseek (ct->c_fp, 0L, SEEK_SET); |
---|
2337 | |
---|
2338 | if (fflush (ce->ce_fp)) { |
---|
2339 | content_error (ce->ce_file, ct, "error writing to"); |
---|
2340 | goto clean_up; |
---|
2341 | } |
---|
2342 | |
---|
2343 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
2344 | |
---|
2345 | ready_to_go: |
---|
2346 | *file = ce->ce_file; |
---|
2347 | return fileno (ce->ce_fp); |
---|
2348 | |
---|
2349 | clean_up: |
---|
2350 | free_encoding (ct, 0); |
---|
2351 | return NOTOK; |
---|
2352 | } |
---|
2353 | |
---|
2354 | |
---|
2355 | /* |
---|
2356 | * External |
---|
2357 | */ |
---|
2358 | |
---|
2359 | static int |
---|
2360 | openExternal (CT ct, CT cb, CE ce, char **file, int *fd) |
---|
2361 | { |
---|
2362 | char cachefile[BUFSIZ]; |
---|
2363 | |
---|
2364 | if (ce->ce_fp) { |
---|
2365 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
2366 | goto ready_already; |
---|
2367 | } |
---|
2368 | |
---|
2369 | if (ce->ce_file) { |
---|
2370 | if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) { |
---|
2371 | content_error (ce->ce_file, ct, "unable to fopen for reading"); |
---|
2372 | return NOTOK; |
---|
2373 | } |
---|
2374 | goto ready_already; |
---|
2375 | } |
---|
2376 | |
---|
2377 | if (find_cache (ct, rcachesw, (int *) 0, cb->c_id, |
---|
2378 | cachefile, sizeof(cachefile)) != NOTOK) { |
---|
2379 | if ((ce->ce_fp = fopen (cachefile, "r"))) { |
---|
2380 | ce->ce_file = getcpy (cachefile); |
---|
2381 | ce->ce_unlink = 0; |
---|
2382 | goto ready_already; |
---|
2383 | } else { |
---|
2384 | admonish (cachefile, "unable to fopen for reading"); |
---|
2385 | } |
---|
2386 | } |
---|
2387 | |
---|
2388 | return OK; |
---|
2389 | |
---|
2390 | ready_already: |
---|
2391 | *file = ce->ce_file; |
---|
2392 | *fd = fileno (ce->ce_fp); |
---|
2393 | return DONE; |
---|
2394 | } |
---|
2395 | |
---|
2396 | /* |
---|
2397 | * File |
---|
2398 | */ |
---|
2399 | |
---|
2400 | static int |
---|
2401 | InitFile (CT ct) |
---|
2402 | { |
---|
2403 | return init_encoding (ct, openFile); |
---|
2404 | } |
---|
2405 | |
---|
2406 | |
---|
2407 | static int |
---|
2408 | openFile (CT ct, char **file) |
---|
2409 | { |
---|
2410 | int fd, cachetype; |
---|
2411 | char cachefile[BUFSIZ]; |
---|
2412 | struct exbody *e = ct->c_ctexbody; |
---|
2413 | CE ce = ct->c_cefile; |
---|
2414 | |
---|
2415 | switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) { |
---|
2416 | case NOTOK: |
---|
2417 | return NOTOK; |
---|
2418 | |
---|
2419 | case OK: |
---|
2420 | break; |
---|
2421 | |
---|
2422 | case DONE: |
---|
2423 | return fd; |
---|
2424 | } |
---|
2425 | |
---|
2426 | if (!e->eb_name) { |
---|
2427 | content_error (NULL, ct, "missing name parameter"); |
---|
2428 | return NOTOK; |
---|
2429 | } |
---|
2430 | |
---|
2431 | ce->ce_file = getcpy (e->eb_name); |
---|
2432 | ce->ce_unlink = 0; |
---|
2433 | |
---|
2434 | if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) { |
---|
2435 | content_error (ce->ce_file, ct, "unable to fopen for reading"); |
---|
2436 | return NOTOK; |
---|
2437 | } |
---|
2438 | |
---|
2439 | if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write")) |
---|
2440 | && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id, |
---|
2441 | cachefile, sizeof(cachefile)) != NOTOK) { |
---|
2442 | int mask; |
---|
2443 | FILE *fp; |
---|
2444 | |
---|
2445 | mask = umask (cachetype ? ~m_gmprot () : 0222); |
---|
2446 | if ((fp = fopen (cachefile, "w"))) { |
---|
2447 | int cc; |
---|
2448 | char buffer[BUFSIZ]; |
---|
2449 | FILE *gp = ce->ce_fp; |
---|
2450 | |
---|
2451 | fseek (gp, 0L, SEEK_SET); |
---|
2452 | |
---|
2453 | while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp)) |
---|
2454 | > 0) |
---|
2455 | fwrite (buffer, sizeof(*buffer), cc, fp); |
---|
2456 | fflush (fp); |
---|
2457 | |
---|
2458 | if (ferror (gp)) { |
---|
2459 | admonish (ce->ce_file, "error reading"); |
---|
2460 | unlink (cachefile); |
---|
2461 | } |
---|
2462 | else |
---|
2463 | if (ferror (fp)) { |
---|
2464 | admonish (cachefile, "error writing"); |
---|
2465 | unlink (cachefile); |
---|
2466 | } |
---|
2467 | fclose (fp); |
---|
2468 | } |
---|
2469 | umask (mask); |
---|
2470 | } |
---|
2471 | |
---|
2472 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
2473 | *file = ce->ce_file; |
---|
2474 | return fileno (ce->ce_fp); |
---|
2475 | } |
---|
2476 | |
---|
2477 | /* |
---|
2478 | * FTP |
---|
2479 | */ |
---|
2480 | |
---|
2481 | static int |
---|
2482 | InitFTP (CT ct) |
---|
2483 | { |
---|
2484 | return init_encoding (ct, openFTP); |
---|
2485 | } |
---|
2486 | |
---|
2487 | |
---|
2488 | static int |
---|
2489 | openFTP (CT ct, char **file) |
---|
2490 | { |
---|
2491 | int cachetype, caching, fd; |
---|
2492 | int len, buflen; |
---|
2493 | char *bp, *ftp, *user, *pass; |
---|
2494 | char buffer[BUFSIZ], cachefile[BUFSIZ]; |
---|
2495 | struct exbody *e; |
---|
2496 | CE ce; |
---|
2497 | static char *username = NULL; |
---|
2498 | static char *password = NULL; |
---|
2499 | |
---|
2500 | e = ct->c_ctexbody; |
---|
2501 | ce = ct->c_cefile; |
---|
2502 | |
---|
2503 | if ((ftp = context_find (nmhaccessftp)) && !*ftp) |
---|
2504 | ftp = NULL; |
---|
2505 | |
---|
2506 | #ifndef BUILTIN_FTP |
---|
2507 | if (!ftp) |
---|
2508 | return NOTOK; |
---|
2509 | #endif |
---|
2510 | |
---|
2511 | switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) { |
---|
2512 | case NOTOK: |
---|
2513 | return NOTOK; |
---|
2514 | |
---|
2515 | case OK: |
---|
2516 | break; |
---|
2517 | |
---|
2518 | case DONE: |
---|
2519 | return fd; |
---|
2520 | } |
---|
2521 | |
---|
2522 | if (!e->eb_name || !e->eb_site) { |
---|
2523 | content_error (NULL, ct, "missing %s parameter", |
---|
2524 | e->eb_name ? "site": "name"); |
---|
2525 | return NOTOK; |
---|
2526 | } |
---|
2527 | |
---|
2528 | if (xpid) { |
---|
2529 | if (xpid < 0) |
---|
2530 | xpid = -xpid; |
---|
2531 | pidcheck (pidwait (xpid, NOTOK)); |
---|
2532 | xpid = 0; |
---|
2533 | } |
---|
2534 | |
---|
2535 | /* Get the buffer ready to go */ |
---|
2536 | bp = buffer; |
---|
2537 | buflen = sizeof(buffer); |
---|
2538 | |
---|
2539 | /* |
---|
2540 | * Construct the query message for user |
---|
2541 | */ |
---|
2542 | snprintf (bp, buflen, "Retrieve %s", e->eb_name); |
---|
2543 | len = strlen (bp); |
---|
2544 | bp += len; |
---|
2545 | buflen -= len; |
---|
2546 | |
---|
2547 | if (e->eb_partno) { |
---|
2548 | snprintf (bp, buflen, " (content %s)", e->eb_partno); |
---|
2549 | len = strlen (bp); |
---|
2550 | bp += len; |
---|
2551 | buflen -= len; |
---|
2552 | } |
---|
2553 | |
---|
2554 | snprintf (bp, buflen, "\n using %sFTP from site %s", |
---|
2555 | e->eb_flags ? "anonymous " : "", e->eb_site); |
---|
2556 | len = strlen (bp); |
---|
2557 | bp += len; |
---|
2558 | buflen -= len; |
---|
2559 | |
---|
2560 | if (e->eb_size > 0) { |
---|
2561 | snprintf (bp, buflen, " (%lu octets)", e->eb_size); |
---|
2562 | len = strlen (bp); |
---|
2563 | bp += len; |
---|
2564 | buflen -= len; |
---|
2565 | } |
---|
2566 | snprintf (bp, buflen, "? "); |
---|
2567 | |
---|
2568 | /* |
---|
2569 | * Now, check the answer |
---|
2570 | */ |
---|
2571 | if (!getanswer (buffer)) |
---|
2572 | return NOTOK; |
---|
2573 | |
---|
2574 | if (e->eb_flags) { |
---|
2575 | user = "anonymous"; |
---|
2576 | snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ()); |
---|
2577 | pass = buffer; |
---|
2578 | } else { |
---|
2579 | ruserpass (e->eb_site, &username, &password); |
---|
2580 | user = username; |
---|
2581 | pass = password; |
---|
2582 | } |
---|
2583 | |
---|
2584 | ce->ce_unlink = (*file == NULL); |
---|
2585 | caching = 0; |
---|
2586 | cachefile[0] = '\0'; |
---|
2587 | if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write")) |
---|
2588 | && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id, |
---|
2589 | cachefile, sizeof(cachefile)) != NOTOK) { |
---|
2590 | if (*file == NULL) { |
---|
2591 | ce->ce_unlink = 0; |
---|
2592 | caching = 1; |
---|
2593 | } |
---|
2594 | } |
---|
2595 | |
---|
2596 | if (*file) |
---|
2597 | ce->ce_file = add (*file, NULL); |
---|
2598 | else if (caching) |
---|
2599 | ce->ce_file = add (cachefile, NULL); |
---|
2600 | else |
---|
2601 | ce->ce_file = add (m_scratch ("", tmp), NULL); |
---|
2602 | |
---|
2603 | if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { |
---|
2604 | content_error (ce->ce_file, ct, "unable to fopen for reading/writing"); |
---|
2605 | return NOTOK; |
---|
2606 | } |
---|
2607 | |
---|
2608 | #ifdef BUILTIN_FTP |
---|
2609 | if (ftp) |
---|
2610 | #endif |
---|
2611 | { |
---|
2612 | int child_id, i, vecp; |
---|
2613 | char *vec[9]; |
---|
2614 | |
---|
2615 | vecp = 0; |
---|
2616 | vec[vecp++] = r1bindex (ftp, '/'); |
---|
2617 | vec[vecp++] = e->eb_site; |
---|
2618 | vec[vecp++] = user; |
---|
2619 | vec[vecp++] = pass; |
---|
2620 | vec[vecp++] = e->eb_dir; |
---|
2621 | vec[vecp++] = e->eb_name; |
---|
2622 | vec[vecp++] = ce->ce_file, |
---|
2623 | vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii") |
---|
2624 | ? "ascii" : "binary"; |
---|
2625 | vec[vecp] = NULL; |
---|
2626 | |
---|
2627 | fflush (stdout); |
---|
2628 | |
---|
2629 | for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++) |
---|
2630 | sleep (5); |
---|
2631 | switch (child_id) { |
---|
2632 | case NOTOK: |
---|
2633 | adios ("fork", "unable to"); |
---|
2634 | /* NOTREACHED */ |
---|
2635 | |
---|
2636 | case OK: |
---|
2637 | close (fileno (ce->ce_fp)); |
---|
2638 | execvp (ftp, vec); |
---|
2639 | fprintf (stderr, "unable to exec "); |
---|
2640 | perror (ftp); |
---|
2641 | _exit (-1); |
---|
2642 | /* NOTREACHED */ |
---|
2643 | |
---|
2644 | default: |
---|
2645 | if (pidXwait (child_id, NULL)) { |
---|
2646 | #ifdef BUILTIN_FTP |
---|
2647 | losing_ftp: |
---|
2648 | #endif |
---|
2649 | username = password = NULL; |
---|
2650 | ce->ce_unlink = 1; |
---|
2651 | return NOTOK; |
---|
2652 | } |
---|
2653 | break; |
---|
2654 | } |
---|
2655 | } |
---|
2656 | #ifdef BUILTIN_FTP |
---|
2657 | else |
---|
2658 | if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name, |
---|
2659 | ce->ce_file, |
---|
2660 | e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0) |
---|
2661 | == NOTOK) |
---|
2662 | goto losing_ftp; |
---|
2663 | #endif |
---|
2664 | |
---|
2665 | if (cachefile[0]) |
---|
2666 | if (caching) |
---|
2667 | chmod (cachefile, cachetype ? m_gmprot () : 0444); |
---|
2668 | else { |
---|
2669 | int mask; |
---|
2670 | FILE *fp; |
---|
2671 | |
---|
2672 | mask = umask (cachetype ? ~m_gmprot () : 0222); |
---|
2673 | if ((fp = fopen (cachefile, "w"))) { |
---|
2674 | int cc; |
---|
2675 | FILE *gp = ce->ce_fp; |
---|
2676 | |
---|
2677 | fseek (gp, 0L, SEEK_SET); |
---|
2678 | |
---|
2679 | while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp)) |
---|
2680 | > 0) |
---|
2681 | fwrite (buffer, sizeof(*buffer), cc, fp); |
---|
2682 | fflush (fp); |
---|
2683 | |
---|
2684 | if (ferror (gp)) { |
---|
2685 | admonish (ce->ce_file, "error reading"); |
---|
2686 | unlink (cachefile); |
---|
2687 | } |
---|
2688 | else |
---|
2689 | if (ferror (fp)) { |
---|
2690 | admonish (cachefile, "error writing"); |
---|
2691 | unlink (cachefile); |
---|
2692 | } |
---|
2693 | fclose (fp); |
---|
2694 | } |
---|
2695 | umask (mask); |
---|
2696 | } |
---|
2697 | |
---|
2698 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
2699 | *file = ce->ce_file; |
---|
2700 | return fileno (ce->ce_fp); |
---|
2701 | } |
---|
2702 | |
---|
2703 | |
---|
2704 | /* |
---|
2705 | * Mail |
---|
2706 | */ |
---|
2707 | |
---|
2708 | static int |
---|
2709 | InitMail (CT ct) |
---|
2710 | { |
---|
2711 | return init_encoding (ct, openMail); |
---|
2712 | } |
---|
2713 | |
---|
2714 | |
---|
2715 | static int |
---|
2716 | openMail (CT ct, char **file) |
---|
2717 | { |
---|
2718 | int child_id, fd, i, vecp; |
---|
2719 | int len, buflen; |
---|
2720 | char *bp, buffer[BUFSIZ], *vec[7]; |
---|
2721 | struct exbody *e = ct->c_ctexbody; |
---|
2722 | CE ce = ct->c_cefile; |
---|
2723 | |
---|
2724 | switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) { |
---|
2725 | case NOTOK: |
---|
2726 | return NOTOK; |
---|
2727 | |
---|
2728 | case OK: |
---|
2729 | break; |
---|
2730 | |
---|
2731 | case DONE: |
---|
2732 | return fd; |
---|
2733 | } |
---|
2734 | |
---|
2735 | if (!e->eb_server) { |
---|
2736 | content_error (NULL, ct, "missing server parameter"); |
---|
2737 | return NOTOK; |
---|
2738 | } |
---|
2739 | |
---|
2740 | if (xpid) { |
---|
2741 | if (xpid < 0) |
---|
2742 | xpid = -xpid; |
---|
2743 | pidcheck (pidwait (xpid, NOTOK)); |
---|
2744 | xpid = 0; |
---|
2745 | } |
---|
2746 | |
---|
2747 | /* Get buffer ready to go */ |
---|
2748 | bp = buffer; |
---|
2749 | buflen = sizeof(buffer); |
---|
2750 | |
---|
2751 | /* Now construct query message */ |
---|
2752 | snprintf (bp, buflen, "Retrieve content"); |
---|
2753 | len = strlen (bp); |
---|
2754 | bp += len; |
---|
2755 | buflen -= len; |
---|
2756 | |
---|
2757 | if (e->eb_partno) { |
---|
2758 | snprintf (bp, buflen, " %s", e->eb_partno); |
---|
2759 | len = strlen (bp); |
---|
2760 | bp += len; |
---|
2761 | buflen -= len; |
---|
2762 | } |
---|
2763 | |
---|
2764 | snprintf (bp, buflen, " by asking %s\n\n%s\n? ", |
---|
2765 | e->eb_server, |
---|
2766 | e->eb_subject ? e->eb_subject : e->eb_body); |
---|
2767 | |
---|
2768 | /* Now, check answer */ |
---|
2769 | if (!getanswer (buffer)) |
---|
2770 | return NOTOK; |
---|
2771 | |
---|
2772 | vecp = 0; |
---|
2773 | vec[vecp++] = r1bindex (mailproc, '/'); |
---|
2774 | vec[vecp++] = e->eb_server; |
---|
2775 | vec[vecp++] = "-subject"; |
---|
2776 | vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request"; |
---|
2777 | vec[vecp++] = "-body"; |
---|
2778 | vec[vecp++] = e->eb_body; |
---|
2779 | vec[vecp] = NULL; |
---|
2780 | |
---|
2781 | for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++) |
---|
2782 | sleep (5); |
---|
2783 | switch (child_id) { |
---|
2784 | case NOTOK: |
---|
2785 | advise ("fork", "unable to"); |
---|
2786 | return NOTOK; |
---|
2787 | |
---|
2788 | case OK: |
---|
2789 | execvp (mailproc, vec); |
---|
2790 | fprintf (stderr, "unable to exec "); |
---|
2791 | perror (mailproc); |
---|
2792 | _exit (-1); |
---|
2793 | /* NOTREACHED */ |
---|
2794 | |
---|
2795 | default: |
---|
2796 | if (pidXwait (child_id, NULL) == OK) |
---|
2797 | advise (NULL, "request sent"); |
---|
2798 | break; |
---|
2799 | } |
---|
2800 | |
---|
2801 | if (*file == NULL) { |
---|
2802 | ce->ce_file = add (m_scratch ("", tmp), NULL); |
---|
2803 | ce->ce_unlink = 1; |
---|
2804 | } else { |
---|
2805 | ce->ce_file = add (*file, NULL); |
---|
2806 | ce->ce_unlink = 0; |
---|
2807 | } |
---|
2808 | |
---|
2809 | if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { |
---|
2810 | content_error (ce->ce_file, ct, "unable to fopen for reading/writing"); |
---|
2811 | return NOTOK; |
---|
2812 | } |
---|
2813 | |
---|
2814 | fseek (ce->ce_fp, 0L, SEEK_SET); |
---|
2815 | *file = ce->ce_file; |
---|
2816 | return fileno (ce->ce_fp); |
---|
2817 | } |
---|
2818 | |
---|
2819 | |
---|
2820 | static char * |
---|
2821 | fgetstr (char *s, int n, FILE *stream) |
---|
2822 | { |
---|
2823 | char *cp, *ep; |
---|
2824 | |
---|
2825 | for (ep = (cp = s) + n; cp < ep; ) { |
---|
2826 | int i; |
---|
2827 | |
---|
2828 | if (!fgets (cp, n, stream)) |
---|
2829 | return (cp != s ? s : NULL); |
---|
2830 | if (cp == s && *cp != '#') |
---|
2831 | return s; |
---|
2832 | |
---|
2833 | cp += (i = strlen (cp)) - 1; |
---|
2834 | if (i <= 1 || *cp-- != '\n' || *cp != '\\') |
---|
2835 | break; |
---|
2836 | *cp = '\0'; |
---|
2837 | n -= (i - 2); |
---|
2838 | } |
---|
2839 | |
---|
2840 | return s; |
---|
2841 | } |
---|
2842 | |
---|
2843 | |
---|
2844 | /* |
---|
2845 | * Parse the composition draft for text and directives. |
---|
2846 | * Do initial setup of Content structure. |
---|
2847 | */ |
---|
2848 | |
---|
2849 | static int |
---|
2850 | user_content (FILE *in, char *file, char *buf, CT *ctp) |
---|
2851 | { |
---|
2852 | int extrnal, vrsn; |
---|
2853 | char *cp, **ap; |
---|
2854 | char buffer[BUFSIZ]; |
---|
2855 | struct multipart *m; |
---|
2856 | struct part **pp; |
---|
2857 | struct stat st; |
---|
2858 | struct str2init *s2i; |
---|
2859 | CI ci; |
---|
2860 | CT ct; |
---|
2861 | CE ce; |
---|
2862 | |
---|
2863 | if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) { |
---|
2864 | *ctp = NULL; |
---|
2865 | return OK; |
---|
2866 | } |
---|
2867 | |
---|
2868 | /* allocate basic Content structure */ |
---|
2869 | if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL) |
---|
2870 | adios (NULL, "out of memory"); |
---|
2871 | *ctp = ct; |
---|
2872 | |
---|
2873 | /* allocate basic structure for handling decoded content */ |
---|
2874 | init_decoded_content (ct); |
---|
2875 | ce = ct->c_cefile; |
---|
2876 | |
---|
2877 | ci = &ct->c_ctinfo; |
---|
2878 | set_id (ct, 0); |
---|
2879 | |
---|
2880 | /* |
---|
2881 | * Handle inline text. Check if line |
---|
2882 | * is one of the following forms: |
---|
2883 | * |
---|
2884 | * 1) doesn't begin with '#' (implicit directive) |
---|
2885 | * 2) begins with "##" (implicit directive) |
---|
2886 | * 3) begins with "#<" |
---|
2887 | */ |
---|
2888 | if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') { |
---|
2889 | int headers; |
---|
2890 | int inlineD; |
---|
2891 | long pos; |
---|
2892 | char content[BUFSIZ]; |
---|
2893 | FILE *out; |
---|
2894 | |
---|
2895 | /* use a temp file to collect the plain text lines */ |
---|
2896 | ce->ce_file = add (m_tmpfil (invo_name), NULL); |
---|
2897 | ce->ce_unlink = 1; |
---|
2898 | |
---|
2899 | if ((out = fopen (ce->ce_file, "w")) == NULL) |
---|
2900 | adios (ce->ce_file, "unable to open for writing"); |
---|
2901 | |
---|
2902 | if (buf[0] == '#' && buf[1] == '<') { |
---|
2903 | strncpy (content, buf + 2, sizeof(content)); |
---|
2904 | inlineD = 1; |
---|
2905 | goto rock_and_roll; |
---|
2906 | } else { |
---|
2907 | inlineD = 0; |
---|
2908 | } |
---|
2909 | |
---|
2910 | /* the directive is implicit */ |
---|
2911 | strncpy (content, "text/plain", sizeof(content)); |
---|
2912 | headers = 0; |
---|
2913 | strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer)); |
---|
2914 | for (;;) { |
---|
2915 | int i; |
---|
2916 | |
---|
2917 | if (headers >= 0 && uprf (buffer, DESCR_FIELD) |
---|
2918 | && buffer[i = strlen (DESCR_FIELD)] == ':') { |
---|
2919 | headers = 1; |
---|
2920 | |
---|
2921 | again_descr: |
---|
2922 | ct->c_descr = add (buffer + i + 1, ct->c_descr); |
---|
2923 | if (!fgetstr (buffer, sizeof(buffer) - 1, in)) |
---|
2924 | adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD); |
---|
2925 | switch (buffer[0]) { |
---|
2926 | case ' ': |
---|
2927 | case '\t': |
---|
2928 | i = -1; |
---|
2929 | goto again_descr; |
---|
2930 | |
---|
2931 | case '#': |
---|
2932 | adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD); |
---|
2933 | /* NOTREACHED */ |
---|
2934 | |
---|
2935 | default: |
---|
2936 | break; |
---|
2937 | } |
---|
2938 | } |
---|
2939 | |
---|
2940 | if (headers != 1 || buffer[0] != '\n') |
---|
2941 | fputs (buffer, out); |
---|
2942 | |
---|
2943 | rock_and_roll: |
---|
2944 | headers = -1; |
---|
2945 | pos = ftell (in); |
---|
2946 | if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL) |
---|
2947 | break; |
---|
2948 | if (buffer[0] == '#') { |
---|
2949 | char *bp; |
---|
2950 | |
---|
2951 | if (buffer[1] != '#') |
---|
2952 | break; |
---|
2953 | for (cp = (bp = buffer) + 1; *cp; cp++) |
---|
2954 | *bp++ = *cp; |
---|
2955 | *bp = '\0'; |
---|
2956 | } |
---|
2957 | } |
---|
2958 | |
---|
2959 | if (listsw) |
---|
2960 | ct->c_end = ftell (out); |
---|
2961 | fclose (out); |
---|
2962 | |
---|
2963 | /* parse content type */ |
---|
2964 | if (get_ctinfo (content, ct, inlineD) == NOTOK) |
---|
2965 | done (1); |
---|
2966 | |
---|
2967 | for (s2i = str2cts; s2i->si_key; s2i++) |
---|
2968 | if (!strcasecmp (ci->ci_type, s2i->si_key)) |
---|
2969 | break; |
---|
2970 | if (!s2i->si_key && !uprf (ci->ci_type, "X-")) |
---|
2971 | s2i++; |
---|
2972 | |
---|
2973 | /* |
---|
2974 | * check type specified (possibly implicitly) |
---|
2975 | */ |
---|
2976 | switch (ct->c_type = s2i->si_val) { |
---|
2977 | case CT_MESSAGE: |
---|
2978 | if (!strcasecmp (ci->ci_subtype, "rfc822")) { |
---|
2979 | ct->c_encoding = CE_7BIT; |
---|
2980 | goto call_init; |
---|
2981 | } |
---|
2982 | /* else fall... */ |
---|
2983 | case CT_MULTIPART: |
---|
2984 | adios (NULL, "it doesn't make sense to define an in-line %s content", |
---|
2985 | ct->c_type == CT_MESSAGE ? "message" : "multipart"); |
---|
2986 | /* NOTREACHED */ |
---|
2987 | |
---|
2988 | default: |
---|
2989 | call_init: |
---|
2990 | if ((ct->c_ctinitfnx = s2i->si_init)) |
---|
2991 | (*ct->c_ctinitfnx) (ct); |
---|
2992 | break; |
---|
2993 | } |
---|
2994 | |
---|
2995 | if (cp) |
---|
2996 | fseek (in, pos, SEEK_SET); |
---|
2997 | return OK; |
---|
2998 | } |
---|
2999 | |
---|
3000 | /* |
---|
3001 | * If we've reached this point, the next line |
---|
3002 | * must be some type of explicit directive. |
---|
3003 | */ |
---|
3004 | |
---|
3005 | /* check if directive is external-type */ |
---|
3006 | extrnal = (buf[1] == '@'); |
---|
3007 | |
---|
3008 | /* parse directive */ |
---|
3009 | if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK) |
---|
3010 | done (1); |
---|
3011 | |
---|
3012 | /* check directive against the list of MIME types */ |
---|
3013 | for (s2i = str2cts; s2i->si_key; s2i++) |
---|
3014 | if (!strcasecmp (ci->ci_type, s2i->si_key)) |
---|
3015 | break; |
---|
3016 | |
---|
3017 | /* |
---|
3018 | * Check if the directive specified a valid type. |
---|
3019 | * This will happen if it was one of the following forms: |
---|
3020 | * |
---|
3021 | * #type/subtype (or) |
---|
3022 | * #@type/subtype |
---|
3023 | */ |
---|
3024 | if (s2i->si_key) { |
---|
3025 | if (!ci->ci_subtype) |
---|
3026 | adios (NULL, "missing subtype in \"#%s\"", ci->ci_type); |
---|
3027 | |
---|
3028 | switch (ct->c_type = s2i->si_val) { |
---|
3029 | case CT_MULTIPART: |
---|
3030 | adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", |
---|
3031 | ci->ci_type, ci->ci_subtype); |
---|
3032 | /* NOTREACHED */ |
---|
3033 | |
---|
3034 | case CT_MESSAGE: |
---|
3035 | if (!strcasecmp (ci->ci_subtype, "partial")) |
---|
3036 | adios (NULL, "sorry, \"#%s/%s\" isn't supported", |
---|
3037 | ci->ci_type, ci->ci_subtype); |
---|
3038 | if (!strcasecmp (ci->ci_subtype, "external-body")) |
---|
3039 | adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"", |
---|
3040 | ci->ci_type, ci->ci_subtype); |
---|
3041 | use_forw: |
---|
3042 | adios (NULL, |
---|
3043 | "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", |
---|
3044 | ci->ci_type, ci->ci_subtype); |
---|
3045 | /* NOTREACHED */ |
---|
3046 | |
---|
3047 | default: |
---|
3048 | if ((ct->c_ctinitfnx = s2i->si_init)) |
---|
3049 | (*ct->c_ctinitfnx) (ct); |
---|
3050 | break; |
---|
3051 | } |
---|
3052 | |
---|
3053 | /* |
---|
3054 | * #@type/subtype (external types directive) |
---|
3055 | */ |
---|
3056 | if (extrnal) { |
---|
3057 | struct exbody *e; |
---|
3058 | CT p; |
---|
3059 | |
---|
3060 | if (!ci->ci_magic) |
---|
3061 | adios (NULL, "need external information for \"#@%s/%s\"", |
---|
3062 | ci->ci_type, ci->ci_subtype); |
---|
3063 | p = ct; |
---|
3064 | |
---|
3065 | snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic); |
---|
3066 | free (ci->ci_magic); |
---|
3067 | ci->ci_magic = NULL; |
---|
3068 | |
---|
3069 | /* |
---|
3070 | * Since we are using the current Content structure to |
---|
3071 | * hold information about the type of the external |
---|
3072 | * reference, we need to create another Content structure |
---|
3073 | * for the message/external-body to wrap it in. |
---|
3074 | */ |
---|
3075 | if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL) |
---|
3076 | adios (NULL, "out of memory"); |
---|
3077 | *ctp = ct; |
---|
3078 | ci = &ct->c_ctinfo; |
---|
3079 | if (get_ctinfo (buffer, ct, 0) == NOTOK) |
---|
3080 | done (1); |
---|
3081 | ct->c_type = CT_MESSAGE; |
---|
3082 | ct->c_subtype = MESSAGE_EXTERNAL; |
---|
3083 | |
---|
3084 | if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL) |
---|
3085 | adios (NULL, "out of memory"); |
---|
3086 | ct->c_ctparams = (void *) e; |
---|
3087 | |
---|
3088 | e->eb_parent = ct; |
---|
3089 | e->eb_content = p; |
---|
3090 | p->c_ctexbody = e; |
---|
3091 | |
---|
3092 | if (params_external (ct, 1) == NOTOK) |
---|
3093 | done (1); |
---|
3094 | |
---|
3095 | return OK; |
---|
3096 | } |
---|
3097 | |
---|
3098 | /* Handle [file] argument */ |
---|
3099 | if (ci->ci_magic) { |
---|
3100 | /* check if specifies command to execute */ |
---|
3101 | if (*ci->ci_magic == '|' || *ci->ci_magic == '!') { |
---|
3102 | for (cp = ci->ci_magic + 1; isspace (*cp); cp++) |
---|
3103 | continue; |
---|
3104 | if (!*cp) |
---|
3105 | adios (NULL, "empty pipe command for #%s directive", ci->ci_type); |
---|
3106 | cp = add (cp, NULL); |
---|
3107 | free (ci->ci_magic); |
---|
3108 | ci->ci_magic = cp; |
---|
3109 | } else { |
---|
3110 | /* record filename of decoded contents */ |
---|
3111 | ce->ce_file = ci->ci_magic; |
---|
3112 | if (access (ce->ce_file, R_OK) == NOTOK) |
---|
3113 | adios ("reading", "unable to access %s for", ce->ce_file); |
---|
3114 | if (listsw && stat (ce->ce_file, &st) != NOTOK) |
---|
3115 | ct->c_end = (long) st.st_size; |
---|
3116 | ci->ci_magic = NULL; |
---|
3117 | } |
---|
3118 | return OK; |
---|
3119 | } |
---|
3120 | |
---|
3121 | /* |
---|
3122 | * No [file] argument, so check profile for |
---|
3123 | * method to compose content. |
---|
3124 | */ |
---|
3125 | snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s", |
---|
3126 | invo_name, ci->ci_type, ci->ci_subtype); |
---|
3127 | if ((cp = context_find (buffer)) == NULL || *cp == '\0') { |
---|
3128 | snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type); |
---|
3129 | if ((cp = context_find (buffer)) == NULL || *cp == '\0') { |
---|
3130 | content_error (NULL, ct, "don't know how to compose content"); |
---|
3131 | done (1); |
---|
3132 | } |
---|
3133 | } |
---|
3134 | ci->ci_magic = add (cp, NULL); |
---|
3135 | return OK; |
---|
3136 | } |
---|
3137 | |
---|
3138 | if (extrnal) |
---|
3139 | adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type); |
---|
3140 | |
---|
3141 | /* |
---|
3142 | * Message directive |
---|
3143 | * #forw [+folder] [msgs] |
---|
3144 | */ |
---|
3145 | if (!strcasecmp (ci->ci_type, "forw")) { |
---|
3146 | int msgnum; |
---|
3147 | char *folder, *arguments[MAXARGS]; |
---|
3148 | struct msgs *mp; |
---|
3149 | |
---|
3150 | if (ci->ci_magic) { |
---|
3151 | ap = brkstring (ci->ci_magic, " ", "\n"); |
---|
3152 | copyip (ap, arguments, MAXARGS); |
---|
3153 | } else { |
---|
3154 | arguments[0] = "cur"; |
---|
3155 | arguments[1] = NULL; |
---|
3156 | } |
---|
3157 | folder = NULL; |
---|
3158 | |
---|
3159 | /* search the arguments for a folder name */ |
---|
3160 | for (ap = arguments; *ap; ap++) { |
---|
3161 | cp = *ap; |
---|
3162 | if (*cp == '+' || *cp == '@') |
---|
3163 | if (folder) |
---|
3164 | adios (NULL, "only one folder per #forw directive"); |
---|
3165 | else |
---|
3166 | folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF); |
---|
3167 | } |
---|
3168 | |
---|
3169 | /* else, use the current folder */ |
---|
3170 | if (!folder) |
---|
3171 | folder = add (getfolder (1), NULL); |
---|
3172 | |
---|
3173 | if (!(mp = folder_read (folder))) |
---|
3174 | adios (NULL, "unable to read folder %s", folder); |
---|
3175 | for (ap = arguments; *ap; ap++) { |
---|
3176 | cp = *ap; |
---|
3177 | if (*cp != '+' && *cp != '@') |
---|
3178 | if (!m_convert (mp, cp)) |
---|
3179 | done (1); |
---|
3180 | } |
---|
3181 | free (folder); |
---|
3182 | free_ctinfo (ct); |
---|
3183 | |
---|
3184 | /* |
---|
3185 | * If there is more than one message to include, make this |
---|
3186 | * a content of type "multipart/digest" and insert each message |
---|
3187 | * as a subpart. If there is only one message, then make this |
---|
3188 | * a content of type "message/rfc822". |
---|
3189 | */ |
---|
3190 | if (mp->numsel > 1) { |
---|
3191 | /* we are forwarding multiple messages */ |
---|
3192 | if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK) |
---|
3193 | done (1); |
---|
3194 | ct->c_type = CT_MULTIPART; |
---|
3195 | ct->c_subtype = MULTI_DIGEST; |
---|
3196 | |
---|
3197 | if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL) |
---|
3198 | adios (NULL, "out of memory"); |
---|
3199 | ct->c_ctparams = (void *) m; |
---|
3200 | pp = &m->mp_parts; |
---|
3201 | |
---|
3202 | for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) { |
---|
3203 | if (is_selected(mp, msgnum)) { |
---|
3204 | struct part *part; |
---|
3205 | CT p; |
---|
3206 | CE pe; |
---|
3207 | |
---|
3208 | if ((p = (CT) calloc (1, sizeof(*p))) == NULL) |
---|
3209 | adios (NULL, "out of memory"); |
---|
3210 | init_decoded_content (p); |
---|
3211 | pe = p->c_cefile; |
---|
3212 | if (get_ctinfo ("message/rfc822", p, 0) == NOTOK) |
---|
3213 | done (1); |
---|
3214 | p->c_type = CT_MESSAGE; |
---|
3215 | p->c_subtype = MESSAGE_RFC822; |
---|
3216 | |
---|
3217 | snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum); |
---|
3218 | pe->ce_file = add (buffer, NULL); |
---|
3219 | if (listsw && stat (pe->ce_file, &st) != NOTOK) |
---|
3220 | p->c_end = (long) st.st_size; |
---|
3221 | |
---|
3222 | if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL) |
---|
3223 | adios (NULL, "out of memory"); |
---|
3224 | *pp = part; |
---|
3225 | pp = &part->mp_next; |
---|
3226 | part->mp_part = p; |
---|
3227 | } |
---|
3228 | } |
---|
3229 | } else { |
---|
3230 | /* we are forwarding one message */ |
---|
3231 | if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK) |
---|
3232 | done (1); |
---|
3233 | ct->c_type = CT_MESSAGE; |
---|
3234 | ct->c_subtype = MESSAGE_RFC822; |
---|
3235 | |
---|
3236 | msgnum = mp->lowsel; |
---|
3237 | snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum); |
---|
3238 | ce->ce_file = add (buffer, NULL); |
---|
3239 | if (listsw && stat (ce->ce_file, &st) != NOTOK) |
---|
3240 | ct->c_end = (long) st.st_size; |
---|
3241 | } |
---|
3242 | |
---|
3243 | folder_free (mp); /* free folder/message structure */ |
---|
3244 | return OK; |
---|
3245 | } |
---|
3246 | |
---|
3247 | /* |
---|
3248 | * #end |
---|
3249 | */ |
---|
3250 | if (!strcasecmp (ci->ci_type, "end")) { |
---|
3251 | free_content (ct); |
---|
3252 | *ctp = NULL; |
---|
3253 | return DONE; |
---|
3254 | } |
---|
3255 | |
---|
3256 | /* |
---|
3257 | * #begin [ alternative | parallel ] |
---|
3258 | */ |
---|
3259 | if (!strcasecmp (ci->ci_type, "begin")) { |
---|
3260 | if (!ci->ci_magic) { |
---|
3261 | vrsn = MULTI_MIXED; |
---|
3262 | cp = SubMultiPart[vrsn - 1].kv_key; |
---|
3263 | } else if (!strcasecmp (ci->ci_magic, "alternative")) { |
---|
3264 | vrsn = MULTI_ALTERNATE; |
---|
3265 | cp = SubMultiPart[vrsn - 1].kv_key; |
---|
3266 | } else if (!strcasecmp (ci->ci_magic, "parallel")) { |
---|
3267 | vrsn = MULTI_PARALLEL; |
---|
3268 | cp = SubMultiPart[vrsn - 1].kv_key; |
---|
3269 | } else if (uprf (ci->ci_magic, "digest")) { |
---|
3270 | goto use_forw; |
---|
3271 | } else { |
---|
3272 | vrsn = MULTI_UNKNOWN; |
---|
3273 | cp = ci->ci_magic; |
---|
3274 | } |
---|
3275 | |
---|
3276 | free_ctinfo (ct); |
---|
3277 | snprintf (buffer, sizeof(buffer), "multipart/%s", cp); |
---|
3278 | if (get_ctinfo (buffer, ct, 0) == NOTOK) |
---|
3279 | done (1); |
---|
3280 | ct->c_type = CT_MULTIPART; |
---|
3281 | ct->c_subtype = vrsn; |
---|
3282 | |
---|
3283 | if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL) |
---|
3284 | adios (NULL, "out of memory"); |
---|
3285 | ct->c_ctparams = (void *) m; |
---|
3286 | |
---|
3287 | pp = &m->mp_parts; |
---|
3288 | while (fgetstr (buffer, sizeof(buffer) - 1, in)) { |
---|
3289 | struct part *part; |
---|
3290 | CT p; |
---|
3291 | |
---|
3292 | if (user_content (in, file, buffer, &p) == DONE) { |
---|
3293 | if (!m->mp_parts) |
---|
3294 | adios (NULL, "empty \"#begin ... #end\" sequence"); |
---|
3295 | return OK; |
---|
3296 | } |
---|
3297 | if (!p) |
---|
3298 | continue; |
---|
3299 | |
---|
3300 | if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL) |
---|
3301 | adios (NULL, "out of memory"); |
---|
3302 | *pp = part; |
---|
3303 | pp = &part->mp_next; |
---|
3304 | part->mp_part = p; |
---|
3305 | } |
---|
3306 | admonish (NULL, "premature end-of-file, missing #end"); |
---|
3307 | return OK; |
---|
3308 | } |
---|
3309 | |
---|
3310 | /* |
---|
3311 | * Unknown directive |
---|
3312 | */ |
---|
3313 | adios (NULL, "unknown directive \"#%s\"", ci->ci_type); |
---|
3314 | return NOTOK; /* NOT REACHED */ |
---|
3315 | } |
---|
3316 | |
---|
3317 | |
---|
3318 | static void |
---|
3319 | set_id (CT ct, int top) |
---|
3320 | { |
---|
3321 | char msgid[BUFSIZ]; |
---|
3322 | static int partno; |
---|
3323 | static time_t clock = 0; |
---|
3324 | static char *msgfmt; |
---|
3325 | |
---|
3326 | if (clock == 0) { |
---|
3327 | time (&clock); |
---|
3328 | snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n", |
---|
3329 | (int) getpid(), (long) clock, LocalName()); |
---|
3330 | partno = 0; |
---|
3331 | msgfmt = getcpy(msgid); |
---|
3332 | } |
---|
3333 | snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno); |
---|
3334 | ct->c_id = getcpy (msgid); |
---|
3335 | } |
---|
3336 | |
---|
3337 | |
---|
3338 | static char ebcdicsafe[0x100] = { |
---|
3339 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3340 | 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3341 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3342 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3343 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, |
---|
3344 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3345 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3346 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3347 | 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3348 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3349 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3350 | 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, |
---|
3351 | 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3352 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3353 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
---|
3354 | 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3355 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3356 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3357 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3358 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3359 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3360 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3361 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3362 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3363 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3364 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3365 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3366 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3367 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3368 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3369 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
3370 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
---|
3371 | }; |
---|
3372 | |
---|
3373 | |
---|
3374 | /* |
---|
3375 | * Fill out, or expand the various contents in the composition |
---|
3376 | * draft. Read-in any necessary files. Parse and execute any |
---|
3377 | * commands specified by profile composition strings. |
---|
3378 | */ |
---|
3379 | |
---|
3380 | static int |
---|
3381 | compose_content (CT ct) |
---|
3382 | { |
---|
3383 | CE ce = ct->c_cefile; |
---|
3384 | |
---|
3385 | switch (ct->c_type) { |
---|
3386 | case CT_MULTIPART: |
---|
3387 | { |
---|
3388 | int partnum; |
---|
3389 | char *pp; |
---|
3390 | char partnam[BUFSIZ]; |
---|
3391 | struct multipart *m = (struct multipart *) ct->c_ctparams; |
---|
3392 | struct part *part; |
---|
3393 | |
---|
3394 | if (ct->c_partno) { |
---|
3395 | snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno); |
---|
3396 | pp = partnam + strlen (partnam); |
---|
3397 | } else { |
---|
3398 | pp = partnam; |
---|
3399 | } |
---|
3400 | |
---|
3401 | /* first, we call compose_content on all the subparts */ |
---|
3402 | for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) { |
---|
3403 | CT p = part->mp_part; |
---|
3404 | |
---|
3405 | sprintf (pp, "%d", partnum); |
---|
3406 | p->c_partno = add (partnam, NULL); |
---|
3407 | if (compose_content (p) == NOTOK) |
---|
3408 | return NOTOK; |
---|
3409 | } |
---|
3410 | |
---|
3411 | /* |
---|
3412 | * If the -rfc934mode switch is given, then check all |
---|
3413 | * the subparts of a multipart/digest. If they are all |
---|
3414 | * message/rfc822, then mark this content and all |
---|
3415 | * subparts with the rfc934 compatibility mode flag. |
---|
3416 | */ |
---|
3417 | if (rfc934sw && ct->c_subtype == MULTI_DIGEST) { |
---|
3418 | int is934 = 1; |
---|
3419 | |
---|
3420 | for (part = m->mp_parts; part; part = part->mp_next) { |
---|
3421 | CT p = part->mp_part; |
---|
3422 | |
---|
3423 | if (p->c_subtype != MESSAGE_RFC822) { |
---|
3424 | is934 = 0; |
---|
3425 | break; |
---|
3426 | } |
---|
3427 | } |
---|
3428 | ct->c_rfc934 = is934; |
---|
3429 | for (part = m->mp_parts; part; part = part->mp_next) { |
---|
3430 | CT p = part->mp_part; |
---|
3431 | |
---|
3432 | if ((p->c_rfc934 = is934)) |
---|
3433 | p->c_end++; |
---|
3434 | } |
---|
3435 | } |
---|
3436 | |
---|
3437 | if (listsw) { |
---|
3438 | ct->c_end = (partnum = strlen (prefix) + 2) + 2; |
---|
3439 | if (ct->c_rfc934) |
---|
3440 | ct->c_end += 1; |
---|
3441 | |
---|
3442 | for (part = m->mp_parts; part; part = part->mp_next) |
---|
3443 | ct->c_end += part->mp_part->c_end + partnum; |
---|
3444 | } |
---|
3445 | } |
---|
3446 | break; |
---|
3447 | |
---|
3448 | case CT_MESSAGE: |
---|
3449 | /* Nothing to do for type message */ |
---|
3450 | break; |
---|
3451 | |
---|
3452 | /* |
---|
3453 | * Discrete types (text/application/audio/image/video) |
---|
3454 | */ |
---|
3455 | default: |
---|
3456 | if (!ce->ce_file) { |
---|
3457 | pid_t child_id; |
---|
3458 | int i, xstdout, len, buflen; |
---|
3459 | char *bp, **ap, *cp; |
---|
3460 | char *vec[4], buffer[BUFSIZ]; |
---|
3461 | FILE *out; |
---|
3462 | CI ci = &ct->c_ctinfo; |
---|
3463 | |
---|
3464 | if (!(cp = ci->ci_magic)) |
---|
3465 | adios (NULL, "internal error(5)"); |
---|
3466 | |
---|
3467 | ce->ce_file = add (m_tmpfil (invo_name), NULL); |
---|
3468 | ce->ce_unlink = 1; |
---|
3469 | |
---|
3470 | xstdout = 0; |
---|
3471 | |
---|
3472 | /* Get buffer ready to go */ |
---|
3473 | bp = buffer; |
---|
3474 | bp[0] = '\0'; |
---|
3475 | buflen = sizeof(buffer); |
---|
3476 | |
---|
3477 | /* |
---|
3478 | * Parse composition string into buffer |
---|
3479 | */ |
---|
3480 | for ( ; *cp; cp++) { |
---|
3481 | if (*cp == '%') { |
---|
3482 | switch (*++cp) { |
---|
3483 | case 'a': |
---|
3484 | { |
---|
3485 | /* insert parameters from directive */ |
---|
3486 | char **ep; |
---|
3487 | char *s = ""; |
---|
3488 | |
---|
3489 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { |
---|
3490 | snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep); |
---|
3491 | len = strlen (bp); |
---|
3492 | bp += len; |
---|
3493 | buflen -= len; |
---|
3494 | s = " "; |
---|
3495 | } |
---|
3496 | } |
---|
3497 | break; |
---|
3498 | |
---|
3499 | case 'F': |
---|
3500 | /* %f, and stdout is not-redirected */ |
---|
3501 | xstdout = 1; |
---|
3502 | /* and fall... */ |
---|
3503 | |
---|
3504 | case 'f': |
---|
3505 | /* |
---|
3506 | * insert temporary filename where |
---|
3507 | * content should be written |
---|
3508 | */ |
---|
3509 | snprintf (bp, buflen, "%s", ce->ce_file); |
---|
3510 | break; |
---|
3511 | |
---|
3512 | case 's': |
---|
3513 | /* insert content subtype */ |
---|
3514 | strncpy (bp, ci->ci_subtype, buflen); |
---|
3515 | break; |
---|
3516 | |
---|
3517 | case '%': |
---|
3518 | /* insert character % */ |
---|
3519 | goto raw; |
---|
3520 | |
---|
3521 | default: |
---|
3522 | *bp++ = *--cp; |
---|
3523 | *bp = '\0'; |
---|
3524 | buflen--; |
---|
3525 | continue; |
---|
3526 | } |
---|
3527 | len = strlen (bp); |
---|
3528 | bp += len; |
---|
3529 | buflen -= len; |
---|
3530 | } else { |
---|
3531 | raw: |
---|
3532 | *bp++ = *cp; |
---|
3533 | *bp = '\0'; |
---|
3534 | buflen--; |
---|
3535 | } |
---|
3536 | } |
---|
3537 | |
---|
3538 | if (verbosw) |
---|
3539 | printf ("composing content %s/%s from command\n\t%s\n", |
---|
3540 | ci->ci_type, ci->ci_subtype, buffer); |
---|
3541 | |
---|
3542 | fflush (stdout); /* not sure if need for -noverbose */ |
---|
3543 | |
---|
3544 | vec[0] = "/bin/sh"; |
---|
3545 | vec[1] = "-c"; |
---|
3546 | vec[2] = buffer; |
---|
3547 | vec[3] = NULL; |
---|
3548 | |
---|
3549 | if ((out = fopen (ce->ce_file, "w")) == NULL) |
---|
3550 | adios (ce->ce_file, "unable to open for writing"); |
---|
3551 | |
---|
3552 | for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++) |
---|
3553 | sleep (5); |
---|
3554 | switch (child_id) { |
---|
3555 | case NOTOK: |
---|
3556 | adios ("fork", "unable to fork"); |
---|
3557 | /* NOTREACHED */ |
---|
3558 | |
---|
3559 | case OK: |
---|
3560 | if (!xstdout) |
---|
3561 | dup2 (fileno (out), 1); |
---|
3562 | close (fileno (out)); |
---|
3563 | execvp ("/bin/sh", vec); |
---|
3564 | fprintf (stderr, "unable to exec "); |
---|
3565 | perror ("/bin/sh"); |
---|
3566 | _exit (-1); |
---|
3567 | /* NOTREACHED */ |
---|
3568 | |
---|
3569 | default: |
---|
3570 | fclose (out); |
---|
3571 | if (pidXwait(child_id, NULL)) |
---|
3572 | done (1); |
---|
3573 | break; |
---|
3574 | } |
---|
3575 | } |
---|
3576 | |
---|
3577 | /* Check size of file */ |
---|
3578 | if (listsw && ct->c_end == 0L) { |
---|
3579 | struct stat st; |
---|
3580 | |
---|
3581 | if (stat (ce->ce_file, &st) != NOTOK) |
---|
3582 | ct->c_end = (long) st.st_size; |
---|
3583 | } |
---|
3584 | break; |
---|
3585 | } |
---|
3586 | |
---|
3587 | return OK; |
---|
3588 | } |
---|
3589 | |
---|
3590 | |
---|
3591 | /* |
---|
3592 | * Scan the content. |
---|
3593 | * |
---|
3594 | * 1) choose a transfer encoding. |
---|
3595 | * 2) check for clashes with multipart boundary string. |
---|
3596 | * 3) for text content, figure out which character set is being used. |
---|
3597 | * |
---|
3598 | * If there is a clash with one of the contents and the multipart boundary, |
---|
3599 | * this function will exit with NOTOK. This will cause the scanning process |
---|
3600 | * to be repeated with a different multipart boundary. It is possible |
---|
3601 | * (although highly unlikely) that this scan will be repeated multiple times. |
---|
3602 | */ |
---|
3603 | |
---|
3604 | static int |
---|
3605 | scan_content (CT ct) |
---|
3606 | { |
---|
3607 | int len; |
---|
3608 | int check8bit, contains8bit = 0; /* check if contains 8bit data */ |
---|
3609 | int checklinelen, linelen = 0; /* check for long lines */ |
---|
3610 | int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */ |
---|
3611 | int checklinespace, linespace = 0; /* check if any line ends with space */ |
---|
3612 | int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */ |
---|
3613 | char *cp, buffer[BUFSIZ]; |
---|
3614 | struct text *t; |
---|
3615 | FILE *in; |
---|
3616 | CE ce = ct->c_cefile; |
---|
3617 | |
---|
3618 | /* |
---|
3619 | * handle multipart by scanning all subparts |
---|
3620 | * and then checking their encoding. |
---|
3621 | */ |
---|
3622 | if (ct->c_type == CT_MULTIPART) { |
---|
3623 | struct multipart *m = (struct multipart *) ct->c_ctparams; |
---|
3624 | struct part *part; |
---|
3625 | |
---|
3626 | /* initially mark the domain of enclosing multipart as 7bit */ |
---|
3627 | ct->c_encoding = CE_7BIT; |
---|
3628 | |
---|
3629 | for (part = m->mp_parts; part; part = part->mp_next) { |
---|
3630 | CT p = part->mp_part; |
---|
3631 | |
---|
3632 | if (scan_content (p) == NOTOK) /* choose encoding for subpart */ |
---|
3633 | return NOTOK; |
---|
3634 | |
---|
3635 | /* if necessary, enlarge encoding for enclosing multipart */ |
---|
3636 | if (p->c_encoding == CE_BINARY) |
---|
3637 | ct->c_encoding = CE_BINARY; |
---|
3638 | if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY) |
---|
3639 | ct->c_encoding = CE_8BIT; |
---|
3640 | } |
---|
3641 | |
---|
3642 | return OK; |
---|
3643 | } |
---|
3644 | |
---|
3645 | /* |
---|
3646 | * Decide what to check while scanning this content. |
---|
3647 | */ |
---|
3648 | switch (ct->c_type) { |
---|
3649 | case CT_TEXT: |
---|
3650 | check8bit = 1; |
---|
3651 | checkboundary = 1; |
---|
3652 | if (ct->c_subtype == TEXT_PLAIN) { |
---|
3653 | checkebcdic = 0; |
---|
3654 | checklinelen = 0; |
---|
3655 | checklinespace = 0; |
---|
3656 | } else { |
---|
3657 | checkebcdic = ebcdicsw; |
---|
3658 | checklinelen = 1; |
---|
3659 | checklinespace = 1; |
---|
3660 | } |
---|
3661 | break; |
---|
3662 | |
---|
3663 | case CT_APPLICATION: |
---|
3664 | check8bit = 1; |
---|
3665 | checkebcdic = ebcdicsw; |
---|
3666 | checklinelen = 1; |
---|
3667 | checklinespace = 1; |
---|
3668 | checkboundary = 1; |
---|
3669 | break; |
---|
3670 | |
---|
3671 | case CT_MESSAGE: |
---|
3672 | check8bit = 0; |
---|
3673 | checkebcdic = 0; |
---|
3674 | checklinelen = 0; |
---|
3675 | checklinespace = 0; |
---|
3676 | |
---|
3677 | /* don't check anything for message/external */ |
---|
3678 | if (ct->c_subtype == MESSAGE_EXTERNAL) |
---|
3679 | checkboundary = 0; |
---|
3680 | else |
---|
3681 | checkboundary = 1; |
---|
3682 | break; |
---|
3683 | |
---|
3684 | case CT_AUDIO: |
---|
3685 | case CT_IMAGE: |
---|
3686 | case CT_VIDEO: |
---|
3687 | /* |
---|
3688 | * Don't check anything for these types, |
---|
3689 | * since we are forcing use of base64. |
---|
3690 | */ |
---|
3691 | check8bit = 0; |
---|
3692 | checkebcdic = 0; |
---|
3693 | checklinelen = 0; |
---|
3694 | checklinespace = 0; |
---|
3695 | checkboundary = 0; |
---|
3696 | break; |
---|
3697 | } |
---|
3698 | |
---|
3699 | /* |
---|
3700 | * Scan the unencoded content |
---|
3701 | */ |
---|
3702 | if (check8bit || checklinelen || checklinespace || checkboundary) { |
---|
3703 | if ((in = fopen (ce->ce_file, "r")) == NULL) |
---|
3704 | adios (ce->ce_file, "unable to open for reading"); |
---|
3705 | len = strlen (prefix); |
---|
3706 | |
---|
3707 | while (fgets (buffer, sizeof(buffer) - 1, in)) { |
---|
3708 | /* |
---|
3709 | * Check for 8bit data. |
---|
3710 | */ |
---|
3711 | if (check8bit) { |
---|
3712 | for (cp = buffer; *cp; cp++) { |
---|
3713 | if (!isascii (*cp)) { |
---|
3714 | contains8bit = 1; |
---|
3715 | check8bit = 0; /* no need to keep checking */ |
---|
3716 | } |
---|
3717 | /* |
---|
3718 | * Check if character is ebcdic-safe. We only check |
---|
3719 | * this if also checking for 8bit data. |
---|
3720 | */ |
---|
3721 | if (checkebcdic && !ebcdicsafe[*cp & 0xff]) { |
---|
3722 | ebcdicunsafe = 1; |
---|
3723 | checkebcdic = 0; /* no need to keep checking */ |
---|
3724 | } |
---|
3725 | } |
---|
3726 | } |
---|
3727 | |
---|
3728 | /* |
---|
3729 | * Check line length. |
---|
3730 | */ |
---|
3731 | if (checklinelen && (strlen (buffer) > CPERLIN + 1)) { |
---|
3732 | linelen = 1; |
---|
3733 | checklinelen = 0; /* no need to keep checking */ |
---|
3734 | } |
---|
3735 | |
---|
3736 | /* |
---|
3737 | * Check if line ends with a space. |
---|
3738 | */ |
---|
3739 | if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) { |
---|
3740 | linespace = 1; |
---|
3741 | checklinespace = 0; /* no need to keep checking */ |
---|
3742 | } |
---|
3743 | |
---|
3744 | /* |
---|
3745 | * Check if content contains a line that clashes |
---|
3746 | * with our standard boundary for multipart messages. |
---|
3747 | */ |
---|
3748 | if (checkboundary && buffer[0] == '-' && buffer[1] == '-') { |
---|
3749 | for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--) |
---|
3750 | if (!isspace (*cp)) |
---|
3751 | break; |
---|
3752 | *++cp = '\0'; |
---|
3753 | if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) { |
---|
3754 | boundaryclash = 1; |
---|
3755 | checkboundary = 0; /* no need to keep checking */ |
---|
3756 | } |
---|
3757 | } |
---|
3758 | } |
---|
3759 | fclose (in); |
---|
3760 | } |
---|
3761 | |
---|
3762 | /* |
---|
3763 | * Decide which transfer encoding to use. |
---|
3764 | */ |
---|
3765 | switch (ct->c_type) { |
---|
3766 | case CT_TEXT: |
---|
3767 | /* |
---|
3768 | * If the text content didn't specify a character |
---|
3769 | * set, we need to figure out which one was used. |
---|
3770 | */ |
---|
3771 | t = (struct text *) ct->c_ctparams; |
---|
3772 | if (t->tx_charset == CHARSET_UNSPECIFIED) { |
---|
3773 | CI ci = &ct->c_ctinfo; |
---|
3774 | char **ap, **ep; |
---|
3775 | |
---|
3776 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) |
---|
3777 | continue; |
---|
3778 | |
---|
3779 | if (contains8bit) { |
---|
3780 | t->tx_charset = CHARSET_UNKNOWN; |
---|
3781 | *ap = concat ("charset=", write_charset_8bit(), NULL); |
---|
3782 | } else { |
---|
3783 | t->tx_charset = CHARSET_USASCII; |
---|
3784 | *ap = add ("charset=us-ascii", NULL); |
---|
3785 | } |
---|
3786 | |
---|
3787 | cp = strchr(*ap++, '='); |
---|
3788 | *ap = NULL; |
---|
3789 | *cp++ = '\0'; |
---|
3790 | *ep = cp; |
---|
3791 | } |
---|
3792 | |
---|
3793 | if (contains8bit || ebcdicunsafe || linelen || linespace || checksw) |
---|
3794 | ct->c_encoding = CE_QUOTED; |
---|
3795 | else |
---|
3796 | ct->c_encoding = CE_7BIT; |
---|
3797 | break; |
---|
3798 | |
---|
3799 | case CT_APPLICATION: |
---|
3800 | /* For application type, use base64, except when postscript */ |
---|
3801 | if (contains8bit || ebcdicunsafe || linelen || linespace || checksw) |
---|
3802 | ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT) |
---|
3803 | ? CE_QUOTED : CE_BASE64; |
---|
3804 | else |
---|
3805 | ct->c_encoding = CE_7BIT; |
---|
3806 | break; |
---|
3807 | |
---|
3808 | case CT_MESSAGE: |
---|
3809 | ct->c_encoding = CE_7BIT; |
---|
3810 | break; |
---|
3811 | |
---|
3812 | case CT_AUDIO: |
---|
3813 | case CT_IMAGE: |
---|
3814 | case CT_VIDEO: |
---|
3815 | /* For audio, image, and video contents, just use base64 */ |
---|
3816 | ct->c_encoding = CE_BASE64; |
---|
3817 | break; |
---|
3818 | } |
---|
3819 | |
---|
3820 | return (boundaryclash ? NOTOK : OK); |
---|
3821 | } |
---|
3822 | |
---|
3823 | |
---|
3824 | /* |
---|
3825 | * Scan the content structures, and build header |
---|
3826 | * fields that will need to be output into the |
---|
3827 | * message. |
---|
3828 | */ |
---|
3829 | |
---|
3830 | static int |
---|
3831 | build_headers (CT ct) |
---|
3832 | { |
---|
3833 | int cc, mailbody, len; |
---|
3834 | char **ap, **ep; |
---|
3835 | char *np, *vp, buffer[BUFSIZ]; |
---|
3836 | CI ci = &ct->c_ctinfo; |
---|
3837 | |
---|
3838 | /* |
---|
3839 | * If message is type multipart, then add the multipart |
---|
3840 | * boundary to the list of attribute/value pairs. |
---|
3841 | */ |
---|
3842 | if (ct->c_type == CT_MULTIPART) { |
---|
3843 | char *cp; |
---|
3844 | static int level = 0; /* store nesting level */ |
---|
3845 | |
---|
3846 | ap = ci->ci_attrs; |
---|
3847 | ep = ci->ci_values; |
---|
3848 | snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++); |
---|
3849 | cp = strchr(*ap++ = add (buffer, NULL), '='); |
---|
3850 | *ap = NULL; |
---|
3851 | *cp++ = '\0'; |
---|
3852 | *ep = cp; |
---|
3853 | } |
---|
3854 | |
---|
3855 | /* |
---|
3856 | * Skip the output of Content-Type, parameters, content |
---|
3857 | * description, and Content-ID if the content is of type |
---|
3858 | * "message" and the rfc934 compatibility flag is set |
---|
3859 | * (which means we are inside multipart/digest and the |
---|
3860 | * switch -rfc934mode was given). |
---|
3861 | */ |
---|
3862 | if (ct->c_type == CT_MESSAGE && ct->c_rfc934) |
---|
3863 | goto skip_headers; |
---|
3864 | |
---|
3865 | /* |
---|
3866 | * output the content type and subtype |
---|
3867 | */ |
---|
3868 | np = add (TYPE_FIELD, NULL); |
---|
3869 | vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL); |
---|
3870 | |
---|
3871 | /* keep track of length of line */ |
---|
3872 | len = strlen (TYPE_FIELD) + strlen (ci->ci_type) |
---|
3873 | + strlen (ci->ci_subtype) + 3; |
---|
3874 | |
---|
3875 | mailbody = ct->c_type == CT_MESSAGE |
---|
3876 | && ct->c_subtype == MESSAGE_EXTERNAL |
---|
3877 | && ((struct exbody *) ct->c_ctparams)->eb_body; |
---|
3878 | |
---|
3879 | /* |
---|
3880 | * Append the attribute/value pairs to |
---|
3881 | * the end of the Content-Type line. |
---|
3882 | */ |
---|
3883 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { |
---|
3884 | if (mailbody && !strcasecmp (*ap, "body")) |
---|
3885 | continue; |
---|
3886 | |
---|
3887 | vp = add (";", vp); |
---|
3888 | len++; |
---|
3889 | |
---|
3890 | snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep); |
---|
3891 | if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) { |
---|
3892 | vp = add ("\n\t", vp); |
---|
3893 | len = 8; |
---|
3894 | } else { |
---|
3895 | vp = add (" ", vp); |
---|
3896 | len++; |
---|
3897 | } |
---|
3898 | vp = add (buffer, vp); |
---|
3899 | len += cc; |
---|
3900 | } |
---|
3901 | |
---|
3902 | /* |
---|
3903 | * Append any RFC-822 comment to the end of |
---|
3904 | * the Content-Type line. |
---|
3905 | */ |
---|
3906 | if (ci->ci_comment) { |
---|
3907 | snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment); |
---|
3908 | if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) { |
---|
3909 | vp = add ("\n\t", vp); |
---|
3910 | len = 8; |
---|
3911 | } else { |
---|
3912 | vp = add (" ", vp); |
---|
3913 | len++; |
---|
3914 | } |
---|
3915 | vp = add (buffer, vp); |
---|
3916 | len += cc; |
---|
3917 | } |
---|
3918 | vp = add ("\n", vp); |
---|
3919 | add_header (ct, np, vp); |
---|
3920 | |
---|
3921 | /* |
---|
3922 | * output the Content-ID |
---|
3923 | */ |
---|
3924 | if (ct->c_id) { |
---|
3925 | np = add (ID_FIELD, NULL); |
---|
3926 | vp = concat (" ", ct->c_id, NULL); |
---|
3927 | add_header (ct, np, vp); |
---|
3928 | } |
---|
3929 | |
---|
3930 | /* |
---|
3931 | * output the Content-Description |
---|
3932 | */ |
---|
3933 | if (ct->c_descr) { |
---|
3934 | np = add (DESCR_FIELD, NULL); |
---|
3935 | vp = concat (" ", ct->c_descr, NULL); |
---|
3936 | add_header (ct, np, vp); |
---|
3937 | } |
---|
3938 | |
---|
3939 | skip_headers: |
---|
3940 | /* |
---|
3941 | * If this is the internal content structure for a |
---|
3942 | * "message/external", then we are done with the |
---|
3943 | * headers (since it has no body). |
---|
3944 | */ |
---|
3945 | if (ct->c_ctexbody) |
---|
3946 | return OK; |
---|
3947 | |
---|
3948 | /* |
---|
3949 | * output the Content-MD5 |
---|
3950 | */ |
---|
3951 | if (checksw) { |
---|
3952 | np = add (MD5_FIELD, NULL); |
---|
3953 | vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0); |
---|
3954 | add_header (ct, np, vp); |
---|
3955 | } |
---|
3956 | |
---|
3957 | /* |
---|
3958 | * output the Content-Transfer-Encoding |
---|
3959 | */ |
---|
3960 | switch (ct->c_encoding) { |
---|
3961 | case CE_7BIT: |
---|
3962 | /* Nothing to output */ |
---|
3963 | #if 0 |
---|
3964 | np = add (ENCODING_FIELD, NULL); |
---|
3965 | vp = concat (" ", "7bit", "\n", NULL); |
---|
3966 | add_header (ct, np, vp); |
---|
3967 | #endif |
---|
3968 | break; |
---|
3969 | |
---|
3970 | case CE_8BIT: |
---|
3971 | if (ct->c_type == CT_MESSAGE) |
---|
3972 | adios (NULL, "internal error, invalid encoding"); |
---|
3973 | |
---|
3974 | np = add (ENCODING_FIELD, NULL); |
---|
3975 | vp = concat (" ", "8bit", "\n", NULL); |
---|
3976 | add_header (ct, np, vp); |
---|
3977 | break; |
---|
3978 | |
---|
3979 | case CE_QUOTED: |
---|
3980 | if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART) |
---|
3981 | adios (NULL, "internal error, invalid encoding"); |
---|
3982 | |
---|
3983 | np = add (ENCODING_FIELD, NULL); |
---|
3984 | vp = concat (" ", "quoted-printable", "\n", NULL); |
---|
3985 | add_header (ct, np, vp); |
---|
3986 | break; |
---|
3987 | |
---|
3988 | case CE_BASE64: |
---|
3989 | if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART) |
---|
3990 | adios (NULL, "internal error, invalid encoding"); |
---|
3991 | |
---|
3992 | np = add (ENCODING_FIELD, NULL); |
---|
3993 | vp = concat (" ", "base64", "\n", NULL); |
---|
3994 | add_header (ct, np, vp); |
---|
3995 | break; |
---|
3996 | |
---|
3997 | case CE_BINARY: |
---|
3998 | if (ct->c_type == CT_MESSAGE) |
---|
3999 | adios (NULL, "internal error, invalid encoding"); |
---|
4000 | |
---|
4001 | np = add (ENCODING_FIELD, NULL); |
---|
4002 | vp = concat (" ", "binary", "\n", NULL); |
---|
4003 | add_header (ct, np, vp); |
---|
4004 | break; |
---|
4005 | |
---|
4006 | default: |
---|
4007 | adios (NULL, "unknown transfer encoding in content"); |
---|
4008 | break; |
---|
4009 | } |
---|
4010 | |
---|
4011 | /* |
---|
4012 | * Additional content specific header processing |
---|
4013 | */ |
---|
4014 | switch (ct->c_type) { |
---|
4015 | case CT_MULTIPART: |
---|
4016 | { |
---|
4017 | struct multipart *m; |
---|
4018 | struct part *part; |
---|
4019 | |
---|
4020 | m = (struct multipart *) ct->c_ctparams; |
---|
4021 | for (part = m->mp_parts; part; part = part->mp_next) { |
---|
4022 | CT p; |
---|
4023 | |
---|
4024 | p = part->mp_part; |
---|
4025 | build_headers (p); |
---|
4026 | } |
---|
4027 | } |
---|
4028 | break; |
---|
4029 | |
---|
4030 | case CT_MESSAGE: |
---|
4031 | if (ct->c_subtype == MESSAGE_EXTERNAL) { |
---|
4032 | struct exbody *e; |
---|
4033 | |
---|
4034 | e = (struct exbody *) ct->c_ctparams; |
---|
4035 | build_headers (e->eb_content); |
---|
4036 | } |
---|
4037 | break; |
---|
4038 | |
---|
4039 | default: |
---|
4040 | /* Nothing to do */ |
---|
4041 | break; |
---|
4042 | } |
---|
4043 | |
---|
4044 | return OK; |
---|
4045 | } |
---|
4046 | |
---|
4047 | |
---|
4048 | static char nib2b64[0x40+1] = |
---|
4049 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
---|
4050 | |
---|
4051 | static char * |
---|
4052 | calculate_digest (CT ct, int asciiP) |
---|
4053 | { |
---|
4054 | int cc; |
---|
4055 | char buffer[BUFSIZ], *vp, *op; |
---|
4056 | unsigned char *dp; |
---|
4057 | unsigned char digest[16]; |
---|
4058 | unsigned char outbuf[25]; |
---|
4059 | FILE *in; |
---|
4060 | MD5_CTX mdContext; |
---|
4061 | CE ce = ct->c_cefile; |
---|
4062 | |
---|
4063 | /* open content */ |
---|
4064 | if ((in = fopen (ce->ce_file, "r")) == NULL) |
---|
4065 | adios (ce->ce_file, "unable to open for reading"); |
---|
4066 | |
---|
4067 | /* Initialize md5 context */ |
---|
4068 | MD5Init (&mdContext); |
---|
4069 | |
---|
4070 | /* calculate md5 message digest */ |
---|
4071 | if (asciiP) { |
---|
4072 | while (fgets (buffer, sizeof(buffer) - 1, in)) { |
---|
4073 | char c, *cp; |
---|
4074 | |
---|
4075 | cp = buffer + strlen (buffer) - 1; |
---|
4076 | if ((c = *cp) == '\n') |
---|
4077 | *cp = '\0'; |
---|
4078 | |
---|
4079 | MD5Update (&mdContext, (unsigned char *) buffer, |
---|
4080 | (unsigned int) strlen (buffer)); |
---|
4081 | |
---|
4082 | if (c == '\n') |
---|
4083 | MD5Update (&mdContext, (unsigned char *) "\r\n", 2); |
---|
4084 | } |
---|
4085 | } else { |
---|
4086 | while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0) |
---|
4087 | MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc); |
---|
4088 | } |
---|
4089 | |
---|
4090 | /* md5 finalization. Write digest and zero md5 context */ |
---|
4091 | MD5Final (digest, &mdContext); |
---|
4092 | |
---|
4093 | /* close content */ |
---|
4094 | fclose (in); |
---|
4095 | |
---|
4096 | /* print debugging info */ |
---|
4097 | if (debugsw) { |
---|
4098 | unsigned char *ep; |
---|
4099 | |
---|
4100 | fprintf (stderr, "MD5 digest="); |
---|
4101 | for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]); |
---|
4102 | dp < ep; dp++) |
---|
4103 | fprintf (stderr, "%02x", *dp & 0xff); |
---|
4104 | fprintf (stderr, "\n"); |
---|
4105 | } |
---|
4106 | |
---|
4107 | /* encode the digest using base64 */ |
---|
4108 | for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]); |
---|
4109 | cc > 0; cc -= 3, op += 4) { |
---|
4110 | unsigned long bits; |
---|
4111 | char *bp; |
---|
4112 | |
---|
4113 | bits = (*dp++ & 0xff) << 16; |
---|
4114 | if (cc > 1) { |
---|
4115 | bits |= (*dp++ & 0xff) << 8; |
---|
4116 | if (cc > 2) |
---|
4117 | bits |= *dp++ & 0xff; |
---|
4118 | } |
---|
4119 | |
---|
4120 | for (bp = op + 4; bp > op; bits >>= 6) |
---|
4121 | *--bp = nib2b64[bits & 0x3f]; |
---|
4122 | if (cc < 3) { |
---|
4123 | *(op + 3) = '='; |
---|
4124 | if (cc < 2) |
---|
4125 | *(op + 2) = '='; |
---|
4126 | } |
---|
4127 | } |
---|
4128 | |
---|
4129 | /* null terminate string */ |
---|
4130 | outbuf[24] = '\0'; |
---|
4131 | |
---|
4132 | /* now make copy and return string */ |
---|
4133 | vp = concat (" ", outbuf, "\n", NULL); |
---|
4134 | return vp; |
---|
4135 | } |
---|
4136 | |
---|
4137 | |
---|
4138 | static int |
---|
4139 | readDigest (CT ct, char *cp) |
---|
4140 | { |
---|
4141 | int bitno, skip; |
---|
4142 | unsigned long bits; |
---|
4143 | char *bp = cp; |
---|
4144 | unsigned char *dp, value, *ep; |
---|
4145 | unsigned char *b, *b1, *b2, *b3; |
---|
4146 | |
---|
4147 | b = (unsigned char *) &bits, |
---|
4148 | b1 = &b[endian > 0 ? 1 : 2], |
---|
4149 | b2 = &b[endian > 0 ? 2 : 1], |
---|
4150 | b3 = &b[endian > 0 ? 3 : 0]; |
---|
4151 | bitno = 18; |
---|
4152 | bits = 0L; |
---|
4153 | skip = 0; |
---|
4154 | |
---|
4155 | for (ep = (dp = ct->c_digest) |
---|
4156 | + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++) |
---|
4157 | switch (*cp) { |
---|
4158 | default: |
---|
4159 | if (skip |
---|
4160 | || (*cp & 0x80) |
---|
4161 | || (value = b642nib[*cp & 0x7f]) > 0x3f) { |
---|
4162 | if (debugsw) |
---|
4163 | fprintf (stderr, "invalid BASE64 encoding\n"); |
---|
4164 | return NOTOK; |
---|
4165 | } |
---|
4166 | |
---|
4167 | bits |= value << bitno; |
---|
4168 | test_end: |
---|
4169 | if ((bitno -= 6) < 0) { |
---|
4170 | if (dp + (3 - skip) > ep) |
---|
4171 | goto invalid_digest; |
---|
4172 | *dp++ = *b1; |
---|
4173 | if (skip < 2) { |
---|
4174 | *dp++ = *b2; |
---|
4175 | if (skip < 1) |
---|
4176 | *dp++ = *b3; |
---|
4177 | } |
---|
4178 | bitno = 18; |
---|
4179 | bits = 0L; |
---|
4180 | skip = 0; |
---|
4181 | } |
---|
4182 | break; |
---|
4183 | |
---|
4184 | case '=': |
---|
4185 | if (++skip > 3) |
---|
4186 | goto self_delimiting; |
---|
4187 | goto test_end; |
---|
4188 | } |
---|
4189 | if (bitno != 18) { |
---|
4190 | if (debugsw) |
---|
4191 | fprintf (stderr, "premature ending (bitno %d)\n", bitno); |
---|
4192 | |
---|
4193 | return NOTOK; |
---|
4194 | } |
---|
4195 | self_delimiting: |
---|
4196 | if (dp != ep) { |
---|
4197 | invalid_digest: |
---|
4198 | if (debugsw) { |
---|
4199 | while (*cp) |
---|
4200 | cp++; |
---|
4201 | fprintf (stderr, "invalid MD5 digest (got %d octets)\n", |
---|
4202 | cp - bp); |
---|
4203 | } |
---|
4204 | |
---|
4205 | return NOTOK; |
---|
4206 | } |
---|
4207 | |
---|
4208 | if (debugsw) { |
---|
4209 | fprintf (stderr, "MD5 digest="); |
---|
4210 | for (dp = ct->c_digest; dp < ep; dp++) |
---|
4211 | fprintf (stderr, "%02x", *dp & 0xff); |
---|
4212 | fprintf (stderr, "\n"); |
---|
4213 | } |
---|
4214 | |
---|
4215 | return OK; |
---|
4216 | } |
---|