1 /*
2  *  Copyright (C) 1995, 1996  Karl-Johan Johnsson.
3  */
4 
5 #include "global.h"
6 #include "decode.h"
7 #include "expand.h"
8 #include "file.h"
9 #include "font.h"
10 #include "mailcap.h"
11 #include "p_I.h"
12 #include "p_attach.h"
13 #include "parse.h"
14 #include "util.h"
15 #include "xutil.h"
16 
17 struct PostAttachment {
18     char		*file_name;
19     char		*type;
20     char		*name;
21     char		*descr;
22     unsigned char	is_inline;
23     unsigned char	enc;
24     unsigned char	needs_enc;
25     unsigned char	has_8bit;
26 };
27 
28 static const char	*get_mime_type(const char*);
29 
check_file(PostAttachment * pa,char * message)30 static int check_file(PostAttachment *pa, char *message)
31 {
32     FILE	*fp;
33     long	n_ascii = 0;
34     long	n_ctrl  = 0;
35     long	n_8bit  = 0;
36     int		ch;
37     const char	*c;
38 
39     fp = fopen(pa->file_name, "r");
40     if (!fp) {
41 	perror(pa->file_name);
42 	strcpy(message, "Couldn't open file!");
43 	return False;
44     }
45 
46     while ((ch = getc(fp)) != EOF)
47 	if (ch >= 128 + 32)
48 	    n_8bit++;
49 	else if (ch >= 128)
50 	    n_ctrl++;
51 	else if (ch >= 32)
52 	    n_ascii++;
53 	else
54 	    switch (ch) {
55 	    case '\t': /*   9 */
56 	    case '\n': /*  10 */
57 	    case '\f': /*  12 */
58 	    case '\r': /*  13 */
59 	    case 14:   /*  SO */
60 	    case 15:   /*  SI */
61 	    case 27:   /* ESC */
62 		n_ascii++;
63 		break;
64 	    default:
65 		n_ctrl++;
66 		break;
67 	    }
68 
69     fclose(fp);
70 
71     c = strrchr(pa->file_name, '.');
72     if (c) {
73 	c = get_mime_type(c + 1);
74 	if (c)
75 	    pa->type = XtNewString(c);
76     }
77 
78     if (n_ctrl > 0) {  /* binary file */
79 	pa->enc = MimeEncBase64;
80 	pa->needs_enc = True;
81 	pa->has_8bit = True;
82 	if (!pa->type)
83 	    strcpy(message, "Couldn't guess content-type type.");
84     } else {
85 	pa->enc = n_8bit > n_ascii ? MimeEncBase64 : MimeEncNone;
86 	pa->needs_enc = False;
87 	pa->has_8bit = n_8bit > 0;
88 	if (!pa->type) {
89 	    pa->type = XtNewString("text/plain");
90 	    strcpy(message, "Maybe text/plain?  "
91 		   "Please give a charset parameter.");
92 	}
93     }
94 
95     return True;
96 }
97 
98 /*************************************************************************/
99 
enc_file_none(PostAttachment * pa,FILE * fin,FILE * fout)100 static int enc_file_none(PostAttachment *pa, FILE *fin, FILE *fout)
101 {
102     int	c, bol = True;
103 
104     while ((c = getc(fin)) != EOF) {
105 	if (c == '\n')
106 	    putc('\r', fout);
107 	else if (bol && c == '.')
108 	    putc('.', fout);
109 	putc(c, fout);
110 	bol = c == '\n';
111     }
112 
113     if (!bol)
114 	fputs("\r\n", fout);
115 
116     return True;
117 }
118 
enc_file_base64(PostAttachment * pa,FILE * fin,FILE * fout)119 static int enc_file_base64(PostAttachment *pa, FILE *fin, FILE *fout)
120 {
121     unsigned char	buf[4];
122     unsigned long	acc;
123     int			n, pos = 0;
124 
125     while ((n = fread(buf, 1, 3, fin)) == 3) {
126 	acc = (buf[0] << 16) | (buf[1] << 8) | buf[2];
127 	buf[0] = base64_alpha[(acc >> 18) & 0x3fu];
128 	buf[1] = base64_alpha[(acc >> 12) & 0x3fu];
129 	buf[2] = base64_alpha[(acc >>  6) & 0x3fu];
130 	buf[3] = base64_alpha[(acc      ) & 0x3fu];
131 	fwrite(buf, 1, 4, fout);
132 	pos += 4;
133 	if (pos >= 76) {
134 	    fwrite("\r\n", 1, 2, fout);
135 	    pos = 0;
136 	}
137     }
138 
139     switch (n) {
140     case 1:
141 	acc = buf[0] << 16;
142 	buf[0] = base64_alpha[(acc >> 18) & 0x3ful];
143 	buf[1] = base64_alpha[(acc >> 12) & 0x3ful];
144 	buf[2] = '=';
145 	buf[3] = '=';
146 	fwrite(buf, 1, 4, fout);
147 	break;
148     case 2:
149 	acc = (buf[0] << 16) | (buf[1] << 8);
150 	buf[0] = base64_alpha[(acc >> 18) & 0x3fu];
151 	buf[1] = base64_alpha[(acc >> 12) & 0x3fu];
152 	buf[2] = base64_alpha[(acc >>  6) & 0x3fu];
153 	buf[3] = '=';
154 	fwrite(buf, 1, 4, fout);
155 	break;
156     }
157 
158     if (pos != 0)
159 	fwrite("\r\n", 1, 2, fout);
160     fwrite("\r\n", 1, 2, fout);
161 
162     return True;
163 }
164 
enc_file_uue(PostAttachment * pa,FILE * fin,FILE * fout)165 static int enc_file_uue(PostAttachment *pa, FILE *fin, FILE *fout)
166 {
167     char	*name = pa->name;
168     char	buf[45];
169     int		n;
170 
171     if (!name) {
172 	name = strrchr(pa->file_name, '/');
173 	if (name)
174 	    name++;
175 	else
176 	    name = pa->file_name;
177     }
178 
179     fprintf(fout, "begin 0644 %s\r\n", name);
180 
181 #if 0
182 #  define UU_CHAR(a)  ((unsigned char)(' ' + ((a) & 0x3fu)))
183 #else
184 #  define UU_CHAR(a)  ((unsigned char)(' ' + 1 + (((a) - 1) & 0x3fu)))
185 #endif
186 
187     while ((n = fread(buf, 1, sizeof buf, fin)) > 0) {
188 	unsigned long	acc;
189 	unsigned char	*c = (unsigned char *)buf;
190 
191 	if (' ' + n == '.')
192 	    putc('.', fout);
193 	putc(' ' + n, fout);
194 	while (n > 2) {
195 	    acc = (c[0] << 16) | (c[1] << 8) | c[2];
196 	    fprintf(fout, "%c%c%c%c",
197 		    UU_CHAR(acc >> 18), UU_CHAR(acc >> 12),
198 		    UU_CHAR(acc >>  6), UU_CHAR(acc      ));
199 	    /*
200 	      (unsigned char)(' ' + ((acc >> 18) & 0x3fu)),
201 	      (unsigned char)(' ' + ((acc >> 12) & 0x3fu)),
202 	      (unsigned char)(' ' + ((acc >>  6) & 0x3fu)),
203 	      (unsigned char)(' ' + ((acc      ) & 0x3fu)));
204 	      */
205 	    n -= 3;
206 	    c += 3;
207 	}
208 
209 	switch (n) {
210 	case 1:
211 	    acc = c[0] << 16;
212 	    fprintf(fout, "%c%c", UU_CHAR(acc >> 18), UU_CHAR(acc >> 12));
213 	    /*
214 	      (unsigned char)(' ' + ((acc >> 18) & 0x3fu)),
215 	      (unsigned char)(' ' + ((acc >> 12) & 0x3fu)));
216 	      */
217 	    break;
218 	case 2:
219 	    acc = (c[0] << 16) | (c[1] << 8);
220 	    fprintf(fout, "%c%c%c",
221 		    UU_CHAR(acc >> 18), UU_CHAR(acc >> 12), UU_CHAR(acc >> 6));
222 	    /*
223 	      (unsigned char)(' ' + ((acc >> 18) & 0x3fu)),
224 	      (unsigned char)(' ' + ((acc >> 12) & 0x3fu)),
225 	      (unsigned char)(' ' + ((acc >>  6) & 0x3fu)));
226 	      */
227 	    break;
228 	}
229 
230 	fputs("\r\n", fout);
231     }
232 
233     fputs("`\r\nend\r\n\r\n", fout);
234 
235     return True;
236 }
237 
enc_file_qp(PostAttachment * pa,FILE * fin,FILE * fout)238 static int enc_file_qp(PostAttachment *pa, FILE *fin, FILE *fout)
239 {
240     static const unsigned char	hex_char[16] = "0123456789ABCDEF";
241     int		ch, next, do_enc, col;
242 
243     next = getc(fin);
244     col = 0;
245 
246     while ((ch = next) != EOF) {
247 	next = getc(fin);
248 
249 	if (ch == '\n') {
250 	    fputs("\r\n", fout);
251 	    col = 0;
252 	    continue;
253 	}
254 
255 	if (col > 68) {
256 	    fputs("=\r\n", fout);
257 	    col = 0;
258 	}
259 
260 	if (ch < 32 || ch > 126)
261 	    do_enc = True;
262 	else
263 	    switch (ch) {
264 	    case ' ':
265 		do_enc = next == '\n' || next == EOF;
266 		break;
267 	    case '.':
268 	    case 'F':
269 		do_enc = col == 0;
270 		break;
271 	    case '=':
272 		do_enc = True;
273 		break;
274 	    default:
275 		do_enc = False;
276 		break;
277 	    }
278 
279 	if (!do_enc) {
280 	    putc(ch, fout);
281 	    col++;
282 	} else {
283 	    putc('=', fout);
284 	    putc(hex_char[(ch >> 4) & 0xfu], fout);
285 	    putc(hex_char[(ch     ) & 0xfu], fout);
286 	    col += 3;
287 	}
288     }
289 
290     fputs("\r\n\r\n" + (col == 0 ? 2 : 0), fout);
291 
292     return True;
293 }
294 
295 /*************************************************************************/
296 
free_attachment(PostAttachment * pa)297 void free_attachment(PostAttachment *pa)
298 {
299     XtFree(pa->file_name);
300     XtFree(pa->type);
301     XtFree(pa->name);
302     XtFree(pa->descr);
303     pa->file_name = NULL;
304     pa->type = NULL;
305     pa->name = NULL;
306     pa->descr = NULL;
307     XtFree((char *)pa);
308 }
309 
create_attachment(char * file_name,char * message)310 PostAttachment *create_attachment(char *file_name, char *message)
311 {
312     PostAttachment	*pa;
313     char		*c;
314 
315     message[0] = '\0';
316     file_name = expand_path(file_name);
317     if (!file_name) {
318 	strcpy(message, "File name error!");
319 	return NULL;
320     }
321 
322     pa = (PostAttachment *)XtMalloc(sizeof *pa);
323     pa->file_name = file_name;
324     pa->type = NULL;
325     pa->name = NULL;
326     pa->descr = NULL;
327     pa->is_inline = False;
328     pa->enc = -1;
329     pa->needs_enc = False;
330     pa->has_8bit = False;
331 
332     if (!check_file(pa, message)) {
333 	free_attachment(pa);
334 	return NULL;
335     }
336 
337     c = strrchr(file_name, '/');
338     if (c)
339 	c++;
340     else
341 	c = file_name;
342     pa->name = XtNewString(c);
343 
344     return pa;
345 }
346 
print_attach_info(PostAttachment * pa,char * buffer)347 void print_attach_info(PostAttachment *pa, char *buffer)
348 {
349     char	*enc = NULL;
350     int		n;
351 
352     if (!pa->type)
353 	strcpy(buffer, "[unknown]");
354     else {
355 	n = strlen(pa->type);
356 	if (n < 18)
357 	    strcpy(buffer, pa->type);
358 	else {
359 	    memcpy(buffer, pa->type, 15);
360 	    strcpy(buffer + 15, "...");
361 	}
362     }
363     n = strlen(buffer);
364     while (n < 20)
365 	buffer[n++] = ' ';
366     buffer[n] = '\0';
367 
368     switch (pa->enc) {
369     case MimeEncNone:
370 	enc = "     ";
371 	break;
372     case MimeEncBase64:
373 	enc = "b64  ";
374 	break;
375     case MimeEncUue:
376 	enc = "uue  ";
377 	break;
378     case MimeEncQP:
379 	enc = "Q-P  ";
380 	break;
381     }
382     if (enc)
383 	strcpy(buffer + n, enc);
384     n += strlen(buffer + n);
385 
386     strncat(buffer + n, pa->file_name, 248 - n);
387 }
388 
print_attachment(FILE * fout,PostAttachment * pa)389 int print_attachment(FILE *fout, PostAttachment *pa)
390 {
391     FILE	*fin;
392     int		ok;
393 
394     if (!pa->type) {
395 	popup_title_notice("Unknown Content-Type", pa->file_name, False);
396 	return False;
397     }
398 
399     fin = fopen(pa->file_name, "r");
400     if (!fin) {
401 	popup_title_notice("Couldn't open file", pa->file_name, False);
402 	return False;
403     }
404 
405     fprintf(fout, "Content-Type: %s\r\n", pa->type);
406     if (pa->descr && pa->descr[0] != '\0')
407 	fprintf(fout, "Content-Description: %s\r\n", pa->descr);
408     fprintf(fout, "Content-Disposition: %s",
409 	    pa->is_inline ? "inline" : "attachment");
410     if (pa->name && pa->name[0] != '\0')
411 	fprintf(fout, "; filename=\"%s\"", pa->name);
412     fputs("\r\n", fout);
413 
414     switch (pa->enc) {
415     case MimeEncNone:
416 	if (pa->has_8bit)
417 	    fputs("Content-Transfer-Encoding: 8bit\r\n", fout);
418 	fputs("\r\n", fout);
419 	ok = enc_file_none(pa, fin, fout);
420 	break;
421     case MimeEncBase64:
422 	fputs("Content-Transfer-Encoding: base64\r\n\r\n", fout);
423 	ok = enc_file_base64(pa, fin, fout);
424 	break;
425     case MimeEncUue:
426 	fputs("Content-Transfer-Encoding: x-uue\r\n\r\n", fout);
427 	ok = enc_file_uue(pa, fin, fout);
428 	break;
429     case MimeEncQP:
430 	fputs("Content-Transfer-Encoding: quoted-printable\r\n\r\n", fout);
431 	ok = enc_file_qp(pa, fin, fout);
432 	break;
433     default:
434 	popup_title_notice("Bad encoding", pa->file_name, False);
435 	ok = False;
436 	break;
437     }
438 
439     if (fclose(fin) < 0) {
440 	perror(pa->file_name);
441 	if (ok) {
442 	    popup_title_notice("File error", pa->file_name, False);
443 	    ok = False;
444 	}
445     }
446 
447     return ok;
448 }
449 
attach_get_enc(PostAttachment * pa)450 int attach_get_enc(PostAttachment *pa)
451 {
452     return pa ? pa->enc : -1;
453 }
454 
attach_is_inline(PostAttachment * pa)455 int attach_is_inline(PostAttachment *pa)
456 {
457     return pa ? pa->is_inline : False;
458 }
459 
attach_get_type(PostAttachment * pa)460 char *attach_get_type(PostAttachment *pa)
461 {
462     return pa && pa->type ? pa->type : "";
463 }
464 
attach_get_name(PostAttachment * pa)465 char *attach_get_name(PostAttachment *pa)
466 {
467     return pa && pa->name ? pa->name : "";
468 }
469 
attach_get_descr(PostAttachment * pa)470 char *attach_get_descr(PostAttachment *pa)
471 {
472     return pa && pa->descr ? pa->descr : "";
473 }
474 
attach_set_enc(PostAttachment * pa,int enc,char * message)475 int attach_set_enc(PostAttachment *pa, int enc, char *message)
476 {
477     message[0] = '\0';
478     switch (enc) {
479     case MimeEncNone:
480 	if (pa->needs_enc) {
481 	    strcpy(message, "Can't send binary file without encoding.");
482 	    return False;
483 	}
484 	break;
485     case MimeEncBase64:
486     case MimeEncUue:
487 	if (!pa->needs_enc)
488 	    strcpy(message,
489 		   "Warning, unnecessary: text file needs no encoding.");
490 	break;
491     case MimeEncQP:
492 	if (pa->needs_enc)
493 	    strcpy(message, "QP for binary file?  If you say so...");
494 	else
495 	    strcpy(message, "Quoted-printable = quoted-unreadable.");
496 	break;
497     default:
498 	strcpy(message, "Error!");
499 	return False;
500     }
501 
502     pa->enc = enc;
503     return True;
504 }
505 
attach_set_inline(PostAttachment * pa,int is_inline,char * message)506 int attach_set_inline(PostAttachment *pa, int is_inline, char *message)
507 {
508     message[0] = '\0';
509     pa->is_inline = is_inline;
510     return True;
511 }
512 
attach_set_type(PostAttachment * pa,char * type,char * message)513 int attach_set_type(PostAttachment *pa, char *type, char *message)
514 {
515     char	*header[2];
516     char	type_buf[80], subtype_buf[80];
517     MimeArg	args[4] = {{0, }, };
518     int		i, ok;
519 
520     if (!type)
521 	type = "";
522 
523     message[0] = '\0';
524     header[0] = XtMalloc(strlen(type) + 32);
525     sprintf(header[0], "Content-Type: %s", type);
526     header[1] = NULL;
527 
528     ok = parse_content_type(header, type_buf, sizeof type_buf,
529 			    subtype_buf, sizeof subtype_buf,
530 			    args, XtNumber(args), True);
531     if (!ok)
532 	strcpy(message, "Parse error!");
533     else if (case_lstrcmp(type_buf, "text") == 0) {
534 	char	*charset = get_charset(args);
535 
536 	if (!charset)
537 	    strcpy(message, "Consider sepcifying a charset parameter.");
538 	else {
539 	    MimeFont	*font;
540 
541 	    font = get_font(charset);
542 	    if (!font)
543 		strcpy(message, "Warning: No font for that charset.");
544 	}
545     } else {
546 	const MailcapData	*mcap;
547 
548 	mcap = mailcap_lookup(type_buf, subtype_buf);
549 	if (!mcap)
550 	    strcpy(message, "Warning: No mailcap entry for that type.");
551     }
552 
553     XtFree(header[0]);
554     for (i = 0 ; i < XtNumber(args) ; i++) {
555 	XtFree(args[i].name);
556 	XtFree(args[i].value);
557     }
558 
559     if (ok) {
560 	XtFree(pa->type);
561 	pa->type = XtNewString(type);
562     }
563 
564     return ok;
565 }
566 
attach_set_name(PostAttachment * pa,char * name,char * message)567 int attach_set_name(PostAttachment *pa, char *name, char *message)
568 {
569     message[0] = '\0';
570     XtFree(pa->name);
571     pa->name = XtNewString(name);
572     return True;
573 }
574 
attach_set_descr(PostAttachment * pa,char * descr,char * message)575 int attach_set_descr(PostAttachment *pa, char *descr, char *message)
576 {
577     message[0] = '\0';
578     XtFree(pa->descr);
579     pa->descr = XtNewString(descr);
580     return True;
581 }
582 
583 /*************************************************************************/
584 
585 typedef struct MimeType	{
586     const char	*type;
587     const char	*suffix;
588 } MimeType;
589 
590 static MimeType	*mime_types = NULL;
591 static long	n_types = -1;
592 
load_mime_types(void)593 static void load_mime_types(void)
594 {
595     static char	*buffer = NULL;
596     char	*c;
597     long	n_alloc;
598 
599     n_types = 0;
600     if (global.mime_types) {
601 	int	fd = open(global.mime_types, O_RDONLY);
602 
603 	if (fd < 0)
604 	    perror(global.mime_types);
605 	else {
606 	    buffer = snarf_file(fd, NULL);
607 	    close(fd);
608 	}
609     }
610 
611     n_alloc = 8;
612     mime_types = (MimeType *)XtMalloc(n_alloc * sizeof mime_types[0]);
613 
614     c = buffer;
615     while (c) {
616 	char	*end = strchr(c, '\n');
617 	char	*type, *suffix;
618 
619 	if (end)
620 	    *end++ = '\0';
621 	type = strtok(c, " \t");
622 	if (type)
623 	    while ((suffix = strtok(NULL, " \t"))) {
624 		if (n_types + 8 > n_alloc) {
625 		    n_alloc = 2 * (n_types + 8);
626 		    mime_types =
627 			(MimeType *)XtRealloc((char *)mime_types,
628 					      n_alloc * sizeof mime_types[0]);
629 		}
630 		ascii_lower(suffix);
631 		mime_types[n_types].type   = type;
632 		mime_types[n_types].suffix = suffix;
633 		n_types++;
634 	    }
635 
636 	c = end;
637     }
638 
639     mime_types[n_types].type   = "image/jpeg";
640     mime_types[n_types].suffix = "jpeg";
641     n_types++;
642     mime_types[n_types].type   = "image/jpeg";
643     mime_types[n_types].suffix = "jpg";
644     n_types++;
645     mime_types[n_types].type   = "image/gif";
646     mime_types[n_types].suffix = "gif";
647     n_types++;
648     mime_types[n_types].type   = "image/png";
649     mime_types[n_types].suffix = "png";
650     n_types++;
651     mime_types[n_types].type   = "application/postscript";
652     mime_types[n_types].suffix = "ps";
653     n_types++;
654 
655     mime_types = (MimeType *)XtRealloc((char *)mime_types,
656 				       n_types * sizeof mime_types[0]);
657 }
658 
get_mime_type(const char * suffix)659 static const char *get_mime_type(const char *suffix)
660 {
661     long	n;
662 
663     if (n_types < 0)
664 	load_mime_types();
665 
666     for (n = 0 ; n < n_types ; n++)
667 	if (case_lstrcmp(suffix, mime_types[n].suffix) == 0)
668 	    return mime_types[n].type;
669 
670     return NULL;
671 }
672