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