Initial commit for branch ical
[alpine.git] / pith / mimedesc.c
blob6d5999c43f304798f180e6bf9ef58fb173d1226f
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mimedesc.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2017 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/mimedesc.h"
21 #include "../pith/mimetype.h"
22 #include "../pith/state.h"
23 #include "../pith/conf.h"
24 #include "../pith/mailview.h"
25 #include "../pith/rfc2231.h"
26 #include "../pith/editorial.h"
27 #include "../pith/mailpart.h"
28 #include "../pith/mailcap.h"
29 #include "../pith/smime.h"
32 /* internal prototypes */
33 int mime_known_text_subtype(char *);
34 ATTACH_S *next_attachment(void);
35 void format_mime_size(char *, size_t, BODY *, int);
36 int mime_show(BODY *);
40 * Def's to help in sorting out multipart/alternative
42 #define SHOW_NONE 0
43 #define SHOW_PARTS 1
44 #define SHOW_ALL_EXT 2
45 #define SHOW_ALL 3
48 * Def's to control format_mime_size output
50 #define FMS_NONE 0x00
51 #define FMS_SPACE 0x01
54 /*----------------------------------------------------------------------
55 Add lines to the attachments structure
57 Args: body -- body of the part being described
58 prefix -- The prefix for numbering the parts
59 num -- The number of this specific part
60 should_show -- Flag indicating which of alternate parts should be shown
61 multalt -- Flag indicating the part is one of the multipart
62 alternative parts (so suppress editorial comment)
64 Result: The ps_global->attachments data structure is filled in. This
65 is called recursively to descend through all the parts of a message.
66 The description strings filled in are malloced and should be freed.
68 ----*/
69 void
70 describe_mime(struct mail_bodystruct *body, char *prefix, int num,
71 int should_show, int multalt, int flags)
73 #define NUMXLEN 512
74 PART *part;
75 char numx[NUMXLEN], string[800], *description;
76 int n, named = 0, can_display_ext;
77 ATTACH_S *a;
79 if(!body)
80 return;
82 if(body->type == TYPEMULTIPART){
83 int alt_to_show = 0;
85 if(strucmp(body->subtype, "alternative") == 0){
86 int effort, best_effort = SHOW_NONE;
88 /*---- Figure out which alternative part to display ---*/
90 * This is kind of complicated because some TEXT types
91 * are more displayable than others. We don't want to
92 * commit to displaying a text-part alternative that we
93 * don't directly recognize unless that's all there is.
95 for(part=body->nested.part, n=1; part; part=part->next, n++)
96 if(flags & FM_FORCEPREFPLN
97 || (!(flags & FM_FORCENOPREFPLN)
98 && F_ON(F_PREFER_PLAIN_TEXT, ps_global)
99 && part->body.type == TYPETEXT
100 && (!part->body.subtype
101 || !strucmp(part->body.subtype, "PLAIN")))){
102 if((effort = mime_show(&part->body)) != SHOW_ALL_EXT){
103 best_effort = effort;
104 alt_to_show = n;
105 break;
108 else if((effort = mime_show(&part->body)) >= best_effort
109 && (part->body.type != TYPETEXT || mime_known_text_subtype(part->body.subtype))
110 && effort != SHOW_ALL_EXT){
111 best_effort = effort;
112 alt_to_show = n;
114 else if(part->body.type == TYPETEXT && alt_to_show == 0){
115 best_effort = effort;
116 alt_to_show = n;
119 else if(!strucmp(body->subtype, "digest")){
120 memset(a = next_attachment(), 0, sizeof(ATTACH_S));
121 if(*prefix){
122 prefix[n = strlen(prefix) - 1] = '\0';
123 a->number = cpystr(prefix);
124 prefix[n] = '.';
126 else
127 a->number = cpystr("");
129 a->description = cpystr("Multipart/Digest");
130 a->body = body;
131 a->can_display = MCD_INTERNAL;
132 (a+1)->description = NULL;
134 #ifdef SMIME
135 else if(!strucmp(body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)){
136 memset(a = next_attachment(), 0, sizeof(ATTACH_S));
137 if(*prefix){
138 prefix[n = strlen(prefix) - 1] = '\0';
139 a->number = cpystr(prefix);
140 prefix[n] = '.';
142 else
143 a->number = cpystr("");
145 a->description = body->description ? cpystr(body->description)
146 : cpystr("");
147 a->body = body;
148 a->can_display = MCD_INTERNAL;
149 (a+1)->description = NULL;
151 #endif /* SMIME */
152 else if(mailcap_can_display(body->type, body->subtype, body, 0)
153 || (can_display_ext
154 = mailcap_can_display(body->type, body->subtype, body, 1))){
155 memset(a = next_attachment(), 0, sizeof(ATTACH_S));
156 if(*prefix){
157 prefix[n = strlen(prefix) - 1] = '\0';
158 a->number = cpystr(prefix);
159 prefix[n] = '.';
161 else
162 a->number = cpystr("");
164 snprintf(string, sizeof(string), "%s/%s", body_type_names(body->type),
165 body->subtype);
166 string[sizeof(string)-1] = '\0';
167 a->description = cpystr(string);
168 a->body = body;
169 a->can_display = MCD_EXTERNAL;
170 if(can_display_ext)
171 a->can_display |= MCD_EXT_PROMPT;
172 (a+1)->description = NULL;
175 for(part=body->nested.part, n=1; part; part=part->next, n++){
176 snprintf(numx, sizeof(numx), "%s%d.", prefix, n);
177 numx[sizeof(numx)-1] = '\0';
179 * Last arg to describe_mime here. If we have chosen one part
180 * of a multipart/alternative to display then we suppress
181 * the editorial messages on the other parts.
183 describe_mime(&(part->body),
184 (part->body.type == TYPEMULTIPART) ? numx : prefix,
185 n, should_show && (n == alt_to_show || !alt_to_show),
186 alt_to_show != 0, flags);
189 else{
190 char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN];
191 size_t ll;
193 a = next_attachment();
194 format_mime_size(a->size, sizeof(a->size), body, FMS_SPACE);
196 a->suppress_editorial = (multalt != 0);
198 snprintf(tmp1, sizeof(tmp1), "%s", body->description ? body->description : "");
199 tmp1[sizeof(tmp1)-1] = '\0';
200 snprintf(tmp2, sizeof(tmp2), "%s", (!body->description && body->type == TYPEMESSAGE && body->encoding <= ENCBINARY && body->subtype && strucmp(body->subtype, "rfc822") == 0 && body->nested.msg->env && body->nested.msg->env->subject) ? body->nested.msg->env->subject : "");
201 tmp2[sizeof(tmp2)-1] = '\0';
203 description = (body->description)
204 ? (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
205 SIZEOF_20KBUF, tmp1)
206 : (body->type == TYPEMESSAGE
207 && body->encoding <= ENCBINARY
208 && body->subtype
209 && strucmp(body->subtype, "rfc822") == 0
210 && body->nested.msg->env
211 && body->nested.msg->env->subject)
212 ? (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp2)
213 : (body->type == TYPEMESSAGE
214 && body->subtype
215 && !strucmp(body->subtype, "delivery-status"))
216 ? "Delivery Status"
217 : NULL;
219 description = iutf8ncpy((char *)(tmp_20k_buf+1000), description, 1000);
220 snprintf(string, sizeof(string), "%s%s%s%s",
221 type_desc(body->type,body->subtype,body->parameter,
222 body->disposition.type ? body->disposition.parameter : NULL, 0),
223 (description && description[0]) ? ", \"" : "",
224 (description && description[0]) ? description : "",
225 (description && description[0]) ? "\"": "");
226 string[sizeof(string)-1] = '\0';
227 a->description = cpystr(string);
228 a->body = body;
230 if(body->disposition.type){
231 named = strucmp(body->disposition.type, "inline");
233 else{
234 char *value;
238 * This test remains for backward compatibility
240 if(body && (value = parameter_val(body->parameter, "name")) != NULL){
241 named = strucmp(value, "Message Body");
242 fs_give((void **) &value);
247 * Make sure we have the tools available to display the
248 * type/subtype, *AND* that we can decode it if needed.
249 * Of course, if it's text, we display it anyway in the
250 * mail_view_screen so put off testing mailcap until we're
251 * explicitly asked to display that segment 'cause it could
252 * be expensive to test...
254 if((body->type == TYPETEXT && !named)
255 || MIME_VCARD(body->type,body->subtype)
256 || MIME_VCALENDAR(body->type, body->subtype)){
257 a->test_deferred = 1;
258 a->can_display = MCD_INTERNAL;
260 else{
261 a->test_deferred = 0;
262 a->can_display = mime_can_display(body->type, body->subtype, body);
266 * Deferred means we can display it
268 a->shown = MIME_VCALENDAR(body->type, body->subtype)
269 || ((a->can_display & MCD_INTERNAL)
270 && !MIME_VCARD(body->type,body->subtype)
271 && (!named || multalt
272 || (body->type == TYPETEXT && num == 1
273 && !(*prefix && strcmp(prefix,"1."))))
274 && (body->type != TYPEMESSAGE
275 || (body->type == TYPEMESSAGE
276 && body->encoding <= ENCBINARY))
277 && should_show);
278 ll = (strlen(prefix) + 16) * sizeof(char);
279 a->number = (char *) fs_get(ll);
280 snprintf(a->number, ll, "%s%d",prefix, num);
281 a->number[ll-1] = '\0';
282 (a+1)->description = NULL;
283 if(body->type == TYPEMESSAGE && body->encoding <= ENCBASE64
284 && body->subtype && strucmp(body->subtype, "rfc822") == 0){
285 body = body->nested.msg->body; /* NUMXLEN = sizeof(numx) */
286 snprintf(numx, sizeof(numx), "%.*s%d.", NUMXLEN-20, prefix, num);
287 numx[sizeof(numx)-1] = '\0';
288 describe_mime(body, numx, 1, should_show, 0, flags);
295 mime_known_text_subtype(char *subtype)
297 char **p;
298 static char *known_types[] = {
299 "plain",
300 "html",
301 "enriched",
302 "richtext",
303 NULL
306 if(!(subtype && *subtype))
307 return(1);
309 for(p = known_types; *p; p++)
310 if(!strucmp(subtype, *p))
311 return(1);
312 return(0);
317 * Returns attribute value or NULL.
318 * Value returned needs to be freed by caller
320 char *
321 parameter_val(PARAMETER *param, char *attribute)
323 if(!(param && attribute && attribute[0]))
324 return(NULL);
326 return(rfc2231_get_param(param, attribute, NULL, NULL));
331 * Get sender_filename, the filename set by the sender in the attachment.
332 * If a sender_filename buffer is passed in, the answer is copied to it
333 * and a pointer to it is returned. If sender_filename is passed in as NULL
334 * then an allocated copy of the sender filename is returned instead.
335 * If ext_ptr is non-NULL then it is set to point to the extension name.
336 * It is not a separate copy, it points into the string sender_filename.
338 char *
339 get_filename_parameter(char *sender_filename, size_t sfsize, BODY *body, char **ext_ptr)
341 char *p = NULL;
342 char *decoded_name = NULL;
343 char *filename = NULL;
344 char tmp[1000];
346 if(!body)
347 return(NULL);
349 if(sender_filename){
350 if(sfsize <= 0)
351 return(NULL);
353 sender_filename[0] = '\0';
357 * First check for Content-Disposition's "filename" parameter and
358 * if that isn't found for the deprecated Content-Type "name" parameter.
360 if((p = parameter_val(body->disposition.parameter, "filename"))
361 || (p = parameter_val(body->parameter, "name"))){
364 * If somebody sent us and incorrectly rfc2047 encoded
365 * parameter value instead of what rfc2231 suggest we
366 * grudglingly try to fix it.
368 if(p[0] == '=' && p[1] == '?')
369 decoded_name = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp,
370 sizeof(tmp), p);
372 if(!decoded_name)
373 decoded_name = p;
375 filename = last_cmpnt(decoded_name);
377 if(!filename)
378 filename = decoded_name;
381 if(filename){
382 if(sender_filename){
383 strncpy(sender_filename, filename, sfsize-1);
384 sender_filename[sfsize-1] = '\0';
386 else
387 sender_filename = cpystr(filename);
390 if(p)
391 fs_give((void **) &p);
393 /* ext_ptr will end up pointing into sender_filename string */
394 if(ext_ptr && sender_filename)
395 mt_get_file_ext(sender_filename, ext_ptr);
397 return(sender_filename);
401 /*----------------------------------------------------------------------
402 Return a pointer to the next attachment struct
404 Args: none
406 ----*/
407 ATTACH_S *
408 next_attachment(void)
410 ATTACH_S *a;
411 int n;
413 for(a = ps_global->atmts; a->description; a++)
416 if((n = a - ps_global->atmts) + 1 >= ps_global->atmts_allocated){
417 ps_global->atmts_allocated *= 2;
418 fs_resize((void **)&ps_global->atmts,
419 ps_global->atmts_allocated * sizeof(ATTACH_S));
420 a = &ps_global->atmts[n];
423 return(a);
428 /*----------------------------------------------------------------------
429 Zero out the attachments structure and free up storage
430 ----*/
431 void
432 zero_atmts(ATTACH_S *atmts)
434 ATTACH_S *a;
436 for(a = atmts; a->description != NULL; a++){
437 fs_give((void **)&(a->description));
438 fs_give((void **)&(a->number));
441 atmts->description = NULL;
445 char *
446 body_type_names(int t)
448 #define TLEN 31
449 static char body_type[TLEN + 1];
450 char *p;
452 body_type[0] = '\0';
453 strncpy(body_type, /* copy the given type */
454 (t > -1 && t < TYPEMAX && body_types[t])
455 ? body_types[t] : "Other", TLEN);
456 body_type[sizeof(body_type)-1] = '\0';
458 for(p = body_type + 1; *p; p++) /* make it presentable */
459 if(isascii((unsigned char) (*p)) && isupper((unsigned char) (*p)))
460 *p = tolower((unsigned char)(*p));
462 return(body_type); /* present it */
466 /*----------------------------------------------------------------------
467 Mapping table use to neatly display charset parameters
468 ----*/
470 static struct set_names {
471 char *rfcname,
472 *humanname;
473 } charset_names[] = {
474 {"US-ASCII", "Plain Text"},
475 {"ISO-8859-1", "Latin 1 (Western Europe)"},
476 {"ISO-8859-2", "Latin 2 (Eastern Europe)"},
477 {"ISO-8859-3", "Latin 3 (Southern Europe)"},
478 {"ISO-8859-4", "Latin 4 (Northern Europe)"},
479 {"ISO-8859-5", "Latin & Cyrillic"},
480 {"ISO-8859-6", "Latin & Arabic"},
481 {"ISO-8859-7", "Latin & Greek"},
482 {"ISO-8859-8", "Latin & Hebrew"},
483 {"ISO-8859-9", "Latin 5 (Turkish)"},
484 {"ISO-8859-10", "Latin 6 (Nordic)"},
485 {"ISO-8859-11", "Latin & Thai"},
486 {"ISO-8859-13", "Latin 7 (Baltic)"},
487 {"ISO-8859-14", "Latin 8 (Celtic)"},
488 {"ISO-8859-15", "Latin 9 (Euro)"},
489 {"KOI8-R", "Latin & Russian"},
490 {"KOI8-U", "Latin & Ukranian"},
491 {"VISCII", "Latin & Vietnamese"},
492 {"GB2312", "Latin & Simplified Chinese"},
493 {"BIG5", "Latin & Traditional Chinese"},
494 {"EUC-JP", "Latin & Japanese"},
495 {"Shift-JIS", "Latin & Japanese"},
496 {"Shift_JIS", "Latin & Japanese"},
497 {"EUC-KR", "Latin & Korean"},
498 {"ISO-2022-CN", "Latin & Chinese"},
499 {"ISO-2022-JP", "Latin & Japanese"},
500 {"ISO-2022-KR", "Latin & Korean"},
501 {"UTF-7", "7-bit encoded Unicode"},
502 {"UTF-8", "Internet-standard Unicode"},
503 {"ISO-2022-JP-2", "Multilingual"},
504 {NULL, NULL}
508 /*----------------------------------------------------------------------
509 Return a nicely formatted discription of the type of the part
510 ----*/
512 char *
513 type_desc(int type, char *subtype, PARAMETER *params, PARAMETER *disp_params, int full)
515 static char type_d[200];
516 int i;
517 char *p, *parmval;
519 p = type_d;
520 sstrncpy(&p, body_type_names(type), sizeof(type_d)-(p-type_d));
521 if(full && subtype){
522 *p++ = '/';
523 sstrncpy(&p, subtype, sizeof(type_d)-(p-type_d));
526 type_d[sizeof(type_d)-1] = '\0';
528 switch(type){
529 case TYPETEXT:
530 parmval = parameter_val(params, "charset");
532 if(parmval){
533 for(i = 0; charset_names[i].rfcname; i++)
534 if(!strucmp(parmval, charset_names[i].rfcname)){
535 if(!strucmp(parmval, ps_global->display_charmap
536 ? ps_global->display_charmap : "us-ascii")
537 || !strucmp(parmval, "us-ascii"))
538 i = -1;
540 break;
543 if(i >= 0){ /* charset to write */
544 if(charset_names[i].rfcname){
545 sstrncpy(&p, " (charset: ", sizeof(type_d)-(p-type_d));
546 sstrncpy(&p, charset_names[i].rfcname
547 ? charset_names[i].rfcname : "Unknown", sizeof(type_d)-(p-type_d));
548 if(full){
549 sstrncpy(&p, " \"", sizeof(type_d)-(p-type_d));
550 sstrncpy(&p, charset_names[i].humanname
551 ? charset_names[i].humanname
552 : parmval, sizeof(type_d)-(p-type_d));
553 if(sizeof(type_d)-(p-type_d) > 0)
554 *p++ = '\"';
557 sstrncpy(&p, ")", sizeof(type_d)-(p-type_d));
559 else{
560 sstrncpy(&p, " (charset: ", sizeof(type_d)-(p-type_d));
561 sstrncpy(&p, parmval, sizeof(type_d)-(p-type_d));
562 sstrncpy(&p, ")", sizeof(type_d)-(p-type_d));
566 fs_give((void **) &parmval);
569 break;
571 case TYPEMESSAGE:
572 if(full && subtype && strucmp(subtype, "external-body") == 0)
573 if((parmval = parameter_val(params, "access-type")) != NULL){
574 snprintf(p, sizeof(type_d)-(p-type_d), " (%s%s)", full ? "Access: " : "", parmval);
575 fs_give((void **) &parmval);
578 break;
580 default:
581 break;
584 if(full && type != TYPEMULTIPART && type != TYPEMESSAGE){
585 unsigned char decodebuf[10000];
586 if((parmval = parameter_val(params, "name")) != NULL){
587 rfc1522_decode_to_utf8(decodebuf, sizeof(decodebuf), parmval);
588 snprintf(p, sizeof(type_d)-(p-type_d), " (Name: \"%s\")", decodebuf);
589 fs_give((void **) &parmval);
591 else if((parmval = parameter_val(disp_params, "filename")) != NULL){
592 rfc1522_decode_to_utf8(decodebuf, sizeof(decodebuf), parmval);
593 snprintf(p, sizeof(type_d)-(p-type_d), " (Filename: \"%s\")", decodebuf);
594 fs_give((void **) &parmval);
598 type_d[sizeof(type_d)-1] = '\0';
600 return(type_d);
606 void
607 format_mime_size(char *string, size_t stringlen, struct mail_bodystruct *b, int flags)
609 char tmp[10], *p = NULL;
610 char *origstring;
613 if(stringlen <= 0)
614 return;
616 origstring = string;
618 if(flags & FMS_SPACE)
619 *string++ = ' ';
621 switch(b->encoding){
622 case ENCBASE64 :
623 if(b->type == TYPETEXT){
624 if(flags & FMS_SPACE)
625 *(string-1) = '~';
626 else
627 *string++ = '~';
630 strncpy(p = string, byte_string((3 * b->size.bytes) / 4), stringlen-(string-origstring));
631 break;
633 default :
634 case ENCQUOTEDPRINTABLE :
635 if(flags & FMS_SPACE)
636 *(string-1) = '~';
637 else
638 *string++ = '~';
640 case ENC8BIT :
641 case ENC7BIT :
642 if(b->type == TYPETEXT)
643 /* lines with no CRLF aren't counted, just add one so it makes more sense */
644 snprintf(string, stringlen-(string-origstring), "%s lines", comatose(b->size.lines+1));
645 else
646 strncpy(p = string, byte_string(b->size.bytes), stringlen-(string-origstring));
648 break;
651 origstring[stringlen-1] = '\0';
653 if(p){
654 for(; *p && (isascii((unsigned char) *p) && (isdigit((unsigned char) *p)
655 || ispunct((unsigned char) *p))); p++)
658 snprintf(tmp, sizeof(tmp), (flags & FMS_SPACE) ? " %-5.5s" : " %s", p);
659 tmp[sizeof(tmp)-1] = '\0';
660 strncpy(p, tmp, stringlen-(p-origstring));
663 origstring[stringlen-1] = '\0';
668 /*----------------------------------------------------------------------
669 Determine if we can show all, some or none of the parts of a body
671 Args: body --- The message body to check
673 Returns: SHOW_ALL, SHOW_ALL_EXT, SHOW_PART or SHOW_NONE depending on
674 how much of the body can be shown and who can show it.
675 ----*/
677 mime_show(struct mail_bodystruct *body)
679 int effort, best_effort;
680 PART *p;
682 if(!body)
683 return(SHOW_NONE);
685 switch(body->type) {
686 case TYPEMESSAGE:
687 if(!strucmp(body->subtype, "rfc822"))
688 return(mime_show(body->nested.msg->body) == SHOW_ALL
689 ? SHOW_ALL: SHOW_PARTS);
690 /* else fall thru to default case... */
692 default:
694 * Since we're testing for internal displayability, give the
695 * internal result over an external viewer
697 effort = mime_can_display(body->type, body->subtype, body);
698 if(effort == MCD_NONE)
699 return(SHOW_NONE);
700 else if(effort & MCD_INTERNAL)
701 return(SHOW_ALL);
702 else
703 return(SHOW_ALL_EXT);
705 case TYPEMULTIPART:
706 best_effort = SHOW_NONE;
707 for(p = body->nested.part; p; p = p->next)
708 if((effort = mime_show(&p->body)) > best_effort)
709 best_effort = effort;
711 return(best_effort);
717 * fcc_size_guess
719 long
720 fcc_size_guess(struct mail_bodystruct *body)
722 long size = 0L;
724 if(body){
725 if(body->type == TYPEMULTIPART){
726 PART *part;
728 for(part = body->nested.part; part; part = part->next)
729 size += fcc_size_guess(&part->body);
731 else{
732 size = body->size.bytes;
734 * If it is ENCBINARY we will be base64 encoding it. This
735 * ideally increases the size by a factor of 4/3, but there
736 * is a per-line increase in that because of the CRLFs and
737 * because the number of characters in the line might not
738 * be a factor of 3. So push it up by 3/2 instead. This still
739 * won't catch all the cases. In particular, attachements with
740 * lots of short lines (< 10) will expand by more than that,
741 * but that's ok since this is an optimization. That's why
742 * so_cs_puts uses the 3/2 factor when it does a resize, so
743 * that it won't have to resize linearly until it gets there.
745 if(body->encoding == ENCBINARY)
746 size = 3*size/2;
750 return(size);
755 /*----------------------------------------------------------------------
756 Format a strings describing one unshown part of a Mime message
758 Args: number -- A string with the part number i.e. "3.2.1"
759 body -- The body part
760 type -- 1 - Not shown, but can be
761 2 - Not shown, cannot be shown
762 3 - Can't print
763 width -- allowed width per line of editorial comment
764 pc -- function used to write the description comment
766 Result: formatted description written to object ref'd by "pc"
767 ----*/
768 char *
769 part_desc(char *number, BODY *body, int type, int width, int flags, gf_io_t pc)
771 char *t;
772 char buftmp[MAILTMPLEN], sizebuf[256];
774 if(!gf_puts(NEWLINE, pc))
775 return("No space for description");
777 format_mime_size(sizebuf, 256, body, FMS_NONE);
779 snprintf(buftmp, sizeof(buftmp), "%s", body->description ? body->description : "");
780 buftmp[sizeof(buftmp)-1] = '\0';
781 snprintf(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, "Part %s, %s%.2048s%s%s %s.",
782 number,
783 body->description == NULL ? "" : "\"",
784 body->description == NULL ? ""
785 : (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, buftmp),
786 body->description == NULL ? "" : "\" ",
787 type_desc(body->type, body->subtype, body->parameter, NULL, 1),
788 sizebuf);
789 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
791 iutf8ncpy((char *)tmp_20k_buf, (char *)(tmp_20k_buf+10000), 10000);
792 tmp_20k_buf[10000] = '\0';
794 t = &tmp_20k_buf[strlen(tmp_20k_buf)];
796 #ifdef SMIME
797 /* if smime and not attempting print */
798 if(F_OFF(F_DONT_DO_SMIME, ps_global) && is_pkcs7_body(body) && type != 3){
800 sstrncpy(&t, "\015\012", SIZEOF_20KBUF-(t-tmp_20k_buf));
802 if(ps_global->smime && ps_global->smime->need_passphrase){
803 sstrncpy(&t,
804 "This part is a PKCS7 S/MIME enclosure. "
805 "You may be able to view it by entering the correct passphrase "
806 "with the \"Decrypt\" command.",
807 SIZEOF_20KBUF-(t-tmp_20k_buf));
809 else{
810 sstrncpy(&t,
811 "This part is a PKCS7 S/MIME enclosure. "
812 "Press \"^E\" for more information.",
813 SIZEOF_20KBUF-(t-tmp_20k_buf));
816 } else
817 #endif
819 if(type){
820 sstrncpy(&t, "\015\012", SIZEOF_20KBUF-(t-tmp_20k_buf));
821 switch(type) {
822 case 1:
823 if(MIME_VCARD(body->type,body->subtype))
824 sstrncpy(&t,
825 /* TRANSLATORS: This is the description of an attachment that isn't being
826 shown but that can be viewed or saved. */
827 _("Not Shown. Use the \"V\" command to view or save to address book."), SIZEOF_20KBUF-(t-tmp_20k_buf));
828 else
829 sstrncpy(&t,
830 /* TRANSLATORS: This is the description of an attachment that isn't being
831 shown but that can be viewed or saved. */
832 _("Not Shown. Use the \"V\" command to view or save this part."), SIZEOF_20KBUF-(t-tmp_20k_buf));
834 break;
836 case 2:
837 sstrncpy(&t, "Cannot ", SIZEOF_20KBUF-(t-tmp_20k_buf));
838 if(body->type != TYPEAUDIO && body->type != TYPEVIDEO)
839 sstrncpy(&t, "dis", SIZEOF_20KBUF-(t-tmp_20k_buf));
841 sstrncpy(&t,
842 "play this part. Press \"V\" then \"S\" to save in a file.", SIZEOF_20KBUF-(t-tmp_20k_buf));
843 break;
845 case 3:
846 sstrncpy(&t, _("Unable to print this part."), SIZEOF_20KBUF-(t-tmp_20k_buf));
847 break;
851 if(!(t = format_editorial(tmp_20k_buf, width, flags, NULL, pc))){
852 if(!gf_puts(NEWLINE, pc))
853 t = "No space for description";
856 return(t);
860 /*----------------------------------------------------------------------
861 Can we display this type/subtype?
863 Args: type -- the MIME type to check
864 subtype -- the MIME subtype
865 params -- parameters
866 use_viewer -- tell caller he should run external viewer cmd to view
868 Result: Returns:
870 MCD_NONE if we can't display this type at all
871 MCD_INTERNAL if we can display it internally
872 MCD_EXTERNAL if it can be displayed via an external viewer
874 ----*/
876 mime_can_display(int type, char *subtype, BODY *body)
878 return((mailcap_can_display(type, subtype, body, 0)
879 ? MCD_EXTERNAL
880 : (mailcap_can_display(type, subtype, body, 1)
881 ? (MCD_EXT_PROMPT | MCD_EXTERNAL) : MCD_NONE))
882 | ((type == TYPETEXT || type == TYPEMESSAGE
883 || MIME_VCARD(type,subtype))
884 ? MCD_INTERNAL : MCD_NONE));