1 | |
---|
2 | /* |
---|
3 | * mhstoresbr.c -- routines to save/store the contents of MIME messages |
---|
4 | * |
---|
5 | * $Id: mhstoresbr.c,v 1.1.1.1 1999-02-07 18:14:15 danw Exp $ |
---|
6 | */ |
---|
7 | |
---|
8 | #include <h/mh.h> |
---|
9 | #include <fcntl.h> |
---|
10 | #include <h/signals.h> |
---|
11 | #include <h/md5.h> |
---|
12 | #include <errno.h> |
---|
13 | #include <setjmp.h> |
---|
14 | #include <signal.h> |
---|
15 | #include <zotnet/mts/mts.h> |
---|
16 | #include <zotnet/tws/tws.h> |
---|
17 | #include <h/mime.h> |
---|
18 | #include <h/mhparse.h> |
---|
19 | |
---|
20 | extern int errno; |
---|
21 | |
---|
22 | /* |
---|
23 | * The list of top-level contents to display |
---|
24 | */ |
---|
25 | extern CT *cts; |
---|
26 | |
---|
27 | int autosw = 0; |
---|
28 | |
---|
29 | /* |
---|
30 | * Cache of current directory. This must be |
---|
31 | * set before these routines are called. |
---|
32 | */ |
---|
33 | char *cwd; |
---|
34 | |
---|
35 | /* |
---|
36 | * The directory in which to store the contents. |
---|
37 | */ |
---|
38 | static char *dir; |
---|
39 | |
---|
40 | /* |
---|
41 | * Type for a compare function for qsort. This keeps |
---|
42 | * the compiler happy. |
---|
43 | */ |
---|
44 | typedef int (*qsort_comp) (const void *, const void *); |
---|
45 | |
---|
46 | |
---|
47 | /* mhmisc.c */ |
---|
48 | int part_ok (CT, int); |
---|
49 | int type_ok (CT, int); |
---|
50 | int make_intermediates (char *); |
---|
51 | void flush_errors (void); |
---|
52 | |
---|
53 | /* mhshowsbr.c */ |
---|
54 | int show_content_aux (CT, int, int, char *, char *); |
---|
55 | |
---|
56 | /* |
---|
57 | * prototypes |
---|
58 | */ |
---|
59 | void store_all_messages (CT *); |
---|
60 | |
---|
61 | /* |
---|
62 | * static prototypes |
---|
63 | */ |
---|
64 | static void store_single_message (CT); |
---|
65 | static int store_switch (CT); |
---|
66 | static int store_generic (CT); |
---|
67 | static int store_application (CT); |
---|
68 | static int store_multi (CT); |
---|
69 | static int store_partial (CT); |
---|
70 | static int store_external (CT); |
---|
71 | static int ct_compar (CT *, CT *); |
---|
72 | static int store_content (CT, CT); |
---|
73 | static int output_content_file (CT, int); |
---|
74 | static int check_folder (char *); |
---|
75 | static int output_content_folder (char *, char *); |
---|
76 | static int parse_format_string (CT, char *, char *, int, char *); |
---|
77 | static void get_storeproc (CT); |
---|
78 | static int copy_some_headers (FILE *, CT); |
---|
79 | |
---|
80 | |
---|
81 | /* |
---|
82 | * Main entry point to store content |
---|
83 | * from a collection of messages. |
---|
84 | */ |
---|
85 | |
---|
86 | void |
---|
87 | store_all_messages (CT *cts) |
---|
88 | { |
---|
89 | CT ct, *ctp; |
---|
90 | char *cp; |
---|
91 | |
---|
92 | /* |
---|
93 | * Check for the directory in which to |
---|
94 | * store any contents. |
---|
95 | */ |
---|
96 | if (autosw) |
---|
97 | dir = getcpy (cwd); |
---|
98 | else if ((cp = context_find (nmhstorage)) && *cp) |
---|
99 | dir = getcpy (cp); |
---|
100 | else |
---|
101 | dir = getcpy (cwd); |
---|
102 | |
---|
103 | for (ctp = cts; *ctp; ctp++) { |
---|
104 | ct = *ctp; |
---|
105 | store_single_message (ct); |
---|
106 | } |
---|
107 | |
---|
108 | flush_errors (); |
---|
109 | } |
---|
110 | |
---|
111 | |
---|
112 | /* |
---|
113 | * Entry point to store the content |
---|
114 | * in a (single) message |
---|
115 | */ |
---|
116 | |
---|
117 | static void |
---|
118 | store_single_message (CT ct) |
---|
119 | { |
---|
120 | if (type_ok (ct, 1)) { |
---|
121 | umask (ct->c_umask); |
---|
122 | store_switch (ct); |
---|
123 | if (ct->c_fp) { |
---|
124 | fclose (ct->c_fp); |
---|
125 | ct->c_fp = NULL; |
---|
126 | } |
---|
127 | if (ct->c_ceclosefnx) |
---|
128 | (*ct->c_ceclosefnx) (ct); |
---|
129 | } |
---|
130 | } |
---|
131 | |
---|
132 | |
---|
133 | /* |
---|
134 | * Switching routine to store different content types |
---|
135 | */ |
---|
136 | |
---|
137 | static int |
---|
138 | store_switch (CT ct) |
---|
139 | { |
---|
140 | switch (ct->c_type) { |
---|
141 | case CT_MULTIPART: |
---|
142 | return store_multi (ct); |
---|
143 | break; |
---|
144 | |
---|
145 | case CT_MESSAGE: |
---|
146 | switch (ct->c_subtype) { |
---|
147 | case MESSAGE_PARTIAL: |
---|
148 | return store_partial (ct); |
---|
149 | break; |
---|
150 | |
---|
151 | case MESSAGE_EXTERNAL: |
---|
152 | return store_external (ct); |
---|
153 | |
---|
154 | case MESSAGE_RFC822: |
---|
155 | default: |
---|
156 | return store_generic (ct); |
---|
157 | break; |
---|
158 | } |
---|
159 | break; |
---|
160 | |
---|
161 | case CT_APPLICATION: |
---|
162 | return store_application (ct); |
---|
163 | break; |
---|
164 | |
---|
165 | case CT_TEXT: |
---|
166 | case CT_AUDIO: |
---|
167 | case CT_IMAGE: |
---|
168 | case CT_VIDEO: |
---|
169 | return store_generic (ct); |
---|
170 | break; |
---|
171 | |
---|
172 | default: |
---|
173 | adios (NULL, "unknown content type %d", ct->c_type); |
---|
174 | break; |
---|
175 | } |
---|
176 | |
---|
177 | return OK; /* NOT REACHED */ |
---|
178 | } |
---|
179 | |
---|
180 | |
---|
181 | /* |
---|
182 | * Generic routine to store a MIME content. |
---|
183 | * (audio, video, image, text, message/rfc922) |
---|
184 | */ |
---|
185 | |
---|
186 | static int |
---|
187 | store_generic (CT ct) |
---|
188 | { |
---|
189 | /* |
---|
190 | * Check if the content specifies a filename. |
---|
191 | * Don't bother with this for type "message" |
---|
192 | * (only "message/rfc822" will use store_generic). |
---|
193 | */ |
---|
194 | if (autosw && ct->c_type != CT_MESSAGE) |
---|
195 | get_storeproc (ct); |
---|
196 | |
---|
197 | return store_content (ct, NULL); |
---|
198 | } |
---|
199 | |
---|
200 | |
---|
201 | /* |
---|
202 | * Store content of type "application" |
---|
203 | */ |
---|
204 | |
---|
205 | static int |
---|
206 | store_application (CT ct) |
---|
207 | { |
---|
208 | char **ap, **ep; |
---|
209 | CI ci = &ct->c_ctinfo; |
---|
210 | |
---|
211 | /* Check if the content specifies a filename */ |
---|
212 | if (autosw) |
---|
213 | get_storeproc (ct); |
---|
214 | |
---|
215 | /* |
---|
216 | * If storeproc is not defined, and the content is type |
---|
217 | * "application/octet-stream", we also check for various |
---|
218 | * attribute/value pairs which specify if this a tar file. |
---|
219 | */ |
---|
220 | if (!ct->c_storeproc && ct->c_subtype == APPLICATION_OCTETS) { |
---|
221 | int tarP = 0, zP = 0; |
---|
222 | |
---|
223 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { |
---|
224 | /* check for "type=tar" attribute */ |
---|
225 | if (!strcasecmp (*ap, "type")) { |
---|
226 | if (strcasecmp (*ep, "tar")) |
---|
227 | break; |
---|
228 | |
---|
229 | tarP = 1; |
---|
230 | continue; |
---|
231 | } |
---|
232 | |
---|
233 | /* check for "conversions=compress" attribute */ |
---|
234 | if ((!strcasecmp (*ap, "conversions") || !strcasecmp (*ap, "x-conversions")) |
---|
235 | && (!strcasecmp (*ep, "compress") || !strcasecmp (*ep, "x-compress"))) { |
---|
236 | zP = 1; |
---|
237 | continue; |
---|
238 | } |
---|
239 | } |
---|
240 | |
---|
241 | if (tarP) { |
---|
242 | ct->c_showproc = add (zP ? "%euncompress | tar tvf -" |
---|
243 | : "%etar tvf -", NULL); |
---|
244 | if (!ct->c_storeproc) |
---|
245 | if (autosw) { |
---|
246 | ct->c_storeproc = add (zP ? "| uncompress | tar xvpf -" |
---|
247 | : "| tar xvpf -", NULL); |
---|
248 | ct->c_umask = 0022; |
---|
249 | } else { |
---|
250 | ct->c_storeproc = add (zP ? "%m%P.tar.Z" : "%m%P.tar", NULL); |
---|
251 | } |
---|
252 | } |
---|
253 | } |
---|
254 | |
---|
255 | return store_content (ct, NULL); |
---|
256 | } |
---|
257 | |
---|
258 | |
---|
259 | /* |
---|
260 | * Store the content of a multipart message |
---|
261 | */ |
---|
262 | |
---|
263 | static int |
---|
264 | store_multi (CT ct) |
---|
265 | { |
---|
266 | int result; |
---|
267 | struct multipart *m = (struct multipart *) ct->c_ctparams; |
---|
268 | struct part *part; |
---|
269 | |
---|
270 | result = NOTOK; |
---|
271 | for (part = m->mp_parts; part; part = part->mp_next) { |
---|
272 | CT p = part->mp_part; |
---|
273 | |
---|
274 | if (part_ok (p, 1) && type_ok (p, 1)) { |
---|
275 | result = store_switch (p); |
---|
276 | if (result == OK && ct->c_subtype == MULTI_ALTERNATE) |
---|
277 | break; |
---|
278 | } |
---|
279 | } |
---|
280 | |
---|
281 | return result; |
---|
282 | } |
---|
283 | |
---|
284 | |
---|
285 | /* |
---|
286 | * Reassemble and store the contents of a collection |
---|
287 | * of messages of type "message/partial". |
---|
288 | */ |
---|
289 | |
---|
290 | static int |
---|
291 | store_partial (CT ct) |
---|
292 | { |
---|
293 | int cur, hi, i; |
---|
294 | CT p, *ctp, *ctq; |
---|
295 | CT *base; |
---|
296 | struct partial *pm, *qm; |
---|
297 | |
---|
298 | qm = (struct partial *) ct->c_ctparams; |
---|
299 | if (qm->pm_stored) |
---|
300 | return OK; |
---|
301 | |
---|
302 | hi = i = 0; |
---|
303 | for (ctp = cts; *ctp; ctp++) { |
---|
304 | p = *ctp; |
---|
305 | if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) { |
---|
306 | pm = (struct partial *) p->c_ctparams; |
---|
307 | if (!pm->pm_stored |
---|
308 | && strcmp (qm->pm_partid, pm->pm_partid) == 0) { |
---|
309 | pm->pm_marked = pm->pm_partno; |
---|
310 | if (pm->pm_maxno) |
---|
311 | hi = pm->pm_maxno; |
---|
312 | pm->pm_stored = 1; |
---|
313 | i++; |
---|
314 | } |
---|
315 | else |
---|
316 | pm->pm_marked = 0; |
---|
317 | } |
---|
318 | } |
---|
319 | |
---|
320 | if (hi == 0) { |
---|
321 | advise (NULL, "missing (at least) last part of multipart message"); |
---|
322 | return NOTOK; |
---|
323 | } |
---|
324 | |
---|
325 | if ((base = (CT *) calloc ((size_t) (i + 1), sizeof(*base))) == NULL) |
---|
326 | adios (NULL, "out of memory"); |
---|
327 | |
---|
328 | ctq = base; |
---|
329 | for (ctp = cts; *ctp; ctp++) { |
---|
330 | p = *ctp; |
---|
331 | if (p->c_type == CT_MESSAGE && p->c_subtype == ct->c_subtype) { |
---|
332 | pm = (struct partial *) p->c_ctparams; |
---|
333 | if (pm->pm_marked) |
---|
334 | *ctq++ = p; |
---|
335 | } |
---|
336 | } |
---|
337 | *ctq = NULL; |
---|
338 | |
---|
339 | if (i > 1) |
---|
340 | qsort ((char *) base, i, sizeof(*base), (qsort_comp) ct_compar); |
---|
341 | |
---|
342 | cur = 1; |
---|
343 | for (ctq = base; *ctq; ctq++) { |
---|
344 | p = *ctq; |
---|
345 | pm = (struct partial *) p->c_ctparams; |
---|
346 | if (pm->pm_marked != cur) { |
---|
347 | if (pm->pm_marked == cur - 1) { |
---|
348 | admonish (NULL, |
---|
349 | "duplicate part %d of %d part multipart message", |
---|
350 | pm->pm_marked, hi); |
---|
351 | continue; |
---|
352 | } |
---|
353 | |
---|
354 | missing_part: |
---|
355 | advise (NULL, |
---|
356 | "missing %spart %d of %d part multipart message", |
---|
357 | cur != hi ? "(at least) " : "", cur, hi); |
---|
358 | goto losing; |
---|
359 | } |
---|
360 | else |
---|
361 | cur++; |
---|
362 | } |
---|
363 | if (hi != --cur) { |
---|
364 | cur = hi; |
---|
365 | goto missing_part; |
---|
366 | } |
---|
367 | |
---|
368 | /* |
---|
369 | * Now cycle through the sorted list of messages of type |
---|
370 | * "message/partial" and save/append them to a file. |
---|
371 | */ |
---|
372 | |
---|
373 | ctq = base; |
---|
374 | ct = *ctq++; |
---|
375 | if (store_content (ct, NULL) == NOTOK) { |
---|
376 | losing: |
---|
377 | free ((char *) base); |
---|
378 | return NOTOK; |
---|
379 | } |
---|
380 | |
---|
381 | for (; *ctq; ctq++) { |
---|
382 | p = *ctq; |
---|
383 | if (store_content (p, ct) == NOTOK) |
---|
384 | goto losing; |
---|
385 | } |
---|
386 | |
---|
387 | free ((char *) base); |
---|
388 | return OK; |
---|
389 | } |
---|
390 | |
---|
391 | |
---|
392 | /* |
---|
393 | * Store content from a message of type "message/external". |
---|
394 | */ |
---|
395 | |
---|
396 | static int |
---|
397 | store_external (CT ct) |
---|
398 | { |
---|
399 | int result = NOTOK; |
---|
400 | struct exbody *e = (struct exbody *) ct->c_ctparams; |
---|
401 | CT p = e->eb_content; |
---|
402 | |
---|
403 | if (!type_ok (p, 1)) |
---|
404 | return OK; |
---|
405 | |
---|
406 | /* |
---|
407 | * Check if the parameters for the external body |
---|
408 | * specified a filename. |
---|
409 | */ |
---|
410 | if (autosw) { |
---|
411 | char *cp; |
---|
412 | |
---|
413 | if ((cp = e->eb_name) |
---|
414 | && *cp != '/' |
---|
415 | && *cp != '.' |
---|
416 | && *cp != '|' |
---|
417 | && *cp != '!' |
---|
418 | && !strchr (cp, '%')) { |
---|
419 | if (!ct->c_storeproc) |
---|
420 | ct->c_storeproc = add (cp, NULL); |
---|
421 | if (!p->c_storeproc) |
---|
422 | p->c_storeproc = add (cp, NULL); |
---|
423 | } |
---|
424 | } |
---|
425 | |
---|
426 | /* |
---|
427 | * Since we will let the Content structure for the |
---|
428 | * external body substitute for the current content, |
---|
429 | * we temporarily change its partno (number inside |
---|
430 | * multipart), so everything looks right. |
---|
431 | */ |
---|
432 | p->c_partno = ct->c_partno; |
---|
433 | |
---|
434 | /* we probably need to check if content is really there */ |
---|
435 | result = store_switch (p); |
---|
436 | |
---|
437 | p->c_partno = NULL; |
---|
438 | return result; |
---|
439 | } |
---|
440 | |
---|
441 | |
---|
442 | /* |
---|
443 | * Compare the numbering from two different |
---|
444 | * message/partials (needed for sorting). |
---|
445 | */ |
---|
446 | |
---|
447 | static int |
---|
448 | ct_compar (CT *a, CT *b) |
---|
449 | { |
---|
450 | struct partial *am = (struct partial *) ((*a)->c_ctparams); |
---|
451 | struct partial *bm = (struct partial *) ((*b)->c_ctparams); |
---|
452 | |
---|
453 | return (am->pm_marked - bm->pm_marked); |
---|
454 | } |
---|
455 | |
---|
456 | |
---|
457 | /* |
---|
458 | * Store contents of a message or message part to |
---|
459 | * a folder, a file, the standard output, or pass |
---|
460 | * the contents to a command. |
---|
461 | * |
---|
462 | * If the current content to be saved is a followup part |
---|
463 | * to a collection of messages of type "message/partial", |
---|
464 | * then field "p" is a pointer to the Content structure |
---|
465 | * to the first message/partial in the group. |
---|
466 | */ |
---|
467 | |
---|
468 | static int |
---|
469 | store_content (CT ct, CT p) |
---|
470 | { |
---|
471 | int appending = 0, msgnum; |
---|
472 | int is_partial = 0, first_partial = 0; |
---|
473 | int last_partial = 0; |
---|
474 | char *cp, buffer[BUFSIZ]; |
---|
475 | |
---|
476 | /* |
---|
477 | * Do special processing for messages of |
---|
478 | * type "message/partial". |
---|
479 | * |
---|
480 | * We first check if this content is of type |
---|
481 | * "message/partial". If it is, then we need to check |
---|
482 | * whether it is the first and/or last in the group. |
---|
483 | * |
---|
484 | * Then if "p" is a valid pointer, it points to the Content |
---|
485 | * structure of the first partial in the group. So we copy |
---|
486 | * the file name and/or folder name from that message. In |
---|
487 | * this case, we also note that we will be appending. |
---|
488 | */ |
---|
489 | if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) { |
---|
490 | struct partial *pm = (struct partial *) ct->c_ctparams; |
---|
491 | |
---|
492 | /* Yep, it's a message/partial */ |
---|
493 | is_partial = 1; |
---|
494 | |
---|
495 | /* But is it the first and/or last in the collection? */ |
---|
496 | if (pm->pm_partno == 1) |
---|
497 | first_partial = 1; |
---|
498 | if (pm->pm_maxno && pm->pm_partno == pm->pm_maxno) |
---|
499 | last_partial = 1; |
---|
500 | |
---|
501 | /* |
---|
502 | * If "p" is a valid pointer, then it points to the |
---|
503 | * Content structure for the first message in the group. |
---|
504 | * So we just copy the filename or foldername information |
---|
505 | * from the previous iteration of this function. |
---|
506 | */ |
---|
507 | if (p) { |
---|
508 | appending = 1; |
---|
509 | ct->c_storage = add (p->c_storage, NULL); |
---|
510 | |
---|
511 | /* record the folder name */ |
---|
512 | if (p->c_folder) { |
---|
513 | ct->c_folder = add (p->c_folder, NULL); |
---|
514 | } |
---|
515 | goto got_filename; |
---|
516 | } |
---|
517 | } |
---|
518 | |
---|
519 | /* |
---|
520 | * Get storage formatting string. |
---|
521 | * |
---|
522 | * 1) If we have storeproc defined, then use that |
---|
523 | * 2) Else check for a mhn-store-<type>/<subtype> entry |
---|
524 | * 3) Else check for a mhn-store-<type> entry |
---|
525 | * 4) Else if content is "message", use "+" (current folder) |
---|
526 | * 5) Else use string "%m%P.%s". |
---|
527 | */ |
---|
528 | if ((cp = ct->c_storeproc) == NULL || *cp == '\0') { |
---|
529 | CI ci = &ct->c_ctinfo; |
---|
530 | |
---|
531 | snprintf (buffer, sizeof(buffer), "%s-store-%s/%s", |
---|
532 | invo_name, ci->ci_type, ci->ci_subtype); |
---|
533 | if ((cp = context_find (buffer)) == NULL || *cp == '\0') { |
---|
534 | snprintf (buffer, sizeof(buffer), "%s-store-%s", invo_name, ci->ci_type); |
---|
535 | if ((cp = context_find (buffer)) == NULL || *cp == '\0') { |
---|
536 | cp = ct->c_type == CT_MESSAGE ? "+" : "%m%P.%s"; |
---|
537 | } |
---|
538 | } |
---|
539 | } |
---|
540 | |
---|
541 | /* |
---|
542 | * Check the beginning of storage formatting string |
---|
543 | * to see if we are saving content to a folder. |
---|
544 | */ |
---|
545 | if (*cp == '+' || *cp == '@') { |
---|
546 | char *tmpfilenam, *folder; |
---|
547 | |
---|
548 | /* Store content in temporary file for now */ |
---|
549 | tmpfilenam = m_scratch ("", invo_name); |
---|
550 | ct->c_storage = add (tmpfilenam, NULL); |
---|
551 | |
---|
552 | /* Get the folder name */ |
---|
553 | if (cp[1]) |
---|
554 | folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF); |
---|
555 | else |
---|
556 | folder = getfolder (1); |
---|
557 | |
---|
558 | /* Check if folder exists */ |
---|
559 | if (check_folder (folder) == NOTOK) |
---|
560 | return NOTOK; |
---|
561 | |
---|
562 | /* Record the folder name */ |
---|
563 | ct->c_folder = add (folder, NULL); |
---|
564 | |
---|
565 | if (cp[1]) |
---|
566 | free (folder); |
---|
567 | |
---|
568 | goto got_filename; |
---|
569 | } |
---|
570 | |
---|
571 | /* |
---|
572 | * Parse and expand the storage formatting string |
---|
573 | * in `cp' into `buffer'. |
---|
574 | */ |
---|
575 | parse_format_string (ct, cp, buffer, sizeof(buffer), dir); |
---|
576 | |
---|
577 | /* |
---|
578 | * If formatting begins with '|' or '!', then pass |
---|
579 | * content to standard input of a command and return. |
---|
580 | */ |
---|
581 | if (buffer[0] == '|' || buffer[0] == '!') |
---|
582 | return show_content_aux (ct, 1, 0, buffer + 1, dir); |
---|
583 | |
---|
584 | /* record the filename */ |
---|
585 | ct->c_storage = add (buffer, NULL); |
---|
586 | |
---|
587 | got_filename: |
---|
588 | /* flush the output stream */ |
---|
589 | fflush (stdout); |
---|
590 | |
---|
591 | /* Now save or append the content to a file */ |
---|
592 | if (output_content_file (ct, appending) == NOTOK) |
---|
593 | return NOTOK; |
---|
594 | |
---|
595 | /* |
---|
596 | * If necessary, link the file into a folder and remove |
---|
597 | * the temporary file. If this message is a partial, |
---|
598 | * then only do this if it is the last one in the group. |
---|
599 | */ |
---|
600 | if (ct->c_folder && (!is_partial || last_partial)) { |
---|
601 | msgnum = output_content_folder (ct->c_folder, ct->c_storage); |
---|
602 | unlink (ct->c_storage); |
---|
603 | if (msgnum == NOTOK) |
---|
604 | return NOTOK; |
---|
605 | } |
---|
606 | |
---|
607 | /* |
---|
608 | * Now print out the name/number of the message |
---|
609 | * that we are storing. |
---|
610 | */ |
---|
611 | if (is_partial) { |
---|
612 | if (first_partial) |
---|
613 | fprintf (stderr, "reassembling partials "); |
---|
614 | if (last_partial) |
---|
615 | fprintf (stderr, "%s", ct->c_file); |
---|
616 | else |
---|
617 | fprintf (stderr, "%s,", ct->c_file); |
---|
618 | } else { |
---|
619 | fprintf (stderr, "storing message %s", ct->c_file); |
---|
620 | if (ct->c_partno) |
---|
621 | fprintf (stderr, " part %s", ct->c_partno); |
---|
622 | } |
---|
623 | |
---|
624 | /* |
---|
625 | * Unless we are in the "middle" of group of message/partials, |
---|
626 | * we now print the name of the file, folder, and/or message |
---|
627 | * to which we are storing the content. |
---|
628 | */ |
---|
629 | if (!is_partial || last_partial) { |
---|
630 | if (ct->c_folder) { |
---|
631 | fprintf (stderr, " to folder %s as message %d\n", ct->c_folder, msgnum); |
---|
632 | } else if (!strcmp(ct->c_storage, "-")) { |
---|
633 | fprintf (stderr, " to stdout\n"); |
---|
634 | } else { |
---|
635 | int cwdlen; |
---|
636 | |
---|
637 | cwdlen = strlen (cwd); |
---|
638 | fprintf (stderr, " as file %s\n", |
---|
639 | strncmp (ct->c_storage, cwd, cwdlen) |
---|
640 | || ct->c_storage[cwdlen] != '/' |
---|
641 | ? ct->c_storage : ct->c_storage + cwdlen + 1); |
---|
642 | } |
---|
643 | } |
---|
644 | |
---|
645 | return OK; |
---|
646 | } |
---|
647 | |
---|
648 | |
---|
649 | /* |
---|
650 | * Output content to a file |
---|
651 | */ |
---|
652 | |
---|
653 | static int |
---|
654 | output_content_file (CT ct, int appending) |
---|
655 | { |
---|
656 | int filterstate; |
---|
657 | char *file, buffer[BUFSIZ]; |
---|
658 | long pos, last; |
---|
659 | FILE *fp; |
---|
660 | |
---|
661 | /* |
---|
662 | * If the pathname is absolute, make sure |
---|
663 | * all the relevant directories exist. |
---|
664 | */ |
---|
665 | if (strchr(ct->c_storage, '/') |
---|
666 | && make_intermediates (ct->c_storage) == NOTOK) |
---|
667 | return NOTOK; |
---|
668 | |
---|
669 | if (ct->c_encoding != CE_7BIT) { |
---|
670 | int cc, fd; |
---|
671 | |
---|
672 | if (!ct->c_ceopenfnx) { |
---|
673 | advise (NULL, "don't know how to decode part %s of message %s", |
---|
674 | ct->c_partno, ct->c_file); |
---|
675 | return NOTOK; |
---|
676 | } |
---|
677 | |
---|
678 | file = appending || !strcmp (ct->c_storage, "-") ? NULL |
---|
679 | : ct->c_storage; |
---|
680 | if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK) |
---|
681 | return NOTOK; |
---|
682 | if (!strcmp (file, ct->c_storage)) { |
---|
683 | (*ct->c_ceclosefnx) (ct); |
---|
684 | fclose (ct->c_fp); |
---|
685 | ct->c_fp = NULL; |
---|
686 | return OK; |
---|
687 | } |
---|
688 | |
---|
689 | /* |
---|
690 | * Send to standard output |
---|
691 | */ |
---|
692 | if (!strcmp (ct->c_storage, "-")) { |
---|
693 | int gd; |
---|
694 | |
---|
695 | if ((gd = dup (fileno (stdout))) == NOTOK) { |
---|
696 | advise ("stdout", "unable to dup"); |
---|
697 | losing: |
---|
698 | (*ct->c_ceclosefnx) (ct); |
---|
699 | return NOTOK; |
---|
700 | } |
---|
701 | if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) { |
---|
702 | advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd, |
---|
703 | appending ? "a" : "w"); |
---|
704 | close (gd); |
---|
705 | goto losing; |
---|
706 | } |
---|
707 | } else { |
---|
708 | /* |
---|
709 | * Open output file |
---|
710 | */ |
---|
711 | if ((fp = fopen (ct->c_storage, appending ? "a" : "w")) == NULL) { |
---|
712 | advise (ct->c_storage, "unable to fopen for %s", |
---|
713 | appending ? "appending" : "writing"); |
---|
714 | goto losing; |
---|
715 | } |
---|
716 | } |
---|
717 | |
---|
718 | /* |
---|
719 | * Filter the header fields of the initial enclosing |
---|
720 | * message/partial into the file. |
---|
721 | */ |
---|
722 | if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) { |
---|
723 | struct partial *pm = (struct partial *) ct->c_ctparams; |
---|
724 | |
---|
725 | if (pm->pm_partno == 1) |
---|
726 | copy_some_headers (fp, ct); |
---|
727 | } |
---|
728 | |
---|
729 | for (;;) { |
---|
730 | switch (cc = read (fd, buffer, sizeof(buffer))) { |
---|
731 | case NOTOK: |
---|
732 | advise (file, "error reading content from"); |
---|
733 | break; |
---|
734 | |
---|
735 | case OK: |
---|
736 | break; |
---|
737 | |
---|
738 | default: |
---|
739 | fwrite (buffer, sizeof(*buffer), cc, fp); |
---|
740 | continue; |
---|
741 | } |
---|
742 | break; |
---|
743 | } |
---|
744 | |
---|
745 | (*ct->c_ceclosefnx) (ct); |
---|
746 | |
---|
747 | if (cc != NOTOK && fflush (fp)) |
---|
748 | advise (ct->c_storage, "error writing to"); |
---|
749 | |
---|
750 | fclose (fp); |
---|
751 | |
---|
752 | return (cc != NOTOK ? OK : NOTOK); |
---|
753 | } |
---|
754 | |
---|
755 | if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { |
---|
756 | advise (ct->c_file, "unable to open for reading"); |
---|
757 | return NOTOK; |
---|
758 | } |
---|
759 | |
---|
760 | pos = ct->c_begin; |
---|
761 | last = ct->c_end; |
---|
762 | fseek (ct->c_fp, pos, SEEK_SET); |
---|
763 | |
---|
764 | if (!strcmp (ct->c_storage, "-")) { |
---|
765 | int gd; |
---|
766 | |
---|
767 | if ((gd = dup (fileno (stdout))) == NOTOK) { |
---|
768 | advise ("stdout", "unable to dup"); |
---|
769 | return NOTOK; |
---|
770 | } |
---|
771 | if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) { |
---|
772 | advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd, |
---|
773 | appending ? "a" : "w"); |
---|
774 | close (gd); |
---|
775 | return NOTOK; |
---|
776 | } |
---|
777 | } else { |
---|
778 | if ((fp = fopen (ct->c_storage, appending ? "a" : "w")) == NULL) { |
---|
779 | advise (ct->c_storage, "unable to fopen for %s", |
---|
780 | appending ? "appending" : "writing"); |
---|
781 | return NOTOK; |
---|
782 | } |
---|
783 | } |
---|
784 | |
---|
785 | /* |
---|
786 | * Copy a few of the header fields of the initial |
---|
787 | * enclosing message/partial into the file. |
---|
788 | */ |
---|
789 | filterstate = 0; |
---|
790 | if (ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_PARTIAL) { |
---|
791 | struct partial *pm = (struct partial *) ct->c_ctparams; |
---|
792 | |
---|
793 | if (pm->pm_partno == 1) { |
---|
794 | copy_some_headers (fp, ct); |
---|
795 | filterstate = 1; |
---|
796 | } |
---|
797 | } |
---|
798 | |
---|
799 | while (fgets (buffer, sizeof(buffer) - 1, ct->c_fp)) { |
---|
800 | if ((pos += strlen (buffer)) > last) { |
---|
801 | int diff; |
---|
802 | |
---|
803 | diff = strlen (buffer) - (pos - last); |
---|
804 | if (diff >= 0) |
---|
805 | buffer[diff] = '\0'; |
---|
806 | } |
---|
807 | /* |
---|
808 | * If this is the first content of a group of |
---|
809 | * message/partial contents, then we only copy a few |
---|
810 | * of the header fields of the enclosed message. |
---|
811 | */ |
---|
812 | if (filterstate) { |
---|
813 | switch (buffer[0]) { |
---|
814 | case ' ': |
---|
815 | case '\t': |
---|
816 | if (filterstate < 0) |
---|
817 | buffer[0] = 0; |
---|
818 | break; |
---|
819 | |
---|
820 | case '\n': |
---|
821 | filterstate = 0; |
---|
822 | break; |
---|
823 | |
---|
824 | default: |
---|
825 | if (!uprf (buffer, XXX_FIELD_PRF) |
---|
826 | && !uprf (buffer, VRSN_FIELD) |
---|
827 | && !uprf (buffer, "Subject:") |
---|
828 | && !uprf (buffer, "Encrypted:") |
---|
829 | && !uprf (buffer, "Message-ID:")) { |
---|
830 | filterstate = -1; |
---|
831 | buffer[0] = 0; |
---|
832 | break; |
---|
833 | } |
---|
834 | filterstate = 1; |
---|
835 | break; |
---|
836 | } |
---|
837 | } |
---|
838 | fputs (buffer, fp); |
---|
839 | if (pos >= last) |
---|
840 | break; |
---|
841 | } |
---|
842 | |
---|
843 | if (fflush (fp)) |
---|
844 | advise (ct->c_storage, "error writing to"); |
---|
845 | |
---|
846 | fclose (fp); |
---|
847 | fclose (ct->c_fp); |
---|
848 | ct->c_fp = NULL; |
---|
849 | return OK; |
---|
850 | } |
---|
851 | |
---|
852 | |
---|
853 | /* |
---|
854 | * Check if folder exists, and create |
---|
855 | * if necessary. |
---|
856 | */ |
---|
857 | |
---|
858 | static int |
---|
859 | check_folder (char *folder) |
---|
860 | { |
---|
861 | char *folderdir; |
---|
862 | struct stat st; |
---|
863 | |
---|
864 | /* expand path to the folder */ |
---|
865 | folderdir = m_mailpath (folder); |
---|
866 | |
---|
867 | /* Check if folder exists */ |
---|
868 | if (stat (folderdir, &st) == NOTOK) { |
---|
869 | int answer; |
---|
870 | char *ep; |
---|
871 | |
---|
872 | if (errno != ENOENT) { |
---|
873 | advise (folderdir, "error on folder"); |
---|
874 | return NOTOK; |
---|
875 | } |
---|
876 | |
---|
877 | ep = concat ("Create folder \"", folderdir, "\"? ", NULL); |
---|
878 | answer = getanswer (ep); |
---|
879 | free (ep); |
---|
880 | |
---|
881 | if (!answer) |
---|
882 | return NOTOK; |
---|
883 | |
---|
884 | if (!makedir (folderdir)) { |
---|
885 | advise (NULL, "unable to create folder %s", folderdir); |
---|
886 | return NOTOK; |
---|
887 | } |
---|
888 | } |
---|
889 | |
---|
890 | return OK; |
---|
891 | } |
---|
892 | |
---|
893 | |
---|
894 | /* |
---|
895 | * Add a file to a folder. |
---|
896 | * |
---|
897 | * Return the new message number of the file |
---|
898 | * when added to the folder. Return -1, if |
---|
899 | * there is an error. |
---|
900 | */ |
---|
901 | |
---|
902 | static int |
---|
903 | output_content_folder (char *folder, char *filename) |
---|
904 | { |
---|
905 | int msgnum; |
---|
906 | struct msgs *mp; |
---|
907 | |
---|
908 | /* Read the folder. */ |
---|
909 | if ((mp = folder_read (folder))) { |
---|
910 | /* Link file into folder */ |
---|
911 | msgnum = folder_addmsg (&mp, filename, 0, 0, 0); |
---|
912 | } else { |
---|
913 | advise (NULL, "unable to read folder %s", folder); |
---|
914 | return NOTOK; |
---|
915 | } |
---|
916 | |
---|
917 | /* free folder structure */ |
---|
918 | folder_free (mp); |
---|
919 | |
---|
920 | /* |
---|
921 | * Return msgnum. We are relying on the fact that |
---|
922 | * msgnum will be -1, if folder_addmsg() had an error. |
---|
923 | */ |
---|
924 | return msgnum; |
---|
925 | } |
---|
926 | |
---|
927 | |
---|
928 | /* |
---|
929 | * Parse and expand the storage formatting string |
---|
930 | * pointed to by "cp" into "buffer". |
---|
931 | */ |
---|
932 | |
---|
933 | static int |
---|
934 | parse_format_string (CT ct, char *cp, char *buffer, int buflen, char *dir) |
---|
935 | { |
---|
936 | int len; |
---|
937 | char *bp; |
---|
938 | CI ci = &ct->c_ctinfo; |
---|
939 | |
---|
940 | /* |
---|
941 | * If storage string is "-", just copy it, and |
---|
942 | * return (send content to standard output). |
---|
943 | */ |
---|
944 | if (cp[0] == '-' && cp[1] == '\0') { |
---|
945 | strncpy (buffer, cp, buflen); |
---|
946 | return 0; |
---|
947 | } |
---|
948 | |
---|
949 | bp = buffer; |
---|
950 | bp[0] = '\0'; |
---|
951 | |
---|
952 | /* |
---|
953 | * If formatting string is a pathname that doesn't |
---|
954 | * begin with '/', then preface the path with the |
---|
955 | * appropriate directory. |
---|
956 | */ |
---|
957 | if (*cp != '/' && *cp != '|' && *cp != '!') { |
---|
958 | snprintf (bp, buflen, "%s/", dir[1] ? dir : ""); |
---|
959 | len = strlen (bp); |
---|
960 | bp += len; |
---|
961 | buflen -= len; |
---|
962 | } |
---|
963 | |
---|
964 | for (; *cp; cp++) { |
---|
965 | |
---|
966 | /* We are processing a storage escape */ |
---|
967 | if (*cp == '%') { |
---|
968 | switch (*++cp) { |
---|
969 | case 'a': |
---|
970 | /* |
---|
971 | * Insert parameters from Content-Type. |
---|
972 | * This is only valid for '|' commands. |
---|
973 | */ |
---|
974 | if (buffer[0] != '|' && buffer[0] != '!') { |
---|
975 | *bp++ = *--cp; |
---|
976 | *bp = '\0'; |
---|
977 | buflen--; |
---|
978 | continue; |
---|
979 | } else { |
---|
980 | char **ap, **ep; |
---|
981 | char *s = ""; |
---|
982 | |
---|
983 | for (ap = ci->ci_attrs, ep = ci->ci_values; |
---|
984 | *ap; ap++, ep++) { |
---|
985 | snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep); |
---|
986 | len = strlen (bp); |
---|
987 | bp += len; |
---|
988 | buflen -= len; |
---|
989 | s = " "; |
---|
990 | } |
---|
991 | } |
---|
992 | break; |
---|
993 | |
---|
994 | case 'm': |
---|
995 | /* insert message number */ |
---|
996 | snprintf (bp, buflen, "%s", r1bindex (ct->c_file, '/')); |
---|
997 | break; |
---|
998 | |
---|
999 | case 'P': |
---|
1000 | /* insert part number with leading dot */ |
---|
1001 | if (ct->c_partno) |
---|
1002 | snprintf (bp, buflen, ".%s", ct->c_partno); |
---|
1003 | break; |
---|
1004 | |
---|
1005 | case 'p': |
---|
1006 | /* insert part number withouth leading dot */ |
---|
1007 | if (ct->c_partno) |
---|
1008 | strncpy (bp, ct->c_partno, buflen); |
---|
1009 | break; |
---|
1010 | |
---|
1011 | case 't': |
---|
1012 | /* insert content type */ |
---|
1013 | strncpy (bp, ci->ci_type, buflen); |
---|
1014 | break; |
---|
1015 | |
---|
1016 | case 's': |
---|
1017 | /* insert content subtype */ |
---|
1018 | strncpy (bp, ci->ci_subtype, buflen); |
---|
1019 | break; |
---|
1020 | |
---|
1021 | case '%': |
---|
1022 | /* insert the character % */ |
---|
1023 | goto raw; |
---|
1024 | |
---|
1025 | default: |
---|
1026 | *bp++ = *--cp; |
---|
1027 | *bp = '\0'; |
---|
1028 | buflen--; |
---|
1029 | continue; |
---|
1030 | } |
---|
1031 | |
---|
1032 | /* Advance bp and decrement buflen */ |
---|
1033 | len = strlen (bp); |
---|
1034 | bp += len; |
---|
1035 | buflen -= len; |
---|
1036 | |
---|
1037 | } else { |
---|
1038 | raw: |
---|
1039 | *bp++ = *cp; |
---|
1040 | *bp = '\0'; |
---|
1041 | buflen--; |
---|
1042 | } |
---|
1043 | } |
---|
1044 | |
---|
1045 | return 0; |
---|
1046 | } |
---|
1047 | |
---|
1048 | |
---|
1049 | /* |
---|
1050 | * Check if the content specifies a filename |
---|
1051 | * in its MIME parameters. |
---|
1052 | */ |
---|
1053 | |
---|
1054 | static void |
---|
1055 | get_storeproc (CT ct) |
---|
1056 | { |
---|
1057 | char **ap, **ep, *cp; |
---|
1058 | CI ci = &ct->c_ctinfo; |
---|
1059 | |
---|
1060 | /* |
---|
1061 | * If the storeproc has already been defined, |
---|
1062 | * we just return (for instance, if this content |
---|
1063 | * is part of a "message/external". |
---|
1064 | */ |
---|
1065 | if (ct->c_storeproc) |
---|
1066 | return; |
---|
1067 | |
---|
1068 | /* |
---|
1069 | * Check the attribute/value pairs, for the attribute "name". |
---|
1070 | * If found, do a few sanity checks and copy the value into |
---|
1071 | * the storeproc. |
---|
1072 | */ |
---|
1073 | for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { |
---|
1074 | if (!strcasecmp (*ap, "name") |
---|
1075 | && *(cp = *ep) != '/' |
---|
1076 | && *cp != '.' |
---|
1077 | && *cp != '|' |
---|
1078 | && *cp != '!' |
---|
1079 | && !strchr (cp, '%')) { |
---|
1080 | ct->c_storeproc = add (cp, NULL); |
---|
1081 | return; |
---|
1082 | } |
---|
1083 | } |
---|
1084 | } |
---|
1085 | |
---|
1086 | |
---|
1087 | /* |
---|
1088 | * Copy some of the header fields of the initial message/partial |
---|
1089 | * message into the header of the reassembled message. |
---|
1090 | */ |
---|
1091 | |
---|
1092 | static int |
---|
1093 | copy_some_headers (FILE *out, CT ct) |
---|
1094 | { |
---|
1095 | HF hp; |
---|
1096 | |
---|
1097 | hp = ct->c_first_hf; /* start at first header field */ |
---|
1098 | |
---|
1099 | while (hp) { |
---|
1100 | /* |
---|
1101 | * A few of the header fields of the enclosing |
---|
1102 | * messages are not copied. |
---|
1103 | */ |
---|
1104 | if (!uprf (hp->name, XXX_FIELD_PRF) |
---|
1105 | && strcasecmp (hp->name, VRSN_FIELD) |
---|
1106 | && strcasecmp (hp->name, "Subject") |
---|
1107 | && strcasecmp (hp->name, "Encrypted") |
---|
1108 | && strcasecmp (hp->name, "Message-ID")) |
---|
1109 | fprintf (out, "%s:%s", hp->name, hp->value); |
---|
1110 | hp = hp->next; /* next header field */ |
---|
1111 | } |
---|
1112 | |
---|
1113 | return OK; |
---|
1114 | } |
---|