* Add new hidden feature "Enable Delete Before Writing" that makes Alpine
[alpine.git] / alpine / mailpart.c
blob3261c63a887dc61334c12039146b7303ca14d827
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailpart.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2021 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 mailpart.c
21 The meat and pototoes of attachment processing here.
23 ====*/
25 #include "headers.h"
26 #include "mailpart.h"
27 #include "status.h"
28 #include "context.h"
29 #include "keymenu.h"
30 #include "alpine.h"
31 #include "reply.h"
32 #include "radio.h"
33 #include "takeaddr.h"
34 #include "mailview.h"
35 #include "mailindx.h"
36 #include "mailcmd.h"
37 #include "help.h"
38 #include "titlebar.h"
39 #include "signal.h"
40 #include "send.h"
41 #include "busy.h"
42 #include "smime.h"
43 #include "../pith/state.h"
44 #include "../pith/conf.h"
45 #include "../pith/store.h"
46 #include "../pith/msgno.h"
47 #include "../pith/detach.h"
48 #include "../pith/handle.h"
49 #include "../pith/filter.h"
50 #include "../pith/bitmap.h"
51 #include "../pith/charset.h"
52 #include "../pith/mimedesc.h"
53 #include "../pith/mailcap.h"
54 #include "../pith/newmail.h"
55 #include "../pith/rfc2231.h"
56 #include "../pith/flag.h"
57 #include "../pith/text.h"
58 #include "../pith/editorial.h"
59 #include "../pith/save.h"
60 #include "../pith/pipe.h"
61 #include "../pith/util.h"
62 #include "../pith/detoken.h"
63 #include "../pith/busy.h"
64 #include "../pith/mimetype.h"
65 #include "../pith/icache.h"
66 #include "../pith/list.h"
67 #include "../pith/ablookup.h"
68 #include "../pith/options.h"
69 #include "../pith/smime.h"
70 #include "../pith/ical.h"
71 #include "../pith/body.h"
72 #include "../pith/init.h"
75 * Information used to paint and maintain a line on the attachment
76 * screen.
78 typedef struct atdisp_line {
79 char *dstring; /* alloc'd var value string */
80 ATTACH_S *attp; /* actual attachment pointer */
81 struct atdisp_line *next, *prev;
82 } ATDISP_S;
86 * struct defining attachment screen's current state
88 typedef struct att_screen {
89 ATDISP_S *current,
90 *top_line;
91 COLOR_PAIR *titlecolor;
92 } ATT_SCREEN_S;
94 static ATT_SCREEN_S *att_screen;
97 #define next_attline(p) ((p) ? (p)->next : NULL)
98 #define prev_attline(p) ((p) ? (p)->prev : NULL)
101 #ifdef _WINDOWS
103 * local global pointer to scroll attachment popup menu
105 static ATTACH_S *scrat_attachp;
106 #endif
109 #define FETCH_READC g_fr_desc->readc
112 /* used to keep track of detached MIME segments total length */
113 static long save_att_length;
116 * Internal Prototypes
118 ATDISP_S *new_attline(ATDISP_S **);
119 void free_attline(ATDISP_S **);
120 int attachment_screen_updater(struct pine *, ATDISP_S *, ATT_SCREEN_S *);
121 void attachment_screen_redrawer(void);
122 void update_att_screen_titlebar(void);
123 ATDISP_S *first_attline(ATDISP_S *);
124 int init_att_progress(char *, MAILSTREAM *, BODY *);
125 long save_att_piped(int);
126 int save_att_percent(void);
127 void save_attachment(int, long, ATTACH_S *);
128 void export_attachment(int, long, ATTACH_S *);
129 char *write_attached_msg(long, ATTACH_S **, STORE_S *, int);
130 void save_msg_att(long, ATTACH_S *);
131 void save_digest_att(long, ATTACH_S *);
132 int save_msg_att_work(long int, ATTACH_S *, MAILSTREAM *, char *,
133 CONTEXT_S *, char *);
134 void export_msg_att(long, ATTACH_S *);
135 void export_digest_att(long, ATTACH_S *);
136 void print_attachment(int, long, ATTACH_S *);
137 int print_msg_att(long, ATTACH_S *, int);
138 void print_digest_att(long, ATTACH_S *);
139 void run_viewer(char *, BODY *, int);
140 STORE_S *format_text_att(long, ATTACH_S *, HANDLE_S **);
141 int display_text_att(long, ATTACH_S *, int);
142 int display_msg_att(long, ATTACH_S *, int);
143 void display_digest_att(long, ATTACH_S *, int);
144 int scroll_attachment(char *, STORE_S *, SourceType, HANDLE_S *, ATTACH_S *, int);
145 int process_attachment_cmd(int, MSGNO_S *, SCROLL_S *);
146 int format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
147 void display_vcard_att(long, ATTACH_S *, int);
148 void display_vcalendar_att(long, ATTACH_S *, int);
149 void display_attach_info(long, ATTACH_S *);
150 int display_html_external_attachment(long int, ATTACH_S *, int);
151 void forward_attachment(MAILSTREAM *, long, ATTACH_S *);
152 void forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
153 void reply_msg_att(MAILSTREAM *, long, ATTACH_S *);
154 void bounce_msg_att(MAILSTREAM *, long, char *, char *);
155 void pipe_attachment(long, ATTACH_S *);
156 int delete_attachment(long, ATTACH_S *);
157 int undelete_attachment(long, ATTACH_S *, int *);
158 #ifdef _WINDOWS
159 int scroll_att_popup(SCROLL_S *, int);
160 void display_text_att_window(ATTACH_S *);
161 void display_msg_att_window(ATTACH_S *);
162 #endif
165 /*----------------------------------------------------------------------
166 Provide attachments in browser for selected action
168 Args: ps -- pointer to pine structure
169 msgmap -- struct containing current message to display
171 Result:
173 Handle painting and navigation of attachment index. It would be nice
174 to someday handle message/rfc822 segments in a neat way (i.e., enable
175 forwarding, take address, etc.).
176 ----*/
177 void
178 attachment_screen(struct pine *ps)
180 UCS ch = 'x';
181 int n, cmd, dline,
182 maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
183 last_type = TYPEOTHER;
184 long msgno;
185 char *q, *last_subtype = NULL, backtag[64], *utf8str;
186 OtherMenu what;
187 ATTACH_S *a;
188 ATDISP_S *current = NULL, *ctmp = NULL;
189 ATT_SCREEN_S screen;
191 ps->prev_screen = attachment_screen;
192 ps->next_screen = SCREEN_FUN_NULL;
194 ps->some_quoting_was_suppressed = 0;
196 if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
197 q_status_message(SM_ORDER | SM_DING, 0, 3,
198 _("Screen too small to view attachment"));
199 ps->next_screen = mail_view_screen;
200 return;
203 if(mn_total_cur(ps->msgmap) > 1L){
204 q_status_message(SM_ORDER | SM_DING, 0, 3,
205 _("Can only view one message's attachments at a time."));
206 return;
208 else if(ps->atmts && ps->atmts->description && !(ps->atmts + 1)->description)
209 q_status_message1(SM_ASYNC, 0, 3,
210 _("Message %s has only one part (the message body), and no attachments."),
211 long2string(mn_get_cur(ps->msgmap)));
213 /*----- Figure max display widths -----*/
214 for(a = ps->atmts; a && a->description != NULL; a++){
215 if((n = utf8_width(a->number)) > maxnumwid)
216 maxnumwid = n;
218 if((n = utf8_width(a->size)) > maxsizewid)
219 maxsizewid = n;
223 * Then, allocate and initialize attachment line list...
225 for(a = ps->atmts; a && a->description; a++)
226 new_attline(&current)->attp = a;
228 memset(&screen, 0, sizeof(screen));
229 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
230 ps->mangled_screen = 1; /* build display */
231 ps->redrawer = attachment_screen_redrawer;
232 att_screen = &screen;
233 current = first_attline(current);
234 what = FirstMenu;
237 * Advance to next attachment if it's likely that we've already
238 * shown it to the user...
241 if (current == NULL){
242 q_status_message1(SM_ORDER | SM_DING, 0, 3,
243 _("Malformed message: %s"),
244 ps->c_client_error ? ps->c_client_error : "?");
245 ps->next_screen = mail_view_screen;
247 else if(current->attp->shown && (ctmp = next_attline(current)))
248 current = ctmp;
250 while(ps->next_screen == SCREEN_FUN_NULL){
251 ps->user_says_cancel = 0;
252 if(km_popped){
253 km_popped--;
254 if(km_popped == 0){
255 clearfooter(ps);
256 ps->mangled_body = 1;
260 if(ps->mangled_screen){
262 * build/rebuild display lines
264 if(old_cols != ps->ttyo->screen_cols){
265 int avail, s1, s2, s4, s5, dwid, sizewid, descwid;
267 avail = ps_global->ttyo->screen_cols;
269 s1 = MAX(MIN(1, avail), 0);
270 avail -= s1;
272 dwid = MAX(MIN(1, avail), 0);
273 avail -= dwid;
275 s2 = MAX(MIN(1, avail), 0);
276 avail -= s2;
278 /* Only give up a third of the display to part numbers */
279 maxnumwid = MIN(maxnumwid, (ps_global->ttyo->screen_cols/3));
280 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
281 avail -= maxnumwid;
283 s4 = MAX(MIN(3, avail), 0);
284 avail -= s4;
286 sizewid = MAX(MIN(maxsizewid, avail), 0);
287 avail -= sizewid;
289 s5 = MAX(MIN(3, avail), 0);
290 avail -= s5;
292 descwid = MAX(0, avail);
294 old_cols = ps->ttyo->screen_cols;
296 for(ctmp = first_attline(current);
297 ctmp && (a = ctmp->attp) && a->description;
298 ctmp = next_attline(ctmp)){
299 size_t len;
300 char numbuf[50];
301 char description[1000];
303 if(ctmp->dstring)
304 fs_give((void **)&ctmp->dstring);
306 len = 6 * MAX(80, ps->ttyo->screen_cols) * sizeof(char);
307 ctmp->dstring = (char *)fs_get(len + 1);
310 * description
312 q = a->body->description;
313 if(!(q && q[0]) && (MIME_MSG_A(a) && a->body->nested.msg->env))
314 q = a->body->nested.msg->env->subject;
316 if(q && q[0]){
317 char buftmp[1000];
319 strncpy(buftmp, q, sizeof(buftmp));
320 buftmp[sizeof(buftmp)-1] = '\0';
322 q = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
323 SIZEOF_20KBUF, buftmp);
326 if(q && !q[0])
327 q = NULL;
329 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 ? "\"" : "");
330 description[sizeof(description)-1] = '\0';
332 utf8_snprintf(ctmp->dstring, len+1,
333 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%-*.*w",
334 s1, s1, "",
335 dwid, dwid,
336 msgno_part_deleted(ps->mail_stream, msgno, a->number) ? "D" : "",
337 s2, s2, "",
338 maxnumwid, maxnumwid,
339 a->number
340 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
341 : "",
342 s4, s4, "",
343 sizewid, sizewid,
344 a->size ? a->size : "",
345 s5, s5, "",
346 descwid, descwid, description);
348 ctmp->dstring[len] = '\0';
352 ps->mangled_header = 1;
353 ps->mangled_footer = 1;
354 ps->mangled_body = 1;
357 /*----------- Check for new mail -----------*/
358 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
359 ps->mangled_header = 1;
362 * If an expunge of the current message happened during the
363 * new mail check we want to bail out of here. See mm_expunged.
365 if(ps->next_screen != SCREEN_FUN_NULL)
366 break;
368 if(ps->mangled_header){
369 update_att_screen_titlebar();
370 ps->mangled_header = 0;
373 if(ps->mangled_screen){
374 ClearLine(1);
375 ps->mangled_screen = 0;
378 dline = attachment_screen_updater(ps, current, &screen);
380 /*---- This displays new mail notification, or errors ---*/
381 if(km_popped){
382 FOOTER_ROWS(ps) = 3;
383 mark_status_unknown();
386 display_message(ch);
387 if(km_popped){
388 FOOTER_ROWS(ps) = 1;
389 mark_status_unknown();
392 if(ps->mangled_footer
393 || current->attp->body->type != last_type
394 || !(last_subtype && current->attp->body->subtype)
395 || strucmp(last_subtype, current->attp->body->subtype)){
397 struct key_menu *km = &att_index_keymenu;
398 bitmap_t bitmap;
400 setbitmap(bitmap);
401 ps->mangled_footer = 0;
402 last_type = current->attp->body->type;
403 last_subtype = current->attp->body->subtype;
405 snprintf(backtag, sizeof(backtag), "Msg #%ld", mn_get_cur(ps->msgmap));
406 backtag[sizeof(backtag)-1] = '\0';
407 km->keys[ATT_PARENT_KEY].label = backtag;
409 if(F_OFF(F_ENABLE_PIPE, ps))
410 clrbitn(ATT_PIPE_KEY, bitmap);
413 * If message or digest, leave Reply and Save and,
414 * conditionally, Bounce on...
416 if(MIME_MSG(last_type, last_subtype)){
417 if(F_OFF(F_ENABLE_BOUNCE, ps))
418 clrbitn(ATT_BOUNCE_KEY, bitmap);
420 km->keys[ATT_EXPORT_KEY].name = "";
421 km->keys[ATT_EXPORT_KEY].label = "";
423 else if(MIME_DGST(last_type, last_subtype)){
424 clrbitn(ATT_BOUNCE_KEY, bitmap);
425 clrbitn(ATT_REPLY_KEY, bitmap);
427 km->keys[ATT_EXPORT_KEY].name = "";
428 km->keys[ATT_EXPORT_KEY].label = "";
430 else{
431 clrbitn(ATT_BOUNCE_KEY, bitmap);
432 clrbitn(ATT_REPLY_KEY, bitmap);
434 if(last_type != TYPETEXT)
435 clrbitn(ATT_PRINT_KEY, bitmap);
437 km->keys[ATT_EXPORT_KEY].name = "E";
438 km->keys[ATT_EXPORT_KEY].label = N_("Export");
441 if(km_popped){
442 FOOTER_ROWS(ps) = 3;
443 clearfooter(ps);
446 if(F_ON(F_ARROW_NAV, ps)){
447 menu_add_binding(km, KEY_LEFT, MC_EXIT);
448 menu_add_binding(km, KEY_RIGHT, MC_VIEW_ATCH);
450 else{
451 menu_clear_binding(km, KEY_LEFT);
452 menu_clear_binding(km, KEY_RIGHT);
455 draw_keymenu(km, bitmap, ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps),
456 0, what);
457 what = SameMenu;
458 if(km_popped){
459 FOOTER_ROWS(ps) = 1;
460 mark_keymenu_dirty();
464 if(F_ON(F_SHOW_CURSOR, ps))
465 MoveCursor(dline, 0);
466 else
467 MoveCursor(MAX(0, ps->ttyo->screen_rows - FOOTER_ROWS(ps)), 0);
469 /*------ Prepare to read the command from the keyboard ----*/
470 #ifdef MOUSE
471 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
472 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
473 ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
474 ps_global->ttyo->screen_cols);
475 #endif
476 ch = READ_COMMAND(&utf8str);
477 #ifdef MOUSE
478 clear_mfunc(mouse_in_content);
479 #endif
481 cmd = menu_command(ch, &att_index_keymenu);
483 if(km_popped)
484 switch(cmd){
485 case MC_NONE :
486 case MC_OTHER :
487 case MC_RESIZE :
488 case MC_REPAINT :
489 km_popped++;
490 break;
492 default:
493 clearfooter(ps);
494 break;
497 switch(cmd){
498 case MC_HELP :
499 if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
500 km_popped = 2;
501 ps->mangled_footer = 1;
502 break;
505 helper(h_attachment_screen, _("HELP FOR ATTACHMENT INDEX"), 0);
506 ps->mangled_screen = 1;
507 break;
509 case MC_OTHER :
510 what = NextMenu;
511 ps->mangled_footer = 1;
512 break;
514 case MC_FULLHDR :
515 ps->full_header++;
516 if(ps->full_header == 1){
517 if(!(ps->quote_suppression_threshold
518 && (ps->some_quoting_was_suppressed /* || in_index != View */)))
519 ps->full_header++;
521 else if(ps->full_header > 2)
522 ps->full_header = 0;
524 switch(ps->full_header){
525 case 0:
526 q_status_message(SM_ORDER, 0, 3,
527 _("Display of full headers is now off."));
528 break;
530 case 1:
531 q_status_message1(SM_ORDER, 0, 3,
532 _("Quotes displayed, use %s to see full headers"),
533 F_ON(F_USE_FK, ps) ? "F9" : "H");
534 break;
536 case 2:
537 q_status_message(SM_ORDER, 0, 3,
538 _("Display of full headers is now on."));
539 break;
543 break;
545 case MC_EXTERNAL:
546 display_html_external_attachment(msgno, current->attp,
547 DA_EXTERNAL | DA_SAVE | (F_OFF(F_EXTERNAL_INLINE_IMAGES, ps_global) ? DA_ALLIMAGES : 0));
548 break;
550 case MC_EXIT : /* exit attachment screen */
551 ps->next_screen = mail_view_screen;
552 break;
554 case MC_QUIT :
555 ps->next_screen = quit_screen;
556 break;
558 case MC_MAIN :
559 ps->next_screen = main_menu_screen;
560 break;
562 case MC_INDEX :
563 ps->next_screen = mail_index_screen;
564 break;
566 case MC_NEXTITEM :
567 if((ctmp = next_attline(current)) != NULL)
568 current = ctmp;
569 else
570 q_status_message(SM_ORDER, 0, 1, _("Already on last attachment"));
572 break;
574 case MC_PREVITEM :
575 if((ctmp = prev_attline(current)) != NULL)
576 current = ctmp;
577 else
578 q_status_message(SM_ORDER, 0, 1, _("Already on first attachment"));
580 break;
582 case MC_PAGEDN : /* page forward */
583 if(next_attline(current)){
584 while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
585 if((ctmp = next_attline(current)) != NULL)
586 current = ctmp;
587 else
588 break;
590 else
591 q_status_message(SM_ORDER, 0, 1,
592 _("Already on last page of attachments"));
594 break;
596 case MC_PAGEUP : /* page backward */
597 if(prev_attline(current)){
598 while(dline-- > HEADER_ROWS(ps))
599 if((ctmp = prev_attline(current)) != NULL)
600 current = ctmp;
601 else
602 break;
604 while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
605 if((ctmp = prev_attline(current)) != NULL)
606 current = ctmp;
607 else
608 break;
610 else
611 q_status_message(SM_ORDER, 0, 1,
612 _("Already on first page of attachments"));
614 break;
616 #ifdef MOUSE
617 case MC_MOUSE:
619 MOUSEPRESS mp;
621 mouse_get_last (NULL, &mp);
622 mp.row -= HEADER_ROWS(ps);
623 ctmp = screen.top_line;
624 while (mp.row && ctmp != NULL) {
625 --mp.row;
626 ctmp = ctmp->next;
629 if (ctmp != NULL){
630 current = ctmp;
632 if (mp.doubleclick){
633 if(mp.button == M_BUTTON_LEFT)
634 display_attachment(msgno, current->attp, DA_SAVE);
636 #ifdef _WINDOWS
637 else if(mp.button == M_BUTTON_RIGHT){
638 MPopup atmt_popup[20];
639 int i = -1, exoffer = 0;
641 dline = attachment_screen_updater(ps,current,&screen);
643 if(dispatch_attachment(current->attp) != MCD_NONE){
644 atmt_popup[++i].type = tQueue;
645 atmt_popup[i].data.val = 'V';
646 atmt_popup[i].label.style = lNormal;
647 atmt_popup[i].label.string = "&View";
649 if(!(current->attp->can_display & MCD_EXTERNAL)
650 && (current->attp->body->type == TYPETEXT
651 || MIME_MSG_A(current->attp)
652 || MIME_DGST_A(current->attp))){
653 exoffer++;
654 atmt_popup[++i].type = tIndex;
655 atmt_popup[i].label.style = lNormal;
656 atmt_popup[i].label.string =
657 "View in New Window";
661 atmt_popup[++i].type = tQueue;
662 atmt_popup[i].data.val = 'S';
663 atmt_popup[i].label.style = lNormal;
664 atmt_popup[i].label.string = "&Save";
666 atmt_popup[++i].type = tQueue;
667 atmt_popup[i].label.style = lNormal;
668 if(current->dstring[1] == 'D'){
669 atmt_popup[i].data.val = 'U';
670 atmt_popup[i].label.string = "&Undelete";
672 else{
673 atmt_popup[i].data.val = 'D';
674 atmt_popup[i].label.string = "&Delete";
677 if(MIME_MSG_A(current->attp)){
678 atmt_popup[++i].type = tQueue;
679 atmt_popup[i].label.style = lNormal;
680 atmt_popup[i].label.string = "&Reply...";
681 atmt_popup[i].data.val = 'R';
683 atmt_popup[++i].type = tQueue;
684 atmt_popup[i].label.style = lNormal;
685 atmt_popup[i].label.string = "&Forward...";
686 atmt_popup[i].data.val = 'F';
689 atmt_popup[++i].type = tQueue;
690 atmt_popup[i].label.style = lNormal;
691 atmt_popup[i].label.string = "&About...";
692 atmt_popup[i].data.val = 'A';
694 atmt_popup[++i].type = tSeparator;
696 atmt_popup[++i].type = tQueue;
697 snprintf(backtag, sizeof(backtag), "View Message #%ld",
698 mn_get_cur(ps->msgmap));
699 backtag[sizeof(backtag)-1] = '\0';
700 atmt_popup[i].label.string = backtag;
701 atmt_popup[i].label.style = lNormal;
702 atmt_popup[i].data.val = '<';
704 atmt_popup[++i].type = tTail;
706 if(mswin_popup(atmt_popup) == 1 && exoffer)
707 display_att_window(current->attp);
710 else if(mp.button == M_BUTTON_RIGHT){
711 MPopup atmt_popup[2];
713 atmt_popup[0].type = tQueue;
714 snprintf(backtag, sizeof(backtag), "View Message #%ld",
715 mn_get_cur(ps->msgmap));
716 backtag[sizeof(backtag)-1] = '\0';
717 atmt_popup[0].label.string = backtag;
718 atmt_popup[0].label.style = lNormal;
719 atmt_popup[0].data.val = '<';
721 atmt_popup[1].type = tTail;
723 mswin_popup(atmt_popup);
724 #endif
727 break;
728 #endif
730 case MC_WHEREIS : /* whereis */
731 /*--- get string ---*/
732 {int rc, found = 0;
733 char *result = NULL, buf[64];
734 static char last[64], tmp[64];
735 HelpType help;
737 ps->mangled_footer = 1;
738 buf[0] = '\0';
739 snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ",
740 (last[0]) ? "[" : "",
741 (last[0]) ? last : "",
742 (last[0]) ? "]" : "");
743 tmp[sizeof(tmp)-1] = '\0';
744 help = NO_HELP;
745 while(1){
746 int flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
748 rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf),
749 tmp,NULL,help,&flags);
750 if(rc == 3)
751 help = help == NO_HELP ? h_attach_index_whereis : NO_HELP;
752 else if(rc == 0 || rc == 1 || !buf[0]){
753 if(rc == 0 && !buf[0] && last[0]){
754 strncpy(buf, last, sizeof(buf));
755 buf[sizeof(buf)-1] = '\0';
758 break;
762 if(rc == 0 && buf[0]){
763 ctmp = current;
764 while((ctmp = next_attline(ctmp)) != NULL)
765 if(srchstr(ctmp->dstring, buf)){
766 found++;
767 break;
770 if(!found){
771 ctmp = first_attline(current);
773 while(ctmp != current)
774 if(srchstr(ctmp->dstring, buf)){
775 found++;
776 break;
778 else
779 ctmp = next_attline(ctmp);
782 else
783 result = _("WhereIs cancelled");
785 if(found && ctmp){
786 strncpy(last, buf, sizeof(last));
787 last[sizeof(last)-1] = '\0';
788 result = "Word found";
789 current = ctmp;
792 q_status_message(SM_ORDER, 0, 3,
793 result ? result : _("Word not found"));
796 break;
798 case MC_DELETE :
799 if(delete_attachment(msgno, current->attp)){
800 int l = current ? strlen(current->attp->number) : 0;
802 current->dstring[1] = 'D';
804 /* Also indicate any children that will be deleted */
806 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
807 if(!strncmp(ctmp->attp->number, current->attp->number, l)
808 && ctmp->attp->number[l] == '.'){
809 ctmp->dstring[1] = 'D';
810 ps->mangled_screen = 1;
814 break;
816 case MC_UNDELETE :
817 if(undelete_attachment(msgno, current->attp, &expbits)){
818 int l = current ? strlen(current->attp->number) : 0;
820 current->dstring[1] = ' ';
822 /* And unflag anything implicitly undeleted */
823 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
824 if(!strncmp(ctmp->attp->number, current->attp->number, l)
825 && ctmp->attp->number[l] == '.'
826 && (!msgno_exceptions(ps->mail_stream, msgno,
827 ctmp->attp->number, &expbits, FALSE)
828 || !(expbits & MSG_EX_DELETE))){
829 ctmp->dstring[1] = ' ';
830 ps->mangled_screen = 1;
834 break;
836 case MC_REPLY :
837 reply_msg_att(ps->mail_stream, msgno, current->attp);
838 break;
840 case MC_FORWARD :
841 forward_attachment(ps->mail_stream, msgno, current->attp);
842 break;
844 case MC_BOUNCE :
845 bounce_msg_att(ps->mail_stream, msgno, current->attp->number,
846 current->attp->body->nested.msg->env->subject);
847 ps->mangled_footer = 1;
848 break;
850 case MC_ABOUTATCH :
851 display_attach_info(msgno, current->attp);
852 break;
854 case MC_VIEW_ATCH : /* View command */
855 display_attachment(msgno, current->attp, DA_SAVE);
856 old_cols = -1;
857 /* fall thru to repaint */
859 case MC_REPAINT : /* redraw */
860 case MC_RESIZE :
861 ps->mangled_screen = 1;
862 break;
864 case MC_EXPORT :
865 export_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
866 ps->mangled_footer = 1;
867 break;
869 case MC_SAVETEXT : /* Save command */
870 save_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
871 ps->mangled_footer = 1;
872 break;
874 case MC_PRINTMSG : /* Save command */
875 print_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
876 ps->mangled_footer = 1;
877 break;
879 case MC_PIPE : /* Pipe command */
880 if(F_ON(F_ENABLE_PIPE, ps)){
881 pipe_attachment(msgno, current->attp);
882 ps->mangled_footer = 1;
883 break;
884 } /* fall thru to complain */
886 case MC_NONE: /* simple timeout */
887 break;
889 case MC_UTF8:
890 bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
891 break;
893 case MC_CHARUP :
894 case MC_CHARDOWN :
895 case MC_CHARRIGHT :
896 case MC_CHARLEFT :
897 case MC_GOTOBOL :
898 case MC_GOTOEOL :
899 case MC_UNKNOWN :
900 default:
901 bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
906 for(current = first_attline(current); current;){ /* clean up */
907 ctmp = current->next;
908 free_attline(&current);
909 current = ctmp;
912 if(screen.titlecolor)
913 free_color_pair(&screen.titlecolor);
917 /*----------------------------------------------------------------------
918 allocate and attach a fresh attachment line struct
920 Args: current -- display line to attach new struct to
922 Returns: newly alloc'd attachment display line
923 ----*/
924 ATDISP_S *
925 new_attline(ATDISP_S **current)
927 ATDISP_S *p;
929 p = (ATDISP_S *)fs_get(sizeof(ATDISP_S));
930 memset((void *)p, 0, sizeof(ATDISP_S));
931 if(current){
932 if(*current){
933 p->next = (*current)->next;
934 (*current)->next = p;
935 p->prev = *current;
936 if(p->next)
937 p->next->prev = p;
940 *current = p;
943 return(p);
947 /*----------------------------------------------------------------------
948 Release system resources associated with attachment display line
950 Args: p -- line to free
952 Result:
953 ----*/
954 void
955 free_attline(ATDISP_S **p)
957 if(p){
958 if((*p)->dstring)
959 fs_give((void **)&(*p)->dstring);
961 fs_give((void **)p);
966 /*----------------------------------------------------------------------
967 Manage display of the attachment screen menu body.
969 Args: ps -- pine struct pointer
970 current -- currently selected display line
971 screen -- reference points for display painting
973 Result:
976 attachment_screen_updater(struct pine *ps, ATDISP_S *current, ATT_SCREEN_S *screen)
978 int dline, return_line = HEADER_ROWS(ps);
979 ATDISP_S *top_line, *ctmp;
981 /* calculate top line of display */
982 dline = 0;
983 ctmp = top_line = first_attline(current);
985 if(((dline++)%(ps->ttyo->screen_rows-HEADER_ROWS(ps)-FOOTER_ROWS(ps)))==0)
986 top_line = ctmp;
987 while(ctmp != current && (ctmp = next_attline(ctmp)));
989 #ifdef _WINDOWS
990 /* Don't know how to manage scroll bar for attachment screen yet. */
991 scroll_setrange (0L, 0L);
992 #endif
994 /* mangled body or new page, force redraw */
995 if(ps->mangled_body || screen->top_line != top_line)
996 screen->current = NULL;
998 /* loop thru painting what's needed */
999 for(dline = 0, ctmp = top_line;
1000 dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
1001 dline++, ctmp = next_attline(ctmp)){
1004 * only fall thru painting if something needs painting...
1006 if(!(!screen->current || ctmp == screen->current || ctmp == current))
1007 continue;
1009 if(F_ON(F_ENABLE_DEL_WHEN_WRITING, ps_global))
1010 ClearLine(dline + HEADER_ROWS(ps));
1011 if(ctmp && ctmp->dstring){
1012 char *p = tmp_20k_buf;
1013 int i, col, x = 0, totlen;
1015 if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
1016 x = 2;
1017 if(ctmp == current){
1018 return_line = dline + HEADER_ROWS(ps);
1019 PutLine0(dline + HEADER_ROWS(ps), 0, "->");
1021 else
1022 PutLine0(dline + HEADER_ROWS(ps), 0, " ");
1025 * Only paint lines if we have to...
1027 if(screen->current)
1028 continue;
1030 else if(ctmp == current){
1031 return_line = dline + HEADER_ROWS(ps);
1032 StartInverse();
1035 totlen = strlen(ctmp->dstring);
1038 * Copy the value to a temp buffer expanding tabs.
1039 * Assume the caller set the widths so as not to overflow the
1040 * right margin.
1042 for(i=0,col=x; ctmp->dstring[i]; i++){
1043 if(ctmp->dstring[i] == TAB){
1044 if((p-tmp_20k_buf) < SIZEOF_20KBUF-8)
1046 *p++ = ' ';
1047 while((++col)&0x07);
1049 else{
1050 col += width_at_this_position((unsigned char *) &ctmp->dstring[i], totlen-i);
1051 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1052 *p++ = ctmp->dstring[i];
1057 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1058 *p = '\0';
1060 PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
1062 if(ctmp == current
1063 && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
1064 EndInverse();
1066 else if(F_OFF(F_ENABLE_DEL_WHEN_WRITING, ps_global))
1067 ClearLine(dline + HEADER_ROWS(ps));
1070 ps->mangled_body = 0;
1071 screen->top_line = top_line;
1072 screen->current = current;
1073 return(return_line);
1077 /*----------------------------------------------------------------------
1078 Redraw the attachment screen based on the global "att_screen" struct
1080 Args: none
1082 Result:
1083 ----*/
1084 void
1085 attachment_screen_redrawer(void)
1087 bitmap_t bitmap;
1089 update_att_screen_titlebar();
1090 ps_global->mangled_header = 0;
1091 ClearLine(1);
1093 ps_global->mangled_body = 1;
1094 (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
1096 setbitmap(bitmap);
1097 draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
1098 1-FOOTER_ROWS(ps_global), 0, SameMenu);
1102 void
1103 update_att_screen_titlebar(void)
1105 long raw_msgno;
1106 COLOR_PAIR *returned_color = NULL;
1107 COLOR_PAIR *titlecolor = NULL;
1108 int colormatch;
1109 SEARCHSET *ss = NULL;
1110 PAT_STATE *pstate = NULL;
1112 if(ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
1113 raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
1114 ss = mail_newsearchset();
1115 ss->first = ss->last = (unsigned long) raw_msgno;
1117 if(ss){
1118 colormatch = get_index_line_color(ps_global->mail_stream,
1119 ss, &pstate, &returned_color);
1120 mail_free_searchset(&ss);
1123 * This is a bit tricky. If there is a colormatch but
1124 * returned_color
1125 * is NULL, that means that the user explicitly wanted the
1126 * Normal color used in this index line, so that is what we
1127 * use. If no colormatch then we will use the TITLE color
1128 * instead of Normal.
1130 if(colormatch){
1131 if(returned_color)
1132 titlecolor = returned_color;
1133 else
1134 titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
1135 ps_global->VAR_NORM_BACK_COLOR);
1138 if(titlecolor
1139 && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
1140 char cbuf[MAXCOLORLEN+1];
1142 strncpy(cbuf, titlecolor->fg, sizeof(cbuf));
1143 cbuf[sizeof(cbuf)-1] = '\0';
1144 strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
1145 titlecolor->fg[MAXCOLORLEN] = '\0';
1146 strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
1147 titlecolor->bg[MAXCOLORLEN] = '\0';
1151 /* Did the color change? */
1152 if((!titlecolor && att_screen->titlecolor)
1154 (titlecolor && !att_screen->titlecolor)
1156 (titlecolor && att_screen->titlecolor
1157 && (strcmp(titlecolor->fg, att_screen->titlecolor->fg)
1158 || strcmp(titlecolor->bg, att_screen->titlecolor->bg)))){
1160 if(att_screen->titlecolor)
1161 free_color_pair(&att_screen->titlecolor);
1163 att_screen->titlecolor = titlecolor;
1164 titlecolor = NULL;
1167 if(titlecolor)
1168 free_color_pair(&titlecolor);
1171 set_titlebar(_("ATTACHMENT INDEX"), ps_global->mail_stream,
1172 ps_global->context_current, ps_global->cur_folder,
1173 ps_global->msgmap, 1, MessageNumber, 0, 0,
1174 att_screen->titlecolor);
1178 /*----------------------------------------------------------------------
1179 Seek back from the given display line to the beginning of the list
1181 Args: p -- display linked list
1183 Result:
1184 ----*/
1185 ATDISP_S *
1186 first_attline(ATDISP_S *p)
1188 while(p && p->prev)
1189 p = p->prev;
1191 return(p);
1196 init_att_progress(char *msg, MAILSTREAM *stream, struct mail_bodystruct *body)
1198 if((save_att_length = body->size.bytes) != 0){
1199 /* if there are display filters, factor in extra copy */
1200 if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
1201 save_att_length += body->size.bytes;
1203 /* if remote folder and segment not cached, factor in IMAP fetch */
1204 if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
1205 && !((body->type == TYPETEXT && body->contents.text.data)
1206 || ((body->type == TYPEMESSAGE)
1207 && body->nested.msg && body->nested.msg->text.text.data)
1208 || body->contents.text.data))
1209 save_att_length += body->size.bytes;
1211 gf_filter_init(); /* reset counters */
1212 pine_gets_bytes(1);
1213 save_att_piped(1);
1214 return(busy_cue(msg, save_att_percent, 0));
1217 return(0);
1221 long
1222 save_att_piped(int reset)
1224 static long x;
1225 long y;
1227 if(reset){
1228 x = y = 0L;
1230 else if((y = gf_bytes_piped()) >= x){
1231 x = y;
1232 y = 0;
1235 return(x + y);
1240 save_att_percent(void)
1242 int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
1243 / save_att_length);
1244 return(MIN(i, 100));
1248 /*----------------------------------------------------------------------
1249 Save the given attachment associated with the given message no
1251 Args: ps
1253 Result:
1254 ----*/
1255 void
1256 save_attachment(int qline, long int msgno, ATTACH_S *a)
1258 if(ps_global->restricted){
1259 q_status_message(SM_ORDER | SM_DING, 0, 4,
1260 "Alpine demo can't save attachments");
1261 return;
1264 if(MIME_MSG_A(a))
1265 save_msg_att(msgno, a);
1266 else if(MIME_DGST_A(a))
1267 save_digest_att(msgno, a);
1268 else if(MIME_VCARD_A(a))
1269 save_vcard_att(ps_global, qline, msgno, a);
1270 else
1271 write_attachment(qline, msgno, a, "SAVE");
1275 /*----------------------------------------------------------------------
1276 Save the given attachment associated with the given message no
1278 Args: ps
1280 Result:
1281 ----*/
1282 void
1283 export_attachment(int qline, long int msgno, ATTACH_S *a)
1285 if(ps_global->restricted){
1286 q_status_message(SM_ORDER | SM_DING, 0, 4,
1287 "Alpine demo can't export attachments");
1288 return;
1291 if(MIME_MSG_A(a))
1292 export_msg_att(msgno, a);
1293 else if(MIME_DGST_A(a))
1294 export_digest_att(msgno, a);
1295 else
1296 q_status_message1(SM_ORDER, 0, 3,
1297 _("Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."),
1298 body_type_names(a->body->type));
1302 void
1303 write_attachment(int qline, long int msgno, ATTACH_S *a, char *method)
1305 char filename[MAXPATH+1], full_filename[MAXPATH+1],
1306 title_buf[64], *err;
1307 int r, rflags = GER_NONE, we_cancel = 0, flags;
1308 static HISTORY_S *history = NULL;
1309 static ESCKEY_S att_save_opts[] = {
1310 {ctrl('T'), 10, "^T", N_("To Files")},
1311 {-1, 0, NULL, NULL},
1312 {-1, 0, NULL, NULL},
1313 {-1, 0, NULL, NULL}};
1315 /*------- Figure out suggested file name ----*/
1316 filename[0] = full_filename[0] = '\0';
1317 (void) get_filename_parameter(filename, sizeof(filename), a->body, NULL);
1319 dprint((9, "export_attachment(name: %s)\n",
1320 filename ? filename : "?"));
1322 r = 0;
1323 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1324 if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
1325 att_save_opts[++r].ch = ctrl('V');
1326 att_save_opts[r].rval = 12;
1327 att_save_opts[r].name = "^V";
1328 att_save_opts[r].label = N_("Downld Msg");
1330 #endif /* !(DOS || MAC) */
1332 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1333 att_save_opts[++r].ch = ctrl('I');
1334 att_save_opts[r].rval = 11;
1335 att_save_opts[r].name = "TAB";
1336 att_save_opts[r].label = N_("Complete");
1339 att_save_opts[++r].ch = -1;
1341 snprintf(title_buf, sizeof(title_buf), "%s ATTACHMENT", method);
1342 title_buf[sizeof(title_buf)-1] = '\0';
1344 flags = (a && a->body && a->body->type == TYPETEXT ? GE_BINARY : 0)
1345 | GE_SEQ_SENSITIVE;
1347 r = get_export_filename(ps_global, filename, NULL, full_filename,
1348 sizeof(filename), "attachment", title_buf,
1349 att_save_opts, &rflags, qline, flags, &history);
1351 if(r < 0){
1352 switch(r){
1353 case -1:
1354 cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
1355 break;
1357 case -2:
1358 q_status_message1(SM_ORDER, 0, 2,
1359 _("Can't save to file outside of %s"),
1360 ps_global->VAR_OPER_DIR);
1361 break;
1364 return;
1366 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1367 else if(r == 12){ /* Download */
1368 char cmd[MAXPATH], *tfp = NULL;
1369 PIPE_S *syspipe;
1370 gf_io_t pc;
1371 long len;
1372 STORE_S *store;
1373 char prompt_buf[256];
1375 if(ps_global->restricted){
1376 q_status_message(SM_ORDER | SM_DING, 3, 3,
1377 "Download disallowed in restricted mode");
1378 return;
1381 err = NULL;
1382 tfp = temp_nam(NULL, "pd");
1383 dprint((1, "Download attachment called!\n"));
1384 if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
1386 snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
1387 prompt_buf[sizeof(prompt_buf)-1] = '\0';
1388 we_cancel = init_att_progress(prompt_buf,
1389 ps_global->mail_stream,
1390 a->body);
1392 gf_set_so_writec(&pc, store);
1393 if((err = detach(ps_global->mail_stream, msgno,
1394 a->number, 0L, &len, pc, NULL, 0)) != NULL)
1395 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1396 "%s: Error writing attachment to \"%s\"",
1397 err, tfp);
1399 /* cancel regardless, so it doesn't get in way of xfer */
1400 cancel_busy_cue(0);
1402 gf_clear_so_writec(store);
1403 if(so_give(&store)) /* close file */
1404 err = "Error writing tempfile for download";
1406 if(!err){
1407 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
1408 ps_global->VAR_DOWNLOAD_CMD, tfp);
1409 if((syspipe = open_system_pipe(cmd, NULL, NULL,
1410 PIPE_USER | PIPE_RESET,
1411 0, pipe_callback, pipe_report_error)) != NULL)
1412 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1413 else
1414 q_status_message(SM_ORDER | SM_DING, 3, 3,
1415 err = "Error running download command");
1418 else
1419 q_status_message(SM_ORDER | SM_DING, 3, 3,
1420 err = "Error building temp file for download");
1422 if(tfp){
1423 our_unlink(tfp);
1424 fs_give((void **)&tfp);
1427 if(!err)
1428 q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
1429 a->number);
1431 return;
1433 #endif /* !(DOS || MAC) */
1435 (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
1436 rflags, full_filename);
1441 * Args stream --
1442 * msgno -- raw message number
1443 * a -- attachment struct
1444 * flags -- comes from get_export_filename
1445 * GER_OVER -- the file was truncated before we wrote
1446 * GER_APPEND -- the file was not truncated prior to our writing,
1447 * so we were appending
1448 * else -- the file didn't previously exist
1449 * file -- the full pathname of the file
1451 * Returns 1 for successful write, 0 for nothing to write, -1 for error
1454 write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
1456 char *l_string, sbuf[256], *err;
1457 int is_text, we_cancel = 0, dt_flags = 0, so_flags;
1458 long len, orig_size;
1459 gf_io_t pc;
1460 STORE_S *store;
1462 if(!(a && a->body && a->number && a->number[0] && file && file[0]
1463 && stream))
1464 return 0;
1466 is_text = (a && a->body && a->body->type == TYPETEXT);
1468 if(flags & GER_APPEND)
1469 orig_size = name_file_size(file);
1471 if(flags & GER_BINARY)
1472 dt_flags |= DT_BINARY;
1474 so_flags = (is_text & !(flags & GER_BINARY) ? WRITE_TO_LOCALE : 0)
1475 | WRITE_ACCESS ;
1477 store = so_get(FileStar, file, so_flags);
1478 if(store == NULL){
1479 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1480 /* TRANSLATORS: Error opening destination <filename>: <error text> */
1481 _("Error opening destination %s: %s"),
1482 file, error_description(errno));
1483 return -1;
1486 snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
1487 sbuf[sizeof(sbuf)-1] = '\0';
1488 we_cancel = init_att_progress(sbuf, stream, a->body);
1490 gf_set_so_writec(&pc, store);
1491 err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, dt_flags);
1492 gf_clear_so_writec(store);
1494 if(we_cancel)
1495 cancel_busy_cue(0);
1497 if(so_give(&store)) /* close file */
1498 err = error_description(errno);
1500 if(err){
1501 if(!(flags & (GER_APPEND | GER_OVER)))
1502 our_unlink(file);
1503 else
1504 our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
1506 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1507 /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
1508 _("%s: Error writing attachment to \"%s\""),
1509 err, file);
1510 return -1;
1512 else{
1513 l_string = cpystr(byte_string(len));
1514 q_status_message8(SM_ORDER, 0, 4,
1515 "Part %s, %s%s %s to \"%s\"%s%s%s",
1516 a->number,
1517 is_text
1518 ? comatose(a->body->size.lines) : l_string,
1519 is_text ? " lines" : "",
1520 flags & GER_OVER
1521 ? "overwritten"
1522 : flags & GER_APPEND ? "appended" : "written",
1523 file,
1524 (is_text || len == a->body->size.bytes)
1525 ? "" : "(decoded from ",
1526 (is_text || len == a->body->size.bytes)
1527 ? "" : byte_string(a->body->size.bytes),
1528 is_text || len == a->body->size.bytes
1529 ? "" : ")");
1530 fs_give((void **)&l_string);
1531 return 1;
1536 char *
1537 write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
1539 char *err = NULL;
1540 long start_of_append;
1541 gf_io_t pc;
1542 MESSAGECACHE *mc;
1544 if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
1545 && (*ap)->body->nested.msg->env){
1546 start_of_append = so_tell(store);
1548 gf_set_so_writec(&pc, store);
1549 if(!(ps_global->mail_stream && msgno > 0L
1550 && msgno <= ps_global->mail_stream->nmsgs
1551 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1552 mc = NULL;
1554 if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
1555 || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
1556 err = error_description(errno);
1558 gf_clear_so_writec(store);
1560 if(err)
1561 ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
1563 else
1564 err = "Can't export message. Missing Envelope data";
1566 return(err);
1570 /*----------------------------------------------------------------------
1571 Save the attachment message/rfc822 to specified folder
1573 Args:
1575 Result:
1576 ----*/
1577 void
1578 save_msg_att(long int msgno, ATTACH_S *a)
1580 char newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
1581 char date[64], nmsgs[80];
1582 CONTEXT_S *cntxt = NULL;
1583 int our_stream = 0, rv;
1584 MAILSTREAM *save_stream;
1585 MESSAGECACHE *mc;
1587 snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
1588 nmsgs[sizeof(nmsgs)-1] = '\0';
1589 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
1590 a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
1591 if(strucmp(newfolder, ps_global->inbox_name) == 0){
1592 save_folder = ps_global->VAR_INBOX_PATH;
1593 cntxt = NULL;
1595 else
1596 save_folder = newfolder;
1598 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1600 mc = (msgno > 0L && ps_global->mail_stream
1601 && msgno <= ps_global->mail_stream->nmsgs)
1602 ? mail_elt(ps_global->mail_stream, msgno) : NULL;
1603 flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
1604 if(mc && mc->day)
1605 mail_date(date, mc);
1606 else
1607 *date = '\0';
1609 if(pith_opt_save_size_changed_prompt)
1610 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1612 rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
1614 if(pith_opt_save_size_changed_prompt)
1615 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1617 if(flags)
1618 fs_give((void **) &flags);
1620 if(rv == 1)
1621 q_status_message2(SM_ORDER, 0, 4,
1622 _("Attached message (part %s) saved to \"%s\""),
1623 a->number,
1624 save_folder);
1625 else if(rv == -1)
1626 cmd_cancelled("Attached message Save");
1627 /* else whatever broke in save_fetch_append shoulda bitched */
1629 if(our_stream)
1630 mail_close(save_stream);
1635 /*----------------------------------------------------------------------
1636 Save the message/rfc822 in the given digest to the specified folder
1638 Args:
1640 Result:
1641 ----*/
1642 void
1643 save_digest_att(long int msgno, ATTACH_S *a)
1645 char newfolder[MAILTMPLEN], *save_folder,
1646 date[64], nmsgs[80];
1647 CONTEXT_S *cntxt = NULL;
1648 int our_stream = 0, rv, cnt = 0;
1649 MAILSTREAM *save_stream;
1650 ATTACH_S *ap;
1652 for(ap = a + 1;
1653 ap->description
1654 && !strncmp(a->number, ap->number, strlen(a->number));
1655 ap++)
1656 if(MIME_MSG(ap->body->type, ap->body->subtype))
1657 cnt++;
1659 snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
1660 nmsgs[sizeof(nmsgs)-1] = '\0';
1662 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
1663 nmsgs, NULL, 0, NULL, NULL, NULL)){
1664 save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
1665 ? ps_global->VAR_INBOX_PATH : newfolder;
1667 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1669 if(pith_opt_save_size_changed_prompt)
1670 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1672 for(ap = a + 1;
1673 ap->description
1674 && !strncmp(a->number, ap->number, strlen(a->number));
1675 ap++)
1676 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1677 *date = '\0';
1678 rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
1679 if(rv != 1)
1680 break;
1683 if(pith_opt_save_size_changed_prompt)
1684 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1686 if(rv == 1)
1687 q_status_message2(SM_ORDER, 0, 4,
1688 _("Attached digest (part %s) saved to \"%s\""),
1689 a->number,
1690 save_folder);
1691 else if(rv == -1)
1692 cmd_cancelled("Attached digest Save");
1693 /* else whatever broke in save_fetch_append shoulda bitched */
1695 if(our_stream)
1696 mail_close(save_stream);
1702 save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
1703 char *save_folder, CONTEXT_S *cntxt, char *date)
1705 STORE_S *so;
1706 int rv = 0;
1708 if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
1709 if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1710 *date = '\0';
1711 rv = save_fetch_append(ps_global->mail_stream, msgno,
1712 a->number,
1713 save_stream, save_folder, cntxt,
1714 a->body->size.bytes,
1715 NULL, date, so);
1717 else{
1718 dprint((1, "Can't allocate store for save: %s\n",
1719 error_description(errno)));
1720 q_status_message(SM_ORDER | SM_DING, 3, 4,
1721 _("Problem creating space for message text."));
1722 rv = 0;
1726 return(rv);
1730 /*----------------------------------------------------------------------
1731 Export the attachment message/rfc822 to specified file
1733 Args:
1735 Result:
1736 ----*/
1737 void
1738 export_msg_att(long int msgno, ATTACH_S *a)
1740 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
1741 int rv, rflags = GER_NONE, i = 1;
1742 ATTACH_S *ap = a;
1743 STORE_S *store;
1744 static HISTORY_S *history = NULL;
1745 static ESCKEY_S opts[] = {
1746 {ctrl('T'), 10, "^T", N_("To Files")},
1747 {-1, 0, NULL, NULL},
1748 {-1, 0, NULL, NULL}};
1750 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1751 opts[i].ch = ctrl('I');
1752 opts[i].rval = 11;
1753 opts[i].name = "TAB";
1754 opts[i].label = N_("Complete");
1757 filename[0] = full_filename[0] = '\0';
1759 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1760 sizeof(filename), "msg attachment",
1761 /* TRANSLATORS: Message Attachment (a screen title) */
1762 _("MSG ATTACHMENT"), opts,
1763 &rflags, -FOOTER_ROWS(ps_global),
1764 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1766 if(rv < 0){
1767 switch(rv){
1768 case -1:
1769 cmd_cancelled("Export");
1770 break;
1772 case -2:
1773 q_status_message1(SM_ORDER, 0, 2,
1774 _("Can't export to file outside of %s"),
1775 ps_global->VAR_OPER_DIR);
1776 break;
1779 return;
1782 /* With name in hand, allocate storage object and save away... */
1783 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1784 if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
1785 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1786 else
1787 q_status_message3(SM_ORDER, 0, 4,
1788 _("Attached message (part %s) %s to \"%s\""),
1789 a->number,
1790 rflags & GER_OVER
1791 ? _("overwritten")
1792 : rflags & GER_APPEND ? _("appended") : _("written"),
1793 full_filename);
1795 if(so_give(&store))
1796 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1797 _("Error writing %s: %s"),
1798 full_filename, error_description(errno));
1800 else
1801 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1802 /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
1803 _("Error opening file \"%s\" to export message: %s"),
1804 full_filename, error_description(errno));
1808 /*----------------------------------------------------------------------
1809 Export the message/rfc822 in the given digest to the specified file
1811 Args:
1813 Result:
1814 ----*/
1815 void
1816 export_digest_att(long int msgno, ATTACH_S *a)
1818 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
1819 int rv, rflags = GER_NONE, i = 1;
1820 long count = 0L;
1821 ATTACH_S *ap;
1822 static HISTORY_S *history = NULL;
1823 STORE_S *store;
1824 static ESCKEY_S opts[] = {
1825 {ctrl('T'), 10, "^T", N_("To Files")},
1826 {-1, 0, NULL, NULL},
1827 {-1, 0, NULL, NULL}};
1829 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1830 opts[i].ch = ctrl('I');
1831 opts[i].rval = 11;
1832 opts[i].name = "TAB";
1833 opts[i].label = N_("Complete");
1836 filename[0] = full_filename[0] = '\0';
1838 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1839 sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
1840 opts, &rflags, -FOOTER_ROWS(ps_global),
1841 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1843 if(rv < 0){
1844 switch(rv){
1845 case -1:
1846 cmd_cancelled("Export");
1847 break;
1849 case -2:
1850 q_status_message1(SM_ORDER, 0, 2,
1851 _("Can't export to file outside of %s"),
1852 ps_global->VAR_OPER_DIR);
1853 break;
1856 return;
1859 /* With name in hand, allocate storage object and save away... */
1860 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1861 count = 0;
1863 for(ap = a + 1;
1864 ap->description
1865 && !strncmp(a->number, ap->number, strlen(a->number))
1866 && !err;
1867 ap++){
1868 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1869 count++;
1870 err = write_attached_msg(msgno, &ap, store,
1871 !count && !(rflags & GER_APPEND));
1875 if(so_give(&store))
1876 err = error_description(errno);
1878 if(err){
1879 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1880 _("Error exporting: %s"), err);
1881 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1882 _("%s messages exported before error occurred"), err);
1884 else
1885 q_status_message4(SM_ORDER, 0, 4,
1886 "%s messages in digest (part %s) %s to \"%s\"",
1887 long2string(count),
1888 a->number,
1889 rflags & GER_OVER
1890 ? "overwritten"
1891 : rflags & GER_APPEND ? "appended" : "written",
1892 full_filename);
1894 else
1895 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1896 _("Error opening file \"%s\" to export digest: %s"),
1897 full_filename, error_description(errno));
1901 /*----------------------------------------------------------------------
1902 Print the given attachment associated with the given message no
1904 Args: ps
1906 Result:
1907 ----*/
1908 void
1909 print_attachment(int qline, long int msgno, ATTACH_S *a)
1911 char prompt[250];
1913 if(ps_global->restricted){
1914 q_status_message(SM_ORDER | SM_DING, 0, 4,
1915 "Alpine demo can't Print attachments");
1916 return;
1919 snprintf(prompt, sizeof(prompt), "attach%s %s",
1920 (a->body->type == TYPETEXT) ? "ment" : "ed message",
1921 MIME_DGST_A(a) ? "digest" : a->number);
1922 prompt[sizeof(prompt)-1] = '\0';
1923 if(open_printer(prompt) >= 0){
1924 if(MIME_MSG_A(a))
1925 (void) print_msg_att(msgno, a, 1);
1926 else if(MIME_DGST_A(a))
1927 print_digest_att(msgno, a);
1928 else
1929 (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
1931 close_printer();
1937 * Print the attachment message/rfc822 to specified file
1939 * Returns 1 on success, 0 on failure.
1942 print_msg_att(long int msgno, ATTACH_S *a, int first)
1944 ATTACH_S *ap = a;
1945 MESSAGECACHE *mc;
1947 if(!(ps_global->mail_stream && msgno > 0L
1948 && msgno <= ps_global->mail_stream->nmsgs
1949 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1950 mc = NULL;
1952 if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
1953 && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
1954 && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
1955 ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
1956 : 1)
1957 && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
1958 return(1);
1961 q_status_message2(SM_ORDER | SM_DING, 3, 3,
1962 _("Error printing message %s, part %s"),
1963 long2string(msgno), a->number);
1964 return(0);
1968 /*----------------------------------------------------------------------
1969 Print the attachment message/rfc822 to specified file
1971 Args:
1973 Result:
1974 ----*/
1975 void
1976 print_digest_att(long int msgno, ATTACH_S *a)
1978 ATTACH_S *ap;
1979 int next = 0;
1981 for(ap = a + 1;
1982 ap->description
1983 && !strncmp(a->number, ap->number, strlen(a->number));
1984 ap++){
1985 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1986 char *p = part_desc(ap->number, ap->body->nested.msg->body,
1987 0, 80, FM_NOINDENT, print_char);
1988 if(p){
1989 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1990 _("Can't print digest: %s"), p);
1991 break;
1993 else if(!print_msg_att(msgno, ap, !next))
1994 break;
1996 next++;
2002 display_html_external_attachment(long int msgno, ATTACH_S *a, int flags)
2004 char dir_path[MAXPATH+1];
2005 char *filename = NULL;
2006 char *file_path; /* file:///some/path/ */
2007 STORE_S *store;
2008 gf_io_t pc;
2009 char *err;
2010 int we_cancel = 0, saved, errs;
2011 char *tool;
2012 ATTACH_S *att;
2013 unsigned long rawno;
2015 if(a->body == NULL){
2016 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Attachment has no body!"));
2017 return 1;
2018 } else if (a->body->type != TYPETEXT
2019 || a->body->subtype == NULL
2020 || strucmp(a->body->subtype, "HTML")){
2021 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Not a TEXT/HTML attachment"));
2022 return 1;
2025 /* zero these variables, just in case. Do not try freeing them. They have short lives */
2026 for(att = ps_global->atmts; att->description != NULL; att++){
2027 att->cid_tmpfile = NULL;
2028 att->tmpdir = NULL;
2031 /* setup the environment first */
2032 if(!ps_global->html_dir){
2033 if(!html_directory_path(ps_global->VAR_HTML_DIRECTORY, dir_path, MAXPATH)){
2034 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2035 _("Error creating full path for %s"), ps_global->VAR_HTML_DIRECTORY);
2036 return 1;
2037 } else if (init_html_directory(dir_path) < 0){
2038 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error initializing %s"), dir_path);
2039 return 1;
2041 ps_global->html_dir = cpystr(dir_path);
2043 else{
2044 strncpy(dir_path, ps_global->html_dir, sizeof(dir_path));
2045 dir_path[sizeof(dir_path)-1] = '\0';
2048 if(create_random_dir(dir_path, sizeof(dir_path)) < 0){
2049 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error creating temp dir in %s"), dir_path);
2050 return 1;
2053 a->tmpdir = cpystr(dir_path);
2054 add_html_log(&ps_global->html_dir_list, a->tmpdir);
2056 /* Process the text/html part */
2057 filename = temp_nam_ext(a->tmpdir, "tmp-html-", HTML_EXT);
2059 if(!filename){
2060 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2061 _("Error \"%s\", Can't create temporary file"),
2062 error_description(errno));
2063 return(1);
2066 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2067 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2068 _("Error \"%s\", Can't write file %s"),
2069 error_description(errno), filename);
2070 if(filename){
2071 our_unlink(filename);
2072 fs_give((void **)&filename);
2074 return(1);
2077 if(a->body->size.bytes){
2078 char msg_buf[128];
2080 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2081 a->description ? "\"" : "",
2082 a->description ? a->description : "attachment number ",
2083 a->description ? "" : a->number,
2084 a->description ? "\"" : "");
2085 msg_buf[sizeof(msg_buf)-1] = '\0';
2086 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream, a->body);
2089 gf_set_so_writec(&pc, store);
2091 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL,
2092 DT_EXTERNAL | ((flags & DA_ALLIMAGES) ? DT_ALLIMAGES : 0));
2094 gf_clear_so_writec(store);
2096 if(we_cancel)
2097 cancel_busy_cue(0);
2099 so_give(&store);
2101 /*----- Download all needed inline attachments ------*/
2102 saved = errs = 0;
2103 rawno = mn_m2raw(ps_global->msgmap, msgno);
2104 for (att = ps_global->atmts; rawno > 0 && att->description != NULL; att++){
2105 if(att->cid_tmpfile){
2106 if(write_attachment_to_file(ps_global->mail_stream, rawno,
2107 att, GER_NONE, att->cid_tmpfile) == 1)
2108 saved++;
2109 else
2110 errs++;
2111 fs_give((void **) &att->cid_tmpfile);
2113 if(att->tmpdir)
2114 fs_give((void **) &att->tmpdir);
2117 if(err){
2118 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2119 "%s: Error saving image to temp file %s",
2120 err, filename);
2121 if(filename){
2122 our_unlink(filename);
2123 fs_give((void **)&filename);
2125 return(1);
2128 tool = get_url_external_handler("http://", 1);
2129 if(tool == NULL) tool = get_url_external_handler("http://", 0);
2130 if(tool == NULL) tool = get_url_external_handler("https://", 1);
2131 if(tool == NULL) tool = get_url_external_handler("https://", 0);
2133 file_path = fs_get((strlen(filename) + strlen("file://") + 1)*sizeof(char));
2134 sprintf(file_path, "file://%s", filename);
2136 /*----- Run the viewer process ----*/
2137 if(do_url_launch(tool, file_path) == 0)
2138 q_status_message(SM_ORDER, 3, 3, "Opened message in external browser");
2139 else
2140 q_status_message(SM_ORDER|SM_DING, 3, 5, "Failed to open message in external browser");
2142 if(filename)
2143 fs_give((void **)&filename);
2145 if(file_path)
2146 fs_give((void **)&file_path);
2148 ps_global->mangled_screen = 1;
2150 return(0);
2154 /*----------------------------------------------------------------------
2155 Unpack and display the given attachment associated with given message no.
2157 Args: msgno -- message no attachment is part of
2158 a -- attachment to display
2160 Returns: 0 on success, non-zero (and error message queued) otherwise
2161 ----*/
2163 display_attachment(long int msgno, ATTACH_S *a, int flags)
2165 char *filename = NULL;
2166 char sender_filename[1000];
2167 char *extp = NULL;
2168 STORE_S *store;
2169 gf_io_t pc;
2170 char *err;
2171 int we_cancel = 0, rv;
2172 char prefix[70];
2173 char ext[32];
2174 char mtype[128];
2176 if(flags & DA_EXTERNAL)
2177 return display_html_external_attachment(msgno, a, flags);
2179 /*------- Display the attachment -------*/
2180 if(dispatch_attachment(a) == MCD_NONE){
2181 /*----- Can't display this type ------*/
2182 if(a->body->encoding < ENCOTHER)
2183 q_status_message4(SM_ORDER | SM_DING, 3, 5,
2184 /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
2185 _("Don't know how to display %s%s%s attachments.%s"),
2186 body_type_names(a->body->type),
2187 a->body->subtype ? "/" : "",
2188 a->body->subtype ? a->body->subtype :"",
2189 (flags & DA_SAVE) ? _(" Try Save.") : "");
2190 else
2191 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2192 _("Don't know how to unpack \"%s\" encoding"),
2193 body_encodings[(a->body->encoding <= ENCMAX)
2194 ? a->body->encoding : ENCOTHER]);
2196 return(1);
2198 else if(!(a->can_display & MCD_EXTERNAL)){
2199 if(a->body->type == TYPEMULTIPART){
2200 if(a->body->subtype){
2201 if(!strucmp(a->body->subtype, "digest"))
2202 display_digest_att(msgno, a, flags);
2203 else
2204 q_status_message1(SM_ORDER, 3, 5,
2205 _("Can't display Multipart/%s"),
2206 a->body->subtype);
2208 else
2209 q_status_message(SM_ORDER, 3, 5,
2210 _("Can't display unknown Multipart Subtype"));
2212 else if(MIME_VCARD_A(a))
2213 display_vcard_att(msgno, a, flags);
2214 else if( MIME_VCALENDAR(a->body->type, a->body->subtype))
2215 display_vcalendar_att(msgno, a, flags);
2216 else if(a->body->type == TYPETEXT){
2218 rv = display_text_att(msgno, a, flags);
2219 } while(rv == MC_FULLHDR);
2221 else if(a->body->type == TYPEMESSAGE){
2223 rv = display_msg_att(msgno, a, flags);
2224 } while(rv == MC_FULLHDR);
2227 ps_global->mangled_screen = 1;
2228 return(0);
2231 /* arrive here if MCD_EXTERNAL */
2233 if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
2234 && (!(flags & DA_DIDPROMPT)))
2235 if(want_to(_("View selected Attachment"), 'y',
2236 0, NO_HELP, WT_NORM) == 'n'){
2237 cmd_cancelled(NULL);
2238 return(1);
2241 sender_filename[0] = '\0';
2242 ext[0] = '\0';
2243 prefix[0] = '\0';
2245 if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
2246 && (a->can_display & MCD_EXT_PROMPT)){
2247 char prompt[256];
2249 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2250 a->body, &extp);
2251 snprintf(prompt, sizeof(prompt),
2252 "Attachment %s%s unrecognized. %s%s%s",
2253 a->body->subtype,
2254 strlen(a->body->subtype) > 12 ? "..." : "",
2255 (extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
2256 (extp && extp[0]) ? extp : "",
2257 (extp && extp[0]) ? ")" : "");
2259 if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
2260 cmd_cancelled(NULL);
2261 return(1);
2265 /*------ Write the image to a temporary file ------*/
2267 /* create type/subtype in mtype */
2268 strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
2269 mtype[sizeof(mtype)-1] = '\0';
2270 if(a->body->subtype){
2271 strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
2272 mtype[sizeof(mtype)-1] = '\0';
2273 strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
2274 mtype[sizeof(mtype)-1] = '\0';
2278 * If we haven't already gotten the filename parameter, get it
2279 * now. It may be used in the temporary filename and possibly
2280 * for its extension.
2282 if(!sender_filename[0])
2283 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2284 a->body, &extp);
2286 if(check_mime_type_by_extension(extp, mtype)
2287 || (!set_mime_extension_by_type(ext, mtype) /* extension from type */
2288 && extp && extp[0])){ /* extension from filename */
2289 strncpy(ext, extp, sizeof(ext));
2290 ext[sizeof(ext)-1] = '\0';
2293 /* create a temp file */
2294 if(sender_filename){
2295 char *p, *q = NULL;
2297 /* get rid of any extension */
2298 if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
2299 *(q-1) = '\0';
2301 /* be careful about what is allowed in the filename */
2302 for(p = sender_filename; *p; p++)
2303 if(!(isascii((unsigned char) *p)
2304 && (isalnum((unsigned char) *p)
2305 || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
2306 break;
2308 if(!*p) /* filename was ok to use */
2309 snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
2312 /* didn't get it yet */
2313 if(!prefix[0]){
2314 snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
2315 ? a->body->subtype : "unk");
2318 /* We are creating a temporary name. This is just a prefix. If you
2319 * need the original name, use the save command, so if the prefix
2320 * is too long, shorten it.
2322 if (strlen(prefix) > 9){
2323 prefix[9] = '-';
2324 prefix[10] = '\0';
2327 filename = temp_nam_ext(NULL, prefix, ext);
2329 if(!filename){
2330 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2331 _("Error \"%s\", Can't create temporary file"),
2332 error_description(errno));
2333 return(1);
2336 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2337 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2338 _("Error \"%s\", Can't write file %s"),
2339 error_description(errno), filename);
2340 if(filename){
2341 our_unlink(filename);
2342 fs_give((void **)&filename);
2345 return(1);
2349 if(a->body->size.bytes){
2350 char msg_buf[128];
2352 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2353 a->description ? "\"" : "",
2354 a->description ? a->description : "attachment number ",
2355 a->description ? "" : a->number,
2356 a->description ? "\"" : "");
2357 msg_buf[sizeof(msg_buf)-1] = '\0';
2358 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
2359 a->body);
2362 if(a->body->type == TYPEMULTIPART){
2363 char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
2364 NULL, 0L);
2366 /* Write to store while converting newlines */
2367 while(h && *h)
2368 if(*h == '\015' && *(h+1) == '\012'){
2369 so_puts(store, NEWLINE);
2370 h += 2;
2372 else
2373 so_writec(*h++, store);
2376 gf_set_so_writec(&pc, store);
2378 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
2380 gf_clear_so_writec(store);
2382 if(we_cancel)
2383 cancel_busy_cue(0);
2385 so_give(&store);
2387 if(err){
2388 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2389 "%s: Error saving image to temp file %s",
2390 err, filename);
2391 if(filename){
2392 our_unlink(filename);
2393 fs_give((void **)&filename);
2396 return(1);
2399 /*----- Run the viewer process ----*/
2400 run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
2401 if(filename)
2402 fs_give((void **)&filename);
2404 return(0);
2408 /*----------------------------------------------------------------------
2409 Fish the required command from mailcap and run it
2411 Args: image_file -- The name of the file to pass viewer
2412 body -- body struct containing type/subtype of part
2414 A side effect may be that scrolltool is called as well if
2415 exec_mailcap_cmd has any substantial output...
2416 ----*/
2417 void
2418 run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
2420 MCAP_CMD_S *mc_cmd = NULL;
2421 int needs_terminal = 0, we_cancel = 0;
2423 we_cancel = busy_cue("Displaying attachment", NULL, 0);
2425 if((mc_cmd = mailcap_build_command(body->type, body->subtype,
2426 body, image_file,
2427 &needs_terminal, chk_extension)) != NULL){
2428 if(we_cancel)
2429 cancel_busy_cue(-1);
2431 exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
2432 if(mc_cmd->command)
2433 fs_give((void **)&mc_cmd->command);
2434 fs_give((void **)&mc_cmd);
2436 else{
2437 if(we_cancel)
2438 cancel_busy_cue(-1);
2440 q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
2441 type_desc(body->type, body->subtype,
2442 body->parameter, NULL, 1));
2447 /*----------------------------------------------------------------------
2448 Detach and provide for browsing a text body part
2450 Args: msgno -- raw message number to get part from
2451 a -- attachment struct for the desired part
2453 Result:
2454 ----*/
2455 STORE_S *
2456 format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
2458 STORE_S *store;
2459 gf_io_t pc;
2461 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2462 if(handlesp)
2463 init_handles(handlesp);
2465 gf_set_so_writec(&pc, store);
2466 (void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
2467 gf_clear_so_writec(store);
2470 return(store);
2474 /*----------------------------------------------------------------------
2475 Detach and provide for browsing a text body part
2477 Args: msgno -- raw message number to get part from
2478 a -- attachment struct for the desired part
2480 Result:
2481 ----*/
2483 display_text_att(long int msgno, ATTACH_S *a, int flags)
2485 STORE_S *store;
2486 HANDLE_S *handles = NULL;
2487 int rv = 0;
2489 if(msgno > 0L)
2490 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2492 if((store = format_text_att(msgno, a, &handles)) != NULL){
2493 rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
2494 free_handles(&handles);
2495 so_give(&store); /* free resources associated with store */
2497 else
2498 q_status_message(SM_ORDER | SM_DING, 3, 3,
2499 _("Error allocating space for attachment."));
2501 return(rv);
2505 /*----------------------------------------------------------------------
2506 Detach and provide for browsing a body part of type "MESSAGE"
2508 Args: msgno -- message number to get partrom
2509 a -- attachment struct for the desired part
2511 Result:
2512 ----*/
2514 display_msg_att(long int msgno, ATTACH_S *a, int flags)
2516 STORE_S *store;
2517 gf_io_t pc;
2518 ATTACH_S *ap = a;
2519 HANDLE_S *handles = NULL;
2520 int rv = 0;
2522 if(msgno > 0L)
2523 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2525 /* BUG, should check this return code */
2526 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2528 /* initialize a storage object */
2529 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2530 q_status_message(SM_ORDER | SM_DING, 3, 3,
2531 _("Error allocating space for message."));
2532 return(rv);
2535 gf_set_so_writec(&pc, store);
2537 if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
2538 if(ps_global->full_header == 2)
2539 q_status_message(SM_INFO, 0, 3,
2540 _("Full header mode ON. All header text being included"));
2542 rv = scroll_attachment((a->body->subtype
2543 && !strucmp(a->body->subtype, "delivery-status"))
2544 ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
2545 store, CharStar, handles, a, flags);
2546 free_handles(&handles);
2549 gf_clear_so_writec(store);
2551 so_give(&store); /* free resources associated with store */
2552 return(rv);
2556 /*----------------------------------------------------------------------
2557 Detach and provide for browsing a multipart body part of type "DIGEST"
2559 Args: msgno -- message number to get partrom
2560 a -- attachment struct for the desired part
2562 Result:
2563 ----*/
2564 void
2565 display_digest_att(long int msgno, ATTACH_S *a, int flags)
2567 STORE_S *store;
2568 ATTACH_S *ap;
2569 HANDLE_S *handles = NULL;
2570 gf_io_t pc;
2571 SourceType src = CharStar;
2572 int bad_news = 0;
2574 if(msgno > 0L)
2575 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2577 /* BUG, should check this return code */
2578 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2580 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
2581 q_status_message(SM_ORDER | SM_DING, 3, 3,
2582 _("Error allocating space for message."));
2583 return;
2586 gf_set_so_writec(&pc, store);
2589 * While in a subtype of this message
2591 for(ap = a + 1;
2592 ap->description
2593 && !strncmp(a->number, ap->number, strlen(a->number))
2594 && !bad_news;
2595 ap++){
2596 if(ap->body->type == TYPEMESSAGE){
2597 char *errstr;
2599 if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
2600 char *tsub;
2602 tsub = cpystr(ap->body->subtype);
2603 convert_possibly_encoded_str_to_utf8((char **) &tsub);
2604 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
2605 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2607 if((errstr = format_editorial(tmp_20k_buf,
2608 ps_global->ttyo->screen_cols, 0,
2609 NULL, pc)) != NULL){
2610 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2611 _("Can't format digest: %s"), errstr);
2612 bad_news++;
2614 else if(!gf_puts(NEWLINE, pc))
2615 bad_news++;
2617 fs_give((void **) &tsub);
2619 else{
2620 if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
2621 0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
2622 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2623 _("Can't format digest: %s"), errstr);
2624 bad_news++;
2626 else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
2627 bad_news++;
2630 else if(ap->body->type == TYPETEXT
2631 && decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
2632 bad_news++;
2633 else if(!gf_puts("Unknown type in Digest", pc))
2634 bad_news++;
2637 if(!bad_news){
2638 if(ps_global->full_header == 2)
2639 q_status_message(SM_INFO, 0, 3,
2640 _("Full header mode ON. All header text being included"));
2642 scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
2645 free_handles(&handles);
2647 gf_clear_so_writec(store);
2648 so_give(&store); /* free resources associated with store */
2653 scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
2655 SCROLL_S sargs;
2657 memset(&sargs, 0, sizeof(SCROLL_S));
2658 sargs.text.text = so_text(store);
2659 sargs.text.src = src;
2660 sargs.text.desc = "attachment";
2661 sargs.text.handles = handles;
2662 sargs.bar.title = title;
2663 sargs.proc.tool = process_attachment_cmd;
2664 sargs.proc.data.p = (void *) a;
2665 sargs.help.text = h_mail_text_att_view;
2666 sargs.help.title = _("HELP FOR ATTACHED TEXT VIEW");
2667 sargs.keys.menu = &att_view_keymenu;
2668 setbitmap(sargs.keys.bitmap);
2670 /* First, fix up "back" key */
2671 if(flags & DA_FROM_VIEW){
2672 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
2674 else{
2675 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
2678 if(!handles){
2679 clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
2680 clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
2681 clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
2684 if(F_OFF(F_ENABLE_PIPE, ps_global))
2685 clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
2687 if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
2688 clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
2691 * If message or digest, leave Reply and Save and,
2692 * conditionally, Bounce on...
2694 if(MIME_MSG_A(a)){
2695 if(F_OFF(F_ENABLE_BOUNCE, ps_global))
2696 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2698 else{
2699 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2700 clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
2701 clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
2703 #ifdef SMIME
2704 if(!(ps_global->smime && ps_global->smime->need_passphrase))
2705 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2707 if(F_ON(F_DONT_DO_SMIME, ps_global) || !MIME_MSG_A(a)){
2708 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2709 clrbitn(ATV_SECURITY_KEY, sargs.keys.bitmap);
2711 #endif
2713 sargs.use_indexline_color = 1;
2715 if(DA_RESIZE & flags)
2716 sargs.resize_exit = 1;
2718 #ifdef _WINDOWS
2719 scrat_attachp = a;
2720 sargs.mouse.popup = scroll_att_popup;
2721 #endif
2723 return(scrolltool(&sargs));
2726 #ifdef SMIME
2727 void
2728 display_smime_info_att(struct pine *ps, ATTACH_S *a)
2730 if(smime_check(a->body->nested.msg->body) == 0){
2731 q_status_message(SM_ORDER | SM_DING, 0, 3,
2732 _("Not a signed or encrypted message"));
2733 return;
2736 display_smime_info(ps, a->body->nested.msg->env, a->body->nested.msg->body);
2738 #endif /* SMIME */
2741 process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2743 int rv = 0, n;
2744 long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2745 #define AD(X) ((ATTACH_S *) (X)->proc.data.p)
2747 switch(cmd){
2748 case MC_EXIT :
2749 rv = 1;
2750 break;
2752 case MC_QUIT :
2753 ps_global->next_screen = quit_screen;
2754 break;
2756 case MC_MAIN :
2757 ps_global->next_screen = main_menu_screen;
2758 break;
2760 case MC_REPLY :
2761 reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
2762 break;
2764 case MC_FORWARD :
2765 forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
2766 break;
2768 case MC_BOUNCE :
2769 bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
2770 AD(sparms)->body->nested.msg->env->subject);
2771 ps_global->mangled_footer = 1;
2772 break;
2774 case MC_DELETE :
2775 delete_attachment(rawno, sparms->proc.data.p);
2776 break;
2778 case MC_UNDELETE :
2779 if(undelete_attachment(rawno, sparms->proc.data.p, &n))
2780 q_status_message1(SM_ORDER, 0, 3,
2781 "Part %s UNdeleted", AD(sparms)->number);
2783 break;
2785 case MC_SAVE :
2786 save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2787 ps_global->mangled_footer = 1;
2788 break;
2790 case MC_EXPORT :
2791 export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2792 ps_global->mangled_footer = 1;
2793 break;
2795 case MC_PRINTMSG :
2796 print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2797 ps_global->mangled_footer = 1;
2798 break;
2800 case MC_PIPE :
2801 pipe_attachment(rawno, sparms->proc.data.p);
2802 ps_global->mangled_footer = 1;
2803 break;
2805 case MC_FULLHDR :
2806 ps_global->full_header++;
2807 if(ps_global->full_header == 1){
2808 if(!(ps_global->quote_suppression_threshold
2809 && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
2810 ps_global->full_header++;
2812 else if(ps_global->full_header > 2)
2813 ps_global->full_header = 0;
2815 switch(ps_global->full_header){
2816 case 0:
2817 q_status_message(SM_ORDER, 0, 3,
2818 _("Display of full headers is now off."));
2819 break;
2821 case 1:
2822 q_status_message1(SM_ORDER, 0, 3,
2823 _("Quotes displayed, use %s to see full headers"),
2824 F_ON(F_USE_FK, ps_global) ? "F9" : "H");
2825 break;
2827 case 2:
2828 q_status_message(SM_ORDER, 0, 3,
2829 _("Display of full headers is now on."));
2830 break;
2834 rv = 1;
2835 break;
2837 #ifdef SMIME
2838 case MC_DECRYPT:
2839 if(ps_global->smime && ps_global->smime->need_passphrase)
2840 smime_get_passphrase();
2841 break;
2843 case MC_SECURITY:
2844 display_smime_info_att(ps_global, sparms->proc.data.p);
2845 break;
2846 #endif
2848 default:
2849 alpine_panic("Unexpected command case");
2850 break;
2853 return(rv);
2858 * Returns 1 on success, 0 on error.
2861 format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
2863 int rv = 1;
2865 if((*a)->body->type != TYPEMESSAGE)
2866 return(gf_puts("[ Undisplayed Attachment of Type ", pc)
2867 && gf_puts(body_type_names((*a)->body->type), pc)
2868 && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
2870 if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
2871 HEADER_S h;
2873 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
2874 FE_DEFAULT);
2875 switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
2876 (*a)->body->nested.msg->env, &h, NULL, NULL,
2877 flags, NULL, pc)){
2878 case -1 : /* write error */
2879 return(0);
2881 case 1 : /* fetch error */
2882 if(!(gf_puts("[ Error fetching header ]", pc)
2883 && !gf_puts(NEWLINE, pc)))
2884 return(0);
2886 break;
2889 gf_puts(NEWLINE, pc);
2891 if(((*a)+1)->description)
2892 ++(*a);
2893 else{
2894 if(!(gf_puts("[Can't display missing text segment]", pc)
2895 && gf_puts(NEWLINE, pc)))
2896 rv = 0;
2897 return rv;
2900 #ifdef SMIME
2901 if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2902 if((*a)->description){
2903 if(!(!format_editorial((*a)->description,
2904 ps_global->ttyo->screen_cols,
2905 flags, NULL, pc)
2906 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2907 rv = 0;
2910 if(((*a)+1)->description)
2911 ++(*a);
2912 else{
2913 if(!(gf_puts("[Can't display missing text segment]", pc)
2914 && gf_puts(NEWLINE, pc)))
2915 rv = 0;
2916 return rv;
2919 #endif /* SMIME */
2921 if(((*a))->description
2922 && (*a)->body && (*a)->body->type == TYPETEXT){
2923 if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2924 rv = 0;
2926 else if(!(gf_puts("[Can't display ", pc)
2927 && gf_puts(((*a)->description && (*a)->body)
2928 ? "first non-" : "missing ", pc)
2929 && gf_puts("text segment]", pc)
2930 && gf_puts(NEWLINE, pc)))
2931 rv = 0;
2933 if(((*a)+1)->description)
2934 ++(*a);
2935 else{
2936 if(!(gf_puts("[Can't display missing text segment]", pc)
2937 && gf_puts(NEWLINE, pc)))
2938 rv = 0;
2939 return rv;
2943 else if((*a)->body->subtype
2944 && strucmp((*a)->body->subtype, "external-body") == 0) {
2945 if(format_editorial("This part is not included and can be fetched as follows:",
2946 ps_global->ttyo->screen_cols, flags, NULL, pc)
2947 || !gf_puts(NEWLINE, pc)
2948 || format_editorial(display_parameters((*a)->body->parameter),
2949 ps_global->ttyo->screen_cols, flags, handlesp, pc))
2950 rv = 0;
2952 else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2953 rv = 0;
2955 return(rv);
2959 void
2960 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2962 STORE_S *in_store, *out_store = NULL;
2963 HANDLE_S *handles = NULL;
2964 URL_HILITE_S uh;
2965 gf_io_t gc, pc;
2966 char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2967 int cmd, indent, begins = 0;
2969 lines = detach_vcard_att(ps_global->mail_stream,
2970 msgno, a->body, a->number);
2971 if(!lines){
2972 q_status_message(SM_ORDER | SM_DING, 3, 3,
2973 _("Error accessing attachment."));
2974 return;
2977 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2978 free_list_array(&lines);
2979 q_status_message(SM_ORDER | SM_DING, 3, 3,
2980 _("Error allocating space for attachment."));
2981 return;
2984 for(ll = lines, indent = 0; ll && *ll; ll++)
2985 if((p = strindex(*ll, ':')) && p - *ll > indent)
2986 indent = p - *ll;
2988 indent += 5;
2989 for(ll = lines; ll && *ll; ll++){
2990 if((p = strindex(*ll, ':')) != NULL){
2991 if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2992 begins++;
2994 snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
2995 (int) MIN(p - *ll, sizeof(tmp)-5), *ll);
2996 tmp[sizeof(tmp)-1] = '\0';
2997 so_puts(in_store, tmp);
2998 p++;
3000 else{
3001 p = *ll;
3002 so_puts(in_store, repeat_char(indent, SPACE));
3005 snprintf(tmp, sizeof(tmp), "%.200s", p);
3006 tmp[sizeof(tmp)-1] = '\0';
3007 so_puts(in_store,
3008 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
3009 SIZEOF_20KBUF, tmp));
3010 so_puts(in_store, "\015\012");
3013 free_list_array(&lines);
3015 so_puts(in_store, "\015\012\015\012");
3018 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
3019 so_seek(in_store, 0L, 0);
3021 init_handles(&handles);
3022 gf_filter_init();
3024 if(F_ON(F_VIEW_SEL_URL, ps_global)
3025 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3026 || F_ON(F_SCAN_ADDR, ps_global))
3027 gf_link_filter(gf_line_test,
3028 gf_line_test_opt(url_hilite,
3029 gf_url_hilite_opt(&uh,&handles,0)));
3031 gf_link_filter(gf_wrap,
3032 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3033 ps_global->ttyo->screen_cols,
3034 NULL, indent, GFW_HANDLES));
3035 gf_link_filter(gf_nvtnl_local, NULL);
3037 gf_set_so_readc(&gc, in_store);
3038 gf_set_so_writec(&pc, out_store);
3040 errstr = gf_pipe(gc, pc);
3042 gf_clear_so_readc(in_store);
3044 if(!errstr){
3045 #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.")
3046 #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.")
3047 errstr = format_editorial((begins > 1)
3048 ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
3049 ps_global->ttyo->screen_cols, 0, NULL, pc);
3052 gf_clear_so_writec(out_store);
3054 if(!errstr)
3055 cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
3056 CharStar, handles, a, flags | DA_RESIZE);
3058 free_handles(&handles);
3059 so_give(&out_store);
3061 else
3062 errstr = _("Error allocating space");
3064 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3066 if(errstr)
3067 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3068 _("Can't format entry : %s"), errstr);
3070 so_give(&in_store);
3073 void
3074 display_vevent_summary(long int msgno, ATTACH_S *a, int flags, int depth)
3076 BODY *b;
3077 VCALENDAR_S *vcal = NULL;
3078 char *b64text, *caltext;
3079 unsigned long callen;
3080 VEVENT_SUMMARY_S *vesy, *vsummary; /* vevent summary */
3081 STORE_S *in_store, *out_store = NULL;
3082 HANDLE_S *handles = NULL;
3083 URL_HILITE_S uh;
3084 gf_io_t gc, pc;
3085 char *errstr = NULL, tmp[MAILTMPLEN], *p;
3086 int cmd, i, k;
3088 b = mail_body(ps_global->mail_stream, msgno, a->number);
3089 if(b->sparep == NULL){
3090 b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
3091 b64text[callen] = '\0'; /* chop off cookie */
3092 caltext = rfc822_base64(b64text, strlen(b64text), &callen);
3093 vcal = ical_parse_text(caltext);
3094 b->sparep = create_body_sparep(iCalType, (void *) vcal);
3096 else if(get_body_sparep_type(b->sparep) == iCalType)
3097 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
3099 vsummary = ical_vevent_summary(vcal);
3101 if(vsummary == NULL){
3102 q_status_message(SM_ORDER | SM_DING, 3, 3,
3103 _("Error parsing event"));
3104 return;
3107 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
3108 q_status_message(SM_ORDER | SM_DING, 3, 3,
3109 _("Error allocating space to process Calendar"));
3110 return;
3113 gf_set_so_readc(&gc, in_store);
3115 for(vesy = vsummary, k = 0; vesy; vesy = vesy->next, k++){
3116 if(depth >= 0 && k != depth)
3117 continue;
3119 if(vesy->cancel){
3120 so_puts(in_store, _("This event was cancelled"));
3121 so_puts(in_store, "\015\012");
3124 if(vesy->priority){
3125 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%d %s",
3126 _("Priority: "), vesy->priority,
3127 vesy->priority == 5 ? _("(Normal)")
3128 : (vesy->priority < 5 ? _("(High)")
3129 : _("(Low)")));
3130 so_puts(in_store, tmp_20k_buf);
3131 so_puts(in_store, "\015\012");
3134 if(vesy->summary){
3135 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3136 _("Summary: "), vesy->summary);
3137 so_puts(in_store, tmp_20k_buf);
3138 so_puts(in_store, "\015\012");
3141 if(vesy->sender){
3142 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3143 _("Sender: "), vesy->sender);
3144 so_puts(in_store, tmp_20k_buf);
3145 so_puts(in_store, "\015\012");
3148 if(vesy->organizer){
3149 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3150 _("Organizer: "), vesy->organizer);
3151 so_puts(in_store, tmp_20k_buf);
3152 so_puts(in_store, "\015\012");
3155 if(vesy->location){
3156 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3157 _("Location: "), vesy->location);
3158 so_puts(in_store, tmp_20k_buf);
3159 so_puts(in_store, "\015\012");
3162 if(vesy->evstart){
3163 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3164 _("Start Date: "), vesy->evstart);
3165 so_puts(in_store, tmp_20k_buf);
3166 so_puts(in_store, "\015\012");
3169 if(vesy->duration){
3170 for(i = 0; vesy->duration[i] != NULL; i++){
3171 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3172 _("Duration: "), vesy->duration[i]);
3173 so_puts(in_store, tmp_20k_buf);
3174 so_puts(in_store, "\015\012");
3176 } else if(vesy->evend){
3177 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3178 _("End Date: "), vesy->evend);
3179 so_puts(in_store, tmp_20k_buf);
3180 so_puts(in_store, "\015\012");
3183 if(vesy->dtstamp){
3184 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3185 vcal->method ? _("Created on: ") : _("Last Revised on; "),
3186 vesy->dtstamp);
3187 so_puts(in_store, tmp_20k_buf);
3188 so_puts(in_store, "\015\012");
3191 if(vesy->description){
3192 char c;
3193 int j, empty;
3195 so_puts(in_store, "\015\012");
3197 for(i = 0; vesy->description[i] != NULL; i++){
3198 so_puts(in_store, _("Description: "));
3199 /* Check if empty description */
3200 empty = 1;
3201 for(j =0; empty == 1 && vesy->description[i][j] != '\0'; j++){
3202 c = vesy->description[i][j];
3203 if(c != '\n' && c != ' ' && c != '\t')
3204 empty = 0;
3206 if(empty){
3207 so_puts(in_store, _("[ No description provided ]"));
3208 so_puts(in_store, "\015\012");
3210 else {
3211 for(j =0; vesy->description[i][j] != '\0'; j++){
3212 c = vesy->description[i][j];
3213 if(c == '\n'){
3214 so_puts(in_store, "\015\012");
3215 continue;
3217 so_writec(c, in_store);
3220 so_puts(in_store, "\015\012");
3224 if(vesy->attendee){
3225 so_puts(in_store, "\015\012");
3226 so_puts(in_store, _("List of Attendees:"));
3227 so_puts(in_store, "\015\012");
3228 for(i = 0; vesy->attendee[i] != NULL; i++){
3229 so_puts(in_store, vesy->attendee[i]);
3230 so_puts(in_store, "\015\012");
3232 so_puts(in_store, "\015\012");
3235 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3236 _("This event was tagged as a %s entry by the sender"), vesy->class);
3237 so_puts(in_store, tmp_20k_buf);
3238 so_puts(in_store, "\015\012\015\012");
3240 if(depth < 0 && vesy->next){
3241 for(i = 0; i < ps_global->ttyo->screen_cols && i < 40; i++)
3242 tmp_20k_buf[i] = '-';
3243 tmp_20k_buf[i]= '\0';
3244 so_puts(in_store, tmp_20k_buf);
3245 so_puts(in_store, "\015\012");
3247 } /* end "for" loop */
3250 so_seek(in_store, 0L, 0);
3251 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
3252 q_status_message(SM_ORDER | SM_DING, 3, 3,
3253 _("Error allocating space to write Calendar"));
3254 return;
3257 gf_set_so_writec(&pc, out_store);
3259 init_handles(&handles);
3260 gf_filter_init();
3262 if(F_ON(F_VIEW_SEL_URL, ps_global)
3263 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3264 || F_ON(F_SCAN_ADDR, ps_global))
3265 gf_link_filter(gf_line_test,
3266 gf_line_test_opt(url_hilite,
3267 gf_url_hilite_opt(&uh,&handles,0)));
3269 gf_link_filter(gf_wrap,
3270 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3271 ps_global->ttyo->screen_cols,
3272 NULL, 0, GFW_HANDLES));
3273 gf_link_filter(gf_nvtnl_local, NULL);
3275 gf_set_so_readc(&gc, in_store);
3276 gf_set_so_writec(&pc, out_store);
3278 errstr = gf_pipe(gc, pc);
3280 gf_clear_so_readc(in_store);
3282 gf_clear_so_writec(out_store);
3284 if(!errstr)
3285 cmd = scroll_attachment(_("CALENDAR EVENT ATTACHMENT"), out_store,
3286 CharStar, handles, a, flags | DA_RESIZE);
3288 free_handles(&handles);
3289 so_give(&out_store);
3291 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3293 if(errstr)
3294 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3295 _("Can't format entry : %s"), errstr);
3296 so_give(&in_store);
3297 free_vevent_summary(&vsummary);
3298 ps_global->mangled_screen = 1;
3302 void
3303 display_vcalendar_att(long int msgno, ATTACH_S *a, int flags)
3305 display_vevent_summary(msgno, a, flags, -1);
3309 /*----------------------------------------------------------------------
3310 Display attachment information
3312 Args: msgno -- message number to get partrom
3313 a -- attachment struct for the desired part
3315 Result: a screen containing information about attachment:
3316 ----*/
3317 void
3318 display_attach_info(long int msgno, ATTACH_S *a)
3320 int i, indent, cols;
3321 char buf1[100], *folded;
3322 STORE_S *store;
3323 PARMLIST_S *plist;
3324 SCROLL_S sargs;
3326 (void) dispatch_attachment(a);
3328 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
3329 q_status_message(SM_ORDER | SM_DING, 3, 3,
3330 _("Error allocating space for message."));
3331 return;
3334 cols = ps_global->ttyo->screen_cols;
3337 * 2 spaces on left
3338 * 16 for text (longest is Display Method == 14)
3339 * 2 for ": "
3341 indent = 20;
3343 /* don't try stupid folding */
3344 cols = MAX(indent+10, cols);
3346 so_puts(store, "Details about Attachment #");
3347 so_puts(store, a->number);
3348 so_puts(store, " :\n\n");
3349 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
3350 so_puts(store, buf1);
3351 so_puts(store, body_type_names(a->body->type));
3352 so_puts(store, "\n");
3353 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
3354 so_puts(store, buf1);
3355 so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
3356 so_puts(store, "\n");
3357 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
3358 so_puts(store, buf1);
3359 so_puts(store, a->body->encoding < ENCMAX
3360 ? body_encodings[a->body->encoding]
3361 : "Unknown");
3362 so_puts(store, "\n");
3363 if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
3364 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
3365 so_puts(store, buf1);
3366 i = 0;
3367 while(rfc2231_list_params(plist)){
3368 if(i++)
3369 so_puts(store, repeat_char(indent, ' '));
3371 so_puts(store, plist->attrib);
3372 so_puts(store, " = ");
3373 so_puts(store, plist->value ? plist->value : "");
3374 so_puts(store, "\n");
3377 rfc2231_free_parmlist(&plist);
3380 if(a->body->description && a->body->description[0]){
3381 char buftmp[MAILTMPLEN];
3382 unsigned char *q;
3384 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
3386 snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
3387 buftmp[sizeof(buftmp)-1] = '\0';
3388 q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
3389 folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
3391 if(folded){
3392 so_puts(store, folded);
3393 fs_give((void **) &folded);
3397 /* BUG: no a->body->language support */
3399 if(a->body->disposition.type){
3400 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
3401 so_puts(store, buf1);
3402 so_puts(store, a->body->disposition.type);
3403 so_puts(store, "\n");
3404 if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
3405 while(rfc2231_list_params(plist)){
3406 so_puts(store, repeat_char(indent, ' '));
3407 so_puts(store, plist->attrib);
3408 so_puts(store, " = ");
3409 so_puts(store, plist->value ? plist->value : "");
3410 so_puts(store, "\n");
3413 rfc2231_free_parmlist(&plist);
3417 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
3418 so_puts(store, buf1);
3419 so_puts(store, comatose((a->body->encoding == ENCBASE64)
3420 ? ((a->body->size.bytes * 3)/4)
3421 : a->body->size.bytes));
3422 so_puts(store, " bytes\n");
3423 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
3424 so_puts(store, buf1);
3425 if(a->can_display == MCD_NONE) {
3426 so_puts(store, "Can't, ");
3427 so_puts(store, (a->body->encoding < ENCOTHER)
3428 ? "Unknown Attachment Format"
3429 : "Unknown Encoding");
3431 else if(!(a->can_display & MCD_EXTERNAL)){
3432 so_puts(store, "Alpine's Internal Pager");
3434 else{
3435 int nt, free_pretty_cmd;
3436 MCAP_CMD_S *mc_cmd;
3437 char *pretty_cmd;
3439 if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
3440 a->body, "<datafile>", &nt,
3441 a->can_display & MCD_EXT_PROMPT)) != NULL){
3442 so_puts(store, "\"");
3443 if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
3444 so_puts(store, pretty_cmd);
3445 so_puts(store, "\"");
3446 if(free_pretty_cmd && pretty_cmd)
3447 fs_give((void **)&pretty_cmd);
3448 if(mc_cmd->command)
3449 fs_give((void **)&mc_cmd->command);
3450 fs_give((void **)&mc_cmd);
3454 so_puts(store, "\n");
3456 memset(&sargs, 0, sizeof(SCROLL_S));
3457 sargs.text.text = so_text(store);
3458 sargs.text.src = CharStar;
3459 sargs.text.desc = "attachment info";
3460 sargs.bar.title = _("ABOUT ATTACHMENT");
3461 sargs.help.text = h_simple_text_view;
3462 sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
3464 sargs.use_indexline_color = 1;
3466 scrolltool(&sargs);
3468 so_give(&store); /* free resources associated with store */
3469 ps_global->mangled_screen = 1;
3473 /*----------------------------------------------------------------------
3475 ----*/
3476 void
3477 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3479 char *sig;
3480 void *msgtext;
3481 ENVELOPE *outgoing;
3482 BODY *body;
3484 if(MIME_MSG_A(a)){
3485 forward_msg_att(stream, msgno, a);
3487 else{
3488 ACTION_S *role = NULL;
3489 REDRAFT_POS_S *redraft_pos = NULL;
3490 long rflags = ROLE_FORWARD;
3491 PAT_STATE dummy;
3493 outgoing = mail_newenvelope();
3494 outgoing->subject = cpystr("Forwarded attachment...");
3496 if(nonempty_patterns(rflags, &dummy)){
3498 * There is no message, but a Current Folder Type might match.
3500 * This has been changed to check against the message
3501 * containing the attachment.
3503 role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3504 if(confirm_role(rflags, &role))
3505 role = combine_inherited_role(role);
3506 else{
3507 role = NULL;
3508 cmd_cancelled("Forward");
3509 mail_free_envelope(&outgoing);
3510 return;
3514 if(role)
3515 q_status_message1(SM_ORDER, 3, 4,
3516 _("Forwarding using role \"%s\""), role->nick);
3518 outgoing->message_id = generate_message_id(role);
3520 * as with all text bound for the composer, build it in
3521 * a storage object of the type it understands...
3523 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3524 int impl, template_len = 0;
3526 if(role && role->template){
3527 char *filtered;
3529 impl = 1;
3530 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3531 if(filtered){
3532 if(*filtered){
3533 so_puts((STORE_S *)msgtext, filtered);
3534 if(impl == 1)
3535 template_len = strlen(filtered);
3538 fs_give((void **)&filtered);
3541 else
3542 impl = 1;
3544 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3545 if(impl == 2)
3546 redraft_pos->offset += template_len;
3548 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3550 fs_give((void **)&sig);
3552 else
3553 so_puts((STORE_S *)msgtext, NEWLINE);
3555 /*---- New Body to start with ----*/
3556 body = mail_newbody();
3557 body->type = TYPEMULTIPART;
3559 /*---- The TEXT part/body ----*/
3560 body->nested.part = mail_newbody_part();
3561 body->nested.part->body.type = TYPETEXT;
3562 body->nested.part->body.contents.text.data = msgtext;
3564 /*---- The corresponding things we're attaching ----*/
3565 body->nested.part->next = mail_newbody_part();
3566 body->nested.part->next->body.id = generate_message_id(role);
3567 copy_body(&body->nested.part->next->body, a->body);
3569 if(fetch_contents(stream, msgno, a->number,
3570 &body->nested.part->next->body)){
3571 pine_send(outgoing, &body, "FORWARD MESSAGE",
3572 role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3574 ps_global->mangled_screen = 1;
3575 pine_free_body(&body);
3576 free_redraft_pos(&redraft_pos);
3578 else{
3579 mail_free_body(&body);
3580 so_give((STORE_S **) &msgtext);
3581 free_redraft_pos(&redraft_pos);
3582 q_status_message(SM_ORDER | SM_DING, 4, 5,
3583 _("Error fetching message contents. Can't forward message."));
3586 else
3587 q_status_message(SM_ORDER | SM_DING, 3, 4,
3588 _("Error allocating message text"));
3590 mail_free_envelope(&outgoing);
3591 free_action(&role);
3596 void
3597 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3599 char *p, *sig = NULL;
3600 int ret;
3601 void *msgtext;
3602 ENVELOPE *outgoing;
3603 BODY *body;
3604 ACTION_S *role = NULL;
3605 REPLY_S reply;
3606 REDRAFT_POS_S *redraft_pos = NULL;
3608 outgoing = mail_newenvelope();
3609 memset((void *)&reply, 0, sizeof(reply));
3611 if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
3613 * as with all text bound for the composer, build it in
3614 * a storage object of the type it understands...
3616 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3617 int impl, template_len = 0;
3618 long rflags = ROLE_FORWARD;
3619 PAT_STATE dummy;
3621 ret = 'n';
3622 if(ps_global->full_header == 2)
3623 ret = want_to(_("Forward message as an attachment"), 'n', 0,
3624 NO_HELP, WT_SEQ_SENSITIVE);
3625 /* Setup possible role */
3626 if(nonempty_patterns(rflags, &dummy)){
3627 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3628 if(confirm_role(rflags, &role))
3629 role = combine_inherited_role(role);
3630 else{ /* cancel reply */
3631 role = NULL;
3632 cmd_cancelled("Forward");
3633 mail_free_envelope(&outgoing);
3634 so_give((STORE_S **) &msgtext);
3635 return;
3639 if(role)
3640 q_status_message1(SM_ORDER, 3, 4,
3641 _("Forwarding using role \"%s\""), role->nick);
3643 outgoing->message_id = generate_message_id(role);
3645 if(role && role->template){
3646 char *filtered;
3648 impl = 1;
3649 filtered = detoken(role, a->body->nested.msg->env,
3650 0, 0, 0, &redraft_pos, &impl);
3651 if(filtered){
3652 if(*filtered){
3653 so_puts((STORE_S *)msgtext, filtered);
3654 if(impl == 1)
3655 template_len = strlen(filtered);
3658 fs_give((void **)&filtered);
3661 else
3662 impl = 1;
3664 if((sig = detoken(role, a->body->nested.msg->env,
3665 2, 0, 1, &redraft_pos, &impl)) != NULL){
3666 if(impl == 2)
3667 redraft_pos->offset += template_len;
3669 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3671 fs_give((void **)&sig);
3673 else
3674 so_puts((STORE_S *)msgtext, NEWLINE);
3676 if(ret == 'y'){
3677 /*---- New Body to start with ----*/
3678 body = mail_newbody();
3679 body->type = TYPEMULTIPART;
3681 /*---- The TEXT part/body ----*/
3682 body->nested.part = mail_newbody_part();
3683 body->nested.part->body.type = TYPETEXT;
3684 body->nested.part->body.contents.text.data = msgtext;
3686 if(!forward_mime_msg(stream, msgno,
3687 p = body_partno(stream, msgno, a->body),
3688 a->body->nested.msg->env,
3689 &body->nested.part->next, msgtext))
3690 mail_free_body(&body);
3692 else{
3693 reply.forw = 1;
3694 if(a->body->nested.msg->body){
3695 char *charset;
3697 charset
3698 = parameter_val(a->body->nested.msg->body->parameter,
3699 "charset");
3701 if(charset && strucmp(charset, "us-ascii") != 0){
3702 CONV_TABLE *ct;
3705 * There is a non-ascii charset,
3706 * is there conversion happening?
3708 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3709 || !ct->table){
3710 reply.orig_charset = charset;
3711 charset = NULL;
3715 if(charset)
3716 fs_give((void **) &charset);
3719 body = forward_body(stream, a->body->nested.msg->env,
3720 a->body->nested.msg->body, msgno,
3721 p = body_partno(stream, msgno, a->body),
3722 msgtext, FWD_NONE);
3725 fs_give((void **) &p);
3727 if(body){
3728 pine_send(outgoing, &body,
3729 "FORWARD MESSAGE",
3730 role, NULL,
3731 reply.forw ? &reply : NULL,
3732 redraft_pos, NULL, NULL, 0);
3734 ps_global->mangled_screen = 1;
3735 pine_free_body(&body);
3736 free_redraft_pos(&redraft_pos);
3737 free_action(&role);
3739 else{
3740 so_give((STORE_S **) &msgtext);
3741 q_status_message(SM_ORDER | SM_DING, 4, 5,
3742 _("Error fetching message contents. Can't forward message."));
3745 else
3746 q_status_message(SM_ORDER | SM_DING, 3, 4,
3747 _("Error allocating message text"));
3749 else
3750 q_status_message1(SM_ORDER,3,4,
3751 _("Error fetching message %s. Can't forward it."),
3752 long2string(msgno));
3754 if(reply.orig_charset)
3755 fs_give((void **)&reply.orig_charset);
3757 mail_free_envelope(&outgoing);
3761 void
3762 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3764 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
3765 ENVELOPE *outgoing;
3766 BODY *body;
3767 void *msgtext;
3768 char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3769 int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3770 int rolemsg = 0, copytomsg = 0;
3771 long rflags;
3772 PAT_STATE dummy;
3773 REDRAFT_POS_S *redraft_pos = NULL;
3774 ACTION_S *role = NULL;
3776 outgoing = mail_newenvelope();
3778 dprint((4,"\n - attachment reply \n"));
3780 saved_from = (ADDRESS *) NULL;
3781 saved_to = (ADDRESS *) NULL;
3782 saved_cc = (ADDRESS *) NULL;
3783 saved_resent = (ADDRESS *) NULL;
3784 outgoing->subject = NULL;
3786 prefix = reply_quote_str(a->body->nested.msg->env);
3788 * For consistency, the first question is always "include text?"
3790 if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0
3791 && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3792 && reply_harvest(ps_global, msgno, a->number,
3793 a->body->nested.msg->env, &saved_from,
3794 &saved_to, &saved_cc, &saved_resent, &flags)){
3795 outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3796 NULL, 0);
3797 clear_cursor_pos();
3798 reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3799 saved_from, saved_to, saved_cc, saved_resent,
3800 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3801 if(errmsg){
3802 if(*errmsg){
3803 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3804 display_message(NO_OP_COMMAND);
3807 fs_give((void **)&errmsg);
3810 if(sp_expunge_count(stream)) /* current msg was expunged */
3811 goto seeyalater;
3813 /* Setup possible role */
3814 rflags = ROLE_REPLY;
3815 if(nonempty_patterns(rflags, &dummy)){
3816 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3817 if(confirm_role(rflags, &role))
3818 role = combine_inherited_role(role);
3819 else{ /* cancel reply */
3820 role = NULL;
3821 cmd_cancelled("Reply");
3822 goto seeyalater;
3826 if(role){
3827 rolemsg++;
3829 /* override fcc gotten in reply_seed */
3830 if(role->fcc && fcc)
3831 fs_give((void **) &fcc);
3834 if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3835 ADDRESS *us_in_to_and_cc, *ap;
3837 us_in_to_and_cc = (ADDRESS *) NULL;
3838 if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3839 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3840 NULL, us_in_to_and_cc, NULL,
3841 a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3842 reply_append_addr(&us_in_to_and_cc, ap);
3844 if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3845 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3846 NULL, us_in_to_and_cc, NULL,
3847 a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3848 reply_append_addr(&us_in_to_and_cc, ap);
3851 * A list of all of our addresses that appear in the To
3852 * and cc fields is in us_in_to_and_cc.
3853 * If there is exactly one address in that list then
3854 * use it for the outgoing From.
3856 if(us_in_to_and_cc && !us_in_to_and_cc->next){
3857 PINEFIELD *custom, *pf;
3858 ADDRESS *a = NULL;
3859 char *addr = NULL;
3862 * Check to see if this address is different from what
3863 * we would have used anyway. If it is, notify the user
3864 * with a status message. This is pretty hokey because we're
3865 * mimicking how pine_send would set the From address and
3866 * there is no coordination between the two.
3869 /* in case user has a custom From value */
3870 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3872 pf = (PINEFIELD *) fs_get(sizeof(*pf));
3873 memset((void *) pf, 0, sizeof(*pf));
3874 pf->name = cpystr("From");
3875 pf->addr = &a;
3876 if(set_default_hdrval(pf, custom) >= UseAsDef
3877 && pf->textbuf && pf->textbuf[0]){
3878 removing_trailing_white_space(pf->textbuf);
3879 (void)removing_double_quotes(pf->textbuf);
3880 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3881 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3882 if(addr)
3883 fs_give((void **) &addr);
3886 if(!*pf->addr)
3887 *pf->addr = generate_from();
3889 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3890 copytomsg++;
3891 if(!role){
3892 role = (ACTION_S *) fs_get(sizeof(*role));
3893 memset((void *) role, 0, sizeof(*role));
3894 role->is_a_role = 1;
3897 role->from = us_in_to_and_cc;
3898 us_in_to_and_cc = NULL;
3901 free_customs(custom);
3902 free_customs(pf);
3905 if(us_in_to_and_cc)
3906 mail_free_address(&us_in_to_and_cc);
3910 if(role){
3911 if(rolemsg && copytomsg)
3912 q_status_message1(SM_ORDER, 3, 4,
3913 _("Replying using role \"%s\" and To as From"), role->nick);
3914 else if(rolemsg)
3915 q_status_message1(SM_ORDER, 3, 4,
3916 _("Replying using role \"%s\""), role->nick);
3917 else if(copytomsg)
3918 q_status_message(SM_ORDER, 3, 4,
3919 _("Replying using incoming To as outgoing From"));
3922 outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3923 outgoing->references = reply_build_refs(a->body->nested.msg->env);
3924 outgoing->message_id = generate_message_id(role);
3926 if(!outgoing->to && !outgoing->cc
3927 && !outgoing->bcc && !outgoing->newsgroups)
3928 q_status_message(SM_ORDER | SM_DING, 3, 6,
3929 _("Warning: no valid addresses to reply to!"));
3932 * Now fix up the body...
3934 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3935 REPLY_S reply;
3937 memset((void *)&reply, 0, sizeof(reply));
3938 reply.forw = 1;
3939 if(a->body->nested.msg->body){
3940 char *charset;
3942 charset
3943 = parameter_val(a->body->nested.msg->body->parameter,
3944 "charset");
3946 if(charset && strucmp(charset, "us-ascii") != 0){
3947 CONV_TABLE *ct;
3950 * There is a non-ascii charset,
3951 * is there conversion happening?
3953 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3954 || !ct->table){
3955 reply.orig_charset = charset;
3956 charset = NULL;
3960 if(charset)
3961 fs_give((void **) &charset);
3964 if((body = reply_body(stream, a->body->nested.msg->env,
3965 a->body->nested.msg->body, msgno,
3966 tp = body_partno(stream, msgno, a->body),
3967 msgtext, prefix, include_text, role,
3968 1, &redraft_pos)) != NULL){
3969 /* partially formatted outgoing message */
3970 pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3971 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3973 pine_free_body(&body);
3974 ps_global->mangled_screen = 1;
3976 else
3977 q_status_message(SM_ORDER | SM_DING, 3, 4,
3978 _("Error building message body"));
3980 fs_give((void **) &tp);
3981 if(reply.orig_charset)
3982 fs_give((void **)&reply.orig_charset);
3984 else
3985 q_status_message(SM_ORDER | SM_DING, 3, 4,
3986 _("Error allocating message text"));
3989 seeyalater:
3990 mail_free_envelope(&outgoing);
3991 mail_free_address(&saved_from);
3992 mail_free_address(&saved_to);
3993 mail_free_address(&saved_cc);
3994 mail_free_address(&saved_resent);
3996 if(prefix)
3997 fs_give((void **) &prefix);
3999 if(fcc)
4000 fs_give((void **) &fcc);
4002 free_redraft_pos(&redraft_pos);
4003 free_action(&role);
4007 void
4008 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
4010 char *errstr;
4012 if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
4013 q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
4017 void
4018 pipe_attachment(long int msgno, ATTACH_S *a)
4020 char *err, *resultfilename = NULL, prompt[80], *p;
4021 int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
4022 long ku;
4023 PIPE_S *syspipe;
4024 HelpType help;
4025 char pipe_command[MAXPATH+1];
4026 unsigned flagsforhist = 1; /* raw=2 /capture=1 */
4027 static HISTORY_S *history = NULL;
4028 ESCKEY_S pipe_opt[6];
4030 if(ps_global->restricted){
4031 q_status_message(SM_ORDER | SM_DING, 0, 4,
4032 "Alpine demo can't pipe attachments");
4033 return;
4036 help = NO_HELP;
4037 pipe_command[0] = '\0';
4039 init_hist(&history, HISTSIZE);
4040 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4041 if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
4042 strncpy(pipe_command, p, sizeof(pipe_command));
4043 pipe_command[sizeof(pipe_command)-1] = '\0';
4044 if(history->hist[history->curindex]){
4045 flagsforhist = history->hist[history->curindex]->flags;
4046 raw = (flagsforhist & 0x2) ? 1 : 0;
4047 capture = (flagsforhist & 0x1) ? 1 : 0;
4051 pipe_opt[j].ch = 0;
4052 pipe_opt[j].rval = 0;
4053 pipe_opt[j].name = "";
4054 pipe_opt[j++].label = "";
4056 pipe_opt[j].ch = ctrl('W');
4057 pipe_opt[j].rval = 10;
4058 pipe_opt[j].name = "^W";
4059 pipe_opt[j++].label = NULL;
4061 pipe_opt[j].ch = ctrl('Y');
4062 pipe_opt[j].rval = 11;
4063 pipe_opt[j].name = "^Y";
4064 pipe_opt[j++].label = NULL;
4066 pipe_opt[j].ch = KEY_UP;
4067 pipe_opt[j].rval = 30;
4068 pipe_opt[j].name = "";
4069 ku = j;
4070 pipe_opt[j++].label = "";
4072 pipe_opt[j].ch = KEY_DOWN;
4073 pipe_opt[j].rval = 31;
4074 pipe_opt[j].name = "";
4075 pipe_opt[j++].label = "";
4077 pipe_opt[j].ch = -1;
4079 while(1){
4080 int flags;
4082 snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
4083 a->number, capture ? "" : "(Free Output) ");
4084 prompt[sizeof(prompt)-1] = '\0';
4085 pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
4086 pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
4089 * 2 is really 1 because there will be one real entry and
4090 * one entry of "" because of the get_prev_hist above.
4092 if(items_in_hist(history) > 2){
4093 pipe_opt[ku].name = HISTORY_UP_KEYNAME;
4094 pipe_opt[ku].label = HISTORY_KEYLABEL;
4095 pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
4096 pipe_opt[ku+1].label = HISTORY_KEYLABEL;
4098 else{
4099 pipe_opt[ku].name = "";
4100 pipe_opt[ku].label = "";
4101 pipe_opt[ku+1].name = "";
4102 pipe_opt[ku+1].label = "";
4105 flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
4106 rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
4107 sizeof(pipe_command), prompt,
4108 pipe_opt, help, &flags);
4109 if(rc == -1){
4110 q_status_message(SM_ORDER | SM_DING, 3, 4,
4111 "Internal problem encountered");
4112 break;
4114 else if(rc == 10){
4115 raw = !raw; /* flip raw text */
4117 else if(rc == 11){
4118 capture = !capture; /* flip capture output */
4120 else if(rc == 30){
4121 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4122 if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
4123 strncpy(pipe_command, p, sizeof(pipe_command));
4124 pipe_command[sizeof(pipe_command)-1] = '\0';
4125 if(history->hist[history->curindex]){
4126 flagsforhist = history->hist[history->curindex]->flags;
4127 raw = (flagsforhist & 0x2) ? 1 : 0;
4128 capture = (flagsforhist & 0x1) ? 1 : 0;
4131 else
4132 Writechar(BELL, 0);
4134 else if(rc == 31){
4135 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4136 if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
4137 strncpy(pipe_command, p, sizeof(pipe_command));
4138 pipe_command[sizeof(pipe_command)-1] = '\0';
4139 if(history->hist[history->curindex]){
4140 flagsforhist = history->hist[history->curindex]->flags;
4141 raw = (flagsforhist & 0x2) ? 1 : 0;
4142 capture = (flagsforhist & 0x1) ? 1 : 0;
4145 else
4146 Writechar(BELL, 0);
4148 else if(rc == 0){
4149 if(pipe_command[0] == '\0'){
4150 cmd_cancelled("Pipe command");
4151 break;
4154 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4155 save_hist(history, pipe_command, flagsforhist, NULL);
4157 flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
4158 flags |= (raw ? PIPE_RAW : 0);
4159 if(!capture){
4160 #ifndef _WINDOWS
4161 ClearScreen();
4162 fflush(stdout);
4163 clear_cursor_pos();
4164 ps_global->mangled_screen = 1;
4165 #endif
4166 flags |= PIPE_RESET;
4169 if((syspipe = open_system_pipe(pipe_command,
4170 (flags&PIPE_RESET) ? NULL : &resultfilename,
4171 NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
4172 int is_text = 0;
4173 gf_io_t pc; /* wire up a generic putchar */
4175 is_text = (a && a->body && a->body->type == TYPETEXT);
4177 gf_set_writec(&pc, syspipe, 0L, PipeStar,
4178 (is_text && !raw) ? WRITE_TO_LOCALE : 0);
4180 /*------ Write the image to a temporary file ------*/
4181 if(raw){ /* pipe raw text */
4182 FETCH_READC_S fetch_part;
4184 err = NULL;
4186 if(capture)
4187 we_cancel = busy_cue(NULL, NULL, 1);
4188 else
4189 suspend_busy_cue();
4191 gf_filter_init();
4192 fetch_readc_init(&fetch_part, ps_global->mail_stream,
4193 msgno, a->number, a->body->size.bytes, 0, 0);
4194 gf_link_filter(gf_nvtnl_local, NULL);
4195 err = gf_pipe(FETCH_READC, pc);
4197 if(capture){
4198 if(we_cancel)
4199 cancel_busy_cue(0);
4201 else
4202 resume_busy_cue(0);
4204 else{
4205 /* BUG: there's got to be a better way */
4206 if(!capture)
4207 ps_global->print = (PRINT_S *) 1;
4209 suspend_busy_cue();
4210 err = detach(ps_global->mail_stream, msgno,
4211 a->number, 0L, (long *)NULL, pc, NULL, 0);
4212 ps_global->print = (PRINT_S *) NULL;
4213 resume_busy_cue(0);
4216 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
4218 if(err)
4219 q_status_message1(SM_ORDER | SM_DING, 3, 4,
4220 _("Error detaching for pipe: %s"), err);
4222 display_output_file(resultfilename,
4223 (err)
4224 ? _("PIPE ATTACHMENT (ERROR)")
4225 : _("PIPE ATTACHMENT"),
4226 NULL, DOF_EMPTY);
4228 fs_give((void **) &resultfilename);
4230 else
4231 q_status_message(SM_ORDER | SM_DING, 3, 4,
4232 _("Error opening pipe"));
4234 break;
4236 else if(rc == 1){
4237 cmd_cancelled("Pipe");
4238 break;
4240 else if(rc == 3)
4241 help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
4247 delete_attachment(long int msgno, ATTACH_S *a)
4249 int expbits, rv = 0;
4251 if(!msgno_exceptions(ps_global->mail_stream, msgno,
4252 a->number, &expbits, FALSE)
4253 || !(expbits & MSG_EX_DELETE)){
4254 expbits |= MSG_EX_DELETE;
4255 msgno_exceptions(ps_global->mail_stream, msgno,
4256 a->number, &expbits, TRUE);
4258 q_status_message1(SM_ORDER, 0, 3,
4259 _("Part %s will be omitted only if message is Saved"),
4260 a->number);
4261 rv = 1;
4263 else
4264 q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
4265 a->number);
4267 return(rv);
4272 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
4274 int rv = 0;
4276 if(msgno_exceptions(ps_global->mail_stream, msgno,
4277 a->number, expbitsp, FALSE)
4278 && ((*expbitsp) & MSG_EX_DELETE)){
4279 (*expbitsp) ^= MSG_EX_DELETE;
4280 msgno_exceptions(ps_global->mail_stream, msgno,
4281 a->number, expbitsp, TRUE);
4282 rv = 1;
4284 else
4285 q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
4286 a->number);
4288 return(rv);
4292 /*----------------------------------------------------------------------
4293 Resolve any deferred tests for attachment displayability
4295 Args: attachment structure
4297 Returns: undefer's attachment's displayability test
4298 ----*/
4300 dispatch_attachment(ATTACH_S *a)
4302 if(a->test_deferred){
4303 a->test_deferred = 0;
4304 a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
4307 return(a->can_display);
4311 #ifdef _WINDOWS
4313 scroll_att_popup(sparms, in_handle)
4314 SCROLL_S *sparms;
4315 int in_handle;
4317 MPopup scrat_popup[20];
4318 int i = -1, n;
4320 if(in_handle){
4321 scrat_popup[++i].type = tIndex;
4322 scrat_popup[i].label.style = lNormal;
4323 scrat_popup[i].label.string = "View Selectable Item";
4324 scrat_popup[i].data.val = ctrl('L');
4327 scrat_popup[++i].type = tQueue;
4328 scrat_popup[i].label.style = lNormal;
4329 scrat_popup[i].label.string = "&Save";
4330 scrat_popup[i].data.val = 'S';
4332 scrat_popup[++i].type = tQueue;
4333 scrat_popup[i].label.style = lNormal;
4334 if(msgno_exceptions(ps_global->mail_stream,
4335 mn_m2raw(ps_global->msgmap,
4336 mn_get_cur(ps_global->msgmap)),
4337 scrat_attachp->number, &n, FALSE)
4338 && (n & MSG_EX_DELETE)){
4339 scrat_popup[i].label.string = "&Undelete";
4340 scrat_popup[i].data.val = 'U';
4342 else{
4343 scrat_popup[i].label.string = "&Delete";
4344 scrat_popup[i].data.val = 'D';
4347 if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
4348 scrat_popup[++i].type = tQueue;
4349 scrat_popup[i].label.style = lNormal;
4350 scrat_popup[i].label.string = "&Reply";
4351 scrat_popup[i].data.val = 'R';
4353 scrat_popup[++i].type = tQueue;
4354 scrat_popup[i].label.style = lNormal;
4355 scrat_popup[i].label.string = "&Forward";
4356 scrat_popup[i].data.val = 'f';
4359 scrat_popup[++i].type = tSeparator;
4361 scrat_popup[++i].type = tQueue;
4362 scrat_popup[i].label.style = lNormal;
4363 scrat_popup[i].label.string = "Attachment Index";
4364 scrat_popup[i].data.val = '<';
4366 scrat_popup[++i].type = tTail;
4368 return(mswin_popup(scrat_popup) == 0 && in_handle);
4372 void
4373 display_att_window(a)
4374 ATTACH_S *a;
4376 #if !defined(DOS) && !defined(OS2)
4377 char prefix[8];
4378 #endif
4380 if(a->body->type == TYPEMULTIPART){
4381 if(a->body->subtype){
4382 /* if(!strucmp(a->body->subtype, "digest"))
4383 display_digest_att(msgno, a, flags);
4384 else */
4385 q_status_message1(SM_ORDER, 3, 5,
4386 "Can't display Multipart/%s",
4387 a->body->subtype);
4389 else
4390 q_status_message(SM_ORDER, 3, 5,
4391 "Can't display unknown Multipart Subtype");
4393 /* else if(MIME_VCARD_A(a))
4394 display_vcard_att_window(msgno, a, flags);*/
4395 else if(a->body->type == TYPETEXT)
4396 display_text_att_window(a);
4397 else if(a->body->type == TYPEMESSAGE)
4398 display_msg_att_window(a);
4402 void
4403 display_text_att_window(a)
4404 ATTACH_S *a;
4406 STORE_S *store;
4407 long msgno;
4409 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4411 if(store = format_text_att(msgno, a, NULL)){
4412 if (mswin_displaytext("ATTACHED TEXT",
4413 so_text(store),
4414 strlen((char *) so_text(store)),
4415 NULL, NULL, 0) >= 0)
4416 store->txt = (void *) NULL; /* free'd in mswin_displaytext */
4418 so_give(&store); /* free resources associated with store */
4420 else
4421 q_status_message(SM_ORDER | SM_DING, 3, 3,
4422 "Error allocating space for attachment.");
4426 void
4427 display_msg_att_window(a)
4428 ATTACH_S *a;
4430 STORE_S *store;
4431 gf_io_t pc;
4432 ATTACH_S *ap = a;
4433 long msgno;
4435 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4437 /* BUG, should check this return code */
4438 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
4440 /* initialize a storage object */
4441 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
4443 gf_set_so_writec(&pc, store);
4445 if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
4446 && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
4447 strlen((char *) so_text(store)),
4448 NULL, NULL, 0) >= 0)
4449 /* free'd in mswin_displaytext */
4450 store->txt = (void *) NULL;
4452 gf_clear_so_writec(store);
4454 so_give(&store);
4456 else
4457 q_status_message(SM_ORDER | SM_DING, 3, 3,
4458 "Error allocating space for message.");
4460 #endif