Add support for tab-completion when selecting by rule
[alpine.git] / alpine / mailpart.c
blob1c69391f7873cddddcdc55f0809c53883660b4ef
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 /*======================================================================
16 mailpart.c
17 The meat and pototoes of attachment processing here.
19 ====*/
21 #include "headers.h"
22 #include "mailpart.h"
23 #include "status.h"
24 #include "context.h"
25 #include "keymenu.h"
26 #include "alpine.h"
27 #include "reply.h"
28 #include "radio.h"
29 #include "takeaddr.h"
30 #include "mailview.h"
31 #include "mailindx.h"
32 #include "mailcmd.h"
33 #include "help.h"
34 #include "titlebar.h"
35 #include "signal.h"
36 #include "send.h"
37 #include "busy.h"
38 #include "smime.h"
39 #include "../pith/state.h"
40 #include "../pith/conf.h"
41 #include "../pith/store.h"
42 #include "../pith/msgno.h"
43 #include "../pith/detach.h"
44 #include "../pith/handle.h"
45 #include "../pith/filter.h"
46 #include "../pith/bitmap.h"
47 #include "../pith/charset.h"
48 #include "../pith/mimedesc.h"
49 #include "../pith/mailcap.h"
50 #include "../pith/newmail.h"
51 #include "../pith/rfc2231.h"
52 #include "../pith/flag.h"
53 #include "../pith/text.h"
54 #include "../pith/editorial.h"
55 #include "../pith/save.h"
56 #include "../pith/pipe.h"
57 #include "../pith/util.h"
58 #include "../pith/detoken.h"
59 #include "../pith/busy.h"
60 #include "../pith/mimetype.h"
61 #include "../pith/icache.h"
62 #include "../pith/list.h"
63 #include "../pith/ablookup.h"
64 #include "../pith/options.h"
65 #include "../pith/smime.h"
66 #include "../pith/ical.h"
67 #include "../pith/body.h"
68 #include "../pith/init.h"
71 * Information used to paint and maintain a line on the attachment
72 * screen.
74 typedef struct atdisp_line {
75 char *dstring; /* alloc'd var value string */
76 ATTACH_S *attp; /* actual attachment pointer */
77 struct atdisp_line *next, *prev;
78 } ATDISP_S;
82 * struct defining attachment screen's current state
84 typedef struct att_screen {
85 ATDISP_S *current,
86 *top_line;
87 COLOR_PAIR *titlecolor;
88 } ATT_SCREEN_S;
90 static ATT_SCREEN_S *att_screen;
93 #define next_attline(p) ((p) ? (p)->next : NULL)
94 #define prev_attline(p) ((p) ? (p)->prev : NULL)
97 #ifdef _WINDOWS
99 * local global pointer to scroll attachment popup menu
101 static ATTACH_S *scrat_attachp;
102 #endif
105 #define FETCH_READC g_fr_desc->readc
108 /* used to keep track of detached MIME segments total length */
109 static long save_att_length;
112 * Internal Prototypes
114 ATDISP_S *new_attline(ATDISP_S **);
115 void free_attline(ATDISP_S **);
116 int attachment_screen_updater(struct pine *, ATDISP_S *, ATT_SCREEN_S *);
117 void attachment_screen_redrawer(void);
118 void update_att_screen_titlebar(void);
119 ATDISP_S *first_attline(ATDISP_S *);
120 int init_att_progress(char *, MAILSTREAM *, BODY *);
121 long save_att_piped(int);
122 int save_att_percent(void);
123 void save_attachment(int, long, ATTACH_S *);
124 void export_attachment(int, long, ATTACH_S *);
125 char *write_attached_msg(long, ATTACH_S **, STORE_S *, int);
126 void save_msg_att(long, ATTACH_S *);
127 void save_digest_att(long, ATTACH_S *);
128 int save_msg_att_work(long int, ATTACH_S *, MAILSTREAM *, char *,
129 CONTEXT_S *, char *);
130 void export_msg_att(long, ATTACH_S *);
131 void export_digest_att(long, ATTACH_S *);
132 void print_attachment(int, long, ATTACH_S *);
133 int print_msg_att(long, ATTACH_S *, int);
134 void print_digest_att(long, ATTACH_S *);
135 void run_viewer(char *, BODY *, int);
136 STORE_S *format_text_att(long, ATTACH_S *, HANDLE_S **);
137 int display_text_att(long, ATTACH_S *, int);
138 int display_msg_att(long, ATTACH_S *, int);
139 void display_digest_att(long, ATTACH_S *, int);
140 int scroll_attachment(char *, STORE_S *, SourceType, HANDLE_S *, ATTACH_S *, int);
141 int process_attachment_cmd(int, MSGNO_S *, SCROLL_S *);
142 int format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
143 void display_vcard_att(long, ATTACH_S *, int);
144 void display_vcalendar_att(long, ATTACH_S *, int);
145 void display_attach_info(long, ATTACH_S *);
146 int display_html_external_attachment(long int, ATTACH_S *, int);
147 void forward_attachment(MAILSTREAM *, long, ATTACH_S *);
148 void forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
149 void reply_msg_att(MAILSTREAM *, long, ATTACH_S *);
150 void bounce_msg_att(MAILSTREAM *, long, char *, char *);
151 void pipe_attachment(long, ATTACH_S *);
152 int delete_attachment(long, ATTACH_S *);
153 int undelete_attachment(long, ATTACH_S *, int *);
154 #ifdef _WINDOWS
155 int scroll_att_popup(SCROLL_S *, int);
156 void display_text_att_window(ATTACH_S *);
157 void display_msg_att_window(ATTACH_S *);
158 #endif
161 /*----------------------------------------------------------------------
162 Provide attachments in browser for selected action
164 Args: ps -- pointer to pine structure
165 msgmap -- struct containing current message to display
167 Result:
169 Handle painting and navigation of attachment index. It would be nice
170 to someday handle message/rfc822 segments in a neat way (i.e., enable
171 forwarding, take address, etc.).
172 ----*/
173 void
174 attachment_screen(struct pine *ps)
176 UCS ch = 'x';
177 int n, cmd, dline,
178 maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
179 last_type = TYPEOTHER;
180 long msgno;
181 char *q, *last_subtype = NULL, backtag[64], *utf8str;
182 OtherMenu what;
183 ATTACH_S *a;
184 ATDISP_S *current = NULL, *ctmp = NULL;
185 ATT_SCREEN_S screen;
187 ps->prev_screen = attachment_screen;
188 ps->next_screen = SCREEN_FUN_NULL;
190 ps->some_quoting_was_suppressed = 0;
192 if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
193 q_status_message(SM_ORDER | SM_DING, 0, 3,
194 _("Screen too small to view attachment"));
195 ps->next_screen = mail_view_screen;
196 return;
199 if(mn_total_cur(ps->msgmap) > 1L){
200 q_status_message(SM_ORDER | SM_DING, 0, 3,
201 _("Can only view one message's attachments at a time."));
202 return;
204 else if(ps->atmts && ps->atmts->description && !(ps->atmts + 1)->description)
205 q_status_message1(SM_ASYNC, 0, 3,
206 _("Message %s has only one part (the message body), and no attachments."),
207 long2string(mn_get_cur(ps->msgmap)));
209 /*----- Figure max display widths -----*/
210 for(a = ps->atmts; a && a->description != NULL; a++){
211 if((n = utf8_width(a->number)) > maxnumwid)
212 maxnumwid = n;
214 if((n = utf8_width(a->size)) > maxsizewid)
215 maxsizewid = n;
219 * Then, allocate and initialize attachment line list...
221 for(a = ps->atmts; a && a->description; a++)
222 new_attline(&current)->attp = a;
224 memset(&screen, 0, sizeof(screen));
225 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
226 ps->mangled_screen = 1; /* build display */
227 ps->redrawer = attachment_screen_redrawer;
228 att_screen = &screen;
229 current = first_attline(current);
230 what = FirstMenu;
233 * Advance to next attachment if it's likely that we've already
234 * shown it to the user...
237 if (current == NULL){
238 q_status_message1(SM_ORDER | SM_DING, 0, 3,
239 _("Malformed message: %s"),
240 ps->c_client_error ? ps->c_client_error : "?");
241 ps->next_screen = mail_view_screen;
243 else if(current->attp->shown && (ctmp = next_attline(current)))
244 current = ctmp;
246 while(ps->next_screen == SCREEN_FUN_NULL){
247 ps->user_says_cancel = 0;
248 if(km_popped){
249 km_popped--;
250 if(km_popped == 0){
251 clearfooter(ps);
252 ps->mangled_body = 1;
256 if(ps->mangled_screen){
258 * build/rebuild display lines
260 if(old_cols != ps->ttyo->screen_cols){
261 int avail, s1, s2, s4, s5, dwid, sizewid, descwid;
263 avail = ps_global->ttyo->screen_cols;
265 s1 = MAX(MIN(1, avail), 0);
266 avail -= s1;
268 dwid = MAX(MIN(1, avail), 0);
269 avail -= dwid;
271 s2 = MAX(MIN(1, avail), 0);
272 avail -= s2;
274 /* Only give up a third of the display to part numbers */
275 maxnumwid = MIN(maxnumwid, (ps_global->ttyo->screen_cols/3));
276 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
277 avail -= maxnumwid;
279 s4 = MAX(MIN(3, avail), 0);
280 avail -= s4;
282 sizewid = MAX(MIN(maxsizewid, avail), 0);
283 avail -= sizewid;
285 s5 = MAX(MIN(3, avail), 0);
286 avail -= s5;
288 descwid = MAX(0, avail);
290 old_cols = ps->ttyo->screen_cols;
292 for(ctmp = first_attline(current);
293 ctmp && (a = ctmp->attp) && a->description;
294 ctmp = next_attline(ctmp)){
295 size_t len;
296 char numbuf[50];
297 char description[1000];
299 if(ctmp->dstring)
300 fs_give((void **)&ctmp->dstring);
302 len = 6 * MAX(80, ps->ttyo->screen_cols) * sizeof(char);
303 ctmp->dstring = (char *)fs_get(len + 1);
306 * description
308 q = a->body->description;
309 if(!(q && q[0]) && (MIME_MSG_A(a) && a->body->nested.msg->env))
310 q = a->body->nested.msg->env->subject;
312 if(q && q[0]){
313 char buftmp[1000];
315 strncpy(buftmp, q, sizeof(buftmp));
316 buftmp[sizeof(buftmp)-1] = '\0';
318 q = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
319 SIZEOF_20KBUF, buftmp);
322 if(q && !q[0])
323 q = NULL;
325 snprintf(description, sizeof(description), "%s%s%s%s", type_desc(a->body->type, a->body->subtype, a->body->parameter, a->body->disposition.type ? a->body->disposition.parameter : NULL, 1), q ? ", \"" : "", q ? q : "", q ? "\"" : "");
326 description[sizeof(description)-1] = '\0';
328 utf8_snprintf(ctmp->dstring, len+1,
329 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%-*.*w",
330 s1, s1, "",
331 dwid, dwid,
332 msgno_part_deleted(ps->mail_stream, msgno, a->number) ? "D" : "",
333 s2, s2, "",
334 maxnumwid, maxnumwid,
335 a->number
336 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
337 : "",
338 s4, s4, "",
339 sizewid, sizewid,
340 a->size ? a->size : "",
341 s5, s5, "",
342 descwid, descwid, description);
344 ctmp->dstring[len] = '\0';
348 ps->mangled_header = 1;
349 ps->mangled_footer = 1;
350 ps->mangled_body = 1;
353 /*----------- Check for new mail -----------*/
354 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
355 ps->mangled_header = 1;
358 * If an expunge of the current message happened during the
359 * new mail check we want to bail out of here. See mm_expunged.
361 if(ps->next_screen != SCREEN_FUN_NULL)
362 break;
364 if(ps->mangled_header){
365 update_att_screen_titlebar();
366 ps->mangled_header = 0;
369 if(ps->mangled_screen){
370 ClearLine(1);
371 ps->mangled_screen = 0;
374 dline = attachment_screen_updater(ps, current, &screen);
376 /*---- This displays new mail notification, or errors ---*/
377 if(km_popped){
378 FOOTER_ROWS(ps) = 3;
379 mark_status_unknown();
382 display_message(ch);
383 if(km_popped){
384 FOOTER_ROWS(ps) = 1;
385 mark_status_unknown();
388 if(ps->mangled_footer
389 || current->attp->body->type != last_type
390 || !(last_subtype && current->attp->body->subtype)
391 || strucmp(last_subtype, current->attp->body->subtype)){
393 struct key_menu *km = &att_index_keymenu;
394 bitmap_t bitmap;
396 setbitmap(bitmap);
397 ps->mangled_footer = 0;
398 last_type = current->attp->body->type;
399 last_subtype = current->attp->body->subtype;
401 snprintf(backtag, sizeof(backtag), "Msg #%ld", mn_get_cur(ps->msgmap));
402 backtag[sizeof(backtag)-1] = '\0';
403 km->keys[ATT_PARENT_KEY].label = backtag;
405 if(F_OFF(F_ENABLE_PIPE, ps))
406 clrbitn(ATT_PIPE_KEY, bitmap);
409 * If message or digest, leave Reply and Save and,
410 * conditionally, Bounce on...
412 if(MIME_MSG(last_type, last_subtype)){
413 if(F_OFF(F_ENABLE_BOUNCE, ps))
414 clrbitn(ATT_BOUNCE_KEY, bitmap);
416 km->keys[ATT_EXPORT_KEY].name = "";
417 km->keys[ATT_EXPORT_KEY].label = "";
419 else if(MIME_DGST(last_type, last_subtype)){
420 clrbitn(ATT_BOUNCE_KEY, bitmap);
421 clrbitn(ATT_REPLY_KEY, bitmap);
423 km->keys[ATT_EXPORT_KEY].name = "";
424 km->keys[ATT_EXPORT_KEY].label = "";
426 else{
427 clrbitn(ATT_BOUNCE_KEY, bitmap);
428 clrbitn(ATT_REPLY_KEY, bitmap);
430 if(last_type != TYPETEXT)
431 clrbitn(ATT_PRINT_KEY, bitmap);
433 km->keys[ATT_EXPORT_KEY].name = "E";
434 km->keys[ATT_EXPORT_KEY].label = N_("Export");
437 if(km_popped){
438 FOOTER_ROWS(ps) = 3;
439 clearfooter(ps);
442 if(F_ON(F_ARROW_NAV, ps)){
443 menu_add_binding(km, KEY_LEFT, MC_EXIT);
444 menu_add_binding(km, KEY_RIGHT, MC_VIEW_ATCH);
446 else{
447 menu_clear_binding(km, KEY_LEFT);
448 menu_clear_binding(km, KEY_RIGHT);
451 draw_keymenu(km, bitmap, ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps),
452 0, what);
453 what = SameMenu;
454 if(km_popped){
455 FOOTER_ROWS(ps) = 1;
456 mark_keymenu_dirty();
460 if(F_ON(F_SHOW_CURSOR, ps))
461 MoveCursor(dline, 0);
462 else
463 MoveCursor(MAX(0, ps->ttyo->screen_rows - FOOTER_ROWS(ps)), 0);
465 /*------ Prepare to read the command from the keyboard ----*/
466 #ifdef MOUSE
467 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
468 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
469 ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
470 ps_global->ttyo->screen_cols);
471 #endif
472 ch = READ_COMMAND(&utf8str);
473 #ifdef MOUSE
474 clear_mfunc(mouse_in_content);
475 #endif
477 cmd = menu_command(ch, &att_index_keymenu);
479 if(km_popped)
480 switch(cmd){
481 case MC_NONE :
482 case MC_OTHER :
483 case MC_RESIZE :
484 case MC_REPAINT :
485 km_popped++;
486 break;
488 default:
489 clearfooter(ps);
490 break;
493 switch(cmd){
494 case MC_HELP :
495 if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
496 km_popped = 2;
497 ps->mangled_footer = 1;
498 break;
501 helper(h_attachment_screen, _("HELP FOR ATTACHMENT INDEX"), 0);
502 ps->mangled_screen = 1;
503 break;
505 case MC_OTHER :
506 what = NextMenu;
507 ps->mangled_footer = 1;
508 break;
510 case MC_FULLHDR :
511 ps->full_header++;
512 if(ps->full_header == 1){
513 if(!(ps->quote_suppression_threshold
514 && (ps->some_quoting_was_suppressed /* || in_index != View */)))
515 ps->full_header++;
517 else if(ps->full_header > 2)
518 ps->full_header = 0;
520 switch(ps->full_header){
521 case 0:
522 q_status_message(SM_ORDER, 0, 3,
523 _("Display of full headers is now off."));
524 break;
526 case 1:
527 q_status_message1(SM_ORDER, 0, 3,
528 _("Quotes displayed, use %s to see full headers"),
529 F_ON(F_USE_FK, ps) ? "F9" : "H");
530 break;
532 case 2:
533 q_status_message(SM_ORDER, 0, 3,
534 _("Display of full headers is now on."));
535 break;
539 break;
541 case MC_EXTERNAL:
542 display_html_external_attachment(msgno, current->attp,
543 DA_EXTERNAL | DA_SAVE | (F_OFF(F_EXTERNAL_INLINE_IMAGES, ps_global) ? DA_ALLIMAGES : 0));
544 break;
546 case MC_EXIT : /* exit attachment screen */
547 ps->next_screen = mail_view_screen;
548 break;
550 case MC_QUIT :
551 ps->next_screen = quit_screen;
552 break;
554 case MC_MAIN :
555 ps->next_screen = main_menu_screen;
556 break;
558 case MC_INDEX :
559 ps->next_screen = mail_index_screen;
560 break;
562 case MC_NEXTITEM :
563 if((ctmp = next_attline(current)) != NULL)
564 current = ctmp;
565 else
566 q_status_message(SM_ORDER, 0, 1, _("Already on last attachment"));
568 break;
570 case MC_PREVITEM :
571 if((ctmp = prev_attline(current)) != NULL)
572 current = ctmp;
573 else
574 q_status_message(SM_ORDER, 0, 1, _("Already on first attachment"));
576 break;
578 case MC_PAGEDN : /* page forward */
579 if(next_attline(current)){
580 while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
581 if((ctmp = next_attline(current)) != NULL)
582 current = ctmp;
583 else
584 break;
586 else
587 q_status_message(SM_ORDER, 0, 1,
588 _("Already on last page of attachments"));
590 break;
592 case MC_PAGEUP : /* page backward */
593 if(prev_attline(current)){
594 while(dline-- > HEADER_ROWS(ps))
595 if((ctmp = prev_attline(current)) != NULL)
596 current = ctmp;
597 else
598 break;
600 while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
601 if((ctmp = prev_attline(current)) != NULL)
602 current = ctmp;
603 else
604 break;
606 else
607 q_status_message(SM_ORDER, 0, 1,
608 _("Already on first page of attachments"));
610 break;
612 #ifdef MOUSE
613 case MC_MOUSE:
615 MOUSEPRESS mp;
617 mouse_get_last (NULL, &mp);
618 mp.row -= HEADER_ROWS(ps);
619 ctmp = screen.top_line;
620 while (mp.row && ctmp != NULL) {
621 --mp.row;
622 ctmp = ctmp->next;
625 if (ctmp != NULL){
626 current = ctmp;
628 if (mp.doubleclick){
629 if(mp.button == M_BUTTON_LEFT)
630 display_attachment(msgno, current->attp, DA_SAVE);
632 #ifdef _WINDOWS
633 else if(mp.button == M_BUTTON_RIGHT){
634 MPopup atmt_popup[20];
635 int i = -1, exoffer = 0;
637 dline = attachment_screen_updater(ps,current,&screen);
639 if(dispatch_attachment(current->attp) != MCD_NONE){
640 atmt_popup[++i].type = tQueue;
641 atmt_popup[i].data.val = 'V';
642 atmt_popup[i].label.style = lNormal;
643 atmt_popup[i].label.string = "&View";
645 if(!(current->attp->can_display & MCD_EXTERNAL)
646 && (current->attp->body->type == TYPETEXT
647 || MIME_MSG_A(current->attp)
648 || MIME_DGST_A(current->attp))){
649 exoffer++;
650 atmt_popup[++i].type = tIndex;
651 atmt_popup[i].label.style = lNormal;
652 atmt_popup[i].label.string =
653 "View in New Window";
657 atmt_popup[++i].type = tQueue;
658 atmt_popup[i].data.val = 'S';
659 atmt_popup[i].label.style = lNormal;
660 atmt_popup[i].label.string = "&Save";
662 atmt_popup[++i].type = tQueue;
663 atmt_popup[i].label.style = lNormal;
664 if(current->dstring[1] == 'D'){
665 atmt_popup[i].data.val = 'U';
666 atmt_popup[i].label.string = "&Undelete";
668 else{
669 atmt_popup[i].data.val = 'D';
670 atmt_popup[i].label.string = "&Delete";
673 if(MIME_MSG_A(current->attp)){
674 atmt_popup[++i].type = tQueue;
675 atmt_popup[i].label.style = lNormal;
676 atmt_popup[i].label.string = "&Reply...";
677 atmt_popup[i].data.val = 'R';
679 atmt_popup[++i].type = tQueue;
680 atmt_popup[i].label.style = lNormal;
681 atmt_popup[i].label.string = "&Forward...";
682 atmt_popup[i].data.val = 'F';
685 atmt_popup[++i].type = tQueue;
686 atmt_popup[i].label.style = lNormal;
687 atmt_popup[i].label.string = "&About...";
688 atmt_popup[i].data.val = 'A';
690 atmt_popup[++i].type = tSeparator;
692 atmt_popup[++i].type = tQueue;
693 snprintf(backtag, sizeof(backtag), "View Message #%ld",
694 mn_get_cur(ps->msgmap));
695 backtag[sizeof(backtag)-1] = '\0';
696 atmt_popup[i].label.string = backtag;
697 atmt_popup[i].label.style = lNormal;
698 atmt_popup[i].data.val = '<';
700 atmt_popup[++i].type = tTail;
702 if(mswin_popup(atmt_popup) == 1 && exoffer)
703 display_att_window(current->attp);
706 else if(mp.button == M_BUTTON_RIGHT){
707 MPopup atmt_popup[2];
709 atmt_popup[0].type = tQueue;
710 snprintf(backtag, sizeof(backtag), "View Message #%ld",
711 mn_get_cur(ps->msgmap));
712 backtag[sizeof(backtag)-1] = '\0';
713 atmt_popup[0].label.string = backtag;
714 atmt_popup[0].label.style = lNormal;
715 atmt_popup[0].data.val = '<';
717 atmt_popup[1].type = tTail;
719 mswin_popup(atmt_popup);
720 #endif
723 break;
724 #endif
726 case MC_WHEREIS : /* whereis */
727 /*--- get string ---*/
728 {int rc, found = 0;
729 char *result = NULL, buf[64];
730 static char last[64], tmp[64];
731 HelpType help;
733 ps->mangled_footer = 1;
734 buf[0] = '\0';
735 snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ",
736 (last[0]) ? "[" : "",
737 (last[0]) ? last : "",
738 (last[0]) ? "]" : "");
739 tmp[sizeof(tmp)-1] = '\0';
740 help = NO_HELP;
741 while(1){
742 int flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
744 rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf),
745 tmp,NULL,help,&flags);
746 if(rc == 3)
747 help = help == NO_HELP ? h_attach_index_whereis : NO_HELP;
748 else if(rc == 0 || rc == 1 || !buf[0]){
749 if(rc == 0 && !buf[0] && last[0]){
750 strncpy(buf, last, sizeof(buf));
751 buf[sizeof(buf)-1] = '\0';
754 break;
758 if(rc == 0 && buf[0]){
759 ctmp = current;
760 while((ctmp = next_attline(ctmp)) != NULL)
761 if(srchstr(ctmp->dstring, buf)){
762 found++;
763 break;
766 if(!found){
767 ctmp = first_attline(current);
769 while(ctmp != current)
770 if(srchstr(ctmp->dstring, buf)){
771 found++;
772 break;
774 else
775 ctmp = next_attline(ctmp);
778 else
779 result = _("WhereIs cancelled");
781 if(found && ctmp){
782 strncpy(last, buf, sizeof(last));
783 last[sizeof(last)-1] = '\0';
784 result = "Word found";
785 current = ctmp;
788 q_status_message(SM_ORDER, 0, 3,
789 result ? result : _("Word not found"));
792 break;
794 case MC_DELETE :
795 if(delete_attachment(msgno, current->attp)){
796 int l = current ? strlen(current->attp->number) : 0;
798 current->dstring[1] = 'D';
800 /* Also indicate any children that will be deleted */
802 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
803 if(!strncmp(ctmp->attp->number, current->attp->number, l)
804 && ctmp->attp->number[l] == '.'){
805 ctmp->dstring[1] = 'D';
806 ps->mangled_screen = 1;
810 break;
812 case MC_UNDELETE :
813 if(undelete_attachment(msgno, current->attp, &expbits)){
814 int l = current ? strlen(current->attp->number) : 0;
816 current->dstring[1] = ' ';
818 /* And unflag anything implicitly undeleted */
819 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
820 if(!strncmp(ctmp->attp->number, current->attp->number, l)
821 && ctmp->attp->number[l] == '.'
822 && (!msgno_exceptions(ps->mail_stream, msgno,
823 ctmp->attp->number, &expbits, FALSE)
824 || !(expbits & MSG_EX_DELETE))){
825 ctmp->dstring[1] = ' ';
826 ps->mangled_screen = 1;
830 break;
832 case MC_REPLY :
833 reply_msg_att(ps->mail_stream, msgno, current->attp);
834 break;
836 case MC_FORWARD :
837 forward_attachment(ps->mail_stream, msgno, current->attp);
838 break;
840 case MC_BOUNCE :
841 bounce_msg_att(ps->mail_stream, msgno, current->attp->number,
842 current->attp->body->nested.msg->env->subject);
843 ps->mangled_footer = 1;
844 break;
846 case MC_ABOUTATCH :
847 display_attach_info(msgno, current->attp);
848 break;
850 case MC_VIEW_ATCH : /* View command */
851 display_attachment(msgno, current->attp, DA_SAVE);
852 old_cols = -1;
853 /* fall thru to repaint */
855 case MC_REPAINT : /* redraw */
856 case MC_RESIZE :
857 ps->mangled_screen = 1;
858 break;
860 case MC_EXPORT :
861 export_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
862 ps->mangled_footer = 1;
863 break;
865 case MC_SAVETEXT : /* Save command */
866 save_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
867 ps->mangled_footer = 1;
868 break;
870 case MC_PRINTMSG : /* Save command */
871 print_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
872 ps->mangled_footer = 1;
873 break;
875 case MC_PIPE : /* Pipe command */
876 if(F_ON(F_ENABLE_PIPE, ps)){
877 pipe_attachment(msgno, current->attp);
878 ps->mangled_footer = 1;
879 break;
880 } /* fall thru to complain */
882 case MC_NONE: /* simple timeout */
883 break;
885 case MC_UTF8:
886 bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
887 break;
889 case MC_CHARUP :
890 case MC_CHARDOWN :
891 case MC_CHARRIGHT :
892 case MC_CHARLEFT :
893 case MC_GOTOBOL :
894 case MC_GOTOEOL :
895 case MC_UNKNOWN :
896 default:
897 bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
902 for(current = first_attline(current); current;){ /* clean up */
903 ctmp = current->next;
904 free_attline(&current);
905 current = ctmp;
908 if(screen.titlecolor)
909 free_color_pair(&screen.titlecolor);
913 /*----------------------------------------------------------------------
914 allocate and attach a fresh attachment line struct
916 Args: current -- display line to attach new struct to
918 Returns: newly alloc'd attachment display line
919 ----*/
920 ATDISP_S *
921 new_attline(ATDISP_S **current)
923 ATDISP_S *p;
925 p = (ATDISP_S *)fs_get(sizeof(ATDISP_S));
926 memset((void *)p, 0, sizeof(ATDISP_S));
927 if(current){
928 if(*current){
929 p->next = (*current)->next;
930 (*current)->next = p;
931 p->prev = *current;
932 if(p->next)
933 p->next->prev = p;
936 *current = p;
939 return(p);
943 /*----------------------------------------------------------------------
944 Release system resources associated with attachment display line
946 Args: p -- line to free
948 Result:
949 ----*/
950 void
951 free_attline(ATDISP_S **p)
953 if(p){
954 if((*p)->dstring)
955 fs_give((void **)&(*p)->dstring);
957 fs_give((void **)p);
962 /*----------------------------------------------------------------------
963 Manage display of the attachment screen menu body.
965 Args: ps -- pine struct pointer
966 current -- currently selected display line
967 screen -- reference points for display painting
969 Result:
972 attachment_screen_updater(struct pine *ps, ATDISP_S *current, ATT_SCREEN_S *screen)
974 int dline, return_line = HEADER_ROWS(ps);
975 ATDISP_S *top_line, *ctmp;
977 /* calculate top line of display */
978 dline = 0;
979 ctmp = top_line = first_attline(current);
981 if(((dline++)%(ps->ttyo->screen_rows-HEADER_ROWS(ps)-FOOTER_ROWS(ps)))==0)
982 top_line = ctmp;
983 while(ctmp != current && (ctmp = next_attline(ctmp)));
985 #ifdef _WINDOWS
986 /* Don't know how to manage scroll bar for attachment screen yet. */
987 scroll_setrange (0L, 0L);
988 #endif
990 /* mangled body or new page, force redraw */
991 if(ps->mangled_body || screen->top_line != top_line)
992 screen->current = NULL;
994 /* loop thru painting what's needed */
995 for(dline = 0, ctmp = top_line;
996 dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
997 dline++, ctmp = next_attline(ctmp)){
1000 * only fall thru painting if something needs painting...
1002 if(!(!screen->current || ctmp == screen->current || ctmp == current))
1003 continue;
1005 if(F_ON(F_ENABLE_DEL_WHEN_WRITING, ps_global))
1006 ClearLine(dline + HEADER_ROWS(ps));
1007 if(ctmp && ctmp->dstring){
1008 char *p = tmp_20k_buf;
1009 int i, col, x = 0, totlen;
1011 if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
1012 x = 2;
1013 if(ctmp == current){
1014 return_line = dline + HEADER_ROWS(ps);
1015 PutLine0(dline + HEADER_ROWS(ps), 0, "->");
1017 else
1018 PutLine0(dline + HEADER_ROWS(ps), 0, " ");
1021 * Only paint lines if we have to...
1023 if(screen->current)
1024 continue;
1026 else if(ctmp == current){
1027 return_line = dline + HEADER_ROWS(ps);
1028 StartInverse();
1031 totlen = strlen(ctmp->dstring);
1034 * Copy the value to a temp buffer expanding tabs.
1035 * Assume the caller set the widths so as not to overflow the
1036 * right margin.
1038 for(i=0,col=x; ctmp->dstring[i]; i++){
1039 if(ctmp->dstring[i] == TAB){
1040 if((p-tmp_20k_buf) < SIZEOF_20KBUF-8)
1042 *p++ = ' ';
1043 while((++col)&0x07);
1045 else{
1046 col += width_at_this_position((unsigned char *) &ctmp->dstring[i], totlen-i);
1047 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1048 *p++ = ctmp->dstring[i];
1053 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1054 *p = '\0';
1056 PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
1058 if(ctmp == current
1059 && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
1060 EndInverse();
1062 else if(F_OFF(F_ENABLE_DEL_WHEN_WRITING, ps_global))
1063 ClearLine(dline + HEADER_ROWS(ps));
1066 ps->mangled_body = 0;
1067 screen->top_line = top_line;
1068 screen->current = current;
1069 return(return_line);
1073 /*----------------------------------------------------------------------
1074 Redraw the attachment screen based on the global "att_screen" struct
1076 Args: none
1078 Result:
1079 ----*/
1080 void
1081 attachment_screen_redrawer(void)
1083 bitmap_t bitmap;
1085 update_att_screen_titlebar();
1086 ps_global->mangled_header = 0;
1087 ClearLine(1);
1089 ps_global->mangled_body = 1;
1090 (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
1092 setbitmap(bitmap);
1093 draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
1094 1-FOOTER_ROWS(ps_global), 0, SameMenu);
1098 void
1099 update_att_screen_titlebar(void)
1101 long raw_msgno;
1102 COLOR_PAIR *returned_color = NULL;
1103 COLOR_PAIR *titlecolor = NULL;
1104 int colormatch;
1105 SEARCHSET *ss = NULL;
1106 PAT_STATE *pstate = NULL;
1108 if(ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
1109 raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
1110 ss = mail_newsearchset();
1111 ss->first = ss->last = (unsigned long) raw_msgno;
1113 if(ss){
1114 colormatch = get_index_line_color(ps_global->mail_stream,
1115 ss, &pstate, &returned_color);
1116 mail_free_searchset(&ss);
1119 * This is a bit tricky. If there is a colormatch but
1120 * returned_color
1121 * is NULL, that means that the user explicitly wanted the
1122 * Normal color used in this index line, so that is what we
1123 * use. If no colormatch then we will use the TITLE color
1124 * instead of Normal.
1126 if(colormatch){
1127 if(returned_color)
1128 titlecolor = returned_color;
1129 else
1130 titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
1131 ps_global->VAR_NORM_BACK_COLOR);
1134 if(titlecolor
1135 && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
1136 char cbuf[MAXCOLORLEN+1];
1138 strncpy(cbuf, titlecolor->fg, sizeof(cbuf));
1139 cbuf[sizeof(cbuf)-1] = '\0';
1140 strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
1141 titlecolor->fg[MAXCOLORLEN] = '\0';
1142 strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
1143 titlecolor->bg[MAXCOLORLEN] = '\0';
1147 /* Did the color change? */
1148 if((!titlecolor && att_screen->titlecolor)
1150 (titlecolor && !att_screen->titlecolor)
1152 (titlecolor && att_screen->titlecolor
1153 && (strcmp(titlecolor->fg, att_screen->titlecolor->fg)
1154 || strcmp(titlecolor->bg, att_screen->titlecolor->bg)))){
1156 if(att_screen->titlecolor)
1157 free_color_pair(&att_screen->titlecolor);
1159 att_screen->titlecolor = titlecolor;
1160 titlecolor = NULL;
1163 if(titlecolor)
1164 free_color_pair(&titlecolor);
1167 set_titlebar(_("ATTACHMENT INDEX"), ps_global->mail_stream,
1168 ps_global->context_current, ps_global->cur_folder,
1169 ps_global->msgmap, 1, MessageNumber, 0, 0,
1170 att_screen->titlecolor);
1174 /*----------------------------------------------------------------------
1175 Seek back from the given display line to the beginning of the list
1177 Args: p -- display linked list
1179 Result:
1180 ----*/
1181 ATDISP_S *
1182 first_attline(ATDISP_S *p)
1184 while(p && p->prev)
1185 p = p->prev;
1187 return(p);
1192 init_att_progress(char *msg, MAILSTREAM *stream, struct mail_bodystruct *body)
1194 if((save_att_length = body->size.bytes) != 0){
1195 /* if there are display filters, factor in extra copy */
1196 if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
1197 save_att_length += body->size.bytes;
1199 /* if remote folder and segment not cached, factor in IMAP fetch */
1200 if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
1201 && !((body->type == TYPETEXT && body->contents.text.data)
1202 || ((body->type == TYPEMESSAGE)
1203 && body->nested.msg && body->nested.msg->text.text.data)
1204 || body->contents.text.data))
1205 save_att_length += body->size.bytes;
1207 gf_filter_init(); /* reset counters */
1208 pine_gets_bytes(1);
1209 save_att_piped(1);
1210 return(busy_cue(msg, save_att_percent, 0));
1213 return(0);
1217 long
1218 save_att_piped(int reset)
1220 static long x;
1221 long y;
1223 if(reset){
1224 x = y = 0L;
1226 else if((y = gf_bytes_piped()) >= x){
1227 x = y;
1228 y = 0;
1231 return(x + y);
1236 save_att_percent(void)
1238 int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
1239 / save_att_length);
1240 return(MIN(i, 100));
1244 /*----------------------------------------------------------------------
1245 Save the given attachment associated with the given message no
1247 Args: ps
1249 Result:
1250 ----*/
1251 void
1252 save_attachment(int qline, long int msgno, ATTACH_S *a)
1254 if(ps_global->restricted){
1255 q_status_message(SM_ORDER | SM_DING, 0, 4,
1256 "Alpine demo can't save attachments");
1257 return;
1260 if(MIME_MSG_A(a))
1261 save_msg_att(msgno, a);
1262 else if(MIME_DGST_A(a))
1263 save_digest_att(msgno, a);
1264 else if(MIME_VCARD_A(a))
1265 save_vcard_att(ps_global, qline, msgno, a);
1266 else
1267 write_attachment(qline, msgno, a, "SAVE");
1271 /*----------------------------------------------------------------------
1272 Save the given attachment associated with the given message no
1274 Args: ps
1276 Result:
1277 ----*/
1278 void
1279 export_attachment(int qline, long int msgno, ATTACH_S *a)
1281 if(ps_global->restricted){
1282 q_status_message(SM_ORDER | SM_DING, 0, 4,
1283 "Alpine demo can't export attachments");
1284 return;
1287 if(MIME_MSG_A(a))
1288 export_msg_att(msgno, a);
1289 else if(MIME_DGST_A(a))
1290 export_digest_att(msgno, a);
1291 else
1292 q_status_message1(SM_ORDER, 0, 3,
1293 _("Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."),
1294 body_type_names(a->body->type));
1298 void
1299 write_attachment(int qline, long int msgno, ATTACH_S *a, char *method)
1301 char filename[MAXPATH+1], full_filename[MAXPATH+1],
1302 title_buf[64], *err;
1303 int r, rflags = GER_NONE, we_cancel = 0, flags;
1304 static HISTORY_S *history = NULL;
1305 static ESCKEY_S att_save_opts[] = {
1306 {ctrl('T'), 10, "^T", N_("To Files")},
1307 {-1, 0, NULL, NULL},
1308 {-1, 0, NULL, NULL},
1309 {-1, 0, NULL, NULL}};
1311 /*------- Figure out suggested file name ----*/
1312 filename[0] = full_filename[0] = '\0';
1313 (void) get_filename_parameter(filename, sizeof(filename), a->body, NULL);
1315 dprint((9, "export_attachment(name: %s)\n",
1316 filename ? filename : "?"));
1318 r = 0;
1319 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1320 if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
1321 att_save_opts[++r].ch = ctrl('V');
1322 att_save_opts[r].rval = 12;
1323 att_save_opts[r].name = "^V";
1324 att_save_opts[r].label = N_("Downld Msg");
1326 #endif /* !(DOS || MAC) */
1328 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1329 att_save_opts[++r].ch = ctrl('I');
1330 att_save_opts[r].rval = 11;
1331 att_save_opts[r].name = "TAB";
1332 att_save_opts[r].label = N_("Complete");
1335 att_save_opts[++r].ch = -1;
1337 snprintf(title_buf, sizeof(title_buf), "%s ATTACHMENT", method);
1338 title_buf[sizeof(title_buf)-1] = '\0';
1340 flags = (a && a->body && a->body->type == TYPETEXT ? GE_BINARY : 0)
1341 | GE_SEQ_SENSITIVE;
1343 r = get_export_filename(ps_global, filename, NULL, full_filename,
1344 sizeof(filename), "attachment", title_buf,
1345 att_save_opts, &rflags, qline, flags, &history);
1347 if(r < 0){
1348 switch(r){
1349 case -1:
1350 cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
1351 break;
1353 case -2:
1354 q_status_message1(SM_ORDER, 0, 2,
1355 _("Can't save to file outside of %s"),
1356 ps_global->VAR_OPER_DIR);
1357 break;
1360 return;
1362 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1363 else if(r == 12){ /* Download */
1364 char cmd[MAXPATH], *tfp = NULL;
1365 PIPE_S *syspipe;
1366 gf_io_t pc;
1367 long len;
1368 STORE_S *store;
1369 char prompt_buf[256];
1371 if(ps_global->restricted){
1372 q_status_message(SM_ORDER | SM_DING, 3, 3,
1373 "Download disallowed in restricted mode");
1374 return;
1377 err = NULL;
1378 tfp = temp_nam(NULL, "pd");
1379 dprint((1, "Download attachment called!\n"));
1380 if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
1382 snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
1383 prompt_buf[sizeof(prompt_buf)-1] = '\0';
1384 we_cancel = init_att_progress(prompt_buf,
1385 ps_global->mail_stream,
1386 a->body);
1388 gf_set_so_writec(&pc, store);
1389 if((err = detach(ps_global->mail_stream, msgno,
1390 a->number, 0L, &len, pc, NULL, 0)) != NULL)
1391 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1392 "%s: Error writing attachment to \"%s\"",
1393 err, tfp);
1395 /* cancel regardless, so it doesn't get in way of xfer */
1396 cancel_busy_cue(0);
1398 gf_clear_so_writec(store);
1399 if(so_give(&store)) /* close file */
1400 err = "Error writing tempfile for download";
1402 if(!err){
1403 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
1404 ps_global->VAR_DOWNLOAD_CMD, tfp);
1405 if((syspipe = open_system_pipe(cmd, NULL, NULL,
1406 PIPE_USER | PIPE_RESET,
1407 0, pipe_callback, pipe_report_error)) != NULL)
1408 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1409 else
1410 q_status_message(SM_ORDER | SM_DING, 3, 3,
1411 err = "Error running download command");
1414 else
1415 q_status_message(SM_ORDER | SM_DING, 3, 3,
1416 err = "Error building temp file for download");
1418 if(tfp){
1419 our_unlink(tfp);
1420 fs_give((void **)&tfp);
1423 if(!err)
1424 q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
1425 a->number);
1427 return;
1429 #endif /* !(DOS || MAC) */
1431 (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
1432 rflags, full_filename);
1437 * Args stream --
1438 * msgno -- raw message number
1439 * a -- attachment struct
1440 * flags -- comes from get_export_filename
1441 * GER_OVER -- the file was truncated before we wrote
1442 * GER_APPEND -- the file was not truncated prior to our writing,
1443 * so we were appending
1444 * else -- the file didn't previously exist
1445 * file -- the full pathname of the file
1447 * Returns 1 for successful write, 0 for nothing to write, -1 for error
1450 write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
1452 char *l_string, sbuf[256], *err, *err2 = NULL;
1453 int is_text, we_cancel = 0, dt_flags = 0, so_flags;
1454 long len, orig_size;
1455 gf_io_t pc;
1456 STORE_S *store;
1458 if(!(a && a->body && a->number && a->number[0] && file && file[0]
1459 && stream))
1460 return 0;
1462 is_text = (a && a->body && a->body->type == TYPETEXT);
1464 if(flags & GER_APPEND)
1465 orig_size = name_file_size(file);
1467 if(flags & GER_BINARY)
1468 dt_flags |= DT_BINARY;
1470 so_flags = (is_text & !(flags & GER_BINARY) ? WRITE_TO_LOCALE : 0)
1471 | WRITE_ACCESS ;
1473 store = so_get(FileStar, file, so_flags);
1474 if(store == NULL){
1475 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1476 /* TRANSLATORS: Error opening destination <filename>: <error text> */
1477 _("Error opening destination %s: %s"),
1478 file, error_description(errno));
1479 return -1;
1482 snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
1483 sbuf[sizeof(sbuf)-1] = '\0';
1484 we_cancel = init_att_progress(sbuf, stream, a->body);
1486 gf_set_so_writec(&pc, store);
1487 err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, dt_flags);
1488 gf_clear_so_writec(store);
1490 if(we_cancel)
1491 cancel_busy_cue(0);
1493 if(so_give(&store)) /* close file */
1494 err2 = error_description(errno);
1496 if(err || err2){
1497 if(!(flags & (GER_APPEND | GER_OVER)))
1498 our_unlink(file);
1499 else
1500 our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
1502 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1503 /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
1504 _("%s: Error writing attachment to \"%s\""),
1505 err ? err : err2, file);
1506 return -1;
1508 else{
1509 l_string = cpystr(byte_string(len));
1510 q_status_message8(SM_ORDER, 0, 4,
1511 "Part %s, %s%s %s to \"%s\"%s%s%s",
1512 a->number,
1513 is_text
1514 ? comatose(a->body->size.lines) : l_string,
1515 is_text ? " lines" : "",
1516 flags & GER_OVER
1517 ? "overwritten"
1518 : flags & GER_APPEND ? "appended" : "written",
1519 file,
1520 (is_text || len == a->body->size.bytes)
1521 ? "" : "(decoded from ",
1522 (is_text || len == a->body->size.bytes)
1523 ? "" : byte_string(a->body->size.bytes),
1524 is_text || len == a->body->size.bytes
1525 ? "" : ")");
1526 fs_give((void **)&l_string);
1527 return 1;
1532 char *
1533 write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
1535 char *err = NULL;
1536 long start_of_append;
1537 gf_io_t pc;
1538 MESSAGECACHE *mc;
1540 if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
1541 && (*ap)->body->nested.msg->env){
1542 start_of_append = so_tell(store);
1544 gf_set_so_writec(&pc, store);
1545 if(!(ps_global->mail_stream && msgno > 0L
1546 && msgno <= ps_global->mail_stream->nmsgs
1547 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1548 mc = NULL;
1550 if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
1551 || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
1552 err = error_description(errno);
1554 gf_clear_so_writec(store);
1556 if(err)
1557 ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
1559 else
1560 err = "Can't export message. Missing Envelope data";
1562 return(err);
1566 /*----------------------------------------------------------------------
1567 Save the attachment message/rfc822 to specified folder
1569 Args:
1571 Result:
1572 ----*/
1573 void
1574 save_msg_att(long int msgno, ATTACH_S *a)
1576 char newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
1577 char date[64], nmsgs[80];
1578 CONTEXT_S *cntxt = NULL;
1579 int our_stream = 0, rv;
1580 MAILSTREAM *save_stream;
1581 MESSAGECACHE *mc;
1583 snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
1584 nmsgs[sizeof(nmsgs)-1] = '\0';
1585 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
1586 a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
1587 if(strucmp(newfolder, ps_global->inbox_name) == 0){
1588 save_folder = ps_global->VAR_INBOX_PATH;
1589 cntxt = NULL;
1591 else
1592 save_folder = newfolder;
1594 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1596 mc = (msgno > 0L && ps_global->mail_stream
1597 && msgno <= ps_global->mail_stream->nmsgs)
1598 ? mail_elt(ps_global->mail_stream, msgno) : NULL;
1599 flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
1600 if(mc && mc->day)
1601 mail_date(date, mc);
1602 else
1603 *date = '\0';
1605 if(pith_opt_save_size_changed_prompt)
1606 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1608 rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
1610 if(pith_opt_save_size_changed_prompt)
1611 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1613 if(flags)
1614 fs_give((void **) &flags);
1616 if(rv == 1)
1617 q_status_message2(SM_ORDER, 0, 4,
1618 _("Attached message (part %s) saved to \"%s\""),
1619 a->number,
1620 save_folder);
1621 else if(rv == -1)
1622 cmd_cancelled("Attached message Save");
1623 /* else whatever broke in save_fetch_append shoulda bitched */
1625 if(our_stream)
1626 mail_close(save_stream);
1631 /*----------------------------------------------------------------------
1632 Save the message/rfc822 in the given digest to the specified folder
1634 Args:
1636 Result:
1637 ----*/
1638 void
1639 save_digest_att(long int msgno, ATTACH_S *a)
1641 char newfolder[MAILTMPLEN], *save_folder,
1642 date[64], nmsgs[80];
1643 CONTEXT_S *cntxt = NULL;
1644 int our_stream = 0, rv, cnt = 0;
1645 MAILSTREAM *save_stream;
1646 ATTACH_S *ap;
1648 for(ap = a + 1;
1649 ap->description
1650 && !strncmp(a->number, ap->number, strlen(a->number));
1651 ap++)
1652 if(MIME_MSG(ap->body->type, ap->body->subtype))
1653 cnt++;
1655 snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
1656 nmsgs[sizeof(nmsgs)-1] = '\0';
1658 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
1659 nmsgs, NULL, 0, NULL, NULL, NULL)){
1660 save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
1661 ? ps_global->VAR_INBOX_PATH : newfolder;
1663 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1665 if(pith_opt_save_size_changed_prompt)
1666 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1668 for(ap = a + 1;
1669 ap->description
1670 && !strncmp(a->number, ap->number, strlen(a->number));
1671 ap++)
1672 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1673 *date = '\0';
1674 rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
1675 if(rv != 1)
1676 break;
1679 if(pith_opt_save_size_changed_prompt)
1680 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1682 if(rv == 1)
1683 q_status_message2(SM_ORDER, 0, 4,
1684 _("Attached digest (part %s) saved to \"%s\""),
1685 a->number,
1686 save_folder);
1687 else if(rv == -1)
1688 cmd_cancelled("Attached digest Save");
1689 /* else whatever broke in save_fetch_append shoulda bitched */
1691 if(our_stream)
1692 mail_close(save_stream);
1698 save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
1699 char *save_folder, CONTEXT_S *cntxt, char *date)
1701 STORE_S *so;
1702 int rv = 0;
1704 if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
1705 if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1706 *date = '\0';
1707 rv = save_fetch_append(ps_global->mail_stream, msgno,
1708 a->number,
1709 save_stream, save_folder, cntxt,
1710 a->body->size.bytes,
1711 NULL, date, so);
1713 else{
1714 dprint((1, "Can't allocate store for save: %s\n",
1715 error_description(errno)));
1716 q_status_message(SM_ORDER | SM_DING, 3, 4,
1717 _("Problem creating space for message text."));
1718 rv = 0;
1722 return(rv);
1726 /*----------------------------------------------------------------------
1727 Export the attachment message/rfc822 to specified file
1729 Args:
1731 Result:
1732 ----*/
1733 void
1734 export_msg_att(long int msgno, ATTACH_S *a)
1736 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
1737 int rv, rflags = GER_NONE, i = 1;
1738 ATTACH_S *ap = a;
1739 STORE_S *store;
1740 static HISTORY_S *history = NULL;
1741 static ESCKEY_S opts[] = {
1742 {ctrl('T'), 10, "^T", N_("To Files")},
1743 {-1, 0, NULL, NULL},
1744 {-1, 0, NULL, NULL}};
1746 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1747 opts[i].ch = ctrl('I');
1748 opts[i].rval = 11;
1749 opts[i].name = "TAB";
1750 opts[i].label = N_("Complete");
1753 filename[0] = full_filename[0] = '\0';
1755 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1756 sizeof(filename), "msg attachment",
1757 /* TRANSLATORS: Message Attachment (a screen title) */
1758 _("MSG ATTACHMENT"), opts,
1759 &rflags, -FOOTER_ROWS(ps_global),
1760 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1762 if(rv < 0){
1763 switch(rv){
1764 case -1:
1765 cmd_cancelled("Export");
1766 break;
1768 case -2:
1769 q_status_message1(SM_ORDER, 0, 2,
1770 _("Can't export to file outside of %s"),
1771 ps_global->VAR_OPER_DIR);
1772 break;
1775 return;
1778 /* With name in hand, allocate storage object and save away... */
1779 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1780 if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
1781 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1782 else
1783 q_status_message3(SM_ORDER, 0, 4,
1784 _("Attached message (part %s) %s to \"%s\""),
1785 a->number,
1786 rflags & GER_OVER
1787 ? _("overwritten")
1788 : rflags & GER_APPEND ? _("appended") : _("written"),
1789 full_filename);
1791 if(so_give(&store))
1792 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1793 _("Error writing %s: %s"),
1794 full_filename, error_description(errno));
1796 else
1797 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1798 /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
1799 _("Error opening file \"%s\" to export message: %s"),
1800 full_filename, error_description(errno));
1804 /*----------------------------------------------------------------------
1805 Export the message/rfc822 in the given digest to the specified file
1807 Args:
1809 Result:
1810 ----*/
1811 void
1812 export_digest_att(long int msgno, ATTACH_S *a)
1814 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
1815 int rv, rflags = GER_NONE, i = 1;
1816 long count = 0L;
1817 ATTACH_S *ap;
1818 static HISTORY_S *history = NULL;
1819 STORE_S *store;
1820 static ESCKEY_S opts[] = {
1821 {ctrl('T'), 10, "^T", N_("To Files")},
1822 {-1, 0, NULL, NULL},
1823 {-1, 0, NULL, NULL}};
1825 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1826 opts[i].ch = ctrl('I');
1827 opts[i].rval = 11;
1828 opts[i].name = "TAB";
1829 opts[i].label = N_("Complete");
1832 filename[0] = full_filename[0] = '\0';
1834 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1835 sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
1836 opts, &rflags, -FOOTER_ROWS(ps_global),
1837 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1839 if(rv < 0){
1840 switch(rv){
1841 case -1:
1842 cmd_cancelled("Export");
1843 break;
1845 case -2:
1846 q_status_message1(SM_ORDER, 0, 2,
1847 _("Can't export to file outside of %s"),
1848 ps_global->VAR_OPER_DIR);
1849 break;
1852 return;
1855 /* With name in hand, allocate storage object and save away... */
1856 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1857 count = 0;
1859 for(ap = a + 1;
1860 ap->description
1861 && !strncmp(a->number, ap->number, strlen(a->number))
1862 && !err;
1863 ap++){
1864 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1865 count++;
1866 err = write_attached_msg(msgno, &ap, store,
1867 !count && !(rflags & GER_APPEND));
1871 if(so_give(&store))
1872 err = error_description(errno);
1874 if(err){
1875 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1876 _("Error exporting: %s"), err);
1877 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1878 _("%s messages exported before error occurred"), err);
1880 else
1881 q_status_message4(SM_ORDER, 0, 4,
1882 "%s messages in digest (part %s) %s to \"%s\"",
1883 long2string(count),
1884 a->number,
1885 rflags & GER_OVER
1886 ? "overwritten"
1887 : rflags & GER_APPEND ? "appended" : "written",
1888 full_filename);
1890 else
1891 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1892 _("Error opening file \"%s\" to export digest: %s"),
1893 full_filename, error_description(errno));
1897 /*----------------------------------------------------------------------
1898 Print the given attachment associated with the given message no
1900 Args: ps
1902 Result:
1903 ----*/
1904 void
1905 print_attachment(int qline, long int msgno, ATTACH_S *a)
1907 char prompt[250];
1909 if(ps_global->restricted){
1910 q_status_message(SM_ORDER | SM_DING, 0, 4,
1911 "Alpine demo can't Print attachments");
1912 return;
1915 snprintf(prompt, sizeof(prompt), "attach%s %s",
1916 (a->body->type == TYPETEXT) ? "ment" : "ed message",
1917 MIME_DGST_A(a) ? "digest" : a->number);
1918 prompt[sizeof(prompt)-1] = '\0';
1919 if(open_printer(prompt) >= 0){
1920 if(MIME_MSG_A(a))
1921 (void) print_msg_att(msgno, a, 1);
1922 else if(MIME_DGST_A(a))
1923 print_digest_att(msgno, a);
1924 else
1925 (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
1927 close_printer();
1933 * Print the attachment message/rfc822 to specified file
1935 * Returns 1 on success, 0 on failure.
1938 print_msg_att(long int msgno, ATTACH_S *a, int first)
1940 ATTACH_S *ap = a;
1941 MESSAGECACHE *mc;
1943 if(!(ps_global->mail_stream && msgno > 0L
1944 && msgno <= ps_global->mail_stream->nmsgs
1945 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1946 mc = NULL;
1948 if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
1949 && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
1950 && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
1951 ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
1952 : 1)
1953 && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
1954 return(1);
1957 q_status_message2(SM_ORDER | SM_DING, 3, 3,
1958 _("Error printing message %s, part %s"),
1959 long2string(msgno), a->number);
1960 return(0);
1964 /*----------------------------------------------------------------------
1965 Print the attachment message/rfc822 to specified file
1967 Args:
1969 Result:
1970 ----*/
1971 void
1972 print_digest_att(long int msgno, ATTACH_S *a)
1974 ATTACH_S *ap;
1975 int next = 0;
1977 for(ap = a + 1;
1978 ap->description
1979 && !strncmp(a->number, ap->number, strlen(a->number));
1980 ap++){
1981 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1982 char *p = part_desc(ap->number, ap->body->nested.msg->body,
1983 0, 80, FM_NOINDENT, print_char);
1984 if(p){
1985 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1986 _("Can't print digest: %s"), p);
1987 break;
1989 else if(!print_msg_att(msgno, ap, !next))
1990 break;
1992 next++;
1998 display_html_external_attachment(long int msgno, ATTACH_S *a, int flags)
2000 char dir_path[MAXPATH+1];
2001 char *filename = NULL;
2002 char *file_path; /* file:///some/path/ */
2003 STORE_S *store;
2004 gf_io_t pc;
2005 char *err;
2006 int we_cancel = 0, errs;
2007 char *tool;
2008 ATTACH_S *att;
2009 unsigned long rawno;
2011 if(a->body == NULL){
2012 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Attachment has no body!"));
2013 return 1;
2014 } else if (a->body->type != TYPETEXT
2015 || a->body->subtype == NULL
2016 || strucmp(a->body->subtype, "HTML")){
2017 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Not a TEXT/HTML attachment"));
2018 return 1;
2021 /* zero these variables, just in case. Do not try freeing them. They have short lives */
2022 for(att = ps_global->atmts; att->description != NULL; att++){
2023 att->cid_tmpfile = NULL;
2024 att->tmpdir = NULL;
2027 /* setup the environment first */
2028 if(!ps_global->html_dir){
2029 if(!html_directory_path(ps_global->VAR_HTML_DIRECTORY, dir_path, MAXPATH)){
2030 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2031 _("Error creating full path for %s"), ps_global->VAR_HTML_DIRECTORY);
2032 return 1;
2033 } else if (init_html_directory(dir_path) < 0){
2034 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error initializing %s"), dir_path);
2035 return 1;
2037 ps_global->html_dir = cpystr(dir_path);
2039 else{
2040 strncpy(dir_path, ps_global->html_dir, sizeof(dir_path));
2041 dir_path[sizeof(dir_path)-1] = '\0';
2044 if(create_random_dir(dir_path, sizeof(dir_path)) < 0){
2045 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error creating temp dir in %s"), dir_path);
2046 return 1;
2049 a->tmpdir = cpystr(dir_path);
2050 add_html_log(&ps_global->html_dir_list, a->tmpdir);
2052 /* Process the text/html part */
2053 filename = temp_nam_ext(a->tmpdir, "tmp-html-", HTML_EXT);
2055 if(!filename){
2056 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2057 _("Error \"%s\", Can't create temporary file"),
2058 error_description(errno));
2059 return(1);
2062 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2063 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2064 _("Error \"%s\", Can't write file %s"),
2065 error_description(errno), filename);
2066 if(filename){
2067 our_unlink(filename);
2068 fs_give((void **)&filename);
2070 return(1);
2073 if(a->body->size.bytes){
2074 char msg_buf[128];
2076 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2077 a->description ? "\"" : "",
2078 a->description ? a->description : "attachment number ",
2079 a->description ? "" : a->number,
2080 a->description ? "\"" : "");
2081 msg_buf[sizeof(msg_buf)-1] = '\0';
2082 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream, a->body);
2085 gf_set_so_writec(&pc, store);
2087 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL,
2088 DT_EXTERNAL | ((flags & DA_ALLIMAGES) ? DT_ALLIMAGES : 0));
2090 gf_clear_so_writec(store);
2092 if(we_cancel)
2093 cancel_busy_cue(0);
2095 so_give(&store);
2097 /*----- Download all needed inline attachments ------*/
2098 errs = 0;
2099 rawno = mn_m2raw(ps_global->msgmap, msgno);
2100 for (att = ps_global->atmts; rawno > 0 && att->description != NULL; att++){
2101 if(att->cid_tmpfile){
2102 if(write_attachment_to_file(ps_global->mail_stream, rawno,
2103 att, GER_NONE, att->cid_tmpfile) != 1
2104 || name_file_size(att->cid_tmpfile) == 0L)
2105 errs++;
2106 fs_give((void **) &att->cid_tmpfile);
2108 if(att->tmpdir)
2109 fs_give((void **) &att->tmpdir);
2112 if(err){
2113 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2114 "%s: Error saving message to temp file %s",
2115 err, filename);
2116 if(filename){
2117 our_unlink(filename);
2118 fs_give((void **)&filename);
2120 return(1);
2123 switch(errs){
2124 case 0 : break;
2125 case 1 : q_status_message(SM_ORDER | SM_DING, 3, 5,
2126 "Failed to download one image. Continuing...");
2127 break;
2128 default: q_status_message1(SM_ORDER | SM_DING, 3, 5,
2129 "Failed to download %s images. Continuing...",
2130 int2string(errs));
2131 break;
2134 tool = get_url_external_handler("http://", 1);
2135 if(tool == NULL) tool = get_url_external_handler("http://", 0);
2136 if(tool == NULL) tool = get_url_external_handler("https://", 1);
2137 if(tool == NULL) tool = get_url_external_handler("https://", 0);
2139 file_path = fs_get((strlen(filename) + strlen("file://") + 1)*sizeof(char));
2140 sprintf(file_path, "file://%s", filename);
2142 /*----- Run the viewer process ----*/
2143 if(do_url_launch(tool, file_path) == 0)
2144 q_status_message(SM_ORDER, 3, 3, "Opened message in external browser");
2145 else
2146 q_status_message(SM_ORDER|SM_DING, 3, 5, "Failed to open message in external browser");
2148 if(filename)
2149 fs_give((void **)&filename);
2151 if(file_path)
2152 fs_give((void **)&file_path);
2154 ps_global->mangled_screen = 1;
2156 return(0);
2160 /*----------------------------------------------------------------------
2161 Unpack and display the given attachment associated with given message no.
2163 Args: msgno -- message no attachment is part of
2164 a -- attachment to display
2166 Returns: 0 on success, non-zero (and error message queued) otherwise
2167 ----*/
2169 display_attachment(long int msgno, ATTACH_S *a, int flags)
2171 char *filename = NULL;
2172 char sender_filename[1000];
2173 char *extp = NULL;
2174 STORE_S *store;
2175 gf_io_t pc;
2176 char *err;
2177 int we_cancel = 0, rv;
2178 char prefix[70 + 1000]; /* 1000 = sizeof(sender_filename) */
2179 char ext[32];
2180 char mtype[128];
2182 if(flags & DA_EXTERNAL)
2183 return display_html_external_attachment(msgno, a, flags);
2185 /*------- Display the attachment -------*/
2186 if(dispatch_attachment(a) == MCD_NONE){
2187 /*----- Can't display this type ------*/
2188 if(a->body->encoding < ENCOTHER)
2189 q_status_message4(SM_ORDER | SM_DING, 3, 5,
2190 /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
2191 _("Don't know how to display %s%s%s attachments.%s"),
2192 body_type_names(a->body->type),
2193 a->body->subtype ? "/" : "",
2194 a->body->subtype ? a->body->subtype :"",
2195 (flags & DA_SAVE) ? _(" Try Save.") : "");
2196 else
2197 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2198 _("Don't know how to unpack \"%s\" encoding"),
2199 body_encodings[(a->body->encoding <= ENCMAX)
2200 ? a->body->encoding : ENCOTHER]);
2202 return(1);
2204 else if(!(a->can_display & MCD_EXTERNAL)){
2205 if(a->body->type == TYPEMULTIPART){
2206 if(a->body->subtype){
2207 if(!strucmp(a->body->subtype, "digest"))
2208 display_digest_att(msgno, a, flags);
2209 else
2210 q_status_message1(SM_ORDER, 3, 5,
2211 _("Can't display Multipart/%s"),
2212 a->body->subtype);
2214 else
2215 q_status_message(SM_ORDER, 3, 5,
2216 _("Can't display unknown Multipart Subtype"));
2218 else if(MIME_VCARD_A(a))
2219 display_vcard_att(msgno, a, flags);
2220 else if( MIME_VCALENDAR(a->body->type, a->body->subtype))
2221 display_vcalendar_att(msgno, a, flags);
2222 else if(a->body->type == TYPETEXT){
2224 rv = display_text_att(msgno, a, flags);
2225 } while(rv == MC_FULLHDR);
2227 else if(a->body->type == TYPEMESSAGE){
2229 rv = display_msg_att(msgno, a, flags);
2230 } while(rv == MC_FULLHDR);
2233 ps_global->mangled_screen = 1;
2234 return(0);
2237 /* arrive here if MCD_EXTERNAL */
2239 if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
2240 && (!(flags & DA_DIDPROMPT)))
2241 if(want_to(_("View selected Attachment"), 'y',
2242 0, NO_HELP, WT_NORM) == 'n'){
2243 cmd_cancelled(NULL);
2244 return(1);
2247 sender_filename[0] = '\0';
2248 ext[0] = '\0';
2249 prefix[0] = '\0';
2251 if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
2252 && (a->can_display & MCD_EXT_PROMPT)){
2253 char prompt[256];
2255 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2256 a->body, &extp);
2257 snprintf(prompt, sizeof(prompt),
2258 "Attachment %s%s unrecognized. %s%s%s",
2259 a->body->subtype,
2260 strlen(a->body->subtype) > 12 ? "..." : "",
2261 (extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
2262 (extp && extp[0]) ? extp : "",
2263 (extp && extp[0]) ? ")" : "");
2265 if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
2266 cmd_cancelled(NULL);
2267 return(1);
2271 /*------ Write the image to a temporary file ------*/
2273 /* create type/subtype in mtype */
2274 strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
2275 mtype[sizeof(mtype)-1] = '\0';
2276 if(a->body->subtype){
2277 strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
2278 mtype[sizeof(mtype)-1] = '\0';
2279 strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
2280 mtype[sizeof(mtype)-1] = '\0';
2284 * If we haven't already gotten the filename parameter, get it
2285 * now. It may be used in the temporary filename and possibly
2286 * for its extension.
2288 if(!sender_filename[0])
2289 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2290 a->body, &extp);
2292 if(check_mime_type_by_extension(extp, mtype)
2293 || (!set_mime_extension_by_type(ext, mtype) /* extension from type */
2294 && extp && extp[0])){ /* extension from filename */
2295 strncpy(ext, extp, sizeof(ext));
2296 ext[sizeof(ext)-1] = '\0';
2299 /* create a temp file */
2300 if(sender_filename){
2301 char *p, *q = NULL;
2303 /* get rid of any extension */
2304 if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
2305 *(q-1) = '\0';
2307 /* be careful about what is allowed in the filename */
2308 for(p = sender_filename; *p; p++)
2309 if(!(isascii((unsigned char) *p)
2310 && (isalnum((unsigned char) *p)
2311 || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
2312 break;
2314 if(!*p) /* filename was ok to use */
2315 snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
2318 /* didn't get it yet */
2319 if(!prefix[0]){
2320 snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
2321 ? a->body->subtype : "unk");
2324 /* We are creating a temporary name. This is just a prefix. If you
2325 * need the original name, use the save command, so if the prefix
2326 * is too long, shorten it.
2328 if (strlen(prefix) > 9){
2329 prefix[9] = '-';
2330 prefix[10] = '\0';
2333 filename = temp_nam_ext(NULL, prefix, ext);
2335 if(!filename){
2336 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2337 _("Error \"%s\", Can't create temporary file"),
2338 error_description(errno));
2339 return(1);
2342 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2343 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2344 _("Error \"%s\", Can't write file %s"),
2345 error_description(errno), filename);
2346 if(filename){
2347 our_unlink(filename);
2348 fs_give((void **)&filename);
2351 return(1);
2355 if(a->body->size.bytes){
2356 char msg_buf[128];
2358 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2359 a->description ? "\"" : "",
2360 a->description ? a->description : "attachment number ",
2361 a->description ? "" : a->number,
2362 a->description ? "\"" : "");
2363 msg_buf[sizeof(msg_buf)-1] = '\0';
2364 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
2365 a->body);
2368 if(a->body->type == TYPEMULTIPART){
2369 char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
2370 NULL, 0L);
2372 /* Write to store while converting newlines */
2373 while(h && *h)
2374 if(*h == '\015' && *(h+1) == '\012'){
2375 so_puts(store, NEWLINE);
2376 h += 2;
2378 else
2379 so_writec(*h++, store);
2382 gf_set_so_writec(&pc, store);
2384 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
2386 gf_clear_so_writec(store);
2388 if(we_cancel)
2389 cancel_busy_cue(0);
2391 so_give(&store);
2393 if(err){
2394 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2395 "%s: Error saving image to temp file %s",
2396 err, filename);
2397 if(filename){
2398 our_unlink(filename);
2399 fs_give((void **)&filename);
2402 return(1);
2405 /*----- Run the viewer process ----*/
2406 run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
2407 if(filename)
2408 fs_give((void **)&filename);
2410 return(0);
2414 /*----------------------------------------------------------------------
2415 Fish the required command from mailcap and run it
2417 Args: image_file -- The name of the file to pass viewer
2418 body -- body struct containing type/subtype of part
2420 A side effect may be that scrolltool is called as well if
2421 exec_mailcap_cmd has any substantial output...
2422 ----*/
2423 void
2424 run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
2426 MCAP_CMD_S *mc_cmd = NULL;
2427 int needs_terminal = 0, we_cancel = 0;
2429 we_cancel = busy_cue("Displaying attachment", NULL, 0);
2431 if((mc_cmd = mailcap_build_command(body->type, body->subtype,
2432 body, image_file,
2433 &needs_terminal, chk_extension)) != NULL){
2434 if(we_cancel)
2435 cancel_busy_cue(-1);
2437 exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
2438 if(mc_cmd->command)
2439 fs_give((void **)&mc_cmd->command);
2440 fs_give((void **)&mc_cmd);
2442 else{
2443 if(we_cancel)
2444 cancel_busy_cue(-1);
2446 q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
2447 type_desc(body->type, body->subtype,
2448 body->parameter, NULL, 1));
2453 /*----------------------------------------------------------------------
2454 Detach and provide for browsing a text body part
2456 Args: msgno -- raw message number to get part from
2457 a -- attachment struct for the desired part
2459 Result:
2460 ----*/
2461 STORE_S *
2462 format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
2464 STORE_S *store;
2465 gf_io_t pc;
2467 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2468 if(handlesp)
2469 init_handles(handlesp);
2471 gf_set_so_writec(&pc, store);
2472 (void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
2473 gf_clear_so_writec(store);
2476 return(store);
2480 /*----------------------------------------------------------------------
2481 Detach and provide for browsing a text body part
2483 Args: msgno -- raw message number to get part from
2484 a -- attachment struct for the desired part
2486 Result:
2487 ----*/
2489 display_text_att(long int msgno, ATTACH_S *a, int flags)
2491 STORE_S *store;
2492 HANDLE_S *handles = NULL;
2493 int rv = 0;
2495 if(msgno > 0L)
2496 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2498 if((store = format_text_att(msgno, a, &handles)) != NULL){
2499 rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
2500 free_handles(&handles);
2501 so_give(&store); /* free resources associated with store */
2503 else
2504 q_status_message(SM_ORDER | SM_DING, 3, 3,
2505 _("Error allocating space for attachment."));
2507 return(rv);
2511 /*----------------------------------------------------------------------
2512 Detach and provide for browsing a body part of type "MESSAGE"
2514 Args: msgno -- message number to get partrom
2515 a -- attachment struct for the desired part
2517 Result:
2518 ----*/
2520 display_msg_att(long int msgno, ATTACH_S *a, int flags)
2522 STORE_S *store;
2523 gf_io_t pc;
2524 ATTACH_S *ap = a;
2525 HANDLE_S *handles = NULL;
2526 int rv = 0;
2528 if(msgno > 0L)
2529 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2531 /* BUG, should check this return code */
2532 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2534 /* initialize a storage object */
2535 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2536 q_status_message(SM_ORDER | SM_DING, 3, 3,
2537 _("Error allocating space for message."));
2538 return(rv);
2541 gf_set_so_writec(&pc, store);
2543 if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
2544 if(ps_global->full_header == 2)
2545 q_status_message(SM_INFO, 0, 3,
2546 _("Full header mode ON. All header text being included"));
2548 rv = scroll_attachment((a->body->subtype
2549 && !strucmp(a->body->subtype, "delivery-status"))
2550 ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
2551 store, CharStar, handles, a, flags);
2552 free_handles(&handles);
2555 gf_clear_so_writec(store);
2557 so_give(&store); /* free resources associated with store */
2558 return(rv);
2562 /*----------------------------------------------------------------------
2563 Detach and provide for browsing a multipart body part of type "DIGEST"
2565 Args: msgno -- message number to get partrom
2566 a -- attachment struct for the desired part
2568 Result:
2569 ----*/
2570 void
2571 display_digest_att(long int msgno, ATTACH_S *a, int flags)
2573 STORE_S *store;
2574 ATTACH_S *ap;
2575 HANDLE_S *handles = NULL;
2576 gf_io_t pc;
2577 SourceType src = CharStar;
2578 int bad_news = 0;
2580 if(msgno > 0L)
2581 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2583 /* BUG, should check this return code */
2584 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2586 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
2587 q_status_message(SM_ORDER | SM_DING, 3, 3,
2588 _("Error allocating space for message."));
2589 return;
2592 gf_set_so_writec(&pc, store);
2595 * While in a subtype of this message
2597 for(ap = a + 1;
2598 ap->description
2599 && !strncmp(a->number, ap->number, strlen(a->number))
2600 && !bad_news;
2601 ap++){
2602 if(ap->body->type == TYPEMESSAGE){
2603 char *errstr;
2605 if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
2606 char *tsub;
2608 tsub = cpystr(ap->body->subtype);
2609 convert_possibly_encoded_str_to_utf8((char **) &tsub);
2610 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
2611 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2613 if((errstr = format_editorial(tmp_20k_buf,
2614 ps_global->ttyo->screen_cols, 0,
2615 NULL, pc)) != NULL){
2616 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2617 _("Can't format digest: %s"), errstr);
2618 bad_news++;
2620 else if(!gf_puts(NEWLINE, pc))
2621 bad_news++;
2623 fs_give((void **) &tsub);
2625 else{
2626 if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
2627 0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
2628 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2629 _("Can't format digest: %s"), errstr);
2630 bad_news++;
2632 else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
2633 bad_news++;
2636 else if(ap->body->type == TYPETEXT
2637 && decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
2638 bad_news++;
2639 else if(!gf_puts("Unknown type in Digest", pc))
2640 bad_news++;
2643 if(!bad_news){
2644 if(ps_global->full_header == 2)
2645 q_status_message(SM_INFO, 0, 3,
2646 _("Full header mode ON. All header text being included"));
2648 scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
2651 free_handles(&handles);
2653 gf_clear_so_writec(store);
2654 so_give(&store); /* free resources associated with store */
2659 scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
2661 SCROLL_S sargs;
2663 memset(&sargs, 0, sizeof(SCROLL_S));
2664 sargs.text.text = so_text(store);
2665 sargs.text.src = src;
2666 sargs.text.desc = "attachment";
2667 sargs.text.handles = handles;
2668 sargs.bar.title = title;
2669 sargs.proc.tool = process_attachment_cmd;
2670 sargs.proc.data.p = (void *) a;
2671 sargs.help.text = h_mail_text_att_view;
2672 sargs.help.title = _("HELP FOR ATTACHED TEXT VIEW");
2673 sargs.keys.menu = &att_view_keymenu;
2674 setbitmap(sargs.keys.bitmap);
2676 /* First, fix up "back" key */
2677 if(flags & DA_FROM_VIEW){
2678 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
2680 else{
2681 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
2684 if(!handles){
2685 clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
2686 clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
2687 clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
2690 if(F_OFF(F_ENABLE_PIPE, ps_global))
2691 clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
2693 if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
2694 clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
2697 * If message or digest, leave Reply and Save and,
2698 * conditionally, Bounce on...
2700 if(MIME_MSG_A(a)){
2701 if(F_OFF(F_ENABLE_BOUNCE, ps_global))
2702 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2704 else{
2705 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2706 clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
2707 clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
2709 #ifdef SMIME
2710 if(!(ps_global->smime && ps_global->smime->need_passphrase))
2711 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2713 if(F_ON(F_DONT_DO_SMIME, ps_global) || !MIME_MSG_A(a)){
2714 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2715 clrbitn(ATV_SECURITY_KEY, sargs.keys.bitmap);
2717 #endif
2719 sargs.use_indexline_color = 1;
2721 if(DA_RESIZE & flags)
2722 sargs.resize_exit = 1;
2724 #ifdef _WINDOWS
2725 scrat_attachp = a;
2726 sargs.mouse.popup = scroll_att_popup;
2727 #endif
2729 return(scrolltool(&sargs));
2732 #ifdef SMIME
2733 void
2734 display_smime_info_att(struct pine *ps, ATTACH_S *a)
2736 if(smime_check(a->body->nested.msg->body) == 0){
2737 q_status_message(SM_ORDER | SM_DING, 0, 3,
2738 _("Not a signed or encrypted message"));
2739 return;
2742 display_smime_info(ps, a->body->nested.msg->env, a->body->nested.msg->body);
2744 #endif /* SMIME */
2747 process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2749 int rv = 0, n;
2750 long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2751 #define AD(X) ((ATTACH_S *) (X)->proc.data.p)
2753 switch(cmd){
2754 case MC_EXIT :
2755 rv = 1;
2756 break;
2758 case MC_QUIT :
2759 ps_global->next_screen = quit_screen;
2760 break;
2762 case MC_MAIN :
2763 ps_global->next_screen = main_menu_screen;
2764 break;
2766 case MC_REPLY :
2767 reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
2768 break;
2770 case MC_FORWARD :
2771 forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
2772 break;
2774 case MC_BOUNCE :
2775 bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
2776 AD(sparms)->body->nested.msg->env->subject);
2777 ps_global->mangled_footer = 1;
2778 break;
2780 case MC_DELETE :
2781 delete_attachment(rawno, sparms->proc.data.p);
2782 break;
2784 case MC_UNDELETE :
2785 if(undelete_attachment(rawno, sparms->proc.data.p, &n))
2786 q_status_message1(SM_ORDER, 0, 3,
2787 "Part %s UNdeleted", AD(sparms)->number);
2789 break;
2791 case MC_SAVE :
2792 save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2793 ps_global->mangled_footer = 1;
2794 break;
2796 case MC_EXPORT :
2797 export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2798 ps_global->mangled_footer = 1;
2799 break;
2801 case MC_PRINTMSG :
2802 print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2803 ps_global->mangled_footer = 1;
2804 break;
2806 case MC_PIPE :
2807 pipe_attachment(rawno, sparms->proc.data.p);
2808 ps_global->mangled_footer = 1;
2809 break;
2811 case MC_FULLHDR :
2812 ps_global->full_header++;
2813 if(ps_global->full_header == 1){
2814 if(!(ps_global->quote_suppression_threshold
2815 && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
2816 ps_global->full_header++;
2818 else if(ps_global->full_header > 2)
2819 ps_global->full_header = 0;
2821 switch(ps_global->full_header){
2822 case 0:
2823 q_status_message(SM_ORDER, 0, 3,
2824 _("Display of full headers is now off."));
2825 break;
2827 case 1:
2828 q_status_message1(SM_ORDER, 0, 3,
2829 _("Quotes displayed, use %s to see full headers"),
2830 F_ON(F_USE_FK, ps_global) ? "F9" : "H");
2831 break;
2833 case 2:
2834 q_status_message(SM_ORDER, 0, 3,
2835 _("Display of full headers is now on."));
2836 break;
2840 rv = 1;
2841 break;
2843 #ifdef SMIME
2844 case MC_DECRYPT:
2845 if(ps_global->smime && ps_global->smime->need_passphrase)
2846 smime_get_passphrase();
2847 break;
2849 case MC_SECURITY:
2850 display_smime_info_att(ps_global, sparms->proc.data.p);
2851 break;
2852 #endif
2854 default:
2855 alpine_panic("Unexpected command case");
2856 break;
2859 return(rv);
2864 * Returns 1 on success, 0 on error.
2867 format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
2869 int rv = 1;
2871 if((*a)->body->type != TYPEMESSAGE)
2872 return(gf_puts("[ Undisplayed Attachment of Type ", pc)
2873 && gf_puts(body_type_names((*a)->body->type), pc)
2874 && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
2876 if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
2877 HEADER_S h;
2879 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
2880 FE_DEFAULT);
2881 switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
2882 (*a)->body->nested.msg->env, &h, NULL, NULL,
2883 flags, NULL, pc)){
2884 case -1 : /* write error */
2885 return(0);
2887 case 1 : /* fetch error */
2888 if(!(gf_puts("[ Error fetching header ]", pc)
2889 && !gf_puts(NEWLINE, pc)))
2890 return(0);
2892 break;
2895 gf_puts(NEWLINE, pc);
2897 if(((*a)+1)->description)
2898 ++(*a);
2899 else{
2900 if(!(gf_puts("[Can't display missing text segment]", pc)
2901 && gf_puts(NEWLINE, pc)))
2902 rv = 0;
2903 return rv;
2906 #ifdef SMIME
2907 if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2908 if((*a)->description){
2909 if(!(!format_editorial((*a)->description,
2910 ps_global->ttyo->screen_cols,
2911 flags, NULL, pc)
2912 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2913 rv = 0;
2916 if(((*a)+1)->description)
2917 ++(*a);
2918 else{
2919 if(!(gf_puts("[Can't display missing text segment]", pc)
2920 && gf_puts(NEWLINE, pc)))
2921 rv = 0;
2922 return rv;
2925 #endif /* SMIME */
2927 if(((*a))->description
2928 && (*a)->body && (*a)->body->type == TYPETEXT){
2929 if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2930 rv = 0;
2932 else if(!(gf_puts("[Can't display ", pc)
2933 && gf_puts(((*a)->description && (*a)->body)
2934 ? "first non-" : "missing ", pc)
2935 && gf_puts("text segment]", pc)
2936 && gf_puts(NEWLINE, pc)))
2937 rv = 0;
2939 if(((*a)+1)->description)
2940 ++(*a);
2941 else{
2942 if(!(gf_puts("[Can't display missing text segment]", pc)
2943 && gf_puts(NEWLINE, pc)))
2944 rv = 0;
2945 return rv;
2949 else if((*a)->body->subtype
2950 && strucmp((*a)->body->subtype, "external-body") == 0) {
2951 if(format_editorial("This part is not included and can be fetched as follows:",
2952 ps_global->ttyo->screen_cols, flags, NULL, pc)
2953 || !gf_puts(NEWLINE, pc)
2954 || format_editorial(display_parameters((*a)->body->parameter),
2955 ps_global->ttyo->screen_cols, flags, handlesp, pc))
2956 rv = 0;
2958 else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2959 rv = 0;
2961 return(rv);
2965 void
2966 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2968 STORE_S *in_store, *out_store = NULL;
2969 HANDLE_S *handles = NULL;
2970 URL_HILITE_S uh;
2971 gf_io_t gc, pc;
2972 char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2973 int cmd = MC_RESIZE, indent, begins = 0;
2975 lines = detach_vcard_att(ps_global->mail_stream,
2976 msgno, a->body, a->number);
2977 if(!lines){
2978 q_status_message(SM_ORDER | SM_DING, 3, 3,
2979 _("Error accessing attachment."));
2980 return;
2983 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2984 free_list_array(&lines);
2985 q_status_message(SM_ORDER | SM_DING, 3, 3,
2986 _("Error allocating space for attachment."));
2987 return;
2990 for(ll = lines, indent = 0; ll && *ll; ll++)
2991 if((p = strindex(*ll, ':')) && p - *ll > indent)
2992 indent = p - *ll;
2994 indent += 5;
2995 for(ll = lines; ll && *ll; ll++){
2996 if((p = strindex(*ll, ':')) != NULL){
2997 if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2998 begins++;
3000 snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
3001 (int) MIN(p - *ll, sizeof(tmp)-5), *ll);
3002 tmp[sizeof(tmp)-1] = '\0';
3003 so_puts(in_store, tmp);
3004 p++;
3006 else{
3007 p = *ll;
3008 so_puts(in_store, repeat_char(indent, SPACE));
3011 snprintf(tmp, sizeof(tmp), "%.200s", p);
3012 tmp[sizeof(tmp)-1] = '\0';
3013 so_puts(in_store,
3014 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
3015 SIZEOF_20KBUF, tmp));
3016 so_puts(in_store, "\015\012");
3019 free_list_array(&lines);
3021 so_puts(in_store, "\015\012\015\012");
3024 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
3025 so_seek(in_store, 0L, 0);
3027 init_handles(&handles);
3028 gf_filter_init();
3030 if(F_ON(F_VIEW_SEL_URL, ps_global)
3031 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3032 || F_ON(F_SCAN_ADDR, ps_global))
3033 gf_link_filter(gf_line_test,
3034 gf_line_test_opt(url_hilite,
3035 gf_url_hilite_opt(&uh,&handles,0)));
3037 gf_link_filter(gf_wrap,
3038 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3039 ps_global->ttyo->screen_cols,
3040 NULL, indent, GFW_HANDLES));
3041 gf_link_filter(gf_nvtnl_local, NULL);
3043 gf_set_so_readc(&gc, in_store);
3044 gf_set_so_writec(&pc, out_store);
3046 errstr = gf_pipe(gc, pc);
3048 gf_clear_so_readc(in_store);
3050 if(!errstr){
3051 #define VCARD_TEXT_ONE _("This is a vCard which has been forwarded to you. You may add parts of it to your address book with the Save command. You will have a chance to edit it before committing it to your address book.")
3052 #define VCARD_TEXT_MORE _("This is a vCard which has been forwarded to you. You may add the entries to your address book with the Save command.")
3053 errstr = format_editorial((begins > 1)
3054 ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
3055 ps_global->ttyo->screen_cols, 0, NULL, pc);
3058 gf_clear_so_writec(out_store);
3060 if(!errstr)
3061 cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
3062 CharStar, handles, a, flags | DA_RESIZE);
3064 free_handles(&handles);
3065 so_give(&out_store);
3067 else
3068 errstr = _("Error allocating space");
3070 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3072 if(errstr)
3073 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3074 _("Can't format entry : %s"), errstr);
3076 so_give(&in_store);
3079 void
3080 display_vevent_summary(long int msgno, ATTACH_S *a, int flags, int depth)
3082 BODY *b;
3083 VCALENDAR_S *vcal = NULL;
3084 char *b64text, *caltext;
3085 unsigned long callen;
3086 VEVENT_SUMMARY_S *vesy, *vsummary; /* vevent summary */
3087 STORE_S *in_store, *out_store = NULL;
3088 HANDLE_S *handles = NULL;
3089 URL_HILITE_S uh;
3090 gf_io_t gc, pc;
3091 char *errstr = NULL;
3092 int cmd, i, k;
3094 b = mail_body(ps_global->mail_stream, msgno, (unsigned char *) a->number);
3095 if(b->sparep == NULL){
3096 b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
3097 b64text[callen] = '\0'; /* chop off cookie */
3098 caltext = rfc822_base64((unsigned char *)b64text, strlen(b64text), &callen);
3099 vcal = ical_parse_text(caltext);
3100 b->sparep = create_body_sparep(iCalType, (void *) vcal);
3102 else if(get_body_sparep_type(b->sparep) == iCalType)
3103 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
3105 vsummary = ical_vevent_summary(vcal);
3107 if(vsummary == NULL){
3108 q_status_message(SM_ORDER | SM_DING, 3, 3,
3109 _("Error parsing event"));
3110 return;
3113 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
3114 q_status_message(SM_ORDER | SM_DING, 3, 3,
3115 _("Error allocating space to process Calendar"));
3116 return;
3119 gf_set_so_readc(&gc, in_store);
3121 for(vesy = vsummary, k = 0; vesy; vesy = vesy->next, k++){
3122 if(depth >= 0 && k != depth)
3123 continue;
3125 if(vesy->cancel){
3126 so_puts(in_store, _("This event was cancelled"));
3127 so_puts(in_store, "\015\012");
3130 if(vesy->priority){
3131 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%d %s",
3132 _("Priority: "), vesy->priority,
3133 vesy->priority == 5 ? _("(Normal)")
3134 : (vesy->priority < 5 ? _("(High)")
3135 : _("(Low)")));
3136 so_puts(in_store, tmp_20k_buf);
3137 so_puts(in_store, "\015\012");
3140 if(vesy->summary){
3141 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3142 _("Summary: "), vesy->summary);
3143 so_puts(in_store, tmp_20k_buf);
3144 so_puts(in_store, "\015\012");
3147 if(vesy->sender){
3148 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3149 _("Sender: "), vesy->sender);
3150 so_puts(in_store, tmp_20k_buf);
3151 so_puts(in_store, "\015\012");
3154 if(vesy->organizer){
3155 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3156 _("Organizer: "), vesy->organizer);
3157 so_puts(in_store, tmp_20k_buf);
3158 so_puts(in_store, "\015\012");
3161 if(vesy->location){
3162 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3163 _("Location: "), vesy->location);
3164 so_puts(in_store, tmp_20k_buf);
3165 so_puts(in_store, "\015\012");
3168 if(vesy->evstart){
3169 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3170 _("Start Date: "), vesy->evstart);
3171 so_puts(in_store, tmp_20k_buf);
3172 so_puts(in_store, "\015\012");
3175 if(vesy->duration){
3176 for(i = 0; vesy->duration[i] != NULL; i++){
3177 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3178 _("Duration: "), vesy->duration[i]);
3179 so_puts(in_store, tmp_20k_buf);
3180 so_puts(in_store, "\015\012");
3182 } else if(vesy->evend){
3183 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3184 _("End Date: "), vesy->evend);
3185 so_puts(in_store, tmp_20k_buf);
3186 so_puts(in_store, "\015\012");
3189 if(vesy->dtstamp){
3190 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3191 vcal->method ? _("Created on: ") : _("Last Revised on; "),
3192 vesy->dtstamp);
3193 so_puts(in_store, tmp_20k_buf);
3194 so_puts(in_store, "\015\012");
3197 if(vesy->description){
3198 char c;
3199 int j, empty;
3201 so_puts(in_store, "\015\012");
3203 for(i = 0; vesy->description[i] != NULL; i++){
3204 so_puts(in_store, _("Description: "));
3205 /* Check if empty description */
3206 empty = 1;
3207 for(j =0; empty == 1 && vesy->description[i][j] != '\0'; j++){
3208 c = vesy->description[i][j];
3209 if(c != '\n' && c != ' ' && c != '\t')
3210 empty = 0;
3212 if(empty){
3213 so_puts(in_store, _("[ No description provided ]"));
3214 so_puts(in_store, "\015\012");
3216 else {
3217 for(j =0; vesy->description[i][j] != '\0'; j++){
3218 c = vesy->description[i][j];
3219 if(c == '\n'){
3220 so_puts(in_store, "\015\012");
3221 continue;
3223 so_writec(c, in_store);
3226 so_puts(in_store, "\015\012");
3230 if(vesy->attendee){
3231 so_puts(in_store, "\015\012");
3232 so_puts(in_store, _("List of Attendees:"));
3233 so_puts(in_store, "\015\012");
3234 for(i = 0; vesy->attendee[i] != NULL; i++){
3235 so_puts(in_store, vesy->attendee[i]);
3236 so_puts(in_store, "\015\012");
3238 so_puts(in_store, "\015\012");
3241 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3242 _("This event was tagged as a %s entry by the sender"), vesy->class);
3243 so_puts(in_store, tmp_20k_buf);
3244 so_puts(in_store, "\015\012\015\012");
3246 if(depth < 0 && vesy->next){
3247 for(i = 0; i < ps_global->ttyo->screen_cols && i < 40; i++)
3248 tmp_20k_buf[i] = '-';
3249 tmp_20k_buf[i]= '\0';
3250 so_puts(in_store, tmp_20k_buf);
3251 so_puts(in_store, "\015\012");
3253 } /* end "for" loop */
3256 so_seek(in_store, 0L, 0);
3257 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
3258 q_status_message(SM_ORDER | SM_DING, 3, 3,
3259 _("Error allocating space to write Calendar"));
3260 return;
3263 gf_set_so_writec(&pc, out_store);
3265 init_handles(&handles);
3266 gf_filter_init();
3268 if(F_ON(F_VIEW_SEL_URL, ps_global)
3269 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3270 || F_ON(F_SCAN_ADDR, ps_global))
3271 gf_link_filter(gf_line_test,
3272 gf_line_test_opt(url_hilite,
3273 gf_url_hilite_opt(&uh,&handles,0)));
3275 gf_link_filter(gf_wrap,
3276 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3277 ps_global->ttyo->screen_cols,
3278 NULL, 0, GFW_HANDLES));
3279 gf_link_filter(gf_nvtnl_local, NULL);
3281 gf_set_so_readc(&gc, in_store);
3282 gf_set_so_writec(&pc, out_store);
3284 errstr = gf_pipe(gc, pc);
3286 gf_clear_so_readc(in_store);
3288 gf_clear_so_writec(out_store);
3290 if(!errstr)
3291 cmd = scroll_attachment(_("CALENDAR EVENT ATTACHMENT"), out_store,
3292 CharStar, handles, a, flags | DA_RESIZE);
3294 free_handles(&handles);
3295 so_give(&out_store);
3297 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3299 if(errstr)
3300 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3301 _("Can't format entry : %s"), errstr);
3302 so_give(&in_store);
3303 free_vevent_summary(&vsummary);
3304 ps_global->mangled_screen = 1;
3308 void
3309 display_vcalendar_att(long int msgno, ATTACH_S *a, int flags)
3311 display_vevent_summary(msgno, a, flags, -1);
3315 /*----------------------------------------------------------------------
3316 Display attachment information
3318 Args: msgno -- message number to get partrom
3319 a -- attachment struct for the desired part
3321 Result: a screen containing information about attachment:
3322 ----*/
3323 void
3324 display_attach_info(long int msgno, ATTACH_S *a)
3326 int i, indent, cols;
3327 char buf1[100], *folded;
3328 STORE_S *store;
3329 PARMLIST_S *plist;
3330 SCROLL_S sargs;
3332 (void) dispatch_attachment(a);
3334 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
3335 q_status_message(SM_ORDER | SM_DING, 3, 3,
3336 _("Error allocating space for message."));
3337 return;
3340 cols = ps_global->ttyo->screen_cols;
3343 * 2 spaces on left
3344 * 16 for text (longest is Display Method == 14)
3345 * 2 for ": "
3347 indent = 20;
3349 /* don't try stupid folding */
3350 cols = MAX(indent+10, cols);
3352 so_puts(store, "Details about Attachment #");
3353 so_puts(store, a->number);
3354 so_puts(store, " :\n\n");
3355 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
3356 so_puts(store, buf1);
3357 so_puts(store, body_type_names(a->body->type));
3358 so_puts(store, "\n");
3359 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
3360 so_puts(store, buf1);
3361 so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
3362 so_puts(store, "\n");
3363 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
3364 so_puts(store, buf1);
3365 so_puts(store, a->body->encoding < ENCMAX
3366 ? body_encodings[a->body->encoding]
3367 : "Unknown");
3368 so_puts(store, "\n");
3369 if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
3370 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
3371 so_puts(store, buf1);
3372 i = 0;
3373 while(rfc2231_list_params(plist)){
3374 if(i++)
3375 so_puts(store, repeat_char(indent, ' '));
3377 so_puts(store, plist->attrib);
3378 so_puts(store, " = ");
3379 so_puts(store, plist->value ? plist->value : "");
3380 so_puts(store, "\n");
3383 rfc2231_free_parmlist(&plist);
3386 if(a->body->description && a->body->description[0]){
3387 char buftmp[MAILTMPLEN];
3388 unsigned char *q;
3390 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
3392 snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
3393 buftmp[sizeof(buftmp)-1] = '\0';
3394 q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
3395 folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
3397 if(folded){
3398 so_puts(store, folded);
3399 fs_give((void **) &folded);
3403 /* BUG: no a->body->language support */
3405 if(a->body->disposition.type){
3406 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
3407 so_puts(store, buf1);
3408 so_puts(store, a->body->disposition.type);
3409 so_puts(store, "\n");
3410 if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
3411 while(rfc2231_list_params(plist)){
3412 so_puts(store, repeat_char(indent, ' '));
3413 so_puts(store, plist->attrib);
3414 so_puts(store, " = ");
3415 so_puts(store, plist->value ? plist->value : "");
3416 so_puts(store, "\n");
3419 rfc2231_free_parmlist(&plist);
3423 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
3424 so_puts(store, buf1);
3425 so_puts(store, comatose((a->body->encoding == ENCBASE64)
3426 ? ((a->body->size.bytes * 3)/4)
3427 : a->body->size.bytes));
3428 so_puts(store, " bytes\n");
3429 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
3430 so_puts(store, buf1);
3431 if(a->can_display == MCD_NONE) {
3432 so_puts(store, "Can't, ");
3433 so_puts(store, (a->body->encoding < ENCOTHER)
3434 ? "Unknown Attachment Format"
3435 : "Unknown Encoding");
3437 else if(!(a->can_display & MCD_EXTERNAL)){
3438 so_puts(store, "Alpine's Internal Pager");
3440 else{
3441 int nt, free_pretty_cmd;
3442 MCAP_CMD_S *mc_cmd;
3443 char *pretty_cmd;
3445 if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
3446 a->body, "<datafile>", &nt,
3447 a->can_display & MCD_EXT_PROMPT)) != NULL){
3448 so_puts(store, "\"");
3449 if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
3450 so_puts(store, pretty_cmd);
3451 so_puts(store, "\"");
3452 if(free_pretty_cmd && pretty_cmd)
3453 fs_give((void **)&pretty_cmd);
3454 if(mc_cmd->command)
3455 fs_give((void **)&mc_cmd->command);
3456 fs_give((void **)&mc_cmd);
3460 so_puts(store, "\n");
3462 memset(&sargs, 0, sizeof(SCROLL_S));
3463 sargs.text.text = so_text(store);
3464 sargs.text.src = CharStar;
3465 sargs.text.desc = "attachment info";
3466 sargs.bar.title = _("ABOUT ATTACHMENT");
3467 sargs.help.text = h_simple_text_view;
3468 sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
3470 sargs.use_indexline_color = 1;
3472 scrolltool(&sargs);
3474 so_give(&store); /* free resources associated with store */
3475 ps_global->mangled_screen = 1;
3479 /*----------------------------------------------------------------------
3481 ----*/
3482 void
3483 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3485 char *sig;
3486 void *msgtext;
3487 ENVELOPE *outgoing;
3488 BODY *body;
3490 if(MIME_MSG_A(a)){
3491 forward_msg_att(stream, msgno, a);
3493 else{
3494 ACTION_S *role = NULL;
3495 REDRAFT_POS_S *redraft_pos = NULL;
3496 long rflags = ROLE_FORWARD;
3497 PAT_STATE dummy;
3499 outgoing = mail_newenvelope();
3500 outgoing->subject = cpystr("Forwarded attachment...");
3502 if(nonempty_patterns(rflags, &dummy)){
3504 * There is no message, but a Current Folder Type might match.
3506 * This has been changed to check against the message
3507 * containing the attachment.
3509 role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3510 if(confirm_role(rflags, &role))
3511 role = combine_inherited_role(role);
3512 else{
3513 role = NULL;
3514 cmd_cancelled("Forward");
3515 mail_free_envelope(&outgoing);
3516 return;
3520 if(role)
3521 q_status_message1(SM_ORDER, 3, 4,
3522 _("Forwarding using role \"%s\""), role->nick);
3524 outgoing->message_id = generate_message_id(role);
3526 * as with all text bound for the composer, build it in
3527 * a storage object of the type it understands...
3529 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3530 int impl, template_len = 0;
3532 if(role && role->template){
3533 char *filtered;
3535 impl = 1;
3536 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3537 if(filtered){
3538 if(*filtered){
3539 so_puts((STORE_S *)msgtext, filtered);
3540 if(impl == 1)
3541 template_len = strlen(filtered);
3544 fs_give((void **)&filtered);
3547 else
3548 impl = 1;
3550 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3551 if(impl == 2)
3552 redraft_pos->offset += template_len;
3554 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3556 fs_give((void **)&sig);
3558 else
3559 so_puts((STORE_S *)msgtext, NEWLINE);
3561 /*---- New Body to start with ----*/
3562 body = mail_newbody();
3563 body->type = TYPEMULTIPART;
3565 /*---- The TEXT part/body ----*/
3566 body->nested.part = mail_newbody_part();
3567 body->nested.part->body.type = TYPETEXT;
3568 body->nested.part->body.contents.text.data = msgtext;
3570 /*---- The corresponding things we're attaching ----*/
3571 body->nested.part->next = mail_newbody_part();
3572 body->nested.part->next->body.id = generate_message_id(role);
3573 copy_body(&body->nested.part->next->body, a->body);
3575 if(fetch_contents(stream, msgno, a->number,
3576 &body->nested.part->next->body)){
3577 pine_send(outgoing, &body, "FORWARD MESSAGE",
3578 role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3580 ps_global->mangled_screen = 1;
3581 pine_free_body(&body);
3582 free_redraft_pos(&redraft_pos);
3584 else{
3585 mail_free_body(&body);
3586 so_give((STORE_S **) &msgtext);
3587 free_redraft_pos(&redraft_pos);
3588 q_status_message(SM_ORDER | SM_DING, 4, 5,
3589 _("Error fetching message contents. Can't forward message."));
3592 else
3593 q_status_message(SM_ORDER | SM_DING, 3, 4,
3594 _("Error allocating message text"));
3596 mail_free_envelope(&outgoing);
3597 free_action(&role);
3602 void
3603 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3605 char *p, *sig = NULL;
3606 int ret;
3607 void *msgtext;
3608 ENVELOPE *outgoing;
3609 BODY *body;
3610 ACTION_S *role = NULL;
3611 REPLY_S reply;
3612 REDRAFT_POS_S *redraft_pos = NULL;
3614 outgoing = mail_newenvelope();
3615 memset((void *)&reply, 0, sizeof(reply));
3617 if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
3619 * as with all text bound for the composer, build it in
3620 * a storage object of the type it understands...
3622 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3623 int impl, template_len = 0;
3624 long rflags = ROLE_FORWARD;
3625 PAT_STATE dummy;
3627 ret = 'n';
3628 if(ps_global->full_header == 2)
3629 ret = want_to(_("Forward message as an attachment"), 'n', 0,
3630 NO_HELP, WT_SEQ_SENSITIVE);
3631 /* Setup possible role */
3632 if(nonempty_patterns(rflags, &dummy)){
3633 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3634 if(confirm_role(rflags, &role))
3635 role = combine_inherited_role(role);
3636 else{ /* cancel reply */
3637 role = NULL;
3638 cmd_cancelled("Forward");
3639 mail_free_envelope(&outgoing);
3640 so_give((STORE_S **) &msgtext);
3641 return;
3645 if(role)
3646 q_status_message1(SM_ORDER, 3, 4,
3647 _("Forwarding using role \"%s\""), role->nick);
3649 outgoing->message_id = generate_message_id(role);
3651 if(role && role->template){
3652 char *filtered;
3654 impl = 1;
3655 filtered = detoken(role, a->body->nested.msg->env,
3656 0, 0, 0, &redraft_pos, &impl);
3657 if(filtered){
3658 if(*filtered){
3659 so_puts((STORE_S *)msgtext, filtered);
3660 if(impl == 1)
3661 template_len = strlen(filtered);
3664 fs_give((void **)&filtered);
3667 else
3668 impl = 1;
3670 if((sig = detoken(role, a->body->nested.msg->env,
3671 2, 0, 1, &redraft_pos, &impl)) != NULL){
3672 if(impl == 2)
3673 redraft_pos->offset += template_len;
3675 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3677 fs_give((void **)&sig);
3679 else
3680 so_puts((STORE_S *)msgtext, NEWLINE);
3682 if(ret == 'y'){
3683 /*---- New Body to start with ----*/
3684 body = mail_newbody();
3685 body->type = TYPEMULTIPART;
3687 /*---- The TEXT part/body ----*/
3688 body->nested.part = mail_newbody_part();
3689 body->nested.part->body.type = TYPETEXT;
3690 body->nested.part->body.contents.text.data = msgtext;
3692 if(!forward_mime_msg(stream, msgno,
3693 p = body_partno(stream, msgno, a->body),
3694 a->body->nested.msg->env,
3695 &body->nested.part->next, msgtext))
3696 mail_free_body(&body);
3698 else{
3699 reply.forw = 1;
3700 if(a->body->nested.msg->body){
3701 char *charset;
3703 charset
3704 = parameter_val(a->body->nested.msg->body->parameter,
3705 "charset");
3707 if(charset && strucmp(charset, "us-ascii") != 0){
3708 CONV_TABLE *ct;
3711 * There is a non-ascii charset,
3712 * is there conversion happening?
3714 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3715 || !ct->table){
3716 reply.orig_charset = charset;
3717 charset = NULL;
3721 if(charset)
3722 fs_give((void **) &charset);
3725 body = forward_body(stream, a->body->nested.msg->env,
3726 a->body->nested.msg->body, msgno,
3727 p = body_partno(stream, msgno, a->body),
3728 msgtext, FWD_NONE);
3731 fs_give((void **) &p);
3733 if(body){
3734 pine_send(outgoing, &body,
3735 "FORWARD MESSAGE",
3736 role, NULL,
3737 reply.forw ? &reply : NULL,
3738 redraft_pos, NULL, NULL, 0);
3740 ps_global->mangled_screen = 1;
3741 pine_free_body(&body);
3742 free_redraft_pos(&redraft_pos);
3743 free_action(&role);
3745 else{
3746 so_give((STORE_S **) &msgtext);
3747 q_status_message(SM_ORDER | SM_DING, 4, 5,
3748 _("Error fetching message contents. Can't forward message."));
3751 else
3752 q_status_message(SM_ORDER | SM_DING, 3, 4,
3753 _("Error allocating message text"));
3755 else
3756 q_status_message1(SM_ORDER,3,4,
3757 _("Error fetching message %s. Can't forward it."),
3758 long2string(msgno));
3760 if(reply.orig_charset)
3761 fs_give((void **)&reply.orig_charset);
3763 mail_free_envelope(&outgoing);
3767 void
3768 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3770 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
3771 ENVELOPE *outgoing;
3772 BODY *body;
3773 void *msgtext;
3774 char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3775 int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3776 int rolemsg = 0, copytomsg = 0;
3777 long rflags;
3778 PAT_STATE dummy;
3779 REDRAFT_POS_S *redraft_pos = NULL;
3780 ACTION_S *role = NULL;
3782 outgoing = mail_newenvelope();
3784 dprint((4,"\n - attachment reply \n"));
3786 saved_from = (ADDRESS *) NULL;
3787 saved_to = (ADDRESS *) NULL;
3788 saved_cc = (ADDRESS *) NULL;
3789 saved_resent = (ADDRESS *) NULL;
3790 outgoing->subject = NULL;
3792 prefix = reply_quote_str(a->body->nested.msg->env);
3794 * For consistency, the first question is always "include text?"
3796 if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0
3797 && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3798 && reply_harvest(ps_global, msgno, a->number,
3799 a->body->nested.msg->env, &saved_from,
3800 &saved_to, &saved_cc, &saved_resent, &flags)){
3801 outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3802 NULL, 0);
3803 clear_cursor_pos();
3804 reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3805 saved_from, saved_to, saved_cc, saved_resent,
3806 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3807 if(errmsg){
3808 if(*errmsg){
3809 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3810 display_message(NO_OP_COMMAND);
3813 fs_give((void **)&errmsg);
3816 if(sp_expunge_count(stream)) /* current msg was expunged */
3817 goto seeyalater;
3819 /* Setup possible role */
3820 rflags = ROLE_REPLY;
3821 if(nonempty_patterns(rflags, &dummy)){
3822 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3823 if(confirm_role(rflags, &role))
3824 role = combine_inherited_role(role);
3825 else{ /* cancel reply */
3826 role = NULL;
3827 cmd_cancelled("Reply");
3828 goto seeyalater;
3832 if(role){
3833 rolemsg++;
3835 /* override fcc gotten in reply_seed */
3836 if(role->fcc && fcc)
3837 fs_give((void **) &fcc);
3840 if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3841 ADDRESS *us_in_to_and_cc, *ap;
3843 us_in_to_and_cc = (ADDRESS *) NULL;
3844 if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3845 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3846 NULL, us_in_to_and_cc, NULL,
3847 a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3848 reply_append_addr(&us_in_to_and_cc, ap);
3850 if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3851 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3852 NULL, us_in_to_and_cc, NULL,
3853 a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3854 reply_append_addr(&us_in_to_and_cc, ap);
3857 * A list of all of our addresses that appear in the To
3858 * and cc fields is in us_in_to_and_cc.
3859 * If there is exactly one address in that list then
3860 * use it for the outgoing From.
3862 if(us_in_to_and_cc && !us_in_to_and_cc->next){
3863 PINEFIELD *custom, *pf;
3864 ADDRESS *a = NULL;
3865 char *addr = NULL;
3868 * Check to see if this address is different from what
3869 * we would have used anyway. If it is, notify the user
3870 * with a status message. This is pretty hokey because we're
3871 * mimicking how pine_send would set the From address and
3872 * there is no coordination between the two.
3875 /* in case user has a custom From value */
3876 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3878 pf = (PINEFIELD *) fs_get(sizeof(*pf));
3879 memset((void *) pf, 0, sizeof(*pf));
3880 pf->name = cpystr("From");
3881 pf->addr = &a;
3882 if(set_default_hdrval(pf, custom) >= UseAsDef
3883 && pf->textbuf && pf->textbuf[0]){
3884 removing_trailing_white_space(pf->textbuf);
3885 (void)removing_double_quotes(pf->textbuf);
3886 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3887 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3888 if(addr)
3889 fs_give((void **) &addr);
3892 if(!*pf->addr)
3893 *pf->addr = generate_from();
3895 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3896 copytomsg++;
3897 if(!role){
3898 role = (ACTION_S *) fs_get(sizeof(*role));
3899 memset((void *) role, 0, sizeof(*role));
3900 role->is_a_role = 1;
3903 role->from = us_in_to_and_cc;
3904 us_in_to_and_cc = NULL;
3907 free_customs(custom);
3908 free_customs(pf);
3911 if(us_in_to_and_cc)
3912 mail_free_address(&us_in_to_and_cc);
3916 if(role){
3917 if(rolemsg && copytomsg)
3918 q_status_message1(SM_ORDER, 3, 4,
3919 _("Replying using role \"%s\" and To as From"), role->nick);
3920 else if(rolemsg)
3921 q_status_message1(SM_ORDER, 3, 4,
3922 _("Replying using role \"%s\""), role->nick);
3923 else if(copytomsg)
3924 q_status_message(SM_ORDER, 3, 4,
3925 _("Replying using incoming To as outgoing From"));
3928 outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3929 outgoing->references = reply_build_refs(a->body->nested.msg->env);
3930 outgoing->message_id = generate_message_id(role);
3932 if(!outgoing->to && !outgoing->cc
3933 && !outgoing->bcc && !outgoing->newsgroups)
3934 q_status_message(SM_ORDER | SM_DING, 3, 6,
3935 _("Warning: no valid addresses to reply to!"));
3938 * Now fix up the body...
3940 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3941 REPLY_S reply;
3943 memset((void *)&reply, 0, sizeof(reply));
3944 reply.forw = 1;
3945 if(a->body->nested.msg->body){
3946 char *charset;
3948 charset
3949 = parameter_val(a->body->nested.msg->body->parameter,
3950 "charset");
3952 if(charset && strucmp(charset, "us-ascii") != 0){
3953 CONV_TABLE *ct;
3956 * There is a non-ascii charset,
3957 * is there conversion happening?
3959 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3960 || !ct->table){
3961 reply.orig_charset = charset;
3962 charset = NULL;
3966 if(charset)
3967 fs_give((void **) &charset);
3970 if((body = reply_body(stream, a->body->nested.msg->env,
3971 a->body->nested.msg->body, msgno,
3972 tp = body_partno(stream, msgno, a->body),
3973 msgtext, prefix, include_text, role,
3974 1, &redraft_pos)) != NULL){
3975 /* partially formatted outgoing message */
3976 pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3977 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3979 pine_free_body(&body);
3980 ps_global->mangled_screen = 1;
3982 else
3983 q_status_message(SM_ORDER | SM_DING, 3, 4,
3984 _("Error building message body"));
3986 fs_give((void **) &tp);
3987 if(reply.orig_charset)
3988 fs_give((void **)&reply.orig_charset);
3990 else
3991 q_status_message(SM_ORDER | SM_DING, 3, 4,
3992 _("Error allocating message text"));
3995 seeyalater:
3996 mail_free_envelope(&outgoing);
3997 mail_free_address(&saved_from);
3998 mail_free_address(&saved_to);
3999 mail_free_address(&saved_cc);
4000 mail_free_address(&saved_resent);
4002 if(prefix)
4003 fs_give((void **) &prefix);
4005 if(fcc)
4006 fs_give((void **) &fcc);
4008 free_redraft_pos(&redraft_pos);
4009 free_action(&role);
4013 void
4014 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
4016 char *errstr;
4018 if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
4019 q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
4023 void
4024 pipe_attachment(long int msgno, ATTACH_S *a)
4026 char *err, *resultfilename = NULL, prompt[80], *p;
4027 int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
4028 long ku;
4029 PIPE_S *syspipe;
4030 HelpType help;
4031 char pipe_command[MAXPATH+1];
4032 unsigned flagsforhist = 1; /* raw=2 /capture=1 */
4033 static HISTORY_S *history = NULL;
4034 ESCKEY_S pipe_opt[6];
4036 if(ps_global->restricted){
4037 q_status_message(SM_ORDER | SM_DING, 0, 4,
4038 "Alpine demo can't pipe attachments");
4039 return;
4042 help = NO_HELP;
4043 pipe_command[0] = '\0';
4045 init_hist(&history, HISTSIZE);
4046 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4047 if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
4048 strncpy(pipe_command, p, sizeof(pipe_command));
4049 pipe_command[sizeof(pipe_command)-1] = '\0';
4050 if(history->hist[history->curindex]){
4051 flagsforhist = history->hist[history->curindex]->flags;
4052 raw = (flagsforhist & 0x2) ? 1 : 0;
4053 capture = (flagsforhist & 0x1) ? 1 : 0;
4057 pipe_opt[j].ch = 0;
4058 pipe_opt[j].rval = 0;
4059 pipe_opt[j].name = "";
4060 pipe_opt[j++].label = "";
4062 pipe_opt[j].ch = ctrl('W');
4063 pipe_opt[j].rval = 10;
4064 pipe_opt[j].name = "^W";
4065 pipe_opt[j++].label = NULL;
4067 pipe_opt[j].ch = ctrl('Y');
4068 pipe_opt[j].rval = 11;
4069 pipe_opt[j].name = "^Y";
4070 pipe_opt[j++].label = NULL;
4072 pipe_opt[j].ch = KEY_UP;
4073 pipe_opt[j].rval = 30;
4074 pipe_opt[j].name = "";
4075 ku = j;
4076 pipe_opt[j++].label = "";
4078 pipe_opt[j].ch = KEY_DOWN;
4079 pipe_opt[j].rval = 31;
4080 pipe_opt[j].name = "";
4081 pipe_opt[j++].label = "";
4083 pipe_opt[j].ch = -1;
4085 while(1){
4086 int flags;
4088 snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
4089 a->number, capture ? "" : "(Free Output) ");
4090 prompt[sizeof(prompt)-1] = '\0';
4091 pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
4092 pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
4095 * 2 is really 1 because there will be one real entry and
4096 * one entry of "" because of the get_prev_hist above.
4098 if(items_in_hist(history) > 2){
4099 pipe_opt[ku].name = HISTORY_UP_KEYNAME;
4100 pipe_opt[ku].label = HISTORY_KEYLABEL;
4101 pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
4102 pipe_opt[ku+1].label = HISTORY_KEYLABEL;
4104 else{
4105 pipe_opt[ku].name = "";
4106 pipe_opt[ku].label = "";
4107 pipe_opt[ku+1].name = "";
4108 pipe_opt[ku+1].label = "";
4111 flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
4112 rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
4113 sizeof(pipe_command), prompt,
4114 pipe_opt, help, &flags);
4115 if(rc == -1){
4116 q_status_message(SM_ORDER | SM_DING, 3, 4,
4117 "Internal problem encountered");
4118 break;
4120 else if(rc == 10){
4121 raw = !raw; /* flip raw text */
4123 else if(rc == 11){
4124 capture = !capture; /* flip capture output */
4126 else if(rc == 30){
4127 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4128 if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
4129 strncpy(pipe_command, p, sizeof(pipe_command));
4130 pipe_command[sizeof(pipe_command)-1] = '\0';
4131 if(history->hist[history->curindex]){
4132 flagsforhist = history->hist[history->curindex]->flags;
4133 raw = (flagsforhist & 0x2) ? 1 : 0;
4134 capture = (flagsforhist & 0x1) ? 1 : 0;
4137 else
4138 Writechar(BELL, 0);
4140 else if(rc == 31){
4141 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4142 if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
4143 strncpy(pipe_command, p, sizeof(pipe_command));
4144 pipe_command[sizeof(pipe_command)-1] = '\0';
4145 if(history->hist[history->curindex]){
4146 flagsforhist = history->hist[history->curindex]->flags;
4147 raw = (flagsforhist & 0x2) ? 1 : 0;
4148 capture = (flagsforhist & 0x1) ? 1 : 0;
4151 else
4152 Writechar(BELL, 0);
4154 else if(rc == 0){
4155 if(pipe_command[0] == '\0'){
4156 cmd_cancelled("Pipe command");
4157 break;
4160 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4161 save_hist(history, pipe_command, flagsforhist, NULL);
4163 flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
4164 flags |= (raw ? PIPE_RAW : 0);
4165 if(!capture){
4166 #ifndef _WINDOWS
4167 ClearScreen();
4168 fflush(stdout);
4169 clear_cursor_pos();
4170 ps_global->mangled_screen = 1;
4171 #endif
4172 flags |= PIPE_RESET;
4175 if((syspipe = open_system_pipe(pipe_command,
4176 (flags&PIPE_RESET) ? NULL : &resultfilename,
4177 NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
4178 int is_text = 0;
4179 gf_io_t pc; /* wire up a generic putchar */
4181 is_text = (a && a->body && a->body->type == TYPETEXT);
4183 gf_set_writec(&pc, syspipe, 0L, PipeStar,
4184 (is_text && !raw) ? WRITE_TO_LOCALE : 0);
4186 /*------ Write the image to a temporary file ------*/
4187 if(raw){ /* pipe raw text */
4188 FETCH_READC_S fetch_part;
4190 err = NULL;
4192 if(capture)
4193 we_cancel = busy_cue(NULL, NULL, 1);
4194 else
4195 suspend_busy_cue();
4197 gf_filter_init();
4198 fetch_readc_init(&fetch_part, ps_global->mail_stream,
4199 msgno, a->number, a->body->size.bytes, 0, 0);
4200 gf_link_filter(gf_nvtnl_local, NULL);
4201 err = gf_pipe(FETCH_READC, pc);
4203 if(capture){
4204 if(we_cancel)
4205 cancel_busy_cue(0);
4207 else
4208 resume_busy_cue(0);
4210 else{
4211 /* BUG: there's got to be a better way */
4212 if(!capture)
4213 ps_global->print = (PRINT_S *) 1;
4215 suspend_busy_cue();
4216 err = detach(ps_global->mail_stream, msgno,
4217 a->number, 0L, (long *)NULL, pc, NULL, 0);
4218 ps_global->print = (PRINT_S *) NULL;
4219 resume_busy_cue(0);
4222 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
4224 if(err)
4225 q_status_message1(SM_ORDER | SM_DING, 3, 4,
4226 _("Error detaching for pipe: %s"), err);
4228 display_output_file(resultfilename,
4229 (err)
4230 ? _("PIPE ATTACHMENT (ERROR)")
4231 : _("PIPE ATTACHMENT"),
4232 NULL, DOF_EMPTY);
4234 fs_give((void **) &resultfilename);
4236 else
4237 q_status_message(SM_ORDER | SM_DING, 3, 4,
4238 _("Error opening pipe"));
4240 break;
4242 else if(rc == 1){
4243 cmd_cancelled("Pipe");
4244 break;
4246 else if(rc == 3)
4247 help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
4253 delete_attachment(long int msgno, ATTACH_S *a)
4255 int expbits, rv = 0;
4257 if(!msgno_exceptions(ps_global->mail_stream, msgno,
4258 a->number, &expbits, FALSE)
4259 || !(expbits & MSG_EX_DELETE)){
4260 expbits |= MSG_EX_DELETE;
4261 msgno_exceptions(ps_global->mail_stream, msgno,
4262 a->number, &expbits, TRUE);
4264 q_status_message1(SM_ORDER, 0, 3,
4265 _("Part %s will be omitted only if message is Saved"),
4266 a->number);
4267 rv = 1;
4269 else
4270 q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
4271 a->number);
4273 return(rv);
4278 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
4280 int rv = 0;
4282 if(msgno_exceptions(ps_global->mail_stream, msgno,
4283 a->number, expbitsp, FALSE)
4284 && ((*expbitsp) & MSG_EX_DELETE)){
4285 (*expbitsp) ^= MSG_EX_DELETE;
4286 msgno_exceptions(ps_global->mail_stream, msgno,
4287 a->number, expbitsp, TRUE);
4288 rv = 1;
4290 else
4291 q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
4292 a->number);
4294 return(rv);
4298 /*----------------------------------------------------------------------
4299 Resolve any deferred tests for attachment displayability
4301 Args: attachment structure
4303 Returns: undefer's attachment's displayability test
4304 ----*/
4306 dispatch_attachment(ATTACH_S *a)
4308 if(a->test_deferred){
4309 a->test_deferred = 0;
4310 a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
4313 return(a->can_display);
4317 #ifdef _WINDOWS
4319 scroll_att_popup(sparms, in_handle)
4320 SCROLL_S *sparms;
4321 int in_handle;
4323 MPopup scrat_popup[20];
4324 int i = -1, n;
4326 if(in_handle){
4327 scrat_popup[++i].type = tIndex;
4328 scrat_popup[i].label.style = lNormal;
4329 scrat_popup[i].label.string = "View Selectable Item";
4330 scrat_popup[i].data.val = ctrl('L');
4333 scrat_popup[++i].type = tQueue;
4334 scrat_popup[i].label.style = lNormal;
4335 scrat_popup[i].label.string = "&Save";
4336 scrat_popup[i].data.val = 'S';
4338 scrat_popup[++i].type = tQueue;
4339 scrat_popup[i].label.style = lNormal;
4340 if(msgno_exceptions(ps_global->mail_stream,
4341 mn_m2raw(ps_global->msgmap,
4342 mn_get_cur(ps_global->msgmap)),
4343 scrat_attachp->number, &n, FALSE)
4344 && (n & MSG_EX_DELETE)){
4345 scrat_popup[i].label.string = "&Undelete";
4346 scrat_popup[i].data.val = 'U';
4348 else{
4349 scrat_popup[i].label.string = "&Delete";
4350 scrat_popup[i].data.val = 'D';
4353 if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
4354 scrat_popup[++i].type = tQueue;
4355 scrat_popup[i].label.style = lNormal;
4356 scrat_popup[i].label.string = "&Reply";
4357 scrat_popup[i].data.val = 'R';
4359 scrat_popup[++i].type = tQueue;
4360 scrat_popup[i].label.style = lNormal;
4361 scrat_popup[i].label.string = "&Forward";
4362 scrat_popup[i].data.val = 'f';
4365 scrat_popup[++i].type = tSeparator;
4367 scrat_popup[++i].type = tQueue;
4368 scrat_popup[i].label.style = lNormal;
4369 scrat_popup[i].label.string = "Attachment Index";
4370 scrat_popup[i].data.val = '<';
4372 scrat_popup[++i].type = tTail;
4374 return(mswin_popup(scrat_popup) == 0 && in_handle);
4378 void
4379 display_att_window(a)
4380 ATTACH_S *a;
4382 #if !defined(DOS) && !defined(OS2)
4383 char prefix[8];
4384 #endif
4386 if(a->body->type == TYPEMULTIPART){
4387 if(a->body->subtype){
4388 /* if(!strucmp(a->body->subtype, "digest"))
4389 display_digest_att(msgno, a, flags);
4390 else */
4391 q_status_message1(SM_ORDER, 3, 5,
4392 "Can't display Multipart/%s",
4393 a->body->subtype);
4395 else
4396 q_status_message(SM_ORDER, 3, 5,
4397 "Can't display unknown Multipart Subtype");
4399 /* else if(MIME_VCARD_A(a))
4400 display_vcard_att_window(msgno, a, flags);*/
4401 else if(a->body->type == TYPETEXT)
4402 display_text_att_window(a);
4403 else if(a->body->type == TYPEMESSAGE)
4404 display_msg_att_window(a);
4408 void
4409 display_text_att_window(a)
4410 ATTACH_S *a;
4412 STORE_S *store;
4413 long msgno;
4415 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4417 if(store = format_text_att(msgno, a, NULL)){
4418 if (mswin_displaytext("ATTACHED TEXT",
4419 so_text(store),
4420 strlen((char *) so_text(store)),
4421 NULL, NULL, 0) >= 0)
4422 store->txt = (void *) NULL; /* free'd in mswin_displaytext */
4424 so_give(&store); /* free resources associated with store */
4426 else
4427 q_status_message(SM_ORDER | SM_DING, 3, 3,
4428 "Error allocating space for attachment.");
4432 void
4433 display_msg_att_window(a)
4434 ATTACH_S *a;
4436 STORE_S *store;
4437 gf_io_t pc;
4438 ATTACH_S *ap = a;
4439 long msgno;
4441 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4443 /* BUG, should check this return code */
4444 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
4446 /* initialize a storage object */
4447 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
4449 gf_set_so_writec(&pc, store);
4451 if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
4452 && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
4453 strlen((char *) so_text(store)),
4454 NULL, NULL, 0) >= 0)
4455 /* free'd in mswin_displaytext */
4456 store->txt = (void *) NULL;
4458 gf_clear_so_writec(store);
4460 so_give(&store);
4462 else
4463 q_status_message(SM_ORDER | SM_DING, 3, 3,
4464 "Error allocating space for message.");
4466 #endif