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