* Addition of a link to the Apache License 2.0. This is available from
[alpine.git] / alpine / mailpart.c
blob0e742fc731e864017304c67a6a65e89bb4d0eace
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-2020 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(ctmp && ctmp->dstring){
1010 char *p = tmp_20k_buf;
1011 int i, col, x = 0, totlen;
1013 if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
1014 x = 2;
1015 if(ctmp == current){
1016 return_line = dline + HEADER_ROWS(ps);
1017 PutLine0(dline + HEADER_ROWS(ps), 0, "->");
1019 else
1020 PutLine0(dline + HEADER_ROWS(ps), 0, " ");
1023 * Only paint lines if we have to...
1025 if(screen->current)
1026 continue;
1028 else if(ctmp == current){
1029 return_line = dline + HEADER_ROWS(ps);
1030 StartInverse();
1033 totlen = strlen(ctmp->dstring);
1036 * Copy the value to a temp buffer expanding tabs.
1037 * Assume the caller set the widths so as not to overflow the
1038 * right margin.
1040 for(i=0,col=x; ctmp->dstring[i]; i++){
1041 if(ctmp->dstring[i] == TAB){
1042 if((p-tmp_20k_buf) < SIZEOF_20KBUF-8)
1044 *p++ = ' ';
1045 while((++col)&0x07);
1047 else{
1048 col += width_at_this_position((unsigned char *) &ctmp->dstring[i], totlen-i);
1049 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1050 *p++ = ctmp->dstring[i];
1055 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1056 *p = '\0';
1058 PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
1060 if(ctmp == current
1061 && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
1062 EndInverse();
1064 else
1065 ClearLine(dline + HEADER_ROWS(ps));
1068 ps->mangled_body = 0;
1069 screen->top_line = top_line;
1070 screen->current = current;
1071 return(return_line);
1075 /*----------------------------------------------------------------------
1076 Redraw the attachment screen based on the global "att_screen" struct
1078 Args: none
1080 Result:
1081 ----*/
1082 void
1083 attachment_screen_redrawer(void)
1085 bitmap_t bitmap;
1087 update_att_screen_titlebar();
1088 ps_global->mangled_header = 0;
1089 ClearLine(1);
1091 ps_global->mangled_body = 1;
1092 (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
1094 setbitmap(bitmap);
1095 draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
1096 1-FOOTER_ROWS(ps_global), 0, SameMenu);
1100 void
1101 update_att_screen_titlebar(void)
1103 long raw_msgno;
1104 COLOR_PAIR *returned_color = NULL;
1105 COLOR_PAIR *titlecolor = NULL;
1106 int colormatch;
1107 SEARCHSET *ss = NULL;
1108 PAT_STATE *pstate = NULL;
1110 if(ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
1111 raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
1112 ss = mail_newsearchset();
1113 ss->first = ss->last = (unsigned long) raw_msgno;
1115 if(ss){
1116 colormatch = get_index_line_color(ps_global->mail_stream,
1117 ss, &pstate, &returned_color);
1118 mail_free_searchset(&ss);
1121 * This is a bit tricky. If there is a colormatch but
1122 * returned_color
1123 * is NULL, that means that the user explicitly wanted the
1124 * Normal color used in this index line, so that is what we
1125 * use. If no colormatch then we will use the TITLE color
1126 * instead of Normal.
1128 if(colormatch){
1129 if(returned_color)
1130 titlecolor = returned_color;
1131 else
1132 titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
1133 ps_global->VAR_NORM_BACK_COLOR);
1136 if(titlecolor
1137 && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
1138 char cbuf[MAXCOLORLEN+1];
1140 strncpy(cbuf, titlecolor->fg, sizeof(cbuf));
1141 cbuf[sizeof(cbuf)-1] = '\0';
1142 strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
1143 titlecolor->fg[MAXCOLORLEN] = '\0';
1144 strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
1145 titlecolor->bg[MAXCOLORLEN] = '\0';
1149 /* Did the color change? */
1150 if((!titlecolor && att_screen->titlecolor)
1152 (titlecolor && !att_screen->titlecolor)
1154 (titlecolor && att_screen->titlecolor
1155 && (strcmp(titlecolor->fg, att_screen->titlecolor->fg)
1156 || strcmp(titlecolor->bg, att_screen->titlecolor->bg)))){
1158 if(att_screen->titlecolor)
1159 free_color_pair(&att_screen->titlecolor);
1161 att_screen->titlecolor = titlecolor;
1162 titlecolor = NULL;
1165 if(titlecolor)
1166 free_color_pair(&titlecolor);
1169 set_titlebar(_("ATTACHMENT INDEX"), ps_global->mail_stream,
1170 ps_global->context_current, ps_global->cur_folder,
1171 ps_global->msgmap, 1, MessageNumber, 0, 0,
1172 att_screen->titlecolor);
1176 /*----------------------------------------------------------------------
1177 Seek back from the given display line to the beginning of the list
1179 Args: p -- display linked list
1181 Result:
1182 ----*/
1183 ATDISP_S *
1184 first_attline(ATDISP_S *p)
1186 while(p && p->prev)
1187 p = p->prev;
1189 return(p);
1194 init_att_progress(char *msg, MAILSTREAM *stream, struct mail_bodystruct *body)
1196 if((save_att_length = body->size.bytes) != 0){
1197 /* if there are display filters, factor in extra copy */
1198 if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
1199 save_att_length += body->size.bytes;
1201 /* if remote folder and segment not cached, factor in IMAP fetch */
1202 if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
1203 && !((body->type == TYPETEXT && body->contents.text.data)
1204 || ((body->type == TYPEMESSAGE)
1205 && body->nested.msg && body->nested.msg->text.text.data)
1206 || body->contents.text.data))
1207 save_att_length += body->size.bytes;
1209 gf_filter_init(); /* reset counters */
1210 pine_gets_bytes(1);
1211 save_att_piped(1);
1212 return(busy_cue(msg, save_att_percent, 0));
1215 return(0);
1219 long
1220 save_att_piped(int reset)
1222 static long x;
1223 long y;
1225 if(reset){
1226 x = y = 0L;
1228 else if((y = gf_bytes_piped()) >= x){
1229 x = y;
1230 y = 0;
1233 return(x + y);
1238 save_att_percent(void)
1240 int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
1241 / save_att_length);
1242 return(MIN(i, 100));
1246 /*----------------------------------------------------------------------
1247 Save the given attachment associated with the given message no
1249 Args: ps
1251 Result:
1252 ----*/
1253 void
1254 save_attachment(int qline, long int msgno, ATTACH_S *a)
1256 if(ps_global->restricted){
1257 q_status_message(SM_ORDER | SM_DING, 0, 4,
1258 "Alpine demo can't save attachments");
1259 return;
1262 if(MIME_MSG_A(a))
1263 save_msg_att(msgno, a);
1264 else if(MIME_DGST_A(a))
1265 save_digest_att(msgno, a);
1266 else if(MIME_VCARD_A(a))
1267 save_vcard_att(ps_global, qline, msgno, a);
1268 else
1269 write_attachment(qline, msgno, a, "SAVE");
1273 /*----------------------------------------------------------------------
1274 Save the given attachment associated with the given message no
1276 Args: ps
1278 Result:
1279 ----*/
1280 void
1281 export_attachment(int qline, long int msgno, ATTACH_S *a)
1283 if(ps_global->restricted){
1284 q_status_message(SM_ORDER | SM_DING, 0, 4,
1285 "Alpine demo can't export attachments");
1286 return;
1289 if(MIME_MSG_A(a))
1290 export_msg_att(msgno, a);
1291 else if(MIME_DGST_A(a))
1292 export_digest_att(msgno, a);
1293 else
1294 q_status_message1(SM_ORDER, 0, 3,
1295 _("Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."),
1296 body_type_names(a->body->type));
1300 void
1301 write_attachment(int qline, long int msgno, ATTACH_S *a, char *method)
1303 char filename[MAXPATH+1], full_filename[MAXPATH+1],
1304 title_buf[64], *err;
1305 int r, rflags = GER_NONE, we_cancel = 0, flags;
1306 static HISTORY_S *history = NULL;
1307 static ESCKEY_S att_save_opts[] = {
1308 {ctrl('T'), 10, "^T", N_("To Files")},
1309 {-1, 0, NULL, NULL},
1310 {-1, 0, NULL, NULL},
1311 {-1, 0, NULL, NULL}};
1313 /*------- Figure out suggested file name ----*/
1314 filename[0] = full_filename[0] = '\0';
1315 (void) get_filename_parameter(filename, sizeof(filename), a->body, NULL);
1317 dprint((9, "export_attachment(name: %s)\n",
1318 filename ? filename : "?"));
1320 r = 0;
1321 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1322 if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
1323 att_save_opts[++r].ch = ctrl('V');
1324 att_save_opts[r].rval = 12;
1325 att_save_opts[r].name = "^V";
1326 att_save_opts[r].label = N_("Downld Msg");
1328 #endif /* !(DOS || MAC) */
1330 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1331 att_save_opts[++r].ch = ctrl('I');
1332 att_save_opts[r].rval = 11;
1333 att_save_opts[r].name = "TAB";
1334 att_save_opts[r].label = N_("Complete");
1337 att_save_opts[++r].ch = -1;
1339 snprintf(title_buf, sizeof(title_buf), "%s ATTACHMENT", method);
1340 title_buf[sizeof(title_buf)-1] = '\0';
1342 flags = (a && a->body && a->body->type == TYPETEXT ? GE_BINARY : 0)
1343 | GE_SEQ_SENSITIVE;
1345 r = get_export_filename(ps_global, filename, NULL, full_filename,
1346 sizeof(filename), "attachment", title_buf,
1347 att_save_opts, &rflags, qline, flags, &history);
1349 if(r < 0){
1350 switch(r){
1351 case -1:
1352 cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
1353 break;
1355 case -2:
1356 q_status_message1(SM_ORDER, 0, 2,
1357 _("Can't save to file outside of %s"),
1358 ps_global->VAR_OPER_DIR);
1359 break;
1362 return;
1364 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1365 else if(r == 12){ /* Download */
1366 char cmd[MAXPATH], *tfp = NULL;
1367 PIPE_S *syspipe;
1368 gf_io_t pc;
1369 long len;
1370 STORE_S *store;
1371 char prompt_buf[256];
1373 if(ps_global->restricted){
1374 q_status_message(SM_ORDER | SM_DING, 3, 3,
1375 "Download disallowed in restricted mode");
1376 return;
1379 err = NULL;
1380 tfp = temp_nam(NULL, "pd");
1381 dprint((1, "Download attachment called!\n"));
1382 if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
1384 snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
1385 prompt_buf[sizeof(prompt_buf)-1] = '\0';
1386 we_cancel = init_att_progress(prompt_buf,
1387 ps_global->mail_stream,
1388 a->body);
1390 gf_set_so_writec(&pc, store);
1391 if((err = detach(ps_global->mail_stream, msgno,
1392 a->number, 0L, &len, pc, NULL, 0)) != NULL)
1393 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1394 "%s: Error writing attachment to \"%s\"",
1395 err, tfp);
1397 /* cancel regardless, so it doesn't get in way of xfer */
1398 cancel_busy_cue(0);
1400 gf_clear_so_writec(store);
1401 if(so_give(&store)) /* close file */
1402 err = "Error writing tempfile for download";
1404 if(!err){
1405 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
1406 ps_global->VAR_DOWNLOAD_CMD, tfp);
1407 if((syspipe = open_system_pipe(cmd, NULL, NULL,
1408 PIPE_USER | PIPE_RESET,
1409 0, pipe_callback, pipe_report_error)) != NULL)
1410 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1411 else
1412 q_status_message(SM_ORDER | SM_DING, 3, 3,
1413 err = "Error running download command");
1416 else
1417 q_status_message(SM_ORDER | SM_DING, 3, 3,
1418 err = "Error building temp file for download");
1420 if(tfp){
1421 our_unlink(tfp);
1422 fs_give((void **)&tfp);
1425 if(!err)
1426 q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
1427 a->number);
1429 return;
1431 #endif /* !(DOS || MAC) */
1433 (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
1434 rflags, full_filename);
1439 * Args stream --
1440 * msgno -- raw message number
1441 * a -- attachment struct
1442 * flags -- comes from get_export_filename
1443 * GER_OVER -- the file was truncated before we wrote
1444 * GER_APPEND -- the file was not truncated prior to our writing,
1445 * so we were appending
1446 * else -- the file didn't previously exist
1447 * file -- the full pathname of the file
1449 * Returns 1 for successful write, 0 for nothing to write, -1 for error
1452 write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
1454 char *l_string, sbuf[256], *err;
1455 int is_text, we_cancel = 0, dt_flags = 0, so_flags;
1456 long len, orig_size;
1457 gf_io_t pc;
1458 STORE_S *store;
1460 if(!(a && a->body && a->number && a->number[0] && file && file[0]
1461 && stream))
1462 return 0;
1464 is_text = (a && a->body && a->body->type == TYPETEXT);
1466 if(flags & GER_APPEND)
1467 orig_size = name_file_size(file);
1469 if(flags & GER_BINARY)
1470 dt_flags |= DT_BINARY;
1472 so_flags = (is_text & !(flags & GER_BINARY) ? WRITE_TO_LOCALE : 0)
1473 | WRITE_ACCESS ;
1475 store = so_get(FileStar, file, so_flags);
1476 if(store == NULL){
1477 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1478 /* TRANSLATORS: Error opening destination <filename>: <error text> */
1479 _("Error opening destination %s: %s"),
1480 file, error_description(errno));
1481 return -1;
1484 snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
1485 sbuf[sizeof(sbuf)-1] = '\0';
1486 we_cancel = init_att_progress(sbuf, stream, a->body);
1488 gf_set_so_writec(&pc, store);
1489 err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, dt_flags);
1490 gf_clear_so_writec(store);
1492 if(we_cancel)
1493 cancel_busy_cue(0);
1495 if(so_give(&store)) /* close file */
1496 err = error_description(errno);
1498 if(err){
1499 if(!(flags & (GER_APPEND | GER_OVER)))
1500 our_unlink(file);
1501 else
1502 our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
1504 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1505 /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
1506 _("%s: Error writing attachment to \"%s\""),
1507 err, file);
1508 return -1;
1510 else{
1511 l_string = cpystr(byte_string(len));
1512 q_status_message8(SM_ORDER, 0, 4,
1513 "Part %s, %s%s %s to \"%s\"%s%s%s",
1514 a->number,
1515 is_text
1516 ? comatose(a->body->size.lines) : l_string,
1517 is_text ? " lines" : "",
1518 flags & GER_OVER
1519 ? "overwritten"
1520 : flags & GER_APPEND ? "appended" : "written",
1521 file,
1522 (is_text || len == a->body->size.bytes)
1523 ? "" : "(decoded from ",
1524 (is_text || len == a->body->size.bytes)
1525 ? "" : byte_string(a->body->size.bytes),
1526 is_text || len == a->body->size.bytes
1527 ? "" : ")");
1528 fs_give((void **)&l_string);
1529 return 1;
1534 char *
1535 write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
1537 char *err = NULL;
1538 long start_of_append;
1539 gf_io_t pc;
1540 MESSAGECACHE *mc;
1542 if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
1543 && (*ap)->body->nested.msg->env){
1544 start_of_append = so_tell(store);
1546 gf_set_so_writec(&pc, store);
1547 if(!(ps_global->mail_stream && msgno > 0L
1548 && msgno <= ps_global->mail_stream->nmsgs
1549 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1550 mc = NULL;
1552 if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
1553 || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
1554 err = error_description(errno);
1556 gf_clear_so_writec(store);
1558 if(err)
1559 ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
1561 else
1562 err = "Can't export message. Missing Envelope data";
1564 return(err);
1568 /*----------------------------------------------------------------------
1569 Save the attachment message/rfc822 to specified folder
1571 Args:
1573 Result:
1574 ----*/
1575 void
1576 save_msg_att(long int msgno, ATTACH_S *a)
1578 char newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
1579 char date[64], nmsgs[80];
1580 CONTEXT_S *cntxt = NULL;
1581 int our_stream = 0, rv;
1582 MAILSTREAM *save_stream;
1583 MESSAGECACHE *mc;
1585 snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
1586 nmsgs[sizeof(nmsgs)-1] = '\0';
1587 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
1588 a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
1589 if(strucmp(newfolder, ps_global->inbox_name) == 0){
1590 save_folder = ps_global->VAR_INBOX_PATH;
1591 cntxt = NULL;
1593 else
1594 save_folder = newfolder;
1596 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1598 mc = (msgno > 0L && ps_global->mail_stream
1599 && msgno <= ps_global->mail_stream->nmsgs)
1600 ? mail_elt(ps_global->mail_stream, msgno) : NULL;
1601 flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
1602 if(mc && mc->day)
1603 mail_date(date, mc);
1604 else
1605 *date = '\0';
1607 if(pith_opt_save_size_changed_prompt)
1608 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1610 rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
1612 if(pith_opt_save_size_changed_prompt)
1613 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1615 if(flags)
1616 fs_give((void **) &flags);
1618 if(rv == 1)
1619 q_status_message2(SM_ORDER, 0, 4,
1620 _("Attached message (part %s) saved to \"%s\""),
1621 a->number,
1622 save_folder);
1623 else if(rv == -1)
1624 cmd_cancelled("Attached message Save");
1625 /* else whatever broke in save_fetch_append shoulda bitched */
1627 if(our_stream)
1628 mail_close(save_stream);
1633 /*----------------------------------------------------------------------
1634 Save the message/rfc822 in the given digest to the specified folder
1636 Args:
1638 Result:
1639 ----*/
1640 void
1641 save_digest_att(long int msgno, ATTACH_S *a)
1643 char newfolder[MAILTMPLEN], *save_folder,
1644 date[64], nmsgs[80];
1645 CONTEXT_S *cntxt = NULL;
1646 int our_stream = 0, rv, cnt = 0;
1647 MAILSTREAM *save_stream;
1648 ATTACH_S *ap;
1650 for(ap = a + 1;
1651 ap->description
1652 && !strncmp(a->number, ap->number, strlen(a->number));
1653 ap++)
1654 if(MIME_MSG(ap->body->type, ap->body->subtype))
1655 cnt++;
1657 snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
1658 nmsgs[sizeof(nmsgs)-1] = '\0';
1660 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
1661 nmsgs, NULL, 0, NULL, NULL, NULL)){
1662 save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
1663 ? ps_global->VAR_INBOX_PATH : newfolder;
1665 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1667 if(pith_opt_save_size_changed_prompt)
1668 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1670 for(ap = a + 1;
1671 ap->description
1672 && !strncmp(a->number, ap->number, strlen(a->number));
1673 ap++)
1674 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1675 *date = '\0';
1676 rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
1677 if(rv != 1)
1678 break;
1681 if(pith_opt_save_size_changed_prompt)
1682 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1684 if(rv == 1)
1685 q_status_message2(SM_ORDER, 0, 4,
1686 _("Attached digest (part %s) saved to \"%s\""),
1687 a->number,
1688 save_folder);
1689 else if(rv == -1)
1690 cmd_cancelled("Attached digest Save");
1691 /* else whatever broke in save_fetch_append shoulda bitched */
1693 if(our_stream)
1694 mail_close(save_stream);
1700 save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
1701 char *save_folder, CONTEXT_S *cntxt, char *date)
1703 STORE_S *so;
1704 int rv = 0;
1706 if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
1707 if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1708 *date = '\0';
1709 rv = save_fetch_append(ps_global->mail_stream, msgno,
1710 a->number,
1711 save_stream, save_folder, cntxt,
1712 a->body->size.bytes,
1713 NULL, date, so);
1715 else{
1716 dprint((1, "Can't allocate store for save: %s\n",
1717 error_description(errno)));
1718 q_status_message(SM_ORDER | SM_DING, 3, 4,
1719 _("Problem creating space for message text."));
1720 rv = 0;
1724 return(rv);
1728 /*----------------------------------------------------------------------
1729 Export the attachment message/rfc822 to specified file
1731 Args:
1733 Result:
1734 ----*/
1735 void
1736 export_msg_att(long int msgno, ATTACH_S *a)
1738 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
1739 int rv, rflags = GER_NONE, i = 1;
1740 ATTACH_S *ap = a;
1741 STORE_S *store;
1742 static HISTORY_S *history = NULL;
1743 static ESCKEY_S opts[] = {
1744 {ctrl('T'), 10, "^T", N_("To Files")},
1745 {-1, 0, NULL, NULL},
1746 {-1, 0, NULL, NULL}};
1748 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1749 opts[i].ch = ctrl('I');
1750 opts[i].rval = 11;
1751 opts[i].name = "TAB";
1752 opts[i].label = N_("Complete");
1755 filename[0] = full_filename[0] = '\0';
1757 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1758 sizeof(filename), "msg attachment",
1759 /* TRANSLATORS: Message Attachment (a screen title) */
1760 _("MSG ATTACHMENT"), opts,
1761 &rflags, -FOOTER_ROWS(ps_global),
1762 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1764 if(rv < 0){
1765 switch(rv){
1766 case -1:
1767 cmd_cancelled("Export");
1768 break;
1770 case -2:
1771 q_status_message1(SM_ORDER, 0, 2,
1772 _("Can't export to file outside of %s"),
1773 ps_global->VAR_OPER_DIR);
1774 break;
1777 return;
1780 /* With name in hand, allocate storage object and save away... */
1781 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1782 if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
1783 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1784 else
1785 q_status_message3(SM_ORDER, 0, 4,
1786 _("Attached message (part %s) %s to \"%s\""),
1787 a->number,
1788 rflags & GER_OVER
1789 ? _("overwritten")
1790 : rflags & GER_APPEND ? _("appended") : _("written"),
1791 full_filename);
1793 if(so_give(&store))
1794 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1795 _("Error writing %s: %s"),
1796 full_filename, error_description(errno));
1798 else
1799 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1800 /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
1801 _("Error opening file \"%s\" to export message: %s"),
1802 full_filename, error_description(errno));
1806 /*----------------------------------------------------------------------
1807 Export the message/rfc822 in the given digest to the specified file
1809 Args:
1811 Result:
1812 ----*/
1813 void
1814 export_digest_att(long int msgno, ATTACH_S *a)
1816 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
1817 int rv, rflags = GER_NONE, i = 1;
1818 long count = 0L;
1819 ATTACH_S *ap;
1820 static HISTORY_S *history = NULL;
1821 STORE_S *store;
1822 static ESCKEY_S opts[] = {
1823 {ctrl('T'), 10, "^T", N_("To Files")},
1824 {-1, 0, NULL, NULL},
1825 {-1, 0, NULL, NULL}};
1827 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1828 opts[i].ch = ctrl('I');
1829 opts[i].rval = 11;
1830 opts[i].name = "TAB";
1831 opts[i].label = N_("Complete");
1834 filename[0] = full_filename[0] = '\0';
1836 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1837 sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
1838 opts, &rflags, -FOOTER_ROWS(ps_global),
1839 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1841 if(rv < 0){
1842 switch(rv){
1843 case -1:
1844 cmd_cancelled("Export");
1845 break;
1847 case -2:
1848 q_status_message1(SM_ORDER, 0, 2,
1849 _("Can't export to file outside of %s"),
1850 ps_global->VAR_OPER_DIR);
1851 break;
1854 return;
1857 /* With name in hand, allocate storage object and save away... */
1858 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1859 count = 0;
1861 for(ap = a + 1;
1862 ap->description
1863 && !strncmp(a->number, ap->number, strlen(a->number))
1864 && !err;
1865 ap++){
1866 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1867 count++;
1868 err = write_attached_msg(msgno, &ap, store,
1869 !count && !(rflags & GER_APPEND));
1873 if(so_give(&store))
1874 err = error_description(errno);
1876 if(err){
1877 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1878 _("Error exporting: %s"), err);
1879 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1880 _("%s messages exported before error occurred"), err);
1882 else
1883 q_status_message4(SM_ORDER, 0, 4,
1884 "%s messages in digest (part %s) %s to \"%s\"",
1885 long2string(count),
1886 a->number,
1887 rflags & GER_OVER
1888 ? "overwritten"
1889 : rflags & GER_APPEND ? "appended" : "written",
1890 full_filename);
1892 else
1893 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1894 _("Error opening file \"%s\" to export digest: %s"),
1895 full_filename, error_description(errno));
1899 /*----------------------------------------------------------------------
1900 Print the given attachment associated with the given message no
1902 Args: ps
1904 Result:
1905 ----*/
1906 void
1907 print_attachment(int qline, long int msgno, ATTACH_S *a)
1909 char prompt[250];
1911 if(ps_global->restricted){
1912 q_status_message(SM_ORDER | SM_DING, 0, 4,
1913 "Alpine demo can't Print attachments");
1914 return;
1917 snprintf(prompt, sizeof(prompt), "attach%s %s",
1918 (a->body->type == TYPETEXT) ? "ment" : "ed message",
1919 MIME_DGST_A(a) ? "digest" : a->number);
1920 prompt[sizeof(prompt)-1] = '\0';
1921 if(open_printer(prompt) >= 0){
1922 if(MIME_MSG_A(a))
1923 (void) print_msg_att(msgno, a, 1);
1924 else if(MIME_DGST_A(a))
1925 print_digest_att(msgno, a);
1926 else
1927 (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
1929 close_printer();
1935 * Print the attachment message/rfc822 to specified file
1937 * Returns 1 on success, 0 on failure.
1940 print_msg_att(long int msgno, ATTACH_S *a, int first)
1942 ATTACH_S *ap = a;
1943 MESSAGECACHE *mc;
1945 if(!(ps_global->mail_stream && msgno > 0L
1946 && msgno <= ps_global->mail_stream->nmsgs
1947 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1948 mc = NULL;
1950 if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
1951 && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
1952 && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
1953 ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
1954 : 1)
1955 && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
1956 return(1);
1959 q_status_message2(SM_ORDER | SM_DING, 3, 3,
1960 _("Error printing message %s, part %s"),
1961 long2string(msgno), a->number);
1962 return(0);
1966 /*----------------------------------------------------------------------
1967 Print the attachment message/rfc822 to specified file
1969 Args:
1971 Result:
1972 ----*/
1973 void
1974 print_digest_att(long int msgno, ATTACH_S *a)
1976 ATTACH_S *ap;
1977 int next = 0;
1979 for(ap = a + 1;
1980 ap->description
1981 && !strncmp(a->number, ap->number, strlen(a->number));
1982 ap++){
1983 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1984 char *p = part_desc(ap->number, ap->body->nested.msg->body,
1985 0, 80, FM_NOINDENT, print_char);
1986 if(p){
1987 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1988 _("Can't print digest: %s"), p);
1989 break;
1991 else if(!print_msg_att(msgno, ap, !next))
1992 break;
1994 next++;
2000 display_html_external_attachment(long int msgno, ATTACH_S *a, int flags)
2002 char dir_path[MAXPATH+1];
2003 char *filename = NULL;
2004 char *file_path; /* file:///some/path/ */
2005 STORE_S *store;
2006 gf_io_t pc;
2007 char *err;
2008 int we_cancel = 0, saved, errs;
2009 char *tool;
2010 ATTACH_S *att;
2011 unsigned long rawno;
2013 if(a->body == NULL){
2014 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Attachment has no body!"));
2015 return 1;
2016 } else if (a->body->type != TYPETEXT
2017 || a->body->subtype == NULL
2018 || strucmp(a->body->subtype, "HTML")){
2019 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Not a TEXT/HTML attachment"));
2020 return 1;
2023 /* zero these variables, just in case. Do not try freeing them. They have short lives */
2024 for(att = ps_global->atmts; att->description != NULL; att++){
2025 att->cid_tmpfile = NULL;
2026 att->tmpdir = NULL;
2029 /* setup the environment first */
2030 if(!ps_global->html_dir){
2031 if(!html_directory_path(ps_global->VAR_HTML_DIRECTORY, dir_path, MAXPATH)){
2032 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2033 _("Error creating full path for %s"), ps_global->VAR_HTML_DIRECTORY);
2034 return 1;
2035 } else if (init_html_directory(dir_path) < 0){
2036 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error initializing %s"), dir_path);
2037 return 1;
2039 ps_global->html_dir = cpystr(dir_path);
2041 else{
2042 strncpy(dir_path, ps_global->html_dir, sizeof(dir_path));
2043 dir_path[sizeof(dir_path)-1] = '\0';
2046 if(create_random_dir(dir_path, sizeof(dir_path)) < 0){
2047 q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error creating temp dir in %s"), dir_path);
2048 return 1;
2051 a->tmpdir = cpystr(dir_path);
2052 add_html_log(&ps_global->html_dir_list, a->tmpdir);
2054 /* Process the text/html part */
2055 filename = temp_nam_ext(a->tmpdir, "tmp-html-", HTML_EXT);
2057 if(!filename){
2058 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2059 _("Error \"%s\", Can't create temporary file"),
2060 error_description(errno));
2061 return(1);
2064 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2065 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2066 _("Error \"%s\", Can't write file %s"),
2067 error_description(errno), filename);
2068 if(filename){
2069 our_unlink(filename);
2070 fs_give((void **)&filename);
2072 return(1);
2075 if(a->body->size.bytes){
2076 char msg_buf[128];
2078 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2079 a->description ? "\"" : "",
2080 a->description ? a->description : "attachment number ",
2081 a->description ? "" : a->number,
2082 a->description ? "\"" : "");
2083 msg_buf[sizeof(msg_buf)-1] = '\0';
2084 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream, a->body);
2087 gf_set_so_writec(&pc, store);
2089 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL,
2090 DT_EXTERNAL | ((flags & DA_ALLIMAGES) ? DT_ALLIMAGES : 0));
2092 gf_clear_so_writec(store);
2094 if(we_cancel)
2095 cancel_busy_cue(0);
2097 so_give(&store);
2099 /*----- Download all needed inline attachments ------*/
2100 saved = errs = 0;
2101 rawno = mn_m2raw(ps_global->msgmap, msgno);
2102 for (att = ps_global->atmts; rawno > 0 && att->description != NULL; att++){
2103 if(att->cid_tmpfile){
2104 if(write_attachment_to_file(ps_global->mail_stream, rawno,
2105 att, GER_NONE, att->cid_tmpfile) == 1)
2106 saved++;
2107 else
2108 errs++;
2109 fs_give((void **) &att->cid_tmpfile);
2111 if(att->tmpdir)
2112 fs_give((void **) &att->tmpdir);
2115 if(err){
2116 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2117 "%s: Error saving image to temp file %s",
2118 err, filename);
2119 if(filename){
2120 our_unlink(filename);
2121 fs_give((void **)&filename);
2123 return(1);
2126 tool = get_url_external_handler("http://", 1);
2127 if(tool == NULL) tool = get_url_external_handler("http://", 0);
2128 if(tool == NULL) tool = get_url_external_handler("https://", 1);
2129 if(tool == NULL) tool = get_url_external_handler("https://", 0);
2131 file_path = fs_get((strlen(filename) + strlen("file://") + 1)*sizeof(char));
2132 sprintf(file_path, "file://%s", filename);
2134 /*----- Run the viewer process ----*/
2135 if(do_url_launch(tool, file_path) == 0)
2136 q_status_message(SM_ORDER, 3, 3, "Opened message in external browser");
2137 else
2138 q_status_message(SM_ORDER|SM_DING, 3, 5, "Failed to open message in external browser");
2140 if(filename)
2141 fs_give((void **)&filename);
2143 if(file_path)
2144 fs_give((void **)&file_path);
2146 ps_global->mangled_screen = 1;
2148 return(0);
2152 /*----------------------------------------------------------------------
2153 Unpack and display the given attachment associated with given message no.
2155 Args: msgno -- message no attachment is part of
2156 a -- attachment to display
2158 Returns: 0 on success, non-zero (and error message queued) otherwise
2159 ----*/
2161 display_attachment(long int msgno, ATTACH_S *a, int flags)
2163 char *filename = NULL;
2164 char sender_filename[1000];
2165 char *extp = NULL;
2166 STORE_S *store;
2167 gf_io_t pc;
2168 char *err;
2169 int we_cancel = 0, rv;
2170 char prefix[70];
2171 char ext[32];
2172 char mtype[128];
2174 if(flags & DA_EXTERNAL)
2175 return display_html_external_attachment(msgno, a, flags);
2177 /*------- Display the attachment -------*/
2178 if(dispatch_attachment(a) == MCD_NONE){
2179 /*----- Can't display this type ------*/
2180 if(a->body->encoding < ENCOTHER)
2181 q_status_message4(SM_ORDER | SM_DING, 3, 5,
2182 /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
2183 _("Don't know how to display %s%s%s attachments.%s"),
2184 body_type_names(a->body->type),
2185 a->body->subtype ? "/" : "",
2186 a->body->subtype ? a->body->subtype :"",
2187 (flags & DA_SAVE) ? _(" Try Save.") : "");
2188 else
2189 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2190 _("Don't know how to unpack \"%s\" encoding"),
2191 body_encodings[(a->body->encoding <= ENCMAX)
2192 ? a->body->encoding : ENCOTHER]);
2194 return(1);
2196 else if(!(a->can_display & MCD_EXTERNAL)){
2197 if(a->body->type == TYPEMULTIPART){
2198 if(a->body->subtype){
2199 if(!strucmp(a->body->subtype, "digest"))
2200 display_digest_att(msgno, a, flags);
2201 else
2202 q_status_message1(SM_ORDER, 3, 5,
2203 _("Can't display Multipart/%s"),
2204 a->body->subtype);
2206 else
2207 q_status_message(SM_ORDER, 3, 5,
2208 _("Can't display unknown Multipart Subtype"));
2210 else if(MIME_VCARD_A(a))
2211 display_vcard_att(msgno, a, flags);
2212 else if( MIME_VCALENDAR(a->body->type, a->body->subtype))
2213 display_vcalendar_att(msgno, a, flags);
2214 else if(a->body->type == TYPETEXT){
2216 rv = display_text_att(msgno, a, flags);
2217 } while(rv == MC_FULLHDR);
2219 else if(a->body->type == TYPEMESSAGE){
2221 rv = display_msg_att(msgno, a, flags);
2222 } while(rv == MC_FULLHDR);
2225 ps_global->mangled_screen = 1;
2226 return(0);
2229 /* arrive here if MCD_EXTERNAL */
2231 if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
2232 && (!(flags & DA_DIDPROMPT)))
2233 if(want_to(_("View selected Attachment"), 'y',
2234 0, NO_HELP, WT_NORM) == 'n'){
2235 cmd_cancelled(NULL);
2236 return(1);
2239 sender_filename[0] = '\0';
2240 ext[0] = '\0';
2241 prefix[0] = '\0';
2243 if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
2244 && (a->can_display & MCD_EXT_PROMPT)){
2245 char prompt[256];
2247 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2248 a->body, &extp);
2249 snprintf(prompt, sizeof(prompt),
2250 "Attachment %s%s unrecognized. %s%s%s",
2251 a->body->subtype,
2252 strlen(a->body->subtype) > 12 ? "..." : "",
2253 (extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
2254 (extp && extp[0]) ? extp : "",
2255 (extp && extp[0]) ? ")" : "");
2257 if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
2258 cmd_cancelled(NULL);
2259 return(1);
2263 /*------ Write the image to a temporary file ------*/
2265 /* create type/subtype in mtype */
2266 strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
2267 mtype[sizeof(mtype)-1] = '\0';
2268 if(a->body->subtype){
2269 strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
2270 mtype[sizeof(mtype)-1] = '\0';
2271 strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
2272 mtype[sizeof(mtype)-1] = '\0';
2276 * If we haven't already gotten the filename parameter, get it
2277 * now. It may be used in the temporary filename and possibly
2278 * for its extension.
2280 if(!sender_filename[0])
2281 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2282 a->body, &extp);
2284 if(check_mime_type_by_extension(extp, mtype)
2285 || (!set_mime_extension_by_type(ext, mtype) /* extension from type */
2286 && extp && extp[0])){ /* extension from filename */
2287 strncpy(ext, extp, sizeof(ext));
2288 ext[sizeof(ext)-1] = '\0';
2291 /* create a temp file */
2292 if(sender_filename){
2293 char *p, *q = NULL;
2295 /* get rid of any extension */
2296 if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
2297 *(q-1) = '\0';
2299 /* be careful about what is allowed in the filename */
2300 for(p = sender_filename; *p; p++)
2301 if(!(isascii((unsigned char) *p)
2302 && (isalnum((unsigned char) *p)
2303 || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
2304 break;
2306 if(!*p) /* filename was ok to use */
2307 snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
2310 /* didn't get it yet */
2311 if(!prefix[0]){
2312 snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
2313 ? a->body->subtype : "unk");
2316 /* We are creating a temporary name. This is just a prefix. If you
2317 * need the original name, use the save command, so if the prefix
2318 * is too long, shorten it.
2320 if (strlen(prefix) > 9){
2321 prefix[9] = '-';
2322 prefix[10] = '\0';
2325 filename = temp_nam_ext(NULL, prefix, ext);
2327 if(!filename){
2328 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2329 _("Error \"%s\", Can't create temporary file"),
2330 error_description(errno));
2331 return(1);
2334 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2335 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2336 _("Error \"%s\", Can't write file %s"),
2337 error_description(errno), filename);
2338 if(filename){
2339 our_unlink(filename);
2340 fs_give((void **)&filename);
2343 return(1);
2347 if(a->body->size.bytes){
2348 char msg_buf[128];
2350 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2351 a->description ? "\"" : "",
2352 a->description ? a->description : "attachment number ",
2353 a->description ? "" : a->number,
2354 a->description ? "\"" : "");
2355 msg_buf[sizeof(msg_buf)-1] = '\0';
2356 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
2357 a->body);
2360 if(a->body->type == TYPEMULTIPART){
2361 char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
2362 NULL, 0L);
2364 /* Write to store while converting newlines */
2365 while(h && *h)
2366 if(*h == '\015' && *(h+1) == '\012'){
2367 so_puts(store, NEWLINE);
2368 h += 2;
2370 else
2371 so_writec(*h++, store);
2374 gf_set_so_writec(&pc, store);
2376 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
2378 gf_clear_so_writec(store);
2380 if(we_cancel)
2381 cancel_busy_cue(0);
2383 so_give(&store);
2385 if(err){
2386 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2387 "%s: Error saving image to temp file %s",
2388 err, filename);
2389 if(filename){
2390 our_unlink(filename);
2391 fs_give((void **)&filename);
2394 return(1);
2397 /*----- Run the viewer process ----*/
2398 run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
2399 if(filename)
2400 fs_give((void **)&filename);
2402 return(0);
2406 /*----------------------------------------------------------------------
2407 Fish the required command from mailcap and run it
2409 Args: image_file -- The name of the file to pass viewer
2410 body -- body struct containing type/subtype of part
2412 A side effect may be that scrolltool is called as well if
2413 exec_mailcap_cmd has any substantial output...
2414 ----*/
2415 void
2416 run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
2418 MCAP_CMD_S *mc_cmd = NULL;
2419 int needs_terminal = 0, we_cancel = 0;
2421 we_cancel = busy_cue("Displaying attachment", NULL, 0);
2423 if((mc_cmd = mailcap_build_command(body->type, body->subtype,
2424 body, image_file,
2425 &needs_terminal, chk_extension)) != NULL){
2426 if(we_cancel)
2427 cancel_busy_cue(-1);
2429 exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
2430 if(mc_cmd->command)
2431 fs_give((void **)&mc_cmd->command);
2432 fs_give((void **)&mc_cmd);
2434 else{
2435 if(we_cancel)
2436 cancel_busy_cue(-1);
2438 q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
2439 type_desc(body->type, body->subtype,
2440 body->parameter, NULL, 1));
2445 /*----------------------------------------------------------------------
2446 Detach and provide for browsing a text body part
2448 Args: msgno -- raw message number to get part from
2449 a -- attachment struct for the desired part
2451 Result:
2452 ----*/
2453 STORE_S *
2454 format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
2456 STORE_S *store;
2457 gf_io_t pc;
2459 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2460 if(handlesp)
2461 init_handles(handlesp);
2463 gf_set_so_writec(&pc, store);
2464 (void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
2465 gf_clear_so_writec(store);
2468 return(store);
2472 /*----------------------------------------------------------------------
2473 Detach and provide for browsing a text body part
2475 Args: msgno -- raw message number to get part from
2476 a -- attachment struct for the desired part
2478 Result:
2479 ----*/
2481 display_text_att(long int msgno, ATTACH_S *a, int flags)
2483 STORE_S *store;
2484 HANDLE_S *handles = NULL;
2485 int rv = 0;
2487 if(msgno > 0L)
2488 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2490 if((store = format_text_att(msgno, a, &handles)) != NULL){
2491 rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
2492 free_handles(&handles);
2493 so_give(&store); /* free resources associated with store */
2495 else
2496 q_status_message(SM_ORDER | SM_DING, 3, 3,
2497 _("Error allocating space for attachment."));
2499 return(rv);
2503 /*----------------------------------------------------------------------
2504 Detach and provide for browsing a body part of type "MESSAGE"
2506 Args: msgno -- message number to get partrom
2507 a -- attachment struct for the desired part
2509 Result:
2510 ----*/
2512 display_msg_att(long int msgno, ATTACH_S *a, int flags)
2514 STORE_S *store;
2515 gf_io_t pc;
2516 ATTACH_S *ap = a;
2517 HANDLE_S *handles = NULL;
2518 int rv = 0;
2520 if(msgno > 0L)
2521 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2523 /* BUG, should check this return code */
2524 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2526 /* initialize a storage object */
2527 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2528 q_status_message(SM_ORDER | SM_DING, 3, 3,
2529 _("Error allocating space for message."));
2530 return(rv);
2533 gf_set_so_writec(&pc, store);
2535 if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
2536 if(ps_global->full_header == 2)
2537 q_status_message(SM_INFO, 0, 3,
2538 _("Full header mode ON. All header text being included"));
2540 rv = scroll_attachment((a->body->subtype
2541 && !strucmp(a->body->subtype, "delivery-status"))
2542 ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
2543 store, CharStar, handles, a, flags);
2544 free_handles(&handles);
2547 gf_clear_so_writec(store);
2549 so_give(&store); /* free resources associated with store */
2550 return(rv);
2554 /*----------------------------------------------------------------------
2555 Detach and provide for browsing a multipart body part of type "DIGEST"
2557 Args: msgno -- message number to get partrom
2558 a -- attachment struct for the desired part
2560 Result:
2561 ----*/
2562 void
2563 display_digest_att(long int msgno, ATTACH_S *a, int flags)
2565 STORE_S *store;
2566 ATTACH_S *ap;
2567 HANDLE_S *handles = NULL;
2568 gf_io_t pc;
2569 SourceType src = CharStar;
2570 int bad_news = 0;
2572 if(msgno > 0L)
2573 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2575 /* BUG, should check this return code */
2576 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2578 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
2579 q_status_message(SM_ORDER | SM_DING, 3, 3,
2580 _("Error allocating space for message."));
2581 return;
2584 gf_set_so_writec(&pc, store);
2587 * While in a subtype of this message
2589 for(ap = a + 1;
2590 ap->description
2591 && !strncmp(a->number, ap->number, strlen(a->number))
2592 && !bad_news;
2593 ap++){
2594 if(ap->body->type == TYPEMESSAGE){
2595 char *errstr;
2597 if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
2598 char *tsub;
2600 tsub = cpystr(ap->body->subtype);
2601 convert_possibly_encoded_str_to_utf8((char **) &tsub);
2602 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
2603 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2605 if((errstr = format_editorial(tmp_20k_buf,
2606 ps_global->ttyo->screen_cols, 0,
2607 NULL, pc)) != NULL){
2608 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2609 _("Can't format digest: %s"), errstr);
2610 bad_news++;
2612 else if(!gf_puts(NEWLINE, pc))
2613 bad_news++;
2615 fs_give((void **) &tsub);
2617 else{
2618 if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
2619 0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
2620 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2621 _("Can't format digest: %s"), errstr);
2622 bad_news++;
2624 else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
2625 bad_news++;
2628 else if(ap->body->type == TYPETEXT
2629 && decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
2630 bad_news++;
2631 else if(!gf_puts("Unknown type in Digest", pc))
2632 bad_news++;
2635 if(!bad_news){
2636 if(ps_global->full_header == 2)
2637 q_status_message(SM_INFO, 0, 3,
2638 _("Full header mode ON. All header text being included"));
2640 scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
2643 free_handles(&handles);
2645 gf_clear_so_writec(store);
2646 so_give(&store); /* free resources associated with store */
2651 scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
2653 SCROLL_S sargs;
2655 memset(&sargs, 0, sizeof(SCROLL_S));
2656 sargs.text.text = so_text(store);
2657 sargs.text.src = src;
2658 sargs.text.desc = "attachment";
2659 sargs.text.handles = handles;
2660 sargs.bar.title = title;
2661 sargs.proc.tool = process_attachment_cmd;
2662 sargs.proc.data.p = (void *) a;
2663 sargs.help.text = h_mail_text_att_view;
2664 sargs.help.title = _("HELP FOR ATTACHED TEXT VIEW");
2665 sargs.keys.menu = &att_view_keymenu;
2666 setbitmap(sargs.keys.bitmap);
2668 /* First, fix up "back" key */
2669 if(flags & DA_FROM_VIEW){
2670 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
2672 else{
2673 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
2676 if(!handles){
2677 clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
2678 clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
2679 clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
2682 if(F_OFF(F_ENABLE_PIPE, ps_global))
2683 clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
2685 if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
2686 clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
2689 * If message or digest, leave Reply and Save and,
2690 * conditionally, Bounce on...
2692 if(MIME_MSG_A(a)){
2693 if(F_OFF(F_ENABLE_BOUNCE, ps_global))
2694 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2696 else{
2697 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2698 clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
2699 clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
2701 #ifdef SMIME
2702 if(!(ps_global->smime && ps_global->smime->need_passphrase))
2703 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2705 if(F_ON(F_DONT_DO_SMIME, ps_global) || !MIME_MSG_A(a)){
2706 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2707 clrbitn(ATV_SECURITY_KEY, sargs.keys.bitmap);
2709 #endif
2711 sargs.use_indexline_color = 1;
2713 if(DA_RESIZE & flags)
2714 sargs.resize_exit = 1;
2716 #ifdef _WINDOWS
2717 scrat_attachp = a;
2718 sargs.mouse.popup = scroll_att_popup;
2719 #endif
2721 return(scrolltool(&sargs));
2724 #ifdef SMIME
2725 void
2726 display_smime_info_att(struct pine *ps, ATTACH_S *a)
2728 if(smime_check(a->body->nested.msg->body) == 0){
2729 q_status_message(SM_ORDER | SM_DING, 0, 3,
2730 _("Not a signed or encrypted message"));
2731 return;
2734 display_smime_info(ps, a->body->nested.msg->env, a->body->nested.msg->body);
2736 #endif /* SMIME */
2739 process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2741 int rv = 0, n;
2742 long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2743 #define AD(X) ((ATTACH_S *) (X)->proc.data.p)
2745 switch(cmd){
2746 case MC_EXIT :
2747 rv = 1;
2748 break;
2750 case MC_QUIT :
2751 ps_global->next_screen = quit_screen;
2752 break;
2754 case MC_MAIN :
2755 ps_global->next_screen = main_menu_screen;
2756 break;
2758 case MC_REPLY :
2759 reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
2760 break;
2762 case MC_FORWARD :
2763 forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
2764 break;
2766 case MC_BOUNCE :
2767 bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
2768 AD(sparms)->body->nested.msg->env->subject);
2769 ps_global->mangled_footer = 1;
2770 break;
2772 case MC_DELETE :
2773 delete_attachment(rawno, sparms->proc.data.p);
2774 break;
2776 case MC_UNDELETE :
2777 if(undelete_attachment(rawno, sparms->proc.data.p, &n))
2778 q_status_message1(SM_ORDER, 0, 3,
2779 "Part %s UNdeleted", AD(sparms)->number);
2781 break;
2783 case MC_SAVE :
2784 save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2785 ps_global->mangled_footer = 1;
2786 break;
2788 case MC_EXPORT :
2789 export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2790 ps_global->mangled_footer = 1;
2791 break;
2793 case MC_PRINTMSG :
2794 print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2795 ps_global->mangled_footer = 1;
2796 break;
2798 case MC_PIPE :
2799 pipe_attachment(rawno, sparms->proc.data.p);
2800 ps_global->mangled_footer = 1;
2801 break;
2803 case MC_FULLHDR :
2804 ps_global->full_header++;
2805 if(ps_global->full_header == 1){
2806 if(!(ps_global->quote_suppression_threshold
2807 && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
2808 ps_global->full_header++;
2810 else if(ps_global->full_header > 2)
2811 ps_global->full_header = 0;
2813 switch(ps_global->full_header){
2814 case 0:
2815 q_status_message(SM_ORDER, 0, 3,
2816 _("Display of full headers is now off."));
2817 break;
2819 case 1:
2820 q_status_message1(SM_ORDER, 0, 3,
2821 _("Quotes displayed, use %s to see full headers"),
2822 F_ON(F_USE_FK, ps_global) ? "F9" : "H");
2823 break;
2825 case 2:
2826 q_status_message(SM_ORDER, 0, 3,
2827 _("Display of full headers is now on."));
2828 break;
2832 rv = 1;
2833 break;
2835 #ifdef SMIME
2836 case MC_DECRYPT:
2837 if(ps_global->smime && ps_global->smime->need_passphrase)
2838 smime_get_passphrase();
2839 break;
2841 case MC_SECURITY:
2842 display_smime_info_att(ps_global, sparms->proc.data.p);
2843 break;
2844 #endif
2846 default:
2847 alpine_panic("Unexpected command case");
2848 break;
2851 return(rv);
2856 * Returns 1 on success, 0 on error.
2859 format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
2861 int rv = 1;
2863 if((*a)->body->type != TYPEMESSAGE)
2864 return(gf_puts("[ Undisplayed Attachment of Type ", pc)
2865 && gf_puts(body_type_names((*a)->body->type), pc)
2866 && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
2868 if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
2869 HEADER_S h;
2871 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
2872 FE_DEFAULT);
2873 switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
2874 (*a)->body->nested.msg->env, &h, NULL, NULL,
2875 flags, NULL, pc)){
2876 case -1 : /* write error */
2877 return(0);
2879 case 1 : /* fetch error */
2880 if(!(gf_puts("[ Error fetching header ]", pc)
2881 && !gf_puts(NEWLINE, pc)))
2882 return(0);
2884 break;
2887 gf_puts(NEWLINE, pc);
2889 if(((*a)+1)->description)
2890 ++(*a);
2891 else{
2892 if(!(gf_puts("[Can't display missing text segment]", pc)
2893 && gf_puts(NEWLINE, pc)))
2894 rv = 0;
2895 return rv;
2898 #ifdef SMIME
2899 if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2900 if((*a)->description){
2901 if(!(!format_editorial((*a)->description,
2902 ps_global->ttyo->screen_cols,
2903 flags, NULL, pc)
2904 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2905 rv = 0;
2908 if(((*a)+1)->description)
2909 ++(*a);
2910 else{
2911 if(!(gf_puts("[Can't display missing text segment]", pc)
2912 && gf_puts(NEWLINE, pc)))
2913 rv = 0;
2914 return rv;
2917 #endif /* SMIME */
2919 if(((*a))->description
2920 && (*a)->body && (*a)->body->type == TYPETEXT){
2921 if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2922 rv = 0;
2924 else if(!(gf_puts("[Can't display ", pc)
2925 && gf_puts(((*a)->description && (*a)->body)
2926 ? "first non-" : "missing ", pc)
2927 && gf_puts("text segment]", pc)
2928 && gf_puts(NEWLINE, pc)))
2929 rv = 0;
2931 if(((*a)+1)->description)
2932 ++(*a);
2933 else{
2934 if(!(gf_puts("[Can't display missing text segment]", pc)
2935 && gf_puts(NEWLINE, pc)))
2936 rv = 0;
2937 return rv;
2941 else if((*a)->body->subtype
2942 && strucmp((*a)->body->subtype, "external-body") == 0) {
2943 if(format_editorial("This part is not included and can be fetched as follows:",
2944 ps_global->ttyo->screen_cols, flags, NULL, pc)
2945 || !gf_puts(NEWLINE, pc)
2946 || format_editorial(display_parameters((*a)->body->parameter),
2947 ps_global->ttyo->screen_cols, flags, handlesp, pc))
2948 rv = 0;
2950 else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2951 rv = 0;
2953 return(rv);
2957 void
2958 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2960 STORE_S *in_store, *out_store = NULL;
2961 HANDLE_S *handles = NULL;
2962 URL_HILITE_S uh;
2963 gf_io_t gc, pc;
2964 char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2965 int cmd, indent, begins = 0;
2967 lines = detach_vcard_att(ps_global->mail_stream,
2968 msgno, a->body, a->number);
2969 if(!lines){
2970 q_status_message(SM_ORDER | SM_DING, 3, 3,
2971 _("Error accessing attachment."));
2972 return;
2975 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2976 free_list_array(&lines);
2977 q_status_message(SM_ORDER | SM_DING, 3, 3,
2978 _("Error allocating space for attachment."));
2979 return;
2982 for(ll = lines, indent = 0; ll && *ll; ll++)
2983 if((p = strindex(*ll, ':')) && p - *ll > indent)
2984 indent = p - *ll;
2986 indent += 5;
2987 for(ll = lines; ll && *ll; ll++){
2988 if((p = strindex(*ll, ':')) != NULL){
2989 if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2990 begins++;
2992 snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
2993 (int) MIN(p - *ll, sizeof(tmp)-5), *ll);
2994 tmp[sizeof(tmp)-1] = '\0';
2995 so_puts(in_store, tmp);
2996 p++;
2998 else{
2999 p = *ll;
3000 so_puts(in_store, repeat_char(indent, SPACE));
3003 snprintf(tmp, sizeof(tmp), "%.200s", p);
3004 tmp[sizeof(tmp)-1] = '\0';
3005 so_puts(in_store,
3006 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
3007 SIZEOF_20KBUF, tmp));
3008 so_puts(in_store, "\015\012");
3011 free_list_array(&lines);
3013 so_puts(in_store, "\015\012\015\012");
3016 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
3017 so_seek(in_store, 0L, 0);
3019 init_handles(&handles);
3020 gf_filter_init();
3022 if(F_ON(F_VIEW_SEL_URL, ps_global)
3023 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3024 || F_ON(F_SCAN_ADDR, ps_global))
3025 gf_link_filter(gf_line_test,
3026 gf_line_test_opt(url_hilite,
3027 gf_url_hilite_opt(&uh,&handles,0)));
3029 gf_link_filter(gf_wrap,
3030 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3031 ps_global->ttyo->screen_cols,
3032 NULL, indent, GFW_HANDLES));
3033 gf_link_filter(gf_nvtnl_local, NULL);
3035 gf_set_so_readc(&gc, in_store);
3036 gf_set_so_writec(&pc, out_store);
3038 errstr = gf_pipe(gc, pc);
3040 gf_clear_so_readc(in_store);
3042 if(!errstr){
3043 #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.")
3044 #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.")
3045 errstr = format_editorial((begins > 1)
3046 ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
3047 ps_global->ttyo->screen_cols, 0, NULL, pc);
3050 gf_clear_so_writec(out_store);
3052 if(!errstr)
3053 cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
3054 CharStar, handles, a, flags | DA_RESIZE);
3056 free_handles(&handles);
3057 so_give(&out_store);
3059 else
3060 errstr = _("Error allocating space");
3062 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3064 if(errstr)
3065 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3066 _("Can't format entry : %s"), errstr);
3068 so_give(&in_store);
3071 void
3072 display_vevent_summary(long int msgno, ATTACH_S *a, int flags, int depth)
3074 BODY *b;
3075 VCALENDAR_S *vcal = NULL;
3076 char *b64text, *caltext;
3077 unsigned long callen;
3078 VEVENT_SUMMARY_S *vesy, *vsummary; /* vevent summary */
3079 STORE_S *in_store, *out_store = NULL;
3080 HANDLE_S *handles = NULL;
3081 URL_HILITE_S uh;
3082 gf_io_t gc, pc;
3083 char *errstr = NULL, tmp[MAILTMPLEN], *p;
3084 int cmd, i, k;
3086 b = mail_body(ps_global->mail_stream, msgno, a->number);
3087 if(b->sparep == NULL){
3088 b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
3089 b64text[callen] = '\0'; /* chop off cookie */
3090 caltext = rfc822_base64(b64text, strlen(b64text), &callen);
3091 vcal = ical_parse_text(caltext);
3092 b->sparep = create_body_sparep(iCalType, (void *) vcal);
3094 else if(get_body_sparep_type(b->sparep) == iCalType)
3095 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
3097 vsummary = ical_vevent_summary(vcal);
3099 if(vsummary == NULL){
3100 q_status_message(SM_ORDER | SM_DING, 3, 3,
3101 _("Error parsing event"));
3102 return;
3105 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
3106 q_status_message(SM_ORDER | SM_DING, 3, 3,
3107 _("Error allocating space to process Calendar"));
3108 return;
3111 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
3112 q_status_message(SM_ORDER | SM_DING, 3, 3,
3113 _("Error allocating space to write Calendar"));
3114 return;
3117 gf_set_so_readc(&gc, in_store);
3118 gf_set_so_writec(&pc, out_store);
3120 for(vesy = vsummary, k = 0; vesy; vesy = vesy->next, k++){
3121 if(depth >= 0 && k != depth)
3122 continue;
3124 if(vesy->cancel){
3125 so_puts(in_store, _("This event was cancelled"));
3126 so_puts(in_store, "\015\012");
3129 if(vesy->priority){
3130 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%d %s",
3131 _("Priority: "), vesy->priority,
3132 vesy->priority == 5 ? _("(Normal)")
3133 : (vesy->priority < 5 ? _("(High)")
3134 : _("(Low)")));
3135 so_puts(in_store, tmp_20k_buf);
3136 so_puts(in_store, "\015\012");
3139 if(vesy->summary){
3140 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3141 _("Summary: "), vesy->summary);
3142 so_puts(in_store, tmp_20k_buf);
3143 so_puts(in_store, "\015\012");
3146 if(vesy->sender){
3147 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3148 _("Sender: "), vesy->sender);
3149 so_puts(in_store, tmp_20k_buf);
3150 so_puts(in_store, "\015\012");
3153 if(vesy->organizer){
3154 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3155 _("Organizer: "), vesy->organizer);
3156 so_puts(in_store, tmp_20k_buf);
3157 so_puts(in_store, "\015\012");
3160 if(vesy->location){
3161 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3162 _("Location: "), vesy->location);
3163 so_puts(in_store, tmp_20k_buf);
3164 so_puts(in_store, "\015\012");
3167 if(vesy->evstart){
3168 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3169 _("Start Date: "), vesy->evstart);
3170 so_puts(in_store, tmp_20k_buf);
3171 so_puts(in_store, "\015\012");
3174 if(vesy->duration){
3175 for(i = 0; vesy->duration[i] != NULL; i++){
3176 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3177 _("Duration: "), vesy->duration[i]);
3178 so_puts(in_store, tmp_20k_buf);
3179 so_puts(in_store, "\015\012");
3181 } else if(vesy->evend){
3182 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3183 _("End Date: "), vesy->evend);
3184 so_puts(in_store, tmp_20k_buf);
3185 so_puts(in_store, "\015\012");
3188 if(vesy->dtstamp){
3189 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3190 vcal->method ? _("Created on: ") : _("Last Revised on; "),
3191 vesy->dtstamp);
3192 so_puts(in_store, tmp_20k_buf);
3193 so_puts(in_store, "\015\012");
3196 if(vesy->description){
3197 char c;
3198 int j, empty;
3200 so_puts(in_store, "\015\012");
3202 for(i = 0; vesy->description[i] != NULL; i++){
3203 so_puts(in_store, _("Description: "));
3204 /* Check if empty description */
3205 empty = 1;
3206 for(j =0; empty == 1 && vesy->description[i][j] != '\0'; j++){
3207 c = vesy->description[i][j];
3208 if(c != '\n' && c != ' ' && c != '\t')
3209 empty = 0;
3211 if(empty){
3212 so_puts(in_store, _("[ No description provided ]"));
3213 so_puts(in_store, "\015\012");
3215 else {
3216 for(j =0; vesy->description[i][j] != '\0'; j++){
3217 c = vesy->description[i][j];
3218 if(c == '\n'){
3219 so_puts(in_store, "\015\012");
3220 continue;
3222 so_writec(c, in_store);
3225 so_puts(in_store, "\015\012");
3229 if(vesy->attendee){
3230 so_puts(in_store, "\015\012");
3231 so_puts(in_store, _("List of Attendees:"));
3232 so_puts(in_store, "\015\012");
3233 for(i = 0; vesy->attendee[i] != NULL; i++){
3234 so_puts(in_store, vesy->attendee[i]);
3235 so_puts(in_store, "\015\012");
3237 so_puts(in_store, "\015\012");
3240 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3241 _("This event was tagged as a %s entry by the sender"), vesy->class);
3242 so_puts(in_store, tmp_20k_buf);
3243 so_puts(in_store, "\015\012\015\012");
3245 if(depth < 0 && vesy->next){
3246 for(i = 0; i < ps_global->ttyo->screen_cols && i < 40; i++)
3247 tmp_20k_buf[i] = '-';
3248 tmp_20k_buf[i]= '\0';
3249 so_puts(in_store, tmp_20k_buf);
3250 so_puts(in_store, "\015\012");
3252 } /* end "for" loop */
3255 so_seek(in_store, 0L, 0);
3257 init_handles(&handles);
3258 gf_filter_init();
3260 if(F_ON(F_VIEW_SEL_URL, ps_global)
3261 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3262 || F_ON(F_SCAN_ADDR, ps_global))
3263 gf_link_filter(gf_line_test,
3264 gf_line_test_opt(url_hilite,
3265 gf_url_hilite_opt(&uh,&handles,0)));
3267 gf_link_filter(gf_wrap,
3268 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3269 ps_global->ttyo->screen_cols,
3270 NULL, 0, GFW_HANDLES));
3271 gf_link_filter(gf_nvtnl_local, NULL);
3273 gf_set_so_readc(&gc, in_store);
3274 gf_set_so_writec(&pc, out_store);
3276 errstr = gf_pipe(gc, pc);
3278 gf_clear_so_readc(in_store);
3280 gf_clear_so_writec(out_store);
3282 if(!errstr)
3283 cmd = scroll_attachment(_("CALENDAR EVENT ATTACHMENT"), out_store,
3284 CharStar, handles, a, flags | DA_RESIZE);
3286 free_handles(&handles);
3287 so_give(&out_store);
3289 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3291 if(errstr)
3292 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3293 _("Can't format entry : %s"), errstr);
3294 so_give(&in_store);
3295 free_vevent_summary(&vsummary);
3296 ps_global->mangled_screen = 1;
3300 void
3301 display_vcalendar_att(long int msgno, ATTACH_S *a, int flags)
3303 display_vevent_summary(msgno, a, flags, -1);
3307 /*----------------------------------------------------------------------
3308 Display attachment information
3310 Args: msgno -- message number to get partrom
3311 a -- attachment struct for the desired part
3313 Result: a screen containing information about attachment:
3314 ----*/
3315 void
3316 display_attach_info(long int msgno, ATTACH_S *a)
3318 int i, indent, cols;
3319 char buf1[100], *folded;
3320 STORE_S *store;
3321 PARMLIST_S *plist;
3322 SCROLL_S sargs;
3324 (void) dispatch_attachment(a);
3326 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
3327 q_status_message(SM_ORDER | SM_DING, 3, 3,
3328 _("Error allocating space for message."));
3329 return;
3332 cols = ps_global->ttyo->screen_cols;
3335 * 2 spaces on left
3336 * 16 for text (longest is Display Method == 14)
3337 * 2 for ": "
3339 indent = 20;
3341 /* don't try stupid folding */
3342 cols = MAX(indent+10, cols);
3344 so_puts(store, "Details about Attachment #");
3345 so_puts(store, a->number);
3346 so_puts(store, " :\n\n");
3347 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
3348 so_puts(store, buf1);
3349 so_puts(store, body_type_names(a->body->type));
3350 so_puts(store, "\n");
3351 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
3352 so_puts(store, buf1);
3353 so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
3354 so_puts(store, "\n");
3355 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
3356 so_puts(store, buf1);
3357 so_puts(store, a->body->encoding < ENCMAX
3358 ? body_encodings[a->body->encoding]
3359 : "Unknown");
3360 so_puts(store, "\n");
3361 if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
3362 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
3363 so_puts(store, buf1);
3364 i = 0;
3365 while(rfc2231_list_params(plist)){
3366 if(i++)
3367 so_puts(store, repeat_char(indent, ' '));
3369 so_puts(store, plist->attrib);
3370 so_puts(store, " = ");
3371 so_puts(store, plist->value ? plist->value : "");
3372 so_puts(store, "\n");
3375 rfc2231_free_parmlist(&plist);
3378 if(a->body->description && a->body->description[0]){
3379 char buftmp[MAILTMPLEN];
3380 unsigned char *q;
3382 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
3384 snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
3385 buftmp[sizeof(buftmp)-1] = '\0';
3386 q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
3387 folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
3389 if(folded){
3390 so_puts(store, folded);
3391 fs_give((void **) &folded);
3395 /* BUG: no a->body->language support */
3397 if(a->body->disposition.type){
3398 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
3399 so_puts(store, buf1);
3400 so_puts(store, a->body->disposition.type);
3401 so_puts(store, "\n");
3402 if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
3403 while(rfc2231_list_params(plist)){
3404 so_puts(store, repeat_char(indent, ' '));
3405 so_puts(store, plist->attrib);
3406 so_puts(store, " = ");
3407 so_puts(store, plist->value ? plist->value : "");
3408 so_puts(store, "\n");
3411 rfc2231_free_parmlist(&plist);
3415 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
3416 so_puts(store, buf1);
3417 so_puts(store, comatose((a->body->encoding == ENCBASE64)
3418 ? ((a->body->size.bytes * 3)/4)
3419 : a->body->size.bytes));
3420 so_puts(store, " bytes\n");
3421 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
3422 so_puts(store, buf1);
3423 if(a->can_display == MCD_NONE) {
3424 so_puts(store, "Can't, ");
3425 so_puts(store, (a->body->encoding < ENCOTHER)
3426 ? "Unknown Attachment Format"
3427 : "Unknown Encoding");
3429 else if(!(a->can_display & MCD_EXTERNAL)){
3430 so_puts(store, "Alpine's Internal Pager");
3432 else{
3433 int nt, free_pretty_cmd;
3434 MCAP_CMD_S *mc_cmd;
3435 char *pretty_cmd;
3437 if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
3438 a->body, "<datafile>", &nt,
3439 a->can_display & MCD_EXT_PROMPT)) != NULL){
3440 so_puts(store, "\"");
3441 if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
3442 so_puts(store, pretty_cmd);
3443 so_puts(store, "\"");
3444 if(free_pretty_cmd && pretty_cmd)
3445 fs_give((void **)&pretty_cmd);
3446 if(mc_cmd->command)
3447 fs_give((void **)&mc_cmd->command);
3448 fs_give((void **)&mc_cmd);
3452 so_puts(store, "\n");
3454 memset(&sargs, 0, sizeof(SCROLL_S));
3455 sargs.text.text = so_text(store);
3456 sargs.text.src = CharStar;
3457 sargs.text.desc = "attachment info";
3458 sargs.bar.title = _("ABOUT ATTACHMENT");
3459 sargs.help.text = h_simple_text_view;
3460 sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
3462 sargs.use_indexline_color = 1;
3464 scrolltool(&sargs);
3466 so_give(&store); /* free resources associated with store */
3467 ps_global->mangled_screen = 1;
3471 /*----------------------------------------------------------------------
3473 ----*/
3474 void
3475 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3477 char *sig;
3478 void *msgtext;
3479 ENVELOPE *outgoing;
3480 BODY *body;
3482 if(MIME_MSG_A(a)){
3483 forward_msg_att(stream, msgno, a);
3485 else{
3486 ACTION_S *role = NULL;
3487 REDRAFT_POS_S *redraft_pos = NULL;
3488 long rflags = ROLE_FORWARD;
3489 PAT_STATE dummy;
3491 outgoing = mail_newenvelope();
3492 outgoing->message_id = generate_message_id();
3493 outgoing->subject = cpystr("Forwarded attachment...");
3495 if(nonempty_patterns(rflags, &dummy)){
3497 * There is no message, but a Current Folder Type might match.
3499 * This has been changed to check against the message
3500 * containing the attachment.
3502 role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3503 if(confirm_role(rflags, &role))
3504 role = combine_inherited_role(role);
3505 else{
3506 role = NULL;
3507 cmd_cancelled("Forward");
3508 mail_free_envelope(&outgoing);
3509 return;
3513 if(role)
3514 q_status_message1(SM_ORDER, 3, 4,
3515 _("Forwarding using role \"%s\""), role->nick);
3518 * as with all text bound for the composer, build it in
3519 * a storage object of the type it understands...
3521 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3522 int impl, template_len = 0;
3524 if(role && role->template){
3525 char *filtered;
3527 impl = 1;
3528 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3529 if(filtered){
3530 if(*filtered){
3531 so_puts((STORE_S *)msgtext, filtered);
3532 if(impl == 1)
3533 template_len = strlen(filtered);
3536 fs_give((void **)&filtered);
3539 else
3540 impl = 1;
3542 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3543 if(impl == 2)
3544 redraft_pos->offset += template_len;
3546 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3548 fs_give((void **)&sig);
3550 else
3551 so_puts((STORE_S *)msgtext, NEWLINE);
3553 /*---- New Body to start with ----*/
3554 body = mail_newbody();
3555 body->type = TYPEMULTIPART;
3557 /*---- The TEXT part/body ----*/
3558 body->nested.part = mail_newbody_part();
3559 body->nested.part->body.type = TYPETEXT;
3560 body->nested.part->body.contents.text.data = msgtext;
3562 /*---- The corresponding things we're attaching ----*/
3563 body->nested.part->next = mail_newbody_part();
3564 body->nested.part->next->body.id = generate_message_id();
3565 copy_body(&body->nested.part->next->body, a->body);
3567 if(fetch_contents(stream, msgno, a->number,
3568 &body->nested.part->next->body)){
3569 pine_send(outgoing, &body, "FORWARD MESSAGE",
3570 role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3572 ps_global->mangled_screen = 1;
3573 pine_free_body(&body);
3574 free_redraft_pos(&redraft_pos);
3576 else{
3577 mail_free_body(&body);
3578 so_give((STORE_S **) &msgtext);
3579 free_redraft_pos(&redraft_pos);
3580 q_status_message(SM_ORDER | SM_DING, 4, 5,
3581 _("Error fetching message contents. Can't forward message."));
3584 else
3585 q_status_message(SM_ORDER | SM_DING, 3, 4,
3586 _("Error allocating message text"));
3588 mail_free_envelope(&outgoing);
3589 free_action(&role);
3594 void
3595 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3597 char *p, *sig = NULL;
3598 int ret;
3599 void *msgtext;
3600 ENVELOPE *outgoing;
3601 BODY *body;
3602 ACTION_S *role = NULL;
3603 REPLY_S reply;
3604 REDRAFT_POS_S *redraft_pos = NULL;
3606 outgoing = mail_newenvelope();
3607 outgoing->message_id = generate_message_id();
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 if(role && role->template){
3644 char *filtered;
3646 impl = 1;
3647 filtered = detoken(role, a->body->nested.msg->env,
3648 0, 0, 0, &redraft_pos, &impl);
3649 if(filtered){
3650 if(*filtered){
3651 so_puts((STORE_S *)msgtext, filtered);
3652 if(impl == 1)
3653 template_len = strlen(filtered);
3656 fs_give((void **)&filtered);
3659 else
3660 impl = 1;
3662 if((sig = detoken(role, a->body->nested.msg->env,
3663 2, 0, 1, &redraft_pos, &impl)) != NULL){
3664 if(impl == 2)
3665 redraft_pos->offset += template_len;
3667 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3669 fs_give((void **)&sig);
3671 else
3672 so_puts((STORE_S *)msgtext, NEWLINE);
3674 if(ret == 'y'){
3675 /*---- New Body to start with ----*/
3676 body = mail_newbody();
3677 body->type = TYPEMULTIPART;
3679 /*---- The TEXT part/body ----*/
3680 body->nested.part = mail_newbody_part();
3681 body->nested.part->body.type = TYPETEXT;
3682 body->nested.part->body.contents.text.data = msgtext;
3684 if(!forward_mime_msg(stream, msgno,
3685 p = body_partno(stream, msgno, a->body),
3686 a->body->nested.msg->env,
3687 &body->nested.part->next, msgtext))
3688 mail_free_body(&body);
3690 else{
3691 reply.forw = 1;
3692 if(a->body->nested.msg->body){
3693 char *charset;
3695 charset
3696 = parameter_val(a->body->nested.msg->body->parameter,
3697 "charset");
3699 if(charset && strucmp(charset, "us-ascii") != 0){
3700 CONV_TABLE *ct;
3703 * There is a non-ascii charset,
3704 * is there conversion happening?
3706 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3707 || !ct->table){
3708 reply.orig_charset = charset;
3709 charset = NULL;
3713 if(charset)
3714 fs_give((void **) &charset);
3717 body = forward_body(stream, a->body->nested.msg->env,
3718 a->body->nested.msg->body, msgno,
3719 p = body_partno(stream, msgno, a->body),
3720 msgtext, FWD_NONE);
3723 fs_give((void **) &p);
3725 if(body){
3726 pine_send(outgoing, &body,
3727 "FORWARD MESSAGE",
3728 role, NULL,
3729 reply.forw ? &reply : NULL,
3730 redraft_pos, NULL, NULL, 0);
3732 ps_global->mangled_screen = 1;
3733 pine_free_body(&body);
3734 free_redraft_pos(&redraft_pos);
3735 free_action(&role);
3737 else{
3738 so_give((STORE_S **) &msgtext);
3739 q_status_message(SM_ORDER | SM_DING, 4, 5,
3740 _("Error fetching message contents. Can't forward message."));
3743 else
3744 q_status_message(SM_ORDER | SM_DING, 3, 4,
3745 _("Error allocating message text"));
3747 else
3748 q_status_message1(SM_ORDER,3,4,
3749 _("Error fetching message %s. Can't forward it."),
3750 long2string(msgno));
3752 if(reply.orig_charset)
3753 fs_give((void **)&reply.orig_charset);
3755 mail_free_envelope(&outgoing);
3759 void
3760 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3762 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
3763 ENVELOPE *outgoing;
3764 BODY *body;
3765 void *msgtext;
3766 char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3767 int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3768 int rolemsg = 0, copytomsg = 0;
3769 long rflags;
3770 PAT_STATE dummy;
3771 REDRAFT_POS_S *redraft_pos = NULL;
3772 ACTION_S *role = NULL;
3774 outgoing = mail_newenvelope();
3776 dprint((4,"\n - attachment reply \n"));
3778 saved_from = (ADDRESS *) NULL;
3779 saved_to = (ADDRESS *) NULL;
3780 saved_cc = (ADDRESS *) NULL;
3781 saved_resent = (ADDRESS *) NULL;
3782 outgoing->subject = NULL;
3784 prefix = reply_quote_str(a->body->nested.msg->env);
3786 * For consistency, the first question is always "include text?"
3788 if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0
3789 && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3790 && reply_harvest(ps_global, msgno, a->number,
3791 a->body->nested.msg->env, &saved_from,
3792 &saved_to, &saved_cc, &saved_resent, &flags)){
3793 outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3794 NULL, 0);
3795 clear_cursor_pos();
3796 reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3797 saved_from, saved_to, saved_cc, saved_resent,
3798 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3799 if(errmsg){
3800 if(*errmsg){
3801 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3802 display_message(NO_OP_COMMAND);
3805 fs_give((void **)&errmsg);
3808 if(sp_expunge_count(stream)) /* current msg was expunged */
3809 goto seeyalater;
3811 /* Setup possible role */
3812 rflags = ROLE_REPLY;
3813 if(nonempty_patterns(rflags, &dummy)){
3814 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3815 if(confirm_role(rflags, &role))
3816 role = combine_inherited_role(role);
3817 else{ /* cancel reply */
3818 role = NULL;
3819 cmd_cancelled("Reply");
3820 goto seeyalater;
3824 if(role){
3825 rolemsg++;
3827 /* override fcc gotten in reply_seed */
3828 if(role->fcc && fcc)
3829 fs_give((void **) &fcc);
3832 if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3833 ADDRESS *us_in_to_and_cc, *ap;
3835 us_in_to_and_cc = (ADDRESS *) NULL;
3836 if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3837 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3838 NULL, us_in_to_and_cc, NULL,
3839 a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3840 reply_append_addr(&us_in_to_and_cc, ap);
3842 if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3843 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3844 NULL, us_in_to_and_cc, NULL,
3845 a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3846 reply_append_addr(&us_in_to_and_cc, ap);
3849 * A list of all of our addresses that appear in the To
3850 * and cc fields is in us_in_to_and_cc.
3851 * If there is exactly one address in that list then
3852 * use it for the outgoing From.
3854 if(us_in_to_and_cc && !us_in_to_and_cc->next){
3855 PINEFIELD *custom, *pf;
3856 ADDRESS *a = NULL;
3857 char *addr = NULL;
3860 * Check to see if this address is different from what
3861 * we would have used anyway. If it is, notify the user
3862 * with a status message. This is pretty hokey because we're
3863 * mimicking how pine_send would set the From address and
3864 * there is no coordination between the two.
3867 /* in case user has a custom From value */
3868 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3870 pf = (PINEFIELD *) fs_get(sizeof(*pf));
3871 memset((void *) pf, 0, sizeof(*pf));
3872 pf->name = cpystr("From");
3873 pf->addr = &a;
3874 if(set_default_hdrval(pf, custom) >= UseAsDef
3875 && pf->textbuf && pf->textbuf[0]){
3876 removing_trailing_white_space(pf->textbuf);
3877 (void)removing_double_quotes(pf->textbuf);
3878 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3879 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3880 if(addr)
3881 fs_give((void **) &addr);
3884 if(!*pf->addr)
3885 *pf->addr = generate_from();
3887 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3888 copytomsg++;
3889 if(!role){
3890 role = (ACTION_S *) fs_get(sizeof(*role));
3891 memset((void *) role, 0, sizeof(*role));
3892 role->is_a_role = 1;
3895 role->from = us_in_to_and_cc;
3896 us_in_to_and_cc = NULL;
3899 free_customs(custom);
3900 free_customs(pf);
3903 if(us_in_to_and_cc)
3904 mail_free_address(&us_in_to_and_cc);
3908 if(role){
3909 if(rolemsg && copytomsg)
3910 q_status_message1(SM_ORDER, 3, 4,
3911 _("Replying using role \"%s\" and To as From"), role->nick);
3912 else if(rolemsg)
3913 q_status_message1(SM_ORDER, 3, 4,
3914 _("Replying using role \"%s\""), role->nick);
3915 else if(copytomsg)
3916 q_status_message(SM_ORDER, 3, 4,
3917 _("Replying using incoming To as outgoing From"));
3920 outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3921 outgoing->references = reply_build_refs(a->body->nested.msg->env);
3922 outgoing->message_id = generate_message_id();
3924 if(!outgoing->to && !outgoing->cc
3925 && !outgoing->bcc && !outgoing->newsgroups)
3926 q_status_message(SM_ORDER | SM_DING, 3, 6,
3927 _("Warning: no valid addresses to reply to!"));
3930 * Now fix up the body...
3932 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3933 REPLY_S reply;
3935 memset((void *)&reply, 0, sizeof(reply));
3936 reply.forw = 1;
3937 if(a->body->nested.msg->body){
3938 char *charset;
3940 charset
3941 = parameter_val(a->body->nested.msg->body->parameter,
3942 "charset");
3944 if(charset && strucmp(charset, "us-ascii") != 0){
3945 CONV_TABLE *ct;
3948 * There is a non-ascii charset,
3949 * is there conversion happening?
3951 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3952 || !ct->table){
3953 reply.orig_charset = charset;
3954 charset = NULL;
3958 if(charset)
3959 fs_give((void **) &charset);
3962 if((body = reply_body(stream, a->body->nested.msg->env,
3963 a->body->nested.msg->body, msgno,
3964 tp = body_partno(stream, msgno, a->body),
3965 msgtext, prefix, include_text, role,
3966 1, &redraft_pos)) != NULL){
3967 /* partially formatted outgoing message */
3968 pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3969 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3971 pine_free_body(&body);
3972 ps_global->mangled_screen = 1;
3974 else
3975 q_status_message(SM_ORDER | SM_DING, 3, 4,
3976 _("Error building message body"));
3978 fs_give((void **) &tp);
3979 if(reply.orig_charset)
3980 fs_give((void **)&reply.orig_charset);
3982 else
3983 q_status_message(SM_ORDER | SM_DING, 3, 4,
3984 _("Error allocating message text"));
3987 seeyalater:
3988 mail_free_envelope(&outgoing);
3989 mail_free_address(&saved_from);
3990 mail_free_address(&saved_to);
3991 mail_free_address(&saved_cc);
3992 mail_free_address(&saved_resent);
3994 if(prefix)
3995 fs_give((void **) &prefix);
3997 if(fcc)
3998 fs_give((void **) &fcc);
4000 free_redraft_pos(&redraft_pos);
4001 free_action(&role);
4005 void
4006 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
4008 char *errstr;
4010 if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
4011 q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
4015 void
4016 pipe_attachment(long int msgno, ATTACH_S *a)
4018 char *err, *resultfilename = NULL, prompt[80], *p;
4019 int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
4020 long ku;
4021 PIPE_S *syspipe;
4022 HelpType help;
4023 char pipe_command[MAXPATH+1];
4024 unsigned flagsforhist = 1; /* raw=2 /capture=1 */
4025 static HISTORY_S *history = NULL;
4026 ESCKEY_S pipe_opt[6];
4028 if(ps_global->restricted){
4029 q_status_message(SM_ORDER | SM_DING, 0, 4,
4030 "Alpine demo can't pipe attachments");
4031 return;
4034 help = NO_HELP;
4035 pipe_command[0] = '\0';
4037 init_hist(&history, HISTSIZE);
4038 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4039 if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
4040 strncpy(pipe_command, p, sizeof(pipe_command));
4041 pipe_command[sizeof(pipe_command)-1] = '\0';
4042 if(history->hist[history->curindex]){
4043 flagsforhist = history->hist[history->curindex]->flags;
4044 raw = (flagsforhist & 0x2) ? 1 : 0;
4045 capture = (flagsforhist & 0x1) ? 1 : 0;
4049 pipe_opt[j].ch = 0;
4050 pipe_opt[j].rval = 0;
4051 pipe_opt[j].name = "";
4052 pipe_opt[j++].label = "";
4054 pipe_opt[j].ch = ctrl('W');
4055 pipe_opt[j].rval = 10;
4056 pipe_opt[j].name = "^W";
4057 pipe_opt[j++].label = NULL;
4059 pipe_opt[j].ch = ctrl('Y');
4060 pipe_opt[j].rval = 11;
4061 pipe_opt[j].name = "^Y";
4062 pipe_opt[j++].label = NULL;
4064 pipe_opt[j].ch = KEY_UP;
4065 pipe_opt[j].rval = 30;
4066 pipe_opt[j].name = "";
4067 ku = j;
4068 pipe_opt[j++].label = "";
4070 pipe_opt[j].ch = KEY_DOWN;
4071 pipe_opt[j].rval = 31;
4072 pipe_opt[j].name = "";
4073 pipe_opt[j++].label = "";
4075 pipe_opt[j].ch = -1;
4077 while(1){
4078 int flags;
4080 snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
4081 a->number, capture ? "" : "(Free Output) ");
4082 prompt[sizeof(prompt)-1] = '\0';
4083 pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
4084 pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
4087 * 2 is really 1 because there will be one real entry and
4088 * one entry of "" because of the get_prev_hist above.
4090 if(items_in_hist(history) > 2){
4091 pipe_opt[ku].name = HISTORY_UP_KEYNAME;
4092 pipe_opt[ku].label = HISTORY_KEYLABEL;
4093 pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
4094 pipe_opt[ku+1].label = HISTORY_KEYLABEL;
4096 else{
4097 pipe_opt[ku].name = "";
4098 pipe_opt[ku].label = "";
4099 pipe_opt[ku+1].name = "";
4100 pipe_opt[ku+1].label = "";
4103 flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
4104 rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
4105 sizeof(pipe_command), prompt,
4106 pipe_opt, help, &flags);
4107 if(rc == -1){
4108 q_status_message(SM_ORDER | SM_DING, 3, 4,
4109 "Internal problem encountered");
4110 break;
4112 else if(rc == 10){
4113 raw = !raw; /* flip raw text */
4115 else if(rc == 11){
4116 capture = !capture; /* flip capture output */
4118 else if(rc == 30){
4119 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4120 if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
4121 strncpy(pipe_command, p, sizeof(pipe_command));
4122 pipe_command[sizeof(pipe_command)-1] = '\0';
4123 if(history->hist[history->curindex]){
4124 flagsforhist = history->hist[history->curindex]->flags;
4125 raw = (flagsforhist & 0x2) ? 1 : 0;
4126 capture = (flagsforhist & 0x1) ? 1 : 0;
4129 else
4130 Writechar(BELL, 0);
4132 else if(rc == 31){
4133 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4134 if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
4135 strncpy(pipe_command, p, sizeof(pipe_command));
4136 pipe_command[sizeof(pipe_command)-1] = '\0';
4137 if(history->hist[history->curindex]){
4138 flagsforhist = history->hist[history->curindex]->flags;
4139 raw = (flagsforhist & 0x2) ? 1 : 0;
4140 capture = (flagsforhist & 0x1) ? 1 : 0;
4143 else
4144 Writechar(BELL, 0);
4146 else if(rc == 0){
4147 if(pipe_command[0] == '\0'){
4148 cmd_cancelled("Pipe command");
4149 break;
4152 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4153 save_hist(history, pipe_command, flagsforhist, NULL);
4155 flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
4156 flags |= (raw ? PIPE_RAW : 0);
4157 if(!capture){
4158 #ifndef _WINDOWS
4159 ClearScreen();
4160 fflush(stdout);
4161 clear_cursor_pos();
4162 ps_global->mangled_screen = 1;
4163 #endif
4164 flags |= PIPE_RESET;
4167 if((syspipe = open_system_pipe(pipe_command,
4168 (flags&PIPE_RESET) ? NULL : &resultfilename,
4169 NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
4170 int is_text = 0;
4171 gf_io_t pc; /* wire up a generic putchar */
4173 is_text = (a && a->body && a->body->type == TYPETEXT);
4175 gf_set_writec(&pc, syspipe, 0L, PipeStar,
4176 (is_text && !raw) ? WRITE_TO_LOCALE : 0);
4178 /*------ Write the image to a temporary file ------*/
4179 if(raw){ /* pipe raw text */
4180 FETCH_READC_S fetch_part;
4182 err = NULL;
4184 if(capture)
4185 we_cancel = busy_cue(NULL, NULL, 1);
4186 else
4187 suspend_busy_cue();
4189 gf_filter_init();
4190 fetch_readc_init(&fetch_part, ps_global->mail_stream,
4191 msgno, a->number, a->body->size.bytes, 0, 0);
4192 gf_link_filter(gf_nvtnl_local, NULL);
4193 err = gf_pipe(FETCH_READC, pc);
4195 if(capture){
4196 if(we_cancel)
4197 cancel_busy_cue(0);
4199 else
4200 resume_busy_cue(0);
4202 else{
4203 /* BUG: there's got to be a better way */
4204 if(!capture)
4205 ps_global->print = (PRINT_S *) 1;
4207 suspend_busy_cue();
4208 err = detach(ps_global->mail_stream, msgno,
4209 a->number, 0L, (long *)NULL, pc, NULL, 0);
4210 ps_global->print = (PRINT_S *) NULL;
4211 resume_busy_cue(0);
4214 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
4216 if(err)
4217 q_status_message1(SM_ORDER | SM_DING, 3, 4,
4218 _("Error detaching for pipe: %s"), err);
4220 display_output_file(resultfilename,
4221 (err)
4222 ? _("PIPE ATTACHMENT (ERROR)")
4223 : _("PIPE ATTACHMENT"),
4224 NULL, DOF_EMPTY);
4226 fs_give((void **) &resultfilename);
4228 else
4229 q_status_message(SM_ORDER | SM_DING, 3, 4,
4230 _("Error opening pipe"));
4232 break;
4234 else if(rc == 1){
4235 cmd_cancelled("Pipe");
4236 break;
4238 else if(rc == 3)
4239 help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
4245 delete_attachment(long int msgno, ATTACH_S *a)
4247 int expbits, rv = 0;
4249 if(!msgno_exceptions(ps_global->mail_stream, msgno,
4250 a->number, &expbits, FALSE)
4251 || !(expbits & MSG_EX_DELETE)){
4252 expbits |= MSG_EX_DELETE;
4253 msgno_exceptions(ps_global->mail_stream, msgno,
4254 a->number, &expbits, TRUE);
4256 q_status_message1(SM_ORDER, 0, 3,
4257 _("Part %s will be omitted only if message is Saved"),
4258 a->number);
4259 rv = 1;
4261 else
4262 q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
4263 a->number);
4265 return(rv);
4270 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
4272 int rv = 0;
4274 if(msgno_exceptions(ps_global->mail_stream, msgno,
4275 a->number, expbitsp, FALSE)
4276 && ((*expbitsp) & MSG_EX_DELETE)){
4277 (*expbitsp) ^= MSG_EX_DELETE;
4278 msgno_exceptions(ps_global->mail_stream, msgno,
4279 a->number, expbitsp, TRUE);
4280 rv = 1;
4282 else
4283 q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
4284 a->number);
4286 return(rv);
4290 /*----------------------------------------------------------------------
4291 Resolve any deferred tests for attachment displayability
4293 Args: attachment structure
4295 Returns: undefer's attachment's displayability test
4296 ----*/
4298 dispatch_attachment(ATTACH_S *a)
4300 if(a->test_deferred){
4301 a->test_deferred = 0;
4302 a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
4305 return(a->can_display);
4309 #ifdef _WINDOWS
4311 scroll_att_popup(sparms, in_handle)
4312 SCROLL_S *sparms;
4313 int in_handle;
4315 MPopup scrat_popup[20];
4316 int i = -1, n;
4318 if(in_handle){
4319 scrat_popup[++i].type = tIndex;
4320 scrat_popup[i].label.style = lNormal;
4321 scrat_popup[i].label.string = "View Selectable Item";
4322 scrat_popup[i].data.val = ctrl('L');
4325 scrat_popup[++i].type = tQueue;
4326 scrat_popup[i].label.style = lNormal;
4327 scrat_popup[i].label.string = "&Save";
4328 scrat_popup[i].data.val = 'S';
4330 scrat_popup[++i].type = tQueue;
4331 scrat_popup[i].label.style = lNormal;
4332 if(msgno_exceptions(ps_global->mail_stream,
4333 mn_m2raw(ps_global->msgmap,
4334 mn_get_cur(ps_global->msgmap)),
4335 scrat_attachp->number, &n, FALSE)
4336 && (n & MSG_EX_DELETE)){
4337 scrat_popup[i].label.string = "&Undelete";
4338 scrat_popup[i].data.val = 'U';
4340 else{
4341 scrat_popup[i].label.string = "&Delete";
4342 scrat_popup[i].data.val = 'D';
4345 if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
4346 scrat_popup[++i].type = tQueue;
4347 scrat_popup[i].label.style = lNormal;
4348 scrat_popup[i].label.string = "&Reply";
4349 scrat_popup[i].data.val = 'R';
4351 scrat_popup[++i].type = tQueue;
4352 scrat_popup[i].label.style = lNormal;
4353 scrat_popup[i].label.string = "&Forward";
4354 scrat_popup[i].data.val = 'f';
4357 scrat_popup[++i].type = tSeparator;
4359 scrat_popup[++i].type = tQueue;
4360 scrat_popup[i].label.style = lNormal;
4361 scrat_popup[i].label.string = "Attachment Index";
4362 scrat_popup[i].data.val = '<';
4364 scrat_popup[++i].type = tTail;
4366 return(mswin_popup(scrat_popup) == 0 && in_handle);
4370 void
4371 display_att_window(a)
4372 ATTACH_S *a;
4374 #if !defined(DOS) && !defined(OS2)
4375 char prefix[8];
4376 #endif
4378 if(a->body->type == TYPEMULTIPART){
4379 if(a->body->subtype){
4380 /* if(!strucmp(a->body->subtype, "digest"))
4381 display_digest_att(msgno, a, flags);
4382 else */
4383 q_status_message1(SM_ORDER, 3, 5,
4384 "Can't display Multipart/%s",
4385 a->body->subtype);
4387 else
4388 q_status_message(SM_ORDER, 3, 5,
4389 "Can't display unknown Multipart Subtype");
4391 /* else if(MIME_VCARD_A(a))
4392 display_vcard_att_window(msgno, a, flags);*/
4393 else if(a->body->type == TYPETEXT)
4394 display_text_att_window(a);
4395 else if(a->body->type == TYPEMESSAGE)
4396 display_msg_att_window(a);
4400 void
4401 display_text_att_window(a)
4402 ATTACH_S *a;
4404 STORE_S *store;
4405 long msgno;
4407 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4409 if(store = format_text_att(msgno, a, NULL)){
4410 if (mswin_displaytext("ATTACHED TEXT",
4411 so_text(store),
4412 strlen((char *) so_text(store)),
4413 NULL, NULL, 0) >= 0)
4414 store->txt = (void *) NULL; /* free'd in mswin_displaytext */
4416 so_give(&store); /* free resources associated with store */
4418 else
4419 q_status_message(SM_ORDER | SM_DING, 3, 3,
4420 "Error allocating space for attachment.");
4424 void
4425 display_msg_att_window(a)
4426 ATTACH_S *a;
4428 STORE_S *store;
4429 gf_io_t pc;
4430 ATTACH_S *ap = a;
4431 long msgno;
4433 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4435 /* BUG, should check this return code */
4436 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
4438 /* initialize a storage object */
4439 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
4441 gf_set_so_writec(&pc, store);
4443 if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
4444 && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
4445 strlen((char *) so_text(store)),
4446 NULL, NULL, 0) >= 0)
4447 /* free'd in mswin_displaytext */
4448 store->txt = (void *) NULL;
4450 gf_clear_so_writec(store);
4452 so_give(&store);
4454 else
4455 q_status_message(SM_ORDER | SM_DING, 3, 3,
4456 "Error allocating space for message.");
4458 #endif