1 #include "elm_defs.h"
2 #include "elm_globals.h"
3 #include "mime.h"
4 #include "sndparts.h"
5 #include "s_elm.h"
6 #include <assert.h>
7
8
9 /*******************/
10 /* Part is parts!! */
11 /*******************/
12
13 /*
14 * The values in this array must correspond to
15 * the BP_CONT_xxxx definitions.
16 */
17 char *Mime_header_names[BP_NUM_CONT_HEADERS] = {
18 "Content-Type:", /* BP_CONT_TYPE */
19 "Content-Transfer-Encoding:", /* BP_CONT_ENCODING */
20 "Content-Description:", /* BP_CONT_DESCRIPTION */
21 "Content-Disposition:", /* BP_CONT_DISPOSITION */
22 "Content-MD5:" /* BP_CONT_MD5 */
23 };
24
25
26 static char *scan_mimetypes P_((const char *, const char *, char *, int));
27
28
encoding_is_reasonable(value)29 PUBLIC int encoding_is_reasonable(value)
30 const char *value;
31 {
32 switch (mime_encoding_type(value)) {
33 case ENCODING_NONE:
34 case ENCODING_7BIT:
35 case ENCODING_8BIT:
36 case ENCODING_QUOTED:
37 case ENCODING_BASE64:
38 case ENCODING_UUENCODE:
39 return TRUE;
40 case ENCODING_BINARY:
41 case ENCODING_EXPERIMENTAL:
42 error1("Content encoding value \"%s\" is not reasonable.", value);
43 return FALSE;
44 case ENCODING_ILLEGAL:
45 default:
46 error1("Content encoding value \"%s\" is illegal.", value);
47 return FALSE;
48 }
49 /*NOTREACHED*/
50 }
51
52
53
54 /*****************************************************************************
55 *
56 * Body part management routines.
57 *
58 ****************************************************************************/
59
60
61 #ifndef NDEBUG
bodypart_integrity_check(part)62 PUBLIC void bodypart_integrity_check(part)
63 const SEND_BODYPART *part;
64 {
65 assert(part != NULL);
66
67 switch (part->part_type) {
68
69 case BP_IS_DUMMY:
70 assert(part->link_count == 0);
71 assert(part->fname != NULL);
72 assert(part->subparts == NULL);
73 assert(part->boundary == NULL);
74 break;
75
76 case BP_IS_MSSGTEXT:
77 assert(part->link_count == 0);
78 assert(part->fname != NULL);
79 assert(part->subparts != NULL);
80 break;
81
82 case BP_IS_MIMEPART:
83 assert(part->link_count >= 0);
84 assert(part->fname != NULL);
85 assert(part->subparts == NULL);
86 assert(part->boundary == NULL);
87 break;
88
89 case BP_IS_MULTIPART:
90 assert(part->link_count >= 0);
91 assert(part->fname == NULL);
92 assert(part->subparts != NULL);
93 break;
94
95 default:
96 assert((part->part_type, FALSE));
97 break;
98
99 }
100
101 }
102 #endif /* !NDEBUG */
103
104
105
106 /*
107 * bodypart_new - Create a new (SEND_BODYPART).
108 *
109 * fname - Pathname of the file that contains this part.
110 * part_type - The BP_IS_xxxx type of this part.
111 *
112 * This routine merely returns an initialized (SEND_BODYPART) for the
113 * part. The only verification is that the filename exists. All
114 * of the MIME headers are initialized to NULL, and none of the
115 * part statistics are filled in.
116 */
bodypart_new(fname,part_type)117 PUBLIC SEND_BODYPART *bodypart_new(fname, part_type)
118 const char *fname;
119 int part_type;
120 {
121 SEND_BODYPART *part;
122 int i;
123
124 assert(fname != NULL);
125 if (part_type != BP_IS_DUMMY && file_access(fname, R_OK) < 0)
126 return (SEND_BODYPART *) NULL;
127
128 part = (SEND_BODYPART *) safe_malloc(sizeof(SEND_BODYPART));
129
130 part->part_type = part_type;
131 part->link_count = 0;
132 part->fname = safe_strdup(fname);
133 part->emit_hdr_proc = NULL;
134 part->emit_body_proc = NULL;
135
136 for (i = 0 ; i < BP_NUM_CONT_HEADERS ; ++i)
137 part->content_header[i] = NULL;
138
139 part->total_chars = 0L;
140 part->ascii_chars = 0L;
141 part->binhi_chars = 0L;
142 part->binlo_chars = 0L;
143 part->max_linelen = 0;
144
145 if (part_type == BP_IS_MSSGTEXT || part_type == BP_IS_MULTIPART)
146 part->subparts = multipart_new((SEND_BODYPART *) NULL, 0L);
147 else
148 part->subparts = NULL;
149 part->boundary = NULL;
150
151 bodypart_integrity_check(part);
152 return part;
153 }
154
155
156 /*
157 * bodypart_destroy - Destroy a (SEND_BODYPART).
158 *
159 * This routine releases the space created by bodypart_new().
160 */
bodypart_destroy(part)161 PUBLIC void bodypart_destroy(part)
162 SEND_BODYPART *part;
163 {
164 int i;
165 bodypart_integrity_check(part);
166 assert(part->link_count == 0);
167 if (part->fname != NULL)
168 free((malloc_t)part->fname);
169 for (i = 0 ; i < BP_NUM_CONT_HEADERS ; ++i) {
170 if (part->content_header[i] != NULL)
171 free((malloc_t)part->content_header[i]);
172 }
173 if (part->subparts != NULL)
174 multipart_destroy(part->subparts);
175 if (part->boundary != NULL)
176 free((malloc_t)part->boundary);
177 free((malloc_t)part);
178 }
179
180
181 /*
182 * bodypart_set_content() - Set a particular MIME header.
183 *
184 * part - The (SEND_BODYPART) to modify.
185 * sel - The BP_CONT_xxxx header to set.
186 * value - The header value. A NULL or empty string de-assigns the header.
187 */
bodypart_set_content(part,sel,value)188 PUBLIC void bodypart_set_content(part, sel, value)
189 SEND_BODYPART *part;
190 int sel;
191 const char *value;
192 {
193 assert(sel >= 0 && sel < BP_NUM_CONT_HEADERS);
194 bodypart_integrity_check(part);
195
196 if (part->content_header[sel] != NULL)
197 free((malloc_t)part->content_header[sel]);
198 part->content_header[sel] = (value && *value ? safe_strdup(value) : NULL);
199 }
200
201
202 /*
203 * bodypart_get_content() - Retrieve a particular MIME header.
204 *
205 * part - The (SEND_BODYPART) to examine.
206 * sel - The BP_CONT_xxxx selector of the header.
207 *
208 * Returns the header value, or an empty string if the header is not set.
209 */
bodypart_get_content(part,sel)210 PUBLIC const char *bodypart_get_content(part, sel)
211 SEND_BODYPART *part;
212 int sel;
213 {
214 const char *value;
215
216 assert(sel >= 0 && sel < BP_NUM_CONT_HEADERS);
217 bodypart_integrity_check(part);
218
219 value = part->content_header[sel];
220 return (value ? value : "");
221 }
222
223
224
225 /*
226 * bodypart_guess_content() - Set a particular MIME header.
227 *
228 * part - The (SEND_BODYPART) to modify.
229 * sel - The BP_CONT_xxxx header to set.
230 *
231 * This routine applies some simple heuristics to guess a
232 * reasonable value for the header.
233 */
bodypart_guess_content(part,sel)234 PUBLIC void bodypart_guess_content(part, sel)
235 SEND_BODYPART *part;
236 int sel;
237 {
238 char buf[SLEN], *value, *fname_tmp, *bp;
239 int len;
240 FILE *fp;
241 float p;
242
243 assert(sel >= 0 && sel < BP_NUM_CONT_HEADERS);
244 bodypart_integrity_check(part);
245
246 switch (sel) {
247
248 case BP_CONT_TYPE:
249 /* FOO - scan a user mimetypes file? */
250 value = scan_mimetypes(system_mimetypes_file, part->fname,
251 buf, sizeof(buf));
252 if (value == NULL) {
253 value = "application/octet-stream";
254 /* following heuristic assumes reasonable text won't be this long */
255 if (part->max_linelen < 200) {
256 p = (float)((part->binlo_chars+part->binhi_chars)/part->total_chars);
257 if (part->ascii_chars == part->total_chars) {
258 value = "text/plain; charset=us-ascii";
259 } else if (part->binlo_chars == 0) {
260 sprintf(buf, "text/plain; charset=%s", charset);
261 value = buf;
262 } else if ((1.5*p)+0.75 <= 1.0) {
263 if (part->binhi_chars == 0)
264 value = "text/plain; charset=us-ascii";
265 else {
266 sprintf(buf, "text/plain; charset=%s", charset);
267 value = buf;
268 }
269 }
270 }
271 }
272 break;
273
274 case BP_CONT_ENCODING:
275 {
276 /* pick based upon rough estimates of the encoded sizes */
277 long qp_size = part->ascii_chars
278 + (part->total_chars-part->ascii_chars) * (sizeof("=00")-1);
279 long b64_size = (part->total_chars * 4) / 3;
280 value = (qp_size < b64_size ? "quoted-printable" : "base64");
281 }
282 if (part->max_linelen <= RFC821_MAXLEN) {
283 if (part->ascii_chars == part->total_chars)
284 value = "7bit";
285 else if (part->binlo_chars == 0)
286 value = "8bit";
287 }
288 break;
289
290 case BP_CONT_DESCRIPTION:
291 value = NULL;
292 if ((fname_tmp = tempnam(temp_dir, "fil.")) != NULL) {
293 MIME_FILE_CMD(buf, part->fname, fname_tmp);
294 if (system_call(buf, 0) == 0) {
295 if ((fp = fopen(fname_tmp, "r")) != NULL) {
296 if (fgets(buf, sizeof(buf), fp) != NULL) {
297 bp = trim_trailing_spaces(buf);
298 len = strlen(part->fname);
299 if (strncmp(buf, part->fname, len) == 0) {
300 for (bp += len ; *bp == ':' || isspace(*bp) ; ++bp)
301 ;
302 }
303 value = bp;
304 }
305 (void) fclose(fp);
306 }
307 }
308 (void) unlink(fname_tmp);
309 (void) free((malloc_t)fname_tmp);
310 }
311 break;
312
313 case BP_CONT_DISPOSITION:
314 sprintf(buf, "attachment; filename=\"%s\"", basename(part->fname));
315 value = buf;
316 break;
317
318 case BP_CONT_MD5:
319 value = NULL; /* FOO - not implemented yet */
320 break;
321
322 default:
323 value = NULL; /* can't happen */
324 break;
325
326 }
327
328 bodypart_set_content(part, sel, value);
329 }
330
331
332
scan_mimetypes(fname_mimetypes,fname_part,retbuf,retbufsiz)333 static char *scan_mimetypes(fname_mimetypes, fname_part, retbuf, retbufsiz)
334 const char *fname_mimetypes;
335 const char *fname_part;
336 char *retbuf;
337 int retbufsiz;
338 {
339 char *s;
340 int len_fname, len_ext, rc;
341 FILE *fp;
342
343 if ((fp = fopen(fname_mimetypes, "r")) == NULL)
344 return (char *) NULL;
345
346 len_fname = strlen(fname_part);
347
348 while (fgets(retbuf, retbufsiz, fp) != NULL) {
349
350 /* trim newline, skip comments and blank lines */
351 (void) trim_trailing_spaces(retbuf);
352 if (retbuf[0] == '\0' || retbuf[0] == '#')
353 continue;
354
355 /* locate end of extension field */
356 if (isspace(retbuf[0]))
357 continue; /* blah - illegal line */
358 for (s = retbuf ; *s != '\0' && !isspace(*s) ; ++s)
359 ;
360
361 /* locate start of content type field */
362 for (*s++ = '\0' ; isspace(*s) ; ++s)
363 ;
364 if (*s == '\0')
365 continue; /* blah - illegal line */
366
367 if ((len_ext = strlen(retbuf)) < len_fname
368 && fname_part[len_fname-len_ext-1] == '.'
369 && streq(fname_part+len_fname-len_ext, retbuf)) {
370 (void) fclose(fp);
371 return s;
372 }
373
374 }
375
376 (void) fclose(fp);
377 return (char *)NULL;
378 }
379
380
381 /*****************************************************************************
382 *
383 * Multipart management routines.
384 *
385 ****************************************************************************/
386
387
388 #ifndef NDEBUG
multipart_integrity_check(multi)389 PUBLIC void multipart_integrity_check(multi)
390 const SEND_MULTIPART *multi;
391 {
392 const SEND_MULTIPART *m1;
393
394 assert(multi != NULL);
395 assert(multi->part == NULL);
396 assert(multi->prev->next == multi);
397 assert(multi->next->prev == multi);
398
399 for (m1 = multi->next ; m1 != multi ; m1 = m1->next) {
400 assert(m1->prev->next == m1);
401 assert(m1->next->prev == m1);
402 assert(m1->part != NULL);
403 assert(m1->part->link_count > 0);
404 }
405 }
406 #endif /* !NDEBUG */
407
408
multipart_new(part,id)409 PUBLIC SEND_MULTIPART *multipart_new(part, id)
410 SEND_BODYPART *part;
411 long id;
412 {
413 SEND_MULTIPART *multi;
414
415 multi = (SEND_MULTIPART *) safe_malloc(sizeof(SEND_MULTIPART));
416 multi->id = id;
417 multi->part = part;
418 multi->prev = multi->next = multi;
419 return multi;
420 }
421
422
multipart_destroy(multi)423 PUBLIC void multipart_destroy(multi)
424 SEND_MULTIPART *multi;
425 {
426 SEND_MULTIPART *mnext;
427
428 multipart_integrity_check(multi);
429 mnext = multi->next;
430
431 while (multi = mnext, multi->part != NULL) {
432 mnext = multi->next;
433 assert(multi->part->link_count >= 0);
434 if (--multi->part->link_count == 0)
435 bodypart_destroy(multi->part);
436 free((malloc_t)multi);
437 }
438
439 free((malloc_t)multi);
440 }
441
442
443 /*ARGSUSED*/
multipart_insertpart(multi,mp_curr,part,id)444 PUBLIC SEND_MULTIPART *multipart_insertpart(multi, mp_curr, part, id)
445 SEND_MULTIPART *multi, *mp_curr;
446 SEND_BODYPART *part;
447 long id;
448 {
449 SEND_MULTIPART *mp_new;
450
451 multipart_integrity_check(multi);
452 mp_new = multipart_new(part, id);
453
454 /* insert "mp_new" before "mp_curr */
455 mp_new->next = mp_curr;
456 mp_new->prev = mp_curr->prev;
457 mp_curr->prev->next = mp_new;
458 mp_curr->prev = mp_new;
459
460 ++part->link_count;
461 multipart_integrity_check(multi);
462 return mp_new;
463 }
464
465
466 /*ARGSUSED*/
multipart_appendpart(multi,mp_curr,part,id)467 PUBLIC SEND_MULTIPART *multipart_appendpart(multi, mp_curr, part, id)
468 SEND_MULTIPART *multi, *mp_curr;
469 SEND_BODYPART *part;
470 long id;
471 {
472 SEND_MULTIPART *mp_new;
473
474 multipart_integrity_check(multi);
475 mp_new = multipart_new(part, id);
476
477 /* append "mp_new" after "mp_curr" */
478 mp_new->prev = mp_curr;
479 mp_new->next = mp_curr->next;
480 mp_curr->next->prev = mp_new;
481 mp_curr->next = mp_new;
482
483 ++part->link_count;
484 multipart_integrity_check(multi);
485 return mp_new;
486 }
487
488
489 /*ARGSUSED*/
multipart_deletepart(multi,mp_curr)490 PUBLIC SEND_BODYPART *multipart_deletepart(multi, mp_curr)
491 SEND_MULTIPART *multi, *mp_curr;
492 {
493 SEND_BODYPART *part;
494
495 multipart_integrity_check(multi);
496 mp_curr->prev->next = mp_curr->next;
497 mp_curr->next->prev = mp_curr->prev;
498 part = mp_curr->part;
499 free((malloc_t)mp_curr);
500 --part->link_count;
501 multipart_integrity_check(multi);
502 return part;
503 }
504
505
506
multipart_next(multi,mp_curr)507 PUBLIC SEND_MULTIPART *multipart_next(multi, mp_curr)
508 SEND_MULTIPART *multi, *mp_curr;
509 {
510 mp_curr = (mp_curr == NULL ? multi->next : mp_curr->next);
511 return (mp_curr->part == NULL ? (SEND_MULTIPART *) NULL : mp_curr);
512 }
513
514
multipart_find(multi,id)515 PUBLIC SEND_MULTIPART *multipart_find(multi, id)
516 SEND_MULTIPART *multi;
517 long id;
518 {
519 while (multi = multi->next, multi->part != NULL && multi->id != id)
520 ;
521 return multi;
522 }
523
524