* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / mimedesc.c
blobd79d976545f5bb503f3b5ded535fec323ecf691b
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/mimedesc.h"
17 #include "../pith/mimetype.h"
18 #include "../pith/state.h"
19 #include "../pith/conf.h"
20 #include "../pith/mailview.h"
21 #include "../pith/rfc2231.h"
22 #include "../pith/editorial.h"
23 #include "../pith/mailpart.h"
24 #include "../pith/mailcap.h"
25 #include "../pith/smime.h"
28 /* internal prototypes */
29 int mime_known_text_subtype(char *);
30 ATTACH_S *next_attachment(void);
31 void format_mime_size(char *, size_t, BODY *, int);
32 int mime_show(BODY *);
36 * Def's to help in sorting out multipart/alternative
38 #define SHOW_NONE 0
39 #define SHOW_PARTS 1
40 #define SHOW_ALL_EXT 2
41 #define SHOW_ALL 3
44 * Def's to control format_mime_size output
46 #define FMS_NONE 0x00
47 #define FMS_SPACE 0x01
50 /*----------------------------------------------------------------------
51 Add lines to the attachments structure
53 Args: body -- body of the part being described
54 prefix -- The prefix for numbering the parts
55 num -- The number of this specific part
56 should_show -- Flag indicating which of alternate parts should be shown
57 multalt -- Flag indicating the part is one of the multipart
58 alternative parts (so suppress editorial comment)
60 Result: The ps_global->attachments data structure is filled in. This
61 is called recursively to descend through all the parts of a message.
62 The description strings filled in are malloced and should be freed.
64 ----*/
65 void
66 describe_mime(struct mail_bodystruct *body, char *prefix, int num,
67 int should_show, int multalt, int flags)
69 #define NUMXLEN 512
70 PART *part;
71 char numx[NUMXLEN], string[800], *description;
72 int n, named = 0, can_display_ext;
73 ATTACH_S *a;
75 if(!body)
76 return;
78 if(body->type == TYPEMULTIPART){
79 int alt_to_show = 0;
81 if(strucmp(body->subtype, "alternative") == 0){
82 int effort, best_effort = SHOW_NONE;
84 /*---- Figure out which alternative part to display ---*/
86 * This is kind of complicated because some TEXT types
87 * are more displayable than others. We don't want to
88 * commit to displaying a text-part alternative that we
89 * don't directly recognize unless that's all there is.
91 for(part=body->nested.part, n=1; part; part=part->next, n++)
92 if(flags & FM_FORCEPREFPLN
93 || (!(flags & FM_FORCENOPREFPLN)
94 && F_ON(F_PREFER_PLAIN_TEXT, ps_global)
95 && part->body.type == TYPETEXT
96 && (!part->body.subtype
97 || !strucmp(part->body.subtype, "PLAIN")))){
98 if((effort = mime_show(&part->body)) != SHOW_ALL_EXT){
99 best_effort = effort;
100 alt_to_show = n;
101 break;
104 else if((effort = mime_show(&part->body)) >= best_effort
105 && (part->body.type != TYPETEXT || mime_known_text_subtype(part->body.subtype))
106 && effort != SHOW_ALL_EXT){
107 best_effort = effort;
108 alt_to_show = n;
110 else if(part->body.type == TYPETEXT && alt_to_show == 0){
111 best_effort = effort;
112 alt_to_show = n;
115 else if(!strucmp(body->subtype, "digest")){
116 memset(a = next_attachment(), 0, sizeof(ATTACH_S));
117 if(*prefix){
118 prefix[n = strlen(prefix) - 1] = '\0';
119 a->number = cpystr(prefix);
120 prefix[n] = '.';
122 else
123 a->number = cpystr("");
125 a->description = cpystr("Multipart/Digest");
126 a->body = body;
127 a->can_display = MCD_INTERNAL;
128 (a+1)->description = NULL;
130 #ifdef SMIME
131 else if(!strucmp(body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)){
132 memset(a = next_attachment(), 0, sizeof(ATTACH_S));
133 if(*prefix){
134 prefix[n = strlen(prefix) - 1] = '\0';
135 a->number = cpystr(prefix);
136 prefix[n] = '.';
138 else
139 a->number = cpystr("");
141 a->description = body->description ? cpystr(body->description)
142 : cpystr("");
143 a->body = body;
144 a->can_display = MCD_INTERNAL;
145 (a+1)->description = NULL;
147 #endif /* SMIME */
148 else if(mailcap_can_display(body->type, body->subtype, body, 0)
149 || (can_display_ext
150 = mailcap_can_display(body->type, body->subtype, body, 1))){
151 memset(a = next_attachment(), 0, sizeof(ATTACH_S));
152 if(*prefix){
153 prefix[n = strlen(prefix) - 1] = '\0';
154 a->number = cpystr(prefix);
155 prefix[n] = '.';
157 else
158 a->number = cpystr("");
160 snprintf(string, sizeof(string), "%s/%s", body_type_names(body->type),
161 body->subtype);
162 string[sizeof(string)-1] = '\0';
163 a->description = cpystr(string);
164 a->body = body;
165 a->can_display = MCD_EXTERNAL;
166 if(can_display_ext)
167 a->can_display |= MCD_EXT_PROMPT;
168 (a+1)->description = NULL;
171 for(part=body->nested.part, n=1; part; part=part->next, n++){
172 snprintf(numx, sizeof(numx), "%s%d.", prefix, n);
173 numx[sizeof(numx)-1] = '\0';
175 * Last arg to describe_mime here. If we have chosen one part
176 * of a multipart/alternative to display then we suppress
177 * the editorial messages on the other parts.
179 describe_mime(&(part->body),
180 (part->body.type == TYPEMULTIPART) ? numx : prefix,
181 n, should_show && (n == alt_to_show || !alt_to_show),
182 alt_to_show != 0, flags);
185 else{
186 char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN];
187 size_t ll;
189 a = next_attachment();
190 format_mime_size(a->size, sizeof(a->size), body, FMS_SPACE);
192 a->suppress_editorial = (multalt != 0);
194 snprintf(tmp1, sizeof(tmp1), "%s", body->description ? body->description : "");
195 tmp1[sizeof(tmp1)-1] = '\0';
196 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 : "");
197 tmp2[sizeof(tmp2)-1] = '\0';
199 description = (body->description)
200 ? (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
201 SIZEOF_20KBUF, tmp1)
202 : (body->type == TYPEMESSAGE
203 && body->encoding <= ENCBINARY
204 && body->subtype
205 && strucmp(body->subtype, "rfc822") == 0
206 && body->nested.msg->env
207 && body->nested.msg->env->subject)
208 ? (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp2)
209 : (body->type == TYPEMESSAGE
210 && body->subtype
211 && !strucmp(body->subtype, "delivery-status"))
212 ? "Delivery Status"
213 : NULL;
215 description = iutf8ncpy((char *)(tmp_20k_buf+1000), description, 1000);
216 snprintf(string, sizeof(string), "%s%s%s%s",
217 type_desc(body->type,body->subtype,body->parameter,
218 body->disposition.type ? body->disposition.parameter : NULL, 0),
219 (description && description[0]) ? ", \"" : "",
220 (description && description[0]) ? description : "",
221 (description && description[0]) ? "\"": "");
222 string[sizeof(string)-1] = '\0';
223 a->description = cpystr(string);
224 a->body = body;
226 if(body->disposition.type){
227 named = strucmp(body->disposition.type, "inline");
229 else{
230 char *value;
234 * This test remains for backward compatibility
236 if(body && (value = parameter_val(body->parameter, "name")) != NULL){
237 named = strucmp(value, "Message Body");
238 fs_give((void **) &value);
243 * Make sure we have the tools available to display the
244 * type/subtype, *AND* that we can decode it if needed.
245 * Of course, if it's text, we display it anyway in the
246 * mail_view_screen so put off testing mailcap until we're
247 * explicitly asked to display that segment 'cause it could
248 * be expensive to test...
250 if((body->type == TYPETEXT && !named)
251 || MIME_VCARD(body->type,body->subtype)
252 || MIME_VCALENDAR(body->type, body->subtype)){
253 a->test_deferred = 1;
254 a->can_display = MCD_INTERNAL;
256 else{
257 a->test_deferred = 0;
258 a->can_display = mime_can_display(body->type, body->subtype, body);
262 * Deferred means we can display it
264 a->shown = MIME_VCALENDAR(body->type, body->subtype)
265 || ((a->can_display & MCD_INTERNAL)
266 && !MIME_VCARD(body->type,body->subtype)
267 && (!named || multalt
268 || (body->type == TYPETEXT && num == 1
269 && !(*prefix && strcmp(prefix,"1."))))
270 && (body->type != TYPEMESSAGE
271 || (body->type == TYPEMESSAGE
272 && body->encoding <= ENCBINARY))
273 && should_show);
274 ll = (strlen(prefix) + 16) * sizeof(char);
275 a->number = (char *) fs_get(ll);
276 snprintf(a->number, ll, "%s%d",prefix, num);
277 a->number[ll-1] = '\0';
278 (a+1)->description = NULL;
279 if(body->type == TYPEMESSAGE && body->encoding <= ENCBASE64
280 && body->subtype && strucmp(body->subtype, "rfc822") == 0){
281 body = body->nested.msg->body; /* NUMXLEN = sizeof(numx) */
282 snprintf(numx, sizeof(numx), "%.*s%d.", NUMXLEN-20, prefix, num);
283 numx[sizeof(numx)-1] = '\0';
284 describe_mime(body, numx, 1, should_show, 0, flags);
291 mime_known_text_subtype(char *subtype)
293 char **p;
294 static char *known_types[] = {
295 "plain",
296 "html",
297 "enriched",
298 "richtext",
299 NULL
302 if(!(subtype && *subtype))
303 return(1);
305 for(p = known_types; *p; p++)
306 if(!strucmp(subtype, *p))
307 return(1);
308 return(0);
313 * Returns attribute value or NULL.
314 * Value returned needs to be freed by caller
316 char *
317 parameter_val(PARAMETER *param, char *attribute)
319 if(!(param && attribute && attribute[0]))
320 return(NULL);
322 return(rfc2231_get_param(param, attribute, NULL, NULL));
327 * Get sender_filename, the filename set by the sender in the attachment.
328 * If a sender_filename buffer is passed in, the answer is copied to it
329 * and a pointer to it is returned. If sender_filename is passed in as NULL
330 * then an allocated copy of the sender filename is returned instead.
331 * If ext_ptr is non-NULL then it is set to point to the extension name.
332 * It is not a separate copy, it points into the string sender_filename.
334 char *
335 get_filename_parameter(char *sender_filename, size_t sfsize, BODY *body, char **ext_ptr)
337 char *p = NULL;
338 char *decoded_name = NULL;
339 char *filename = NULL;
340 char tmp[1000];
342 if(!body)
343 return(NULL);
345 if(sender_filename){
346 if(sfsize <= 0)
347 return(NULL);
349 sender_filename[0] = '\0';
353 * First check for Content-Disposition's "filename" parameter and
354 * if that isn't found for the deprecated Content-Type "name" parameter.
356 if((p = parameter_val(body->disposition.parameter, "filename"))
357 || (p = parameter_val(body->parameter, "name"))){
360 * If somebody sent us and incorrectly rfc2047 encoded
361 * parameter value instead of what rfc2231 suggest we
362 * grudglingly try to fix it.
364 if(p[0] == '=' && p[1] == '?')
365 decoded_name = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp,
366 sizeof(tmp), p);
368 if(!decoded_name)
369 decoded_name = p;
371 filename = last_cmpnt(decoded_name);
373 if(!filename)
374 filename = decoded_name;
377 if(filename){
378 if(sender_filename){
379 strncpy(sender_filename, filename, sfsize-1);
380 sender_filename[sfsize-1] = '\0';
382 else
383 sender_filename = cpystr(filename);
386 if(p)
387 fs_give((void **) &p);
389 /* ext_ptr will end up pointing into sender_filename string */
390 if(ext_ptr && sender_filename)
391 mt_get_file_ext(sender_filename, ext_ptr);
393 return(sender_filename);
397 /*----------------------------------------------------------------------
398 Return a pointer to the next attachment struct
400 Args: none
402 ----*/
403 ATTACH_S *
404 next_attachment(void)
406 ATTACH_S *a;
407 int n;
409 for(a = ps_global->atmts; a->description; a++)
412 if((n = a - ps_global->atmts) + 1 >= ps_global->atmts_allocated){
413 ps_global->atmts_allocated *= 2;
414 fs_resize((void **)&ps_global->atmts,
415 ps_global->atmts_allocated * sizeof(ATTACH_S));
416 a = &ps_global->atmts[n];
419 return(a);
424 /*----------------------------------------------------------------------
425 Zero out the attachments structure and free up storage
426 ----*/
427 void
428 zero_atmts(ATTACH_S *atmts)
430 ATTACH_S *a;
432 for(a = atmts; a->description != NULL; a++){
433 a->tmpdir = NULL; /* short lived variabled, cleared right after use */
434 a->cid_tmpfile = NULL; /* resetting pointer is enough */
435 fs_give((void **)&(a->description));
436 fs_give((void **)&(a->number));
439 atmts->description = NULL;
443 char *
444 body_type_names(int t)
446 #define TLEN 31
447 static char body_type[TLEN + 1];
448 char *p;
450 body_type[0] = '\0';
451 strncpy(body_type, /* copy the given type */
452 (t > -1 && t < TYPEMAX && body_types[t])
453 ? body_types[t] : "Other", TLEN);
454 body_type[sizeof(body_type)-1] = '\0';
456 for(p = body_type + 1; *p; p++) /* make it presentable */
457 if(isascii((unsigned char) (*p)) && isupper((unsigned char) (*p)))
458 *p = tolower((unsigned char)(*p));
460 return(body_type); /* present it */
464 /*----------------------------------------------------------------------
465 Mapping table use to neatly display charset parameters
466 ----*/
468 static struct set_names {
469 char *rfcname,
470 *humanname;
471 } charset_names[] = {
472 {"US-ASCII", "Plain Text"},
473 {"ISO-8859-1", "Latin 1 (Western Europe)"},
474 {"ISO-8859-2", "Latin 2 (Eastern Europe)"},
475 {"ISO-8859-3", "Latin 3 (Southern Europe)"},
476 {"ISO-8859-4", "Latin 4 (Northern Europe)"},
477 {"ISO-8859-5", "Latin & Cyrillic"},
478 {"ISO-8859-6", "Latin & Arabic"},
479 {"ISO-8859-7", "Latin & Greek"},
480 {"ISO-8859-8", "Latin & Hebrew"},
481 {"ISO-8859-9", "Latin 5 (Turkish)"},
482 {"ISO-8859-10", "Latin 6 (Nordic)"},
483 {"ISO-8859-11", "Latin & Thai"},
484 {"ISO-8859-13", "Latin 7 (Baltic)"},
485 {"ISO-8859-14", "Latin 8 (Celtic)"},
486 {"ISO-8859-15", "Latin 9 (Euro)"},
487 {"KOI8-R", "Latin & Russian"},
488 {"KOI8-U", "Latin & Ukrainian"},
489 {"VISCII", "Latin & Vietnamese"},
490 {"GB2312", "Latin & Simplified Chinese"},
491 {"BIG5", "Latin & Traditional Chinese"},
492 {"EUC-JP", "Latin & Japanese"},
493 {"Shift-JIS", "Latin & Japanese"},
494 {"Shift_JIS", "Latin & Japanese"},
495 {"EUC-KR", "Latin & Korean"},
496 {"ISO-2022-CN", "Latin & Chinese"},
497 {"ISO-2022-JP", "Latin & Japanese"},
498 {"ISO-2022-KR", "Latin & Korean"},
499 {"UTF-7", "7-bit encoded Unicode"},
500 {"UTF-8", "Internet-standard Unicode"},
501 {"ISO-2022-JP-2", "Multilingual"},
502 {NULL, NULL}
506 /*----------------------------------------------------------------------
507 Return a nicely formatted description of the type of the part
508 ----*/
510 char *
511 type_desc(int type, char *subtype, PARAMETER *params, PARAMETER *disp_params, int full)
513 static char type_d[200];
514 int i;
515 char *p, *parmval;
517 p = type_d;
518 sstrncpy(&p, body_type_names(type), sizeof(type_d)-(p-type_d));
519 if(full && subtype){
520 *p++ = '/';
521 sstrncpy(&p, subtype, sizeof(type_d)-(p-type_d));
524 type_d[sizeof(type_d)-1] = '\0';
526 switch(type){
527 case TYPETEXT:
528 parmval = parameter_val(params, "charset");
530 if(parmval){
531 for(i = 0; charset_names[i].rfcname; i++)
532 if(!strucmp(parmval, charset_names[i].rfcname)){
533 if(!strucmp(parmval, ps_global->display_charmap
534 ? ps_global->display_charmap : "us-ascii")
535 || !strucmp(parmval, "us-ascii"))
536 i = -1;
538 break;
541 if(i >= 0){ /* charset to write */
542 if(charset_names[i].rfcname){
543 sstrncpy(&p, " (charset: ", sizeof(type_d)-(p-type_d));
544 sstrncpy(&p, charset_names[i].rfcname
545 ? charset_names[i].rfcname : "Unknown", sizeof(type_d)-(p-type_d));
546 if(full){
547 sstrncpy(&p, " \"", sizeof(type_d)-(p-type_d));
548 sstrncpy(&p, charset_names[i].humanname
549 ? charset_names[i].humanname
550 : parmval, sizeof(type_d)-(p-type_d));
551 if(sizeof(type_d)-(p-type_d) > 0)
552 *p++ = '\"';
555 sstrncpy(&p, ")", sizeof(type_d)-(p-type_d));
557 else{
558 sstrncpy(&p, " (charset: ", sizeof(type_d)-(p-type_d));
559 sstrncpy(&p, parmval, sizeof(type_d)-(p-type_d));
560 sstrncpy(&p, ")", sizeof(type_d)-(p-type_d));
564 fs_give((void **) &parmval);
567 break;
569 case TYPEMESSAGE:
570 if(full && subtype && strucmp(subtype, "external-body") == 0)
571 if((parmval = parameter_val(params, "access-type")) != NULL){
572 snprintf(p, sizeof(type_d)-(p-type_d), " (%s%s)", full ? "Access: " : "", parmval);
573 fs_give((void **) &parmval);
576 break;
578 default:
579 break;
582 if(full && type != TYPEMULTIPART && type != TYPEMESSAGE){
583 unsigned char decodebuf[10000];
584 if((parmval = parameter_val(params, "name")) != NULL){
585 rfc1522_decode_to_utf8(decodebuf, sizeof(decodebuf), parmval);
586 snprintf(p, sizeof(type_d)-(p-type_d), " (Name: \"%s\")", decodebuf);
587 fs_give((void **) &parmval);
589 else if((parmval = parameter_val(disp_params, "filename")) != NULL){
590 rfc1522_decode_to_utf8(decodebuf, sizeof(decodebuf), parmval);
591 snprintf(p, sizeof(type_d)-(p-type_d), " (Filename: \"%s\")", decodebuf);
592 fs_give((void **) &parmval);
596 type_d[sizeof(type_d)-1] = '\0';
598 return(type_d);
604 void
605 format_mime_size(char *string, size_t stringlen, struct mail_bodystruct *b, int flags)
607 char tmp[10], *p = NULL;
608 char *origstring;
611 if(stringlen <= 0)
612 return;
614 origstring = string;
616 if(flags & FMS_SPACE)
617 *string++ = ' ';
619 switch(b->encoding){
620 case ENCBASE64 :
621 if(b->type == TYPETEXT){
622 if(flags & FMS_SPACE)
623 *(string-1) = '~';
624 else
625 *string++ = '~';
628 strncpy(p = string, byte_string((3 * b->size.bytes) / 4), stringlen-(string-origstring));
629 break;
631 default :
632 case ENCQUOTEDPRINTABLE :
633 if(flags & FMS_SPACE)
634 *(string-1) = '~';
635 else
636 *string++ = '~';
638 case ENC8BIT :
639 case ENC7BIT :
640 if(b->type == TYPETEXT)
641 /* lines with no CRLF aren't counted, just add one so it makes more sense */
642 snprintf(string, stringlen-(string-origstring), "%s lines", comatose(b->size.lines+1));
643 else
644 strncpy(p = string, byte_string(b->size.bytes), stringlen-(string-origstring));
646 break;
649 origstring[stringlen-1] = '\0';
651 if(p){
652 for(; *p && (isascii((unsigned char) *p) && (isdigit((unsigned char) *p)
653 || ispunct((unsigned char) *p))); p++)
656 snprintf(tmp, sizeof(tmp), (flags & FMS_SPACE) ? " %-5.5s" : " %s", p);
657 tmp[sizeof(tmp)-1] = '\0';
658 strncpy(p, tmp, stringlen-(p-origstring));
661 origstring[stringlen-1] = '\0';
666 /*----------------------------------------------------------------------
667 Determine if we can show all, some or none of the parts of a body
669 Args: body --- The message body to check
671 Returns: SHOW_ALL, SHOW_ALL_EXT, SHOW_PART or SHOW_NONE depending on
672 how much of the body can be shown and who can show it.
673 ----*/
675 mime_show(struct mail_bodystruct *body)
677 int effort, best_effort;
678 PART *p;
680 if(!body)
681 return(SHOW_NONE);
683 switch(body->type) {
684 case TYPEMESSAGE:
685 if(!strucmp(body->subtype, "rfc822"))
686 return(mime_show(body->nested.msg->body) == SHOW_ALL
687 ? SHOW_ALL: SHOW_PARTS);
688 /* else fall thru to default case... */
690 default:
692 * Since we're testing for internal displayability, give the
693 * internal result over an external viewer
695 effort = mime_can_display(body->type, body->subtype, body);
696 if(effort == MCD_NONE)
697 return(SHOW_NONE);
698 else if(effort & MCD_INTERNAL)
699 return(SHOW_ALL);
700 else
701 return(SHOW_ALL_EXT);
703 case TYPEMULTIPART:
704 best_effort = SHOW_NONE;
705 for(p = body->nested.part; p; p = p->next)
706 if((effort = mime_show(&p->body)) > best_effort)
707 best_effort = effort;
709 return(best_effort);
715 * fcc_size_guess
717 long
718 fcc_size_guess(struct mail_bodystruct *body)
720 long size = 0L;
722 if(body){
723 if(body->type == TYPEMULTIPART){
724 PART *part;
726 for(part = body->nested.part; part; part = part->next)
727 size += fcc_size_guess(&part->body);
729 else{
730 size = body->size.bytes;
732 * If it is ENCBINARY we will be base64 encoding it. This
733 * ideally increases the size by a factor of 4/3, but there
734 * is a per-line increase in that because of the CRLFs and
735 * because the number of characters in the line might not
736 * be a factor of 3. So push it up by 3/2 instead. This still
737 * won't catch all the cases. In particular, attachments with
738 * lots of short lines (< 10) will expand by more than that,
739 * but that's ok since this is an optimization. That's why
740 * so_cs_puts uses the 3/2 factor when it does a resize, so
741 * that it won't have to resize linearly until it gets there.
743 if(body->encoding == ENCBINARY)
744 size = 3*size/2;
748 return(size);
753 /*----------------------------------------------------------------------
754 Format a strings describing one unshown part of a Mime message
756 Args: number -- A string with the part number i.e. "3.2.1"
757 body -- The body part
758 type -- 1 - Not shown, but can be
759 2 - Not shown, cannot be shown
760 3 - Can't print
761 width -- allowed width per line of editorial comment
762 pc -- function used to write the description comment
764 Result: formatted description written to object ref'd by "pc"
765 ----*/
766 char *
767 part_desc(char *number, BODY *body, int type, int width, int flags, gf_io_t pc)
769 char *t;
770 char buftmp[MAILTMPLEN], sizebuf[256];
772 if(!gf_puts(NEWLINE, pc))
773 return("No space for description");
775 format_mime_size(sizebuf, 256, body, FMS_NONE);
777 snprintf(buftmp, sizeof(buftmp), "%s", body->description ? body->description : "");
778 buftmp[sizeof(buftmp)-1] = '\0';
779 snprintf(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, "Part %s, %s%.2048s%s%s %s.",
780 number,
781 body->description == NULL ? "" : "\"",
782 body->description == NULL ? ""
783 : (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, buftmp),
784 body->description == NULL ? "" : "\" ",
785 type_desc(body->type, body->subtype, body->parameter, NULL, 1),
786 sizebuf);
787 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
789 iutf8ncpy((char *)tmp_20k_buf, (char *)(tmp_20k_buf+10000), 10000);
790 tmp_20k_buf[10000] = '\0';
792 t = &tmp_20k_buf[strlen(tmp_20k_buf)];
794 #ifdef SMIME
795 /* if smime and not attempting print */
796 if(F_OFF(F_DONT_DO_SMIME, ps_global) && is_pkcs7_body(body) && type != 3){
798 sstrncpy(&t, "\015\012", SIZEOF_20KBUF-(t-tmp_20k_buf));
800 if(ps_global->smime && ps_global->smime->need_passphrase){
801 sstrncpy(&t,
802 "This part is a PKCS7 S/MIME enclosure. "
803 "You may be able to view it by entering the correct passphrase "
804 "with the \"Decrypt\" command.",
805 SIZEOF_20KBUF-(t-tmp_20k_buf));
807 else{
808 sstrncpy(&t,
809 "This part is a PKCS7 S/MIME enclosure. "
810 "Press \"^E\" for more information.",
811 SIZEOF_20KBUF-(t-tmp_20k_buf));
814 } else
815 #endif
817 if(type){
818 sstrncpy(&t, "\015\012", SIZEOF_20KBUF-(t-tmp_20k_buf));
819 switch(type) {
820 case 1:
821 if(MIME_VCARD(body->type,body->subtype))
822 sstrncpy(&t,
823 /* TRANSLATORS: This is the description of an attachment that isn't being
824 shown but that can be viewed or saved. */
825 _("Not Shown. Use the \"V\" command to view or save to address book."), SIZEOF_20KBUF-(t-tmp_20k_buf));
826 else
827 sstrncpy(&t,
828 /* TRANSLATORS: This is the description of an attachment that isn't being
829 shown but that can be viewed or saved. */
830 _("Not Shown. Use the \"V\" command to view or save this part."), SIZEOF_20KBUF-(t-tmp_20k_buf));
832 break;
834 case 2:
835 sstrncpy(&t, "Cannot ", SIZEOF_20KBUF-(t-tmp_20k_buf));
836 if(body->type != TYPEAUDIO && body->type != TYPEVIDEO)
837 sstrncpy(&t, "dis", SIZEOF_20KBUF-(t-tmp_20k_buf));
839 sstrncpy(&t,
840 "play this part. Press \"V\" then \"S\" to save in a file.", SIZEOF_20KBUF-(t-tmp_20k_buf));
841 break;
843 case 3:
844 sstrncpy(&t, _("Unable to print this part."), SIZEOF_20KBUF-(t-tmp_20k_buf));
845 break;
849 if(!(t = format_editorial(tmp_20k_buf, width, flags, NULL, pc))){
850 if(!gf_puts(NEWLINE, pc))
851 t = "No space for description";
854 return(t);
858 /*----------------------------------------------------------------------
859 Can we display this type/subtype?
861 Args: type -- the MIME type to check
862 subtype -- the MIME subtype
863 params -- parameters
864 use_viewer -- tell caller he should run external viewer cmd to view
866 Result: Returns:
868 MCD_NONE if we can't display this type at all
869 MCD_INTERNAL if we can display it internally
870 MCD_EXTERNAL if it can be displayed via an external viewer
872 ----*/
874 mime_can_display(int type, char *subtype, BODY *body)
876 return((mailcap_can_display(type, subtype, body, 0)
877 ? MCD_EXTERNAL
878 : (mailcap_can_display(type, subtype, body, 1)
879 ? (MCD_EXT_PROMPT | MCD_EXTERNAL) : MCD_NONE))
880 | ((type == TYPETEXT || type == TYPEMESSAGE
881 || MIME_VCARD(type,subtype))
882 ? MCD_INTERNAL : MCD_NONE));