* new version 2.20.5
[alpine.git] / alpine / mailpart.c
blobbe16f6ddeb72c41c45ae786fab0dee2336e336df
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 2006-2008 University of Washington
8 * Copyright 2013-2015 Eduardo Chappa
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"
73 * Information used to paint and maintain a line on the attachment
74 * screen.
76 typedef struct atdisp_line {
77 char *dstring; /* alloc'd var value string */
78 ATTACH_S *attp; /* actual attachment pointer */
79 struct atdisp_line *next, *prev;
80 } ATDISP_S;
84 * struct defining attachment screen's current state
86 typedef struct att_screen {
87 ATDISP_S *current,
88 *top_line;
89 COLOR_PAIR *titlecolor;
90 } ATT_SCREEN_S;
92 static ATT_SCREEN_S *att_screen;
95 #define next_attline(p) ((p) ? (p)->next : NULL)
96 #define prev_attline(p) ((p) ? (p)->prev : NULL)
99 #ifdef _WINDOWS
101 * local global pointer to scroll attachment popup menu
103 static ATTACH_S *scrat_attachp;
104 #endif
107 #define FETCH_READC g_fr_desc->readc
110 /* used to keep track of detached MIME segments total length */
111 static long save_att_length;
114 * Internal Prototypes
116 ATDISP_S *new_attline(ATDISP_S **);
117 void free_attline(ATDISP_S **);
118 int attachment_screen_updater(struct pine *, ATDISP_S *, ATT_SCREEN_S *);
119 void attachment_screen_redrawer(void);
120 void update_att_screen_titlebar(void);
121 ATDISP_S *first_attline(ATDISP_S *);
122 int init_att_progress(char *, MAILSTREAM *, BODY *);
123 long save_att_piped(int);
124 int save_att_percent(void);
125 void save_attachment(int, long, ATTACH_S *);
126 void export_attachment(int, long, ATTACH_S *);
127 char *write_attached_msg(long, ATTACH_S **, STORE_S *, int);
128 void save_msg_att(long, ATTACH_S *);
129 void save_digest_att(long, ATTACH_S *);
130 int save_msg_att_work(long int, ATTACH_S *, MAILSTREAM *, char *,
131 CONTEXT_S *, char *);
132 void export_msg_att(long, ATTACH_S *);
133 void export_digest_att(long, ATTACH_S *);
134 void print_attachment(int, long, ATTACH_S *);
135 int print_msg_att(long, ATTACH_S *, int);
136 void print_digest_att(long, ATTACH_S *);
137 void run_viewer(char *, BODY *, int);
138 STORE_S *format_text_att(long, ATTACH_S *, HANDLE_S **);
139 int display_text_att(long, ATTACH_S *, int);
140 int display_msg_att(long, ATTACH_S *, int);
141 void display_digest_att(long, ATTACH_S *, int);
142 int scroll_attachment(char *, STORE_S *, SourceType, HANDLE_S *, ATTACH_S *, int);
143 int process_attachment_cmd(int, MSGNO_S *, SCROLL_S *);
144 int format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
145 void display_vcard_att(long, ATTACH_S *, int);
146 void display_attach_info(long, ATTACH_S *);
147 void forward_attachment(MAILSTREAM *, long, ATTACH_S *);
148 void forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
149 void reply_msg_att(MAILSTREAM *, long, ATTACH_S *);
150 void bounce_msg_att(MAILSTREAM *, long, char *, char *);
151 void pipe_attachment(long, ATTACH_S *);
152 int delete_attachment(long, ATTACH_S *);
153 int undelete_attachment(long, ATTACH_S *, int *);
154 #ifdef _WINDOWS
155 int scroll_att_popup(SCROLL_S *, int);
156 void display_text_att_window(ATTACH_S *);
157 void display_msg_att_window(ATTACH_S *);
158 #endif
161 /*----------------------------------------------------------------------
162 Provide attachments in browser for selected action
164 Args: ps -- pointer to pine structure
165 msgmap -- struct containing current message to display
167 Result:
169 Handle painting and navigation of attachment index. It would be nice
170 to someday handle message/rfc822 segments in a neat way (i.e., enable
171 forwarding, take address, etc.).
172 ----*/
173 void
174 attachment_screen(struct pine *ps)
176 UCS ch = 'x';
177 int n, cmd, dline,
178 maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
179 last_type = TYPEOTHER;
180 long msgno;
181 char *q, *last_subtype = NULL, backtag[64], *utf8str;
182 OtherMenu what;
183 ATTACH_S *a;
184 ATDISP_S *current = NULL, *ctmp = NULL;
185 ATT_SCREEN_S screen;
187 ps->prev_screen = attachment_screen;
188 ps->next_screen = SCREEN_FUN_NULL;
190 ps->some_quoting_was_suppressed = 0;
192 if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
193 q_status_message(SM_ORDER | SM_DING, 0, 3,
194 _("Screen too small to view attachment"));
195 ps->next_screen = mail_view_screen;
196 return;
199 if(mn_total_cur(ps->msgmap) > 1L){
200 q_status_message(SM_ORDER | SM_DING, 0, 3,
201 _("Can only view one message's attachments at a time."));
202 return;
204 else if(ps->atmts && ps->atmts->description && !(ps->atmts + 1)->description)
205 q_status_message1(SM_ASYNC, 0, 3,
206 _("Message %s has only one part (the message body), and no attachments."),
207 long2string(mn_get_cur(ps->msgmap)));
209 /*----- Figure max display widths -----*/
210 for(a = ps->atmts; a && a->description != NULL; a++){
211 if((n = utf8_width(a->number)) > maxnumwid)
212 maxnumwid = n;
214 if((n = utf8_width(a->size)) > maxsizewid)
215 maxsizewid = n;
219 * Then, allocate and initialize attachment line list...
221 for(a = ps->atmts; a && a->description; a++)
222 new_attline(&current)->attp = a;
224 memset(&screen, 0, sizeof(screen));
225 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
226 ps->mangled_screen = 1; /* build display */
227 ps->redrawer = attachment_screen_redrawer;
228 att_screen = &screen;
229 current = first_attline(current);
230 what = FirstMenu;
233 * Advance to next attachment if it's likely that we've already
234 * shown it to the user...
237 if (current == NULL){
238 q_status_message1(SM_ORDER | SM_DING, 0, 3,
239 _("Malformed message: %s"),
240 ps->c_client_error ? ps->c_client_error : "?");
241 ps->next_screen = mail_view_screen;
243 else if(current->attp->shown && (ctmp = next_attline(current)))
244 current = ctmp;
246 while(ps->next_screen == SCREEN_FUN_NULL){
247 ps->user_says_cancel = 0;
248 if(km_popped){
249 km_popped--;
250 if(km_popped == 0){
251 clearfooter(ps);
252 ps->mangled_body = 1;
256 if(ps->mangled_screen){
258 * build/rebuild display lines
260 if(old_cols != ps->ttyo->screen_cols){
261 int avail, s1, s2, s4, s5, dwid, sizewid, descwid;
263 avail = ps_global->ttyo->screen_cols;
265 s1 = MAX(MIN(1, avail), 0);
266 avail -= s1;
268 dwid = MAX(MIN(1, avail), 0);
269 avail -= dwid;
271 s2 = MAX(MIN(1, avail), 0);
272 avail -= s2;
274 /* Only give up a third of the display to part numbers */
275 maxnumwid = MIN(maxnumwid, (ps_global->ttyo->screen_cols/3));
276 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
277 avail -= maxnumwid;
279 s4 = MAX(MIN(3, avail), 0);
280 avail -= s4;
282 sizewid = MAX(MIN(maxsizewid, avail), 0);
283 avail -= sizewid;
285 s5 = MAX(MIN(3, avail), 0);
286 avail -= s5;
288 descwid = MAX(0, avail);
290 old_cols = ps->ttyo->screen_cols;
292 for(ctmp = first_attline(current);
293 ctmp && (a = ctmp->attp) && a->description;
294 ctmp = next_attline(ctmp)){
295 size_t len;
296 char numbuf[50];
297 char description[1000];
299 if(ctmp->dstring)
300 fs_give((void **)&ctmp->dstring);
302 len = 6 * MAX(80, ps->ttyo->screen_cols) * sizeof(char);
303 ctmp->dstring = (char *)fs_get(len + 1);
306 * description
308 q = a->body->description;
309 if(!(q && q[0]) && (MIME_MSG_A(a) && a->body->nested.msg->env))
310 q = a->body->nested.msg->env->subject;
312 if(q && q[0]){
313 char buftmp[1000];
315 strncpy(buftmp, q, sizeof(buftmp));
316 buftmp[sizeof(buftmp)-1] = '\0';
318 q = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
319 SIZEOF_20KBUF, buftmp);
322 if(q && !q[0])
323 q = NULL;
325 snprintf(description, sizeof(description), "%s%s%s%s", type_desc(a->body->type, a->body->subtype, a->body->parameter, a->body->disposition.type ? a->body->disposition.parameter : NULL, 1), q ? ", \"" : "", q ? q : "", q ? "\"" : "");
326 description[sizeof(description)-1] = '\0';
328 utf8_snprintf(ctmp->dstring, len+1,
329 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%-*.*w",
330 s1, s1, "",
331 dwid, dwid,
332 msgno_part_deleted(ps->mail_stream, msgno, a->number) ? "D" : "",
333 s2, s2, "",
334 maxnumwid, maxnumwid,
335 a->number
336 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
337 : "",
338 s4, s4, "",
339 sizewid, sizewid,
340 a->size ? a->size : "",
341 s5, s5, "",
342 descwid, descwid, description);
344 ctmp->dstring[len] = '\0';
348 ps->mangled_header = 1;
349 ps->mangled_footer = 1;
350 ps->mangled_body = 1;
353 /*----------- Check for new mail -----------*/
354 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
355 ps->mangled_header = 1;
358 * If an expunge of the current message happened during the
359 * new mail check we want to bail out of here. See mm_expunged.
361 if(ps->next_screen != SCREEN_FUN_NULL)
362 break;
364 if(ps->mangled_header){
365 update_att_screen_titlebar();
366 ps->mangled_header = 0;
369 if(ps->mangled_screen){
370 ClearLine(1);
371 ps->mangled_screen = 0;
374 dline = attachment_screen_updater(ps, current, &screen);
376 /*---- This displays new mail notification, or errors ---*/
377 if(km_popped){
378 FOOTER_ROWS(ps) = 3;
379 mark_status_unknown();
382 display_message(ch);
383 if(km_popped){
384 FOOTER_ROWS(ps) = 1;
385 mark_status_unknown();
388 if(ps->mangled_footer
389 || current->attp->body->type != last_type
390 || !(last_subtype && current->attp->body->subtype)
391 || strucmp(last_subtype, current->attp->body->subtype)){
393 struct key_menu *km = &att_index_keymenu;
394 bitmap_t bitmap;
396 setbitmap(bitmap);
397 ps->mangled_footer = 0;
398 last_type = current->attp->body->type;
399 last_subtype = current->attp->body->subtype;
401 snprintf(backtag, sizeof(backtag), "Msg #%ld", mn_get_cur(ps->msgmap));
402 backtag[sizeof(backtag)-1] = '\0';
403 km->keys[ATT_PARENT_KEY].label = backtag;
405 if(F_OFF(F_ENABLE_PIPE, ps))
406 clrbitn(ATT_PIPE_KEY, bitmap);
409 * If message or digest, leave Reply and Save and,
410 * conditionally, Bounce on...
412 if(MIME_MSG(last_type, last_subtype)){
413 if(F_OFF(F_ENABLE_BOUNCE, ps))
414 clrbitn(ATT_BOUNCE_KEY, bitmap);
416 km->keys[ATT_EXPORT_KEY].name = "";
417 km->keys[ATT_EXPORT_KEY].label = "";
419 else if(MIME_DGST(last_type, last_subtype)){
420 clrbitn(ATT_BOUNCE_KEY, bitmap);
421 clrbitn(ATT_REPLY_KEY, bitmap);
423 km->keys[ATT_EXPORT_KEY].name = "";
424 km->keys[ATT_EXPORT_KEY].label = "";
426 else{
427 clrbitn(ATT_BOUNCE_KEY, bitmap);
428 clrbitn(ATT_REPLY_KEY, bitmap);
430 if(last_type != TYPETEXT)
431 clrbitn(ATT_PRINT_KEY, bitmap);
433 km->keys[ATT_EXPORT_KEY].name = "E";
434 km->keys[ATT_EXPORT_KEY].label = N_("Export");
437 if(km_popped){
438 FOOTER_ROWS(ps) = 3;
439 clearfooter(ps);
442 if(F_ON(F_ARROW_NAV, ps)){
443 menu_add_binding(km, KEY_LEFT, MC_EXIT);
444 menu_add_binding(km, KEY_RIGHT, MC_VIEW_ATCH);
446 else{
447 menu_clear_binding(km, KEY_LEFT);
448 menu_clear_binding(km, KEY_RIGHT);
451 draw_keymenu(km, bitmap, ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps),
452 0, what);
453 what = SameMenu;
454 if(km_popped){
455 FOOTER_ROWS(ps) = 1;
456 mark_keymenu_dirty();
460 if(F_ON(F_SHOW_CURSOR, ps))
461 MoveCursor(dline, 0);
462 else
463 MoveCursor(MAX(0, ps->ttyo->screen_rows - FOOTER_ROWS(ps)), 0);
465 /*------ Prepare to read the command from the keyboard ----*/
466 #ifdef MOUSE
467 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
468 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
469 ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
470 ps_global->ttyo->screen_cols);
471 #endif
472 ch = READ_COMMAND(&utf8str);
473 #ifdef MOUSE
474 clear_mfunc(mouse_in_content);
475 #endif
477 cmd = menu_command(ch, &att_index_keymenu);
479 if(km_popped)
480 switch(cmd){
481 case MC_NONE :
482 case MC_OTHER :
483 case MC_RESIZE :
484 case MC_REPAINT :
485 km_popped++;
486 break;
488 default:
489 clearfooter(ps);
490 break;
493 switch(cmd){
494 case MC_HELP :
495 if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
496 km_popped = 2;
497 ps->mangled_footer = 1;
498 break;
501 helper(h_attachment_screen, _("HELP FOR ATTACHMENT INDEX"), 0);
502 ps->mangled_screen = 1;
503 break;
505 case MC_OTHER :
506 what = NextMenu;
507 ps->mangled_footer = 1;
508 break;
510 case MC_FULLHDR :
511 ps->full_header++;
512 if(ps->full_header == 1){
513 if(!(ps->quote_suppression_threshold
514 && (ps->some_quoting_was_suppressed /* || in_index != View */)))
515 ps->full_header++;
517 else if(ps->full_header > 2)
518 ps->full_header = 0;
520 switch(ps->full_header){
521 case 0:
522 q_status_message(SM_ORDER, 0, 3,
523 _("Display of full headers is now off."));
524 break;
526 case 1:
527 q_status_message1(SM_ORDER, 0, 3,
528 _("Quotes displayed, use %s to see full headers"),
529 F_ON(F_USE_FK, ps) ? "F9" : "H");
530 break;
532 case 2:
533 q_status_message(SM_ORDER, 0, 3,
534 _("Display of full headers is now on."));
535 break;
539 break;
541 case MC_EXIT : /* exit attachment screen */
542 ps->next_screen = mail_view_screen;
543 break;
545 case MC_QUIT :
546 ps->next_screen = quit_screen;
547 break;
549 case MC_MAIN :
550 ps->next_screen = main_menu_screen;
551 break;
553 case MC_INDEX :
554 ps->next_screen = mail_index_screen;
555 break;
557 case MC_NEXTITEM :
558 if((ctmp = next_attline(current)) != NULL)
559 current = ctmp;
560 else
561 q_status_message(SM_ORDER, 0, 1, _("Already on last attachment"));
563 break;
565 case MC_PREVITEM :
566 if((ctmp = prev_attline(current)) != NULL)
567 current = ctmp;
568 else
569 q_status_message(SM_ORDER, 0, 1, _("Already on first attachment"));
571 break;
573 case MC_PAGEDN : /* page forward */
574 if(next_attline(current)){
575 while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
576 if((ctmp = next_attline(current)) != NULL)
577 current = ctmp;
578 else
579 break;
581 else
582 q_status_message(SM_ORDER, 0, 1,
583 _("Already on last page of attachments"));
585 break;
587 case MC_PAGEUP : /* page backward */
588 if(prev_attline(current)){
589 while(dline-- > HEADER_ROWS(ps))
590 if((ctmp = prev_attline(current)) != NULL)
591 current = ctmp;
592 else
593 break;
595 while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
596 if((ctmp = prev_attline(current)) != NULL)
597 current = ctmp;
598 else
599 break;
601 else
602 q_status_message(SM_ORDER, 0, 1,
603 _("Already on first page of attachments"));
605 break;
607 #ifdef MOUSE
608 case MC_MOUSE:
610 MOUSEPRESS mp;
612 mouse_get_last (NULL, &mp);
613 mp.row -= HEADER_ROWS(ps);
614 ctmp = screen.top_line;
615 while (mp.row && ctmp != NULL) {
616 --mp.row;
617 ctmp = ctmp->next;
620 if (ctmp != NULL){
621 current = ctmp;
623 if (mp.doubleclick){
624 if(mp.button == M_BUTTON_LEFT)
625 display_attachment(msgno, current->attp, DA_SAVE);
627 #ifdef _WINDOWS
628 else if(mp.button == M_BUTTON_RIGHT){
629 MPopup atmt_popup[20];
630 int i = -1, exoffer = 0;
632 dline = attachment_screen_updater(ps,current,&screen);
634 if(dispatch_attachment(current->attp) != MCD_NONE){
635 atmt_popup[++i].type = tQueue;
636 atmt_popup[i].data.val = 'V';
637 atmt_popup[i].label.style = lNormal;
638 atmt_popup[i].label.string = "&View";
640 if(!(current->attp->can_display & MCD_EXTERNAL)
641 && (current->attp->body->type == TYPETEXT
642 || MIME_MSG_A(current->attp)
643 || MIME_DGST_A(current->attp))){
644 exoffer++;
645 atmt_popup[++i].type = tIndex;
646 atmt_popup[i].label.style = lNormal;
647 atmt_popup[i].label.string =
648 "View in New Window";
652 atmt_popup[++i].type = tQueue;
653 atmt_popup[i].data.val = 'S';
654 atmt_popup[i].label.style = lNormal;
655 atmt_popup[i].label.string = "&Save";
657 atmt_popup[++i].type = tQueue;
658 atmt_popup[i].label.style = lNormal;
659 if(current->dstring[1] == 'D'){
660 atmt_popup[i].data.val = 'U';
661 atmt_popup[i].label.string = "&Undelete";
663 else{
664 atmt_popup[i].data.val = 'D';
665 atmt_popup[i].label.string = "&Delete";
668 if(MIME_MSG_A(current->attp)){
669 atmt_popup[++i].type = tQueue;
670 atmt_popup[i].label.style = lNormal;
671 atmt_popup[i].label.string = "&Reply...";
672 atmt_popup[i].data.val = 'R';
674 atmt_popup[++i].type = tQueue;
675 atmt_popup[i].label.style = lNormal;
676 atmt_popup[i].label.string = "&Forward...";
677 atmt_popup[i].data.val = 'F';
680 atmt_popup[++i].type = tQueue;
681 atmt_popup[i].label.style = lNormal;
682 atmt_popup[i].label.string = "&About...";
683 atmt_popup[i].data.val = 'A';
685 atmt_popup[++i].type = tSeparator;
687 atmt_popup[++i].type = tQueue;
688 snprintf(backtag, sizeof(backtag), "View Message #%ld",
689 mn_get_cur(ps->msgmap));
690 backtag[sizeof(backtag)-1] = '\0';
691 atmt_popup[i].label.string = backtag;
692 atmt_popup[i].label.style = lNormal;
693 atmt_popup[i].data.val = '<';
695 atmt_popup[++i].type = tTail;
697 if(mswin_popup(atmt_popup) == 1 && exoffer)
698 display_att_window(current->attp);
701 else if(mp.button == M_BUTTON_RIGHT){
702 MPopup atmt_popup[2];
704 atmt_popup[0].type = tQueue;
705 snprintf(backtag, sizeof(backtag), "View Message #%ld",
706 mn_get_cur(ps->msgmap));
707 backtag[sizeof(backtag)-1] = '\0';
708 atmt_popup[0].label.string = backtag;
709 atmt_popup[0].label.style = lNormal;
710 atmt_popup[0].data.val = '<';
712 atmt_popup[1].type = tTail;
714 mswin_popup(atmt_popup);
715 #endif
718 break;
719 #endif
721 case MC_WHEREIS : /* whereis */
722 /*--- get string ---*/
723 {int rc, found = 0;
724 char *result = NULL, buf[64];
725 static char last[64], tmp[64];
726 HelpType help;
728 ps->mangled_footer = 1;
729 buf[0] = '\0';
730 snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ",
731 (last[0]) ? "[" : "",
732 (last[0]) ? last : "",
733 (last[0]) ? "]" : "");
734 tmp[sizeof(tmp)-1] = '\0';
735 help = NO_HELP;
736 while(1){
737 int flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
739 rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf),
740 tmp,NULL,help,&flags);
741 if(rc == 3)
742 help = help == NO_HELP ? h_attach_index_whereis : NO_HELP;
743 else if(rc == 0 || rc == 1 || !buf[0]){
744 if(rc == 0 && !buf[0] && last[0]){
745 strncpy(buf, last, sizeof(buf));
746 buf[sizeof(buf)-1] = '\0';
749 break;
753 if(rc == 0 && buf[0]){
754 ctmp = current;
755 while((ctmp = next_attline(ctmp)) != NULL)
756 if(srchstr(ctmp->dstring, buf)){
757 found++;
758 break;
761 if(!found){
762 ctmp = first_attline(current);
764 while(ctmp != current)
765 if(srchstr(ctmp->dstring, buf)){
766 found++;
767 break;
769 else
770 ctmp = next_attline(ctmp);
773 else
774 result = _("WhereIs cancelled");
776 if(found && ctmp){
777 strncpy(last, buf, sizeof(last));
778 last[sizeof(last)-1] = '\0';
779 result = "Word found";
780 current = ctmp;
783 q_status_message(SM_ORDER, 0, 3,
784 result ? result : _("Word not found"));
787 break;
789 case MC_DELETE :
790 if(delete_attachment(msgno, current->attp)){
791 int l = current ? strlen(current->attp->number) : 0;
793 current->dstring[1] = 'D';
795 /* Also indicate any children that will be deleted */
797 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
798 if(!strncmp(ctmp->attp->number, current->attp->number, l)
799 && ctmp->attp->number[l] == '.'){
800 ctmp->dstring[1] = 'D';
801 ps->mangled_screen = 1;
805 break;
807 case MC_UNDELETE :
808 if(undelete_attachment(msgno, current->attp, &expbits)){
809 int l = current ? strlen(current->attp->number) : 0;
811 current->dstring[1] = ' ';
813 /* And unflag anything implicitly undeleted */
814 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
815 if(!strncmp(ctmp->attp->number, current->attp->number, l)
816 && ctmp->attp->number[l] == '.'
817 && (!msgno_exceptions(ps->mail_stream, msgno,
818 ctmp->attp->number, &expbits, FALSE)
819 || !(expbits & MSG_EX_DELETE))){
820 ctmp->dstring[1] = ' ';
821 ps->mangled_screen = 1;
825 break;
827 case MC_REPLY :
828 reply_msg_att(ps->mail_stream, msgno, current->attp);
829 break;
831 case MC_FORWARD :
832 forward_attachment(ps->mail_stream, msgno, current->attp);
833 break;
835 case MC_BOUNCE :
836 bounce_msg_att(ps->mail_stream, msgno, current->attp->number,
837 current->attp->body->nested.msg->env->subject);
838 ps->mangled_footer = 1;
839 break;
841 case MC_ABOUTATCH :
842 display_attach_info(msgno, current->attp);
843 break;
845 case MC_VIEW_ATCH : /* View command */
846 display_attachment(msgno, current->attp, DA_SAVE);
847 old_cols = -1;
848 /* fall thru to repaint */
850 case MC_REPAINT : /* redraw */
851 case MC_RESIZE :
852 ps->mangled_screen = 1;
853 break;
855 case MC_EXPORT :
856 export_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
857 ps->mangled_footer = 1;
858 break;
860 case MC_SAVETEXT : /* Save command */
861 save_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
862 ps->mangled_footer = 1;
863 break;
865 case MC_PRINTMSG : /* Save command */
866 print_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
867 ps->mangled_footer = 1;
868 break;
870 case MC_PIPE : /* Pipe command */
871 if(F_ON(F_ENABLE_PIPE, ps)){
872 pipe_attachment(msgno, current->attp);
873 ps->mangled_footer = 1;
874 break;
875 } /* fall thru to complain */
877 case MC_NONE: /* simple timeout */
878 break;
880 case MC_UTF8:
881 bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
882 break;
884 case MC_CHARUP :
885 case MC_CHARDOWN :
886 case MC_CHARRIGHT :
887 case MC_CHARLEFT :
888 case MC_GOTOBOL :
889 case MC_GOTOEOL :
890 case MC_UNKNOWN :
891 default:
892 bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
897 for(current = first_attline(current); current;){ /* clean up */
898 ctmp = current->next;
899 free_attline(&current);
900 current = ctmp;
903 if(screen.titlecolor)
904 free_color_pair(&screen.titlecolor);
908 /*----------------------------------------------------------------------
909 allocate and attach a fresh attachment line struct
911 Args: current -- display line to attache new struct to
913 Returns: newly alloc'd attachment display line
914 ----*/
915 ATDISP_S *
916 new_attline(ATDISP_S **current)
918 ATDISP_S *p;
920 p = (ATDISP_S *)fs_get(sizeof(ATDISP_S));
921 memset((void *)p, 0, sizeof(ATDISP_S));
922 if(current){
923 if(*current){
924 p->next = (*current)->next;
925 (*current)->next = p;
926 p->prev = *current;
927 if(p->next)
928 p->next->prev = p;
931 *current = p;
934 return(p);
938 /*----------------------------------------------------------------------
939 Release system resources associated with attachment display line
941 Args: p -- line to free
943 Result:
944 ----*/
945 void
946 free_attline(ATDISP_S **p)
948 if(p){
949 if((*p)->dstring)
950 fs_give((void **)&(*p)->dstring);
952 fs_give((void **)p);
957 /*----------------------------------------------------------------------
958 Manage display of the attachment screen menu body.
960 Args: ps -- pine struct pointer
961 current -- currently selected display line
962 screen -- reference points for display painting
964 Result:
967 attachment_screen_updater(struct pine *ps, ATDISP_S *current, ATT_SCREEN_S *screen)
969 int dline, return_line = HEADER_ROWS(ps);
970 ATDISP_S *top_line, *ctmp;
972 /* calculate top line of display */
973 dline = 0;
974 ctmp = top_line = first_attline(current);
976 if(((dline++)%(ps->ttyo->screen_rows-HEADER_ROWS(ps)-FOOTER_ROWS(ps)))==0)
977 top_line = ctmp;
978 while(ctmp != current && (ctmp = next_attline(ctmp)));
980 #ifdef _WINDOWS
981 /* Don't know how to manage scroll bar for attachment screen yet. */
982 scroll_setrange (0L, 0L);
983 #endif
985 /* mangled body or new page, force redraw */
986 if(ps->mangled_body || screen->top_line != top_line)
987 screen->current = NULL;
989 /* loop thru painting what's needed */
990 for(dline = 0, ctmp = top_line;
991 dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
992 dline++, ctmp = next_attline(ctmp)){
995 * only fall thru painting if something needs painting...
997 if(!(!screen->current || ctmp == screen->current || ctmp == current))
998 continue;
1000 if(ctmp && ctmp->dstring){
1001 char *p = tmp_20k_buf;
1002 int i, col, x = 0, totlen;
1004 if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
1005 x = 2;
1006 if(ctmp == current){
1007 return_line = dline + HEADER_ROWS(ps);
1008 PutLine0(dline + HEADER_ROWS(ps), 0, "->");
1010 else
1011 PutLine0(dline + HEADER_ROWS(ps), 0, " ");
1014 * Only paint lines if we have to...
1016 if(screen->current)
1017 continue;
1019 else if(ctmp == current){
1020 return_line = dline + HEADER_ROWS(ps);
1021 StartInverse();
1024 totlen = strlen(ctmp->dstring);
1027 * Copy the value to a temp buffer expanding tabs.
1028 * Assume the caller set the widths so as not to overflow the
1029 * right margin.
1031 for(i=0,col=x; ctmp->dstring[i]; i++){
1032 if(ctmp->dstring[i] == TAB){
1033 if((p-tmp_20k_buf) < SIZEOF_20KBUF-8)
1035 *p++ = ' ';
1036 while((++col)&0x07);
1038 else{
1039 col += width_at_this_position((unsigned char *) &ctmp->dstring[i], totlen-i);
1040 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1041 *p++ = ctmp->dstring[i];
1046 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1047 *p = '\0';
1049 PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
1051 if(ctmp == current
1052 && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
1053 EndInverse();
1055 else
1056 ClearLine(dline + HEADER_ROWS(ps));
1059 ps->mangled_body = 0;
1060 screen->top_line = top_line;
1061 screen->current = current;
1062 return(return_line);
1066 /*----------------------------------------------------------------------
1067 Redraw the attachment screen based on the global "att_screen" struct
1069 Args: none
1071 Result:
1072 ----*/
1073 void
1074 attachment_screen_redrawer(void)
1076 bitmap_t bitmap;
1078 update_att_screen_titlebar();
1079 ps_global->mangled_header = 0;
1080 ClearLine(1);
1082 ps_global->mangled_body = 1;
1083 (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
1085 setbitmap(bitmap);
1086 draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
1087 1-FOOTER_ROWS(ps_global), 0, SameMenu);
1091 void
1092 update_att_screen_titlebar(void)
1094 long raw_msgno;
1095 COLOR_PAIR *returned_color = NULL;
1096 COLOR_PAIR *titlecolor = NULL;
1097 int colormatch;
1098 SEARCHSET *ss = NULL;
1099 PAT_STATE *pstate = NULL;
1101 if(ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
1102 raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
1103 ss = mail_newsearchset();
1104 ss->first = ss->last = (unsigned long) raw_msgno;
1106 if(ss){
1107 colormatch = get_index_line_color(ps_global->mail_stream,
1108 ss, &pstate, &returned_color);
1109 mail_free_searchset(&ss);
1112 * This is a bit tricky. If there is a colormatch but
1113 * returned_color
1114 * is NULL, that means that the user explicitly wanted the
1115 * Normal color used in this index line, so that is what we
1116 * use. If no colormatch then we will use the TITLE color
1117 * instead of Normal.
1119 if(colormatch){
1120 if(returned_color)
1121 titlecolor = returned_color;
1122 else
1123 titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
1124 ps_global->VAR_NORM_BACK_COLOR);
1127 if(titlecolor
1128 && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
1129 char cbuf[MAXCOLORLEN+1];
1131 strncpy(cbuf, titlecolor->fg, sizeof(cbuf));
1132 cbuf[sizeof(cbuf)-1] = '\0';
1133 strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
1134 titlecolor->fg[MAXCOLORLEN] = '\0';
1135 strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
1136 titlecolor->bg[MAXCOLORLEN] = '\0';
1140 /* Did the color change? */
1141 if((!titlecolor && att_screen->titlecolor)
1143 (titlecolor && !att_screen->titlecolor)
1145 (titlecolor && att_screen->titlecolor
1146 && (strcmp(titlecolor->fg, att_screen->titlecolor->fg)
1147 || strcmp(titlecolor->bg, att_screen->titlecolor->bg)))){
1149 if(att_screen->titlecolor)
1150 free_color_pair(&att_screen->titlecolor);
1152 att_screen->titlecolor = titlecolor;
1153 titlecolor = NULL;
1156 if(titlecolor)
1157 free_color_pair(&titlecolor);
1160 set_titlebar(_("ATTACHMENT INDEX"), ps_global->mail_stream,
1161 ps_global->context_current, ps_global->cur_folder,
1162 ps_global->msgmap, 1, MessageNumber, 0, 0,
1163 att_screen->titlecolor);
1167 /*----------------------------------------------------------------------
1168 Seek back from the given display line to the beginning of the list
1170 Args: p -- display linked list
1172 Result:
1173 ----*/
1174 ATDISP_S *
1175 first_attline(ATDISP_S *p)
1177 while(p && p->prev)
1178 p = p->prev;
1180 return(p);
1185 init_att_progress(char *msg, MAILSTREAM *stream, struct mail_bodystruct *body)
1187 if((save_att_length = body->size.bytes) != 0){
1188 /* if there are display filters, factor in extra copy */
1189 if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
1190 save_att_length += body->size.bytes;
1192 /* if remote folder and segment not cached, factor in IMAP fetch */
1193 if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
1194 && !((body->type == TYPETEXT && body->contents.text.data)
1195 || ((body->type == TYPEMESSAGE)
1196 && body->nested.msg && body->nested.msg->text.text.data)
1197 || body->contents.text.data))
1198 save_att_length += body->size.bytes;
1200 gf_filter_init(); /* reset counters */
1201 pine_gets_bytes(1);
1202 save_att_piped(1);
1203 return(busy_cue(msg, save_att_percent, 0));
1206 return(0);
1210 long
1211 save_att_piped(int reset)
1213 static long x;
1214 long y;
1216 if(reset){
1217 x = y = 0L;
1219 else if((y = gf_bytes_piped()) >= x){
1220 x = y;
1221 y = 0;
1224 return(x + y);
1229 save_att_percent(void)
1231 int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
1232 / save_att_length);
1233 return(MIN(i, 100));
1237 /*----------------------------------------------------------------------
1238 Save the given attachment associated with the given message no
1240 Args: ps
1242 Result:
1243 ----*/
1244 void
1245 save_attachment(int qline, long int msgno, ATTACH_S *a)
1247 if(ps_global->restricted){
1248 q_status_message(SM_ORDER | SM_DING, 0, 4,
1249 "Alpine demo can't save attachments");
1250 return;
1253 if(MIME_MSG_A(a))
1254 save_msg_att(msgno, a);
1255 else if(MIME_DGST_A(a))
1256 save_digest_att(msgno, a);
1257 else if(MIME_VCARD_A(a))
1258 save_vcard_att(ps_global, qline, msgno, a);
1259 else
1260 write_attachment(qline, msgno, a, "SAVE");
1264 /*----------------------------------------------------------------------
1265 Save the given attachment associated with the given message no
1267 Args: ps
1269 Result:
1270 ----*/
1271 void
1272 export_attachment(int qline, long int msgno, ATTACH_S *a)
1274 if(ps_global->restricted){
1275 q_status_message(SM_ORDER | SM_DING, 0, 4,
1276 "Alpine demo can't export attachments");
1277 return;
1280 if(MIME_MSG_A(a))
1281 export_msg_att(msgno, a);
1282 else if(MIME_DGST_A(a))
1283 export_digest_att(msgno, a);
1284 else
1285 q_status_message1(SM_ORDER, 0, 3,
1286 _("Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."),
1287 body_type_names(a->body->type));
1291 void
1292 write_attachment(int qline, long int msgno, ATTACH_S *a, char *method)
1294 char filename[MAXPATH+1], full_filename[MAXPATH+1],
1295 title_buf[64], *err;
1296 int r, rflags = GER_NONE, we_cancel = 0;
1297 static HISTORY_S *history = NULL;
1298 static ESCKEY_S att_save_opts[] = {
1299 {ctrl('T'), 10, "^T", N_("To Files")},
1300 {-1, 0, NULL, NULL},
1301 {-1, 0, NULL, NULL},
1302 {-1, 0, NULL, NULL}};
1304 /*------- Figure out suggested file name ----*/
1305 filename[0] = full_filename[0] = '\0';
1306 (void) get_filename_parameter(filename, sizeof(filename), a->body, NULL);
1308 dprint((9, "export_attachment(name: %s)\n",
1309 filename ? filename : "?"));
1311 r = 0;
1312 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1313 if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
1314 att_save_opts[++r].ch = ctrl('V');
1315 att_save_opts[r].rval = 12;
1316 att_save_opts[r].name = "^V";
1317 att_save_opts[r].label = N_("Downld Msg");
1319 #endif /* !(DOS || MAC) */
1321 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1322 att_save_opts[++r].ch = ctrl('I');
1323 att_save_opts[r].rval = 11;
1324 att_save_opts[r].name = "TAB";
1325 att_save_opts[r].label = N_("Complete");
1328 att_save_opts[++r].ch = -1;
1330 snprintf(title_buf, sizeof(title_buf), "%s ATTACHMENT", method);
1331 title_buf[sizeof(title_buf)-1] = '\0';
1333 r = get_export_filename(ps_global, filename, NULL, full_filename,
1334 sizeof(filename), "attachment", title_buf,
1335 att_save_opts, &rflags, qline, GE_SEQ_SENSITIVE, &history);
1337 if(r < 0){
1338 switch(r){
1339 case -1:
1340 cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
1341 break;
1343 case -2:
1344 q_status_message1(SM_ORDER, 0, 2,
1345 _("Can't save to file outside of %s"),
1346 ps_global->VAR_OPER_DIR);
1347 break;
1350 return;
1352 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1353 else if(r == 12){ /* Download */
1354 char cmd[MAXPATH], *tfp = NULL;
1355 PIPE_S *syspipe;
1356 gf_io_t pc;
1357 long len;
1358 STORE_S *store;
1359 char prompt_buf[256];
1361 if(ps_global->restricted){
1362 q_status_message(SM_ORDER | SM_DING, 3, 3,
1363 "Download disallowed in restricted mode");
1364 return;
1367 err = NULL;
1368 tfp = temp_nam(NULL, "pd");
1369 dprint((1, "Download attachment called!\n"));
1370 if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
1372 snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
1373 prompt_buf[sizeof(prompt_buf)-1] = '\0';
1374 we_cancel = init_att_progress(prompt_buf,
1375 ps_global->mail_stream,
1376 a->body);
1378 gf_set_so_writec(&pc, store);
1379 if((err = detach(ps_global->mail_stream, msgno,
1380 a->number, 0L, &len, pc, NULL, 0)) != NULL)
1381 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1382 "%s: Error writing attachment to \"%s\"",
1383 err, tfp);
1385 /* cancel regardless, so it doesn't get in way of xfer */
1386 cancel_busy_cue(0);
1388 gf_clear_so_writec(store);
1389 if(so_give(&store)) /* close file */
1390 err = "Error writing tempfile for download";
1392 if(!err){
1393 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
1394 ps_global->VAR_DOWNLOAD_CMD, tfp);
1395 if((syspipe = open_system_pipe(cmd, NULL, NULL,
1396 PIPE_USER | PIPE_RESET,
1397 0, pipe_callback, pipe_report_error)) != NULL)
1398 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1399 else
1400 q_status_message(SM_ORDER | SM_DING, 3, 3,
1401 err = "Error running download command");
1404 else
1405 q_status_message(SM_ORDER | SM_DING, 3, 3,
1406 err = "Error building temp file for download");
1408 if(tfp){
1409 our_unlink(tfp);
1410 fs_give((void **)&tfp);
1413 if(!err)
1414 q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
1415 a->number);
1417 return;
1419 #endif /* !(DOS || MAC) */
1421 (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
1422 rflags, full_filename);
1427 * Args stream --
1428 * msgno -- raw message number
1429 * a -- attachment struct
1430 * flags -- comes from get_export_filename
1431 * GER_OVER -- the file was truncated before we wrote
1432 * GER_APPEND -- the file was not truncated prior to our writing,
1433 * so we were appending
1434 * else -- the file didn't previously exist
1435 * file -- the full pathname of the file
1437 * Returns 1 for successful write, 0 for nothing to write, -1 for error
1440 write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
1442 char *l_string, sbuf[256], *err;
1443 int is_text, we_cancel = 0;
1444 long len, orig_size;
1445 gf_io_t pc;
1446 STORE_S *store;
1448 if(!(a && a->body && a->number && a->number[0] && file && file[0]
1449 && stream))
1450 return 0;
1452 is_text = (a && a->body && a->body->type == TYPETEXT);
1454 if(flags & GER_APPEND)
1455 orig_size = name_file_size(file);
1457 store = so_get(FileStar, file, WRITE_ACCESS | (is_text ? WRITE_TO_LOCALE : 0));
1458 if(store == NULL){
1459 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1460 /* TRANSLATORS: Error opening destination <filename>: <error text> */
1461 _("Error opening destination %s: %s"),
1462 file, error_description(errno));
1463 return -1;
1466 snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
1467 sbuf[sizeof(sbuf)-1] = '\0';
1468 we_cancel = init_att_progress(sbuf, stream, a->body);
1470 gf_set_so_writec(&pc, store);
1471 err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, 0);
1472 gf_clear_so_writec(store);
1474 if(we_cancel)
1475 cancel_busy_cue(0);
1477 if(so_give(&store)) /* close file */
1478 err = error_description(errno);
1480 if(err){
1481 if(!(flags & (GER_APPEND | GER_OVER)))
1482 our_unlink(file);
1483 else
1484 our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
1486 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1487 /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
1488 _("%s: Error writing attachment to \"%s\""),
1489 err, file);
1490 return -1;
1492 else{
1493 l_string = cpystr(byte_string(len));
1494 q_status_message8(SM_ORDER, 0, 4,
1495 "Part %s, %s%s %s to \"%s\"%s%s%s",
1496 a->number,
1497 is_text
1498 ? comatose(a->body->size.lines) : l_string,
1499 is_text ? " lines" : "",
1500 flags & GER_OVER
1501 ? "overwritten"
1502 : flags & GER_APPEND ? "appended" : "written",
1503 file,
1504 (is_text || len == a->body->size.bytes)
1505 ? "" : "(decoded from ",
1506 (is_text || len == a->body->size.bytes)
1507 ? "" : byte_string(a->body->size.bytes),
1508 is_text || len == a->body->size.bytes
1509 ? "" : ")");
1510 fs_give((void **)&l_string);
1511 return 1;
1516 char *
1517 write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
1519 char *err = NULL;
1520 long start_of_append;
1521 gf_io_t pc;
1522 MESSAGECACHE *mc;
1524 if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
1525 && (*ap)->body->nested.msg->env){
1526 start_of_append = so_tell(store);
1528 gf_set_so_writec(&pc, store);
1529 if(!(ps_global->mail_stream && msgno > 0L
1530 && msgno <= ps_global->mail_stream->nmsgs
1531 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1532 mc = NULL;
1534 if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
1535 || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
1536 err = error_description(errno);
1538 gf_clear_so_writec(store);
1540 if(err)
1541 ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
1543 else
1544 err = "Can't export message. Missing Envelope data";
1546 return(err);
1550 /*----------------------------------------------------------------------
1551 Save the attachment message/rfc822 to specified folder
1553 Args:
1555 Result:
1556 ----*/
1557 void
1558 save_msg_att(long int msgno, ATTACH_S *a)
1560 char newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
1561 char date[64], nmsgs[80];
1562 CONTEXT_S *cntxt = NULL;
1563 int our_stream = 0, rv;
1564 MAILSTREAM *save_stream;
1565 MESSAGECACHE *mc;
1567 snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
1568 nmsgs[sizeof(nmsgs)-1] = '\0';
1569 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
1570 a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
1571 if(strucmp(newfolder, ps_global->inbox_name) == 0){
1572 save_folder = ps_global->VAR_INBOX_PATH;
1573 cntxt = NULL;
1575 else
1576 save_folder = newfolder;
1578 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1580 mc = (msgno > 0L && ps_global->mail_stream
1581 && msgno <= ps_global->mail_stream->nmsgs)
1582 ? mail_elt(ps_global->mail_stream, msgno) : NULL;
1583 flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
1584 if(mc && mc->day)
1585 mail_date(date, mc);
1586 else
1587 *date = '\0';
1589 if(pith_opt_save_size_changed_prompt)
1590 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1592 rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
1594 if(pith_opt_save_size_changed_prompt)
1595 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1597 if(flags)
1598 fs_give((void **) &flags);
1600 if(rv == 1)
1601 q_status_message2(SM_ORDER, 0, 4,
1602 _("Attached message (part %s) saved to \"%s\""),
1603 a->number,
1604 save_folder);
1605 else if(rv == -1)
1606 cmd_cancelled("Attached message Save");
1607 /* else whatever broke in save_fetch_append shoulda bitched */
1609 if(our_stream)
1610 mail_close(save_stream);
1615 /*----------------------------------------------------------------------
1616 Save the message/rfc822 in the given digest to the specified folder
1618 Args:
1620 Result:
1621 ----*/
1622 void
1623 save_digest_att(long int msgno, ATTACH_S *a)
1625 char newfolder[MAILTMPLEN], *save_folder,
1626 date[64], nmsgs[80];
1627 CONTEXT_S *cntxt = NULL;
1628 int our_stream = 0, rv, cnt = 0;
1629 MAILSTREAM *save_stream;
1630 ATTACH_S *ap;
1632 for(ap = a + 1;
1633 ap->description
1634 && !strncmp(a->number, ap->number, strlen(a->number));
1635 ap++)
1636 if(MIME_MSG(ap->body->type, ap->body->subtype))
1637 cnt++;
1639 snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
1640 nmsgs[sizeof(nmsgs)-1] = '\0';
1642 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
1643 nmsgs, NULL, 0, NULL, NULL, NULL)){
1644 save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
1645 ? ps_global->VAR_INBOX_PATH : newfolder;
1647 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1649 if(pith_opt_save_size_changed_prompt)
1650 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1652 for(ap = a + 1;
1653 ap->description
1654 && !strncmp(a->number, ap->number, strlen(a->number));
1655 ap++)
1656 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1657 *date = '\0';
1658 rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
1659 if(rv != 1)
1660 break;
1663 if(pith_opt_save_size_changed_prompt)
1664 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1666 if(rv == 1)
1667 q_status_message2(SM_ORDER, 0, 4,
1668 _("Attached digest (part %s) saved to \"%s\""),
1669 a->number,
1670 save_folder);
1671 else if(rv == -1)
1672 cmd_cancelled("Attached digest Save");
1673 /* else whatever broke in save_fetch_append shoulda bitched */
1675 if(our_stream)
1676 mail_close(save_stream);
1682 save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
1683 char *save_folder, CONTEXT_S *cntxt, char *date)
1685 STORE_S *so;
1686 int rv = 0;
1688 if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
1689 if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1690 *date = '\0';
1691 rv = save_fetch_append(ps_global->mail_stream, msgno,
1692 a->number,
1693 save_stream, save_folder, cntxt,
1694 a->body->size.bytes,
1695 NULL, date, so);
1697 else{
1698 dprint((1, "Can't allocate store for save: %s\n",
1699 error_description(errno)));
1700 q_status_message(SM_ORDER | SM_DING, 3, 4,
1701 _("Problem creating space for message text."));
1702 rv = 0;
1706 return(rv);
1710 /*----------------------------------------------------------------------
1711 Export the attachment message/rfc822 to specified file
1713 Args:
1715 Result:
1716 ----*/
1717 void
1718 export_msg_att(long int msgno, ATTACH_S *a)
1720 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
1721 int rv, rflags = GER_NONE, i = 1;
1722 ATTACH_S *ap = a;
1723 STORE_S *store;
1724 static HISTORY_S *history = NULL;
1725 static ESCKEY_S opts[] = {
1726 {ctrl('T'), 10, "^T", N_("To Files")},
1727 {-1, 0, NULL, NULL},
1728 {-1, 0, NULL, NULL}};
1730 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1731 opts[i].ch = ctrl('I');
1732 opts[i].rval = 11;
1733 opts[i].name = "TAB";
1734 opts[i].label = N_("Complete");
1737 filename[0] = full_filename[0] = '\0';
1739 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1740 sizeof(filename), "msg attachment",
1741 /* TRANSLATORS: Message Attachment (a screen title) */
1742 _("MSG ATTACHMENT"), opts,
1743 &rflags, -FOOTER_ROWS(ps_global),
1744 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1746 if(rv < 0){
1747 switch(rv){
1748 case -1:
1749 cmd_cancelled("Export");
1750 break;
1752 case -2:
1753 q_status_message1(SM_ORDER, 0, 2,
1754 _("Can't export to file outside of %s"),
1755 ps_global->VAR_OPER_DIR);
1756 break;
1759 return;
1762 /* With name in hand, allocate storage object and save away... */
1763 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1764 if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
1765 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1766 else
1767 q_status_message3(SM_ORDER, 0, 4,
1768 _("Attached message (part %s) %s to \"%s\""),
1769 a->number,
1770 rflags & GER_OVER
1771 ? _("overwritten")
1772 : rflags & GER_APPEND ? _("appended") : _("written"),
1773 full_filename);
1775 if(so_give(&store))
1776 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1777 _("Error writing %s: %s"),
1778 full_filename, error_description(errno));
1780 else
1781 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1782 /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
1783 _("Error opening file \"%s\" to export message: %s"),
1784 full_filename, error_description(errno));
1788 /*----------------------------------------------------------------------
1789 Export the message/rfc822 in the given digest to the specified file
1791 Args:
1793 Result:
1794 ----*/
1795 void
1796 export_digest_att(long int msgno, ATTACH_S *a)
1798 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
1799 int rv, rflags = GER_NONE, i = 1;
1800 long count = 0L;
1801 ATTACH_S *ap;
1802 static HISTORY_S *history = NULL;
1803 STORE_S *store;
1804 static ESCKEY_S opts[] = {
1805 {ctrl('T'), 10, "^T", N_("To Files")},
1806 {-1, 0, NULL, NULL},
1807 {-1, 0, NULL, NULL}};
1809 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1810 opts[i].ch = ctrl('I');
1811 opts[i].rval = 11;
1812 opts[i].name = "TAB";
1813 opts[i].label = N_("Complete");
1816 filename[0] = full_filename[0] = '\0';
1818 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1819 sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
1820 opts, &rflags, -FOOTER_ROWS(ps_global),
1821 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1823 if(rv < 0){
1824 switch(rv){
1825 case -1:
1826 cmd_cancelled("Export");
1827 break;
1829 case -2:
1830 q_status_message1(SM_ORDER, 0, 2,
1831 _("Can't export to file outside of %s"),
1832 ps_global->VAR_OPER_DIR);
1833 break;
1836 return;
1839 /* With name in hand, allocate storage object and save away... */
1840 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1841 count = 0;
1843 for(ap = a + 1;
1844 ap->description
1845 && !strncmp(a->number, ap->number, strlen(a->number))
1846 && !err;
1847 ap++){
1848 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1849 count++;
1850 err = write_attached_msg(msgno, &ap, store,
1851 !count && !(rflags & GER_APPEND));
1855 if(so_give(&store))
1856 err = error_description(errno);
1858 if(err){
1859 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1860 _("Error exporting: %s"), err);
1861 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1862 _("%s messages exported before error occurred"), err);
1864 else
1865 q_status_message4(SM_ORDER, 0, 4,
1866 "%s messages in digest (part %s) %s to \"%s\"",
1867 long2string(count),
1868 a->number,
1869 rflags & GER_OVER
1870 ? "overwritten"
1871 : rflags & GER_APPEND ? "appended" : "written",
1872 full_filename);
1874 else
1875 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1876 _("Error opening file \"%s\" to export digest: %s"),
1877 full_filename, error_description(errno));
1881 /*----------------------------------------------------------------------
1882 Print the given attachment associated with the given message no
1884 Args: ps
1886 Result:
1887 ----*/
1888 void
1889 print_attachment(int qline, long int msgno, ATTACH_S *a)
1891 char prompt[250];
1893 if(ps_global->restricted){
1894 q_status_message(SM_ORDER | SM_DING, 0, 4,
1895 "Alpine demo can't Print attachments");
1896 return;
1899 snprintf(prompt, sizeof(prompt), "attach%s %s",
1900 (a->body->type == TYPETEXT) ? "ment" : "ed message",
1901 MIME_DGST_A(a) ? "digest" : a->number);
1902 prompt[sizeof(prompt)-1] = '\0';
1903 if(open_printer(prompt) >= 0){
1904 if(MIME_MSG_A(a))
1905 (void) print_msg_att(msgno, a, 1);
1906 else if(MIME_DGST_A(a))
1907 print_digest_att(msgno, a);
1908 else
1909 (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
1911 close_printer();
1917 * Print the attachment message/rfc822 to specified file
1919 * Returns 1 on success, 0 on failure.
1922 print_msg_att(long int msgno, ATTACH_S *a, int first)
1924 ATTACH_S *ap = a;
1925 MESSAGECACHE *mc;
1927 if(!(ps_global->mail_stream && msgno > 0L
1928 && msgno <= ps_global->mail_stream->nmsgs
1929 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1930 mc = NULL;
1932 if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
1933 && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
1934 && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
1935 ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
1936 : 1)
1937 && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
1938 return(1);
1941 q_status_message2(SM_ORDER | SM_DING, 3, 3,
1942 _("Error printing message %s, part %s"),
1943 long2string(msgno), a->number);
1944 return(0);
1948 /*----------------------------------------------------------------------
1949 Print the attachment message/rfc822 to specified file
1951 Args:
1953 Result:
1954 ----*/
1955 void
1956 print_digest_att(long int msgno, ATTACH_S *a)
1958 ATTACH_S *ap;
1959 int next = 0;
1961 for(ap = a + 1;
1962 ap->description
1963 && !strncmp(a->number, ap->number, strlen(a->number));
1964 ap++){
1965 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1966 char *p = part_desc(ap->number, ap->body->nested.msg->body,
1967 0, 80, FM_NOINDENT, print_char);
1968 if(p){
1969 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1970 _("Can't print digest: %s"), p);
1971 break;
1973 else if(!print_msg_att(msgno, ap, !next))
1974 break;
1976 next++;
1982 /*----------------------------------------------------------------------
1983 Unpack and display the given attachment associated with given message no.
1985 Args: msgno -- message no attachment is part of
1986 a -- attachment to display
1988 Returns: 0 on success, non-zero (and error message queued) otherwise
1989 ----*/
1991 display_attachment(long int msgno, ATTACH_S *a, int flags)
1993 char *filename = NULL;
1994 char sender_filename[1000];
1995 char *extp = NULL;
1996 STORE_S *store;
1997 gf_io_t pc;
1998 char *err;
1999 int we_cancel = 0, rv;
2000 char prefix[70];
2001 char ext[32];
2002 char mtype[128];
2004 /*------- Display the attachment -------*/
2005 if(dispatch_attachment(a) == MCD_NONE){
2006 /*----- Can't display this type ------*/
2007 if(a->body->encoding < ENCOTHER)
2008 q_status_message4(SM_ORDER | SM_DING, 3, 5,
2009 /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
2010 _("Don't know how to display %s%s%s attachments.%s"),
2011 body_type_names(a->body->type),
2012 a->body->subtype ? "/" : "",
2013 a->body->subtype ? a->body->subtype :"",
2014 (flags & DA_SAVE) ? _(" Try Save.") : "");
2015 else
2016 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2017 _("Don't know how to unpack \"%s\" encoding"),
2018 body_encodings[(a->body->encoding <= ENCMAX)
2019 ? a->body->encoding : ENCOTHER]);
2021 return(1);
2023 else if(!(a->can_display & MCD_EXTERNAL)){
2024 if(a->body->type == TYPEMULTIPART){
2025 if(a->body->subtype){
2026 if(!strucmp(a->body->subtype, "digest"))
2027 display_digest_att(msgno, a, flags);
2028 else
2029 q_status_message1(SM_ORDER, 3, 5,
2030 _("Can't display Multipart/%s"),
2031 a->body->subtype);
2033 else
2034 q_status_message(SM_ORDER, 3, 5,
2035 _("Can't display unknown Multipart Subtype"));
2037 else if(MIME_VCARD_A(a))
2038 display_vcard_att(msgno, a, flags);
2039 else if(a->body->type == TYPETEXT){
2041 rv = display_text_att(msgno, a, flags);
2042 } while(rv == MC_FULLHDR);
2044 else if(a->body->type == TYPEMESSAGE){
2046 rv = display_msg_att(msgno, a, flags);
2047 } while(rv == MC_FULLHDR);
2050 ps_global->mangled_screen = 1;
2051 return(0);
2054 /* arrive here if MCD_EXTERNAL */
2056 if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
2057 && (!(flags & DA_DIDPROMPT)))
2058 if(want_to(_("View selected Attachment"), 'y',
2059 0, NO_HELP, WT_NORM) == 'n'){
2060 cmd_cancelled(NULL);
2061 return(1);
2064 sender_filename[0] = '\0';
2065 ext[0] = '\0';
2066 prefix[0] = '\0';
2068 if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
2069 && (a->can_display & MCD_EXT_PROMPT)){
2070 char prompt[256];
2072 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2073 a->body, &extp);
2074 snprintf(prompt, sizeof(prompt),
2075 "Attachment %s%s unrecognized. %s%s%s",
2076 a->body->subtype,
2077 strlen(a->body->subtype) > 12 ? "..." : "",
2078 (extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
2079 (extp && extp[0]) ? extp : "",
2080 (extp && extp[0]) ? ")" : "");
2082 if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
2083 cmd_cancelled(NULL);
2084 return(1);
2088 /*------ Write the image to a temporary file ------*/
2090 /* create type/subtype in mtype */
2091 strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
2092 mtype[sizeof(mtype)-1] = '\0';
2093 if(a->body->subtype){
2094 strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
2095 mtype[sizeof(mtype)-1] = '\0';
2096 strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
2097 mtype[sizeof(mtype)-1] = '\0';
2101 * If we haven't already gotten the filename parameter, get it
2102 * now. It may be used in the temporary filename and possibly
2103 * for its extension.
2105 if(!sender_filename[0])
2106 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2107 a->body, &extp);
2109 if(!set_mime_extension_by_type(ext, mtype)){ /* extension from type */
2110 if(extp && extp[0]){ /* extension from filename */
2111 strncpy(ext, extp, sizeof(ext));
2112 ext[sizeof(ext)-1] = '\0';
2116 /* create a temp file */
2117 if(sender_filename){
2118 char *p, *q = NULL;
2120 /* get rid of any extension */
2121 if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
2122 *(q-1) = '\0';
2124 /* be careful about what is allowed in the filename */
2125 for(p = sender_filename; *p; p++)
2126 if(!(isascii((unsigned char) *p)
2127 && (isalnum((unsigned char) *p)
2128 || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
2129 break;
2131 if(!*p) /* filename was ok to use */
2132 snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
2135 /* didn't get it yet */
2136 if(!prefix[0]){
2137 snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
2138 ? a->body->subtype : "unk");
2141 /* We are creating a temporary name. This is just a prefix. If you
2142 * need the original name, use the save command, so if the prefix
2143 * is too long, shorten it.
2145 if (strlen(prefix) > 9){
2146 prefix[9] = '-';
2147 prefix[10] = '\0';
2150 filename = temp_nam_ext(NULL, prefix, ext);
2152 if(!filename){
2153 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2154 _("Error \"%s\", Can't create temporary file"),
2155 error_description(errno));
2156 return(1);
2159 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2160 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2161 _("Error \"%s\", Can't write file %s"),
2162 error_description(errno), filename);
2163 if(filename){
2164 our_unlink(filename);
2165 fs_give((void **)&filename);
2168 return(1);
2172 if(a->body->size.bytes){
2173 char msg_buf[128];
2175 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2176 a->description ? "\"" : "",
2177 a->description ? a->description : "attachment number ",
2178 a->description ? "" : a->number,
2179 a->description ? "\"" : "");
2180 msg_buf[sizeof(msg_buf)-1] = '\0';
2181 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
2182 a->body);
2185 if(a->body->type == TYPEMULTIPART){
2186 char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
2187 NULL, 0L);
2189 /* Write to store while converting newlines */
2190 while(h && *h)
2191 if(*h == '\015' && *(h+1) == '\012'){
2192 so_puts(store, NEWLINE);
2193 h += 2;
2195 else
2196 so_writec(*h++, store);
2199 gf_set_so_writec(&pc, store);
2201 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
2203 gf_clear_so_writec(store);
2205 if(we_cancel)
2206 cancel_busy_cue(0);
2208 so_give(&store);
2210 if(err){
2211 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2212 "%s: Error saving image to temp file %s",
2213 err, filename);
2214 if(filename){
2215 our_unlink(filename);
2216 fs_give((void **)&filename);
2219 return(1);
2222 /*----- Run the viewer process ----*/
2223 run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
2224 if(filename)
2225 fs_give((void **)&filename);
2227 return(0);
2231 /*----------------------------------------------------------------------
2232 Fish the required command from mailcap and run it
2234 Args: image_file -- The name of the file to pass viewer
2235 body -- body struct containing type/subtype of part
2237 A side effect may be that scrolltool is called as well if
2238 exec_mailcap_cmd has any substantial output...
2239 ----*/
2240 void
2241 run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
2243 MCAP_CMD_S *mc_cmd = NULL;
2244 int needs_terminal = 0, we_cancel = 0;
2246 we_cancel = busy_cue("Displaying attachment", NULL, 0);
2248 if((mc_cmd = mailcap_build_command(body->type, body->subtype,
2249 body, image_file,
2250 &needs_terminal, chk_extension)) != NULL){
2251 if(we_cancel)
2252 cancel_busy_cue(-1);
2254 exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
2255 if(mc_cmd->command)
2256 fs_give((void **)&mc_cmd->command);
2257 fs_give((void **)&mc_cmd);
2259 else{
2260 if(we_cancel)
2261 cancel_busy_cue(-1);
2263 q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
2264 type_desc(body->type, body->subtype,
2265 body->parameter, NULL, 1));
2270 /*----------------------------------------------------------------------
2271 Detach and provide for browsing a text body part
2273 Args: msgno -- raw message number to get part from
2274 a -- attachment struct for the desired part
2276 Result:
2277 ----*/
2278 STORE_S *
2279 format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
2281 STORE_S *store;
2282 gf_io_t pc;
2284 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2285 if(handlesp)
2286 init_handles(handlesp);
2288 gf_set_so_writec(&pc, store);
2289 (void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
2290 gf_clear_so_writec(store);
2293 return(store);
2297 /*----------------------------------------------------------------------
2298 Detach and provide for browsing a text body part
2300 Args: msgno -- raw message number to get part from
2301 a -- attachment struct for the desired part
2303 Result:
2304 ----*/
2306 display_text_att(long int msgno, ATTACH_S *a, int flags)
2308 STORE_S *store;
2309 HANDLE_S *handles = NULL;
2310 int rv = 0;
2312 if(msgno > 0L)
2313 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2315 if((store = format_text_att(msgno, a, &handles)) != NULL){
2316 rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
2317 free_handles(&handles);
2318 so_give(&store); /* free resources associated with store */
2320 else
2321 q_status_message(SM_ORDER | SM_DING, 3, 3,
2322 _("Error allocating space for attachment."));
2324 return(rv);
2328 /*----------------------------------------------------------------------
2329 Detach and provide for browsing a body part of type "MESSAGE"
2331 Args: msgno -- message number to get partrom
2332 a -- attachment struct for the desired part
2334 Result:
2335 ----*/
2337 display_msg_att(long int msgno, ATTACH_S *a, int flags)
2339 STORE_S *store;
2340 gf_io_t pc;
2341 ATTACH_S *ap = a;
2342 HANDLE_S *handles = NULL;
2343 int rv = 0;
2345 if(msgno > 0L)
2346 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2348 /* BUG, should check this return code */
2349 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2351 /* initialize a storage object */
2352 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2353 q_status_message(SM_ORDER | SM_DING, 3, 3,
2354 _("Error allocating space for message."));
2355 return(rv);
2358 gf_set_so_writec(&pc, store);
2360 if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
2361 if(ps_global->full_header == 2)
2362 q_status_message(SM_INFO, 0, 3,
2363 _("Full header mode ON. All header text being included"));
2365 rv = scroll_attachment((a->body->subtype
2366 && !strucmp(a->body->subtype, "delivery-status"))
2367 ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
2368 store, CharStar, handles, a, flags);
2369 free_handles(&handles);
2372 gf_clear_so_writec(store);
2374 so_give(&store); /* free resources associated with store */
2375 return(rv);
2379 /*----------------------------------------------------------------------
2380 Detach and provide for browsing a multipart body part of type "DIGEST"
2382 Args: msgno -- message number to get partrom
2383 a -- attachment struct for the desired part
2385 Result:
2386 ----*/
2387 void
2388 display_digest_att(long int msgno, ATTACH_S *a, int flags)
2390 STORE_S *store;
2391 ATTACH_S *ap;
2392 HANDLE_S *handles = NULL;
2393 gf_io_t pc;
2394 SourceType src = CharStar;
2395 int bad_news = 0;
2397 if(msgno > 0L)
2398 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2400 /* BUG, should check this return code */
2401 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2403 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
2404 q_status_message(SM_ORDER | SM_DING, 3, 3,
2405 _("Error allocating space for message."));
2406 return;
2409 gf_set_so_writec(&pc, store);
2412 * While in a subtype of this message
2414 for(ap = a + 1;
2415 ap->description
2416 && !strncmp(a->number, ap->number, strlen(a->number))
2417 && !bad_news;
2418 ap++){
2419 if(ap->body->type == TYPEMESSAGE){
2420 char *errstr;
2422 if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
2423 char *tsub;
2425 tsub = cpystr(ap->body->subtype);
2426 convert_possibly_encoded_str_to_utf8((char **) &tsub);
2427 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
2428 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2430 if((errstr = format_editorial(tmp_20k_buf,
2431 ps_global->ttyo->screen_cols, 0,
2432 NULL, pc)) != NULL){
2433 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2434 _("Can't format digest: %s"), errstr);
2435 bad_news++;
2437 else if(!gf_puts(NEWLINE, pc))
2438 bad_news++;
2440 fs_give((void **) &tsub);
2442 else{
2443 if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
2444 0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
2445 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2446 _("Can't format digest: %s"), errstr);
2447 bad_news++;
2449 else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
2450 bad_news++;
2453 else if(ap->body->type == TYPETEXT
2454 && decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
2455 bad_news++;
2456 else if(!gf_puts("Unknown type in Digest", pc))
2457 bad_news++;
2460 if(!bad_news){
2461 if(ps_global->full_header == 2)
2462 q_status_message(SM_INFO, 0, 3,
2463 _("Full header mode ON. All header text being included"));
2465 scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
2468 free_handles(&handles);
2470 gf_clear_so_writec(store);
2471 so_give(&store); /* free resources associated with store */
2476 scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
2478 SCROLL_S sargs;
2480 memset(&sargs, 0, sizeof(SCROLL_S));
2481 sargs.text.text = so_text(store);
2482 sargs.text.src = src;
2483 sargs.text.desc = "attachment";
2484 sargs.text.handles = handles;
2485 sargs.bar.title = title;
2486 sargs.proc.tool = process_attachment_cmd;
2487 sargs.proc.data.p = (void *) a;
2488 sargs.help.text = h_mail_text_att_view;
2489 sargs.help.title = _("HELP FOR ATTACHED TEXT VIEW");
2490 sargs.keys.menu = &att_view_keymenu;
2491 setbitmap(sargs.keys.bitmap);
2493 /* First, fix up "back" key */
2494 if(flags & DA_FROM_VIEW){
2495 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
2497 else{
2498 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
2501 if(!handles){
2502 clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
2503 clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
2504 clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
2507 if(F_OFF(F_ENABLE_PIPE, ps_global))
2508 clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
2510 if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
2511 clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
2514 * If message or digest, leave Reply and Save and,
2515 * conditionally, Bounce on...
2517 if(MIME_MSG_A(a)){
2518 if(F_OFF(F_ENABLE_BOUNCE, ps_global))
2519 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2521 else{
2522 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2523 clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
2524 clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
2526 #ifdef SMIME
2527 if(!(ps_global->smime && ps_global->smime->need_passphrase))
2528 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2530 if(F_ON(F_DONT_DO_SMIME, ps_global) || !MIME_MSG_A(a)){
2531 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2532 clrbitn(ATV_SECURITY_KEY, sargs.keys.bitmap);
2534 #endif
2536 sargs.use_indexline_color = 1;
2538 if(DA_RESIZE & flags)
2539 sargs.resize_exit = 1;
2541 #ifdef _WINDOWS
2542 scrat_attachp = a;
2543 sargs.mouse.popup = scroll_att_popup;
2544 #endif
2546 return(scrolltool(&sargs));
2549 #ifdef SMIME
2550 void
2551 display_smime_info_att(struct pine *ps, ATTACH_S *a)
2553 if(smime_check(a->body->nested.msg->body) == 0){
2554 q_status_message(SM_ORDER | SM_DING, 0, 3,
2555 _("Not a signed or encrypted message"));
2556 return;
2559 display_smime_info(ps, a->body->nested.msg->env, a->body->nested.msg->body);
2561 #endif /* SMIME */
2564 process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2566 int rv = 0, n;
2567 long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2568 #define AD(X) ((ATTACH_S *) (X)->proc.data.p)
2570 switch(cmd){
2571 case MC_EXIT :
2572 rv = 1;
2573 break;
2575 case MC_QUIT :
2576 ps_global->next_screen = quit_screen;
2577 break;
2579 case MC_MAIN :
2580 ps_global->next_screen = main_menu_screen;
2581 break;
2583 case MC_REPLY :
2584 reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
2585 break;
2587 case MC_FORWARD :
2588 forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
2589 break;
2591 case MC_BOUNCE :
2592 bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
2593 AD(sparms)->body->nested.msg->env->subject);
2594 ps_global->mangled_footer = 1;
2595 break;
2597 case MC_DELETE :
2598 delete_attachment(rawno, sparms->proc.data.p);
2599 break;
2601 case MC_UNDELETE :
2602 if(undelete_attachment(rawno, sparms->proc.data.p, &n))
2603 q_status_message1(SM_ORDER, 0, 3,
2604 "Part %s UNdeleted", AD(sparms)->number);
2606 break;
2608 case MC_SAVE :
2609 save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2610 ps_global->mangled_footer = 1;
2611 break;
2613 case MC_EXPORT :
2614 export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2615 ps_global->mangled_footer = 1;
2616 break;
2618 case MC_PRINTMSG :
2619 print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2620 ps_global->mangled_footer = 1;
2621 break;
2623 case MC_PIPE :
2624 pipe_attachment(rawno, sparms->proc.data.p);
2625 ps_global->mangled_footer = 1;
2626 break;
2628 case MC_FULLHDR :
2629 ps_global->full_header++;
2630 if(ps_global->full_header == 1){
2631 if(!(ps_global->quote_suppression_threshold
2632 && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
2633 ps_global->full_header++;
2635 else if(ps_global->full_header > 2)
2636 ps_global->full_header = 0;
2638 switch(ps_global->full_header){
2639 case 0:
2640 q_status_message(SM_ORDER, 0, 3,
2641 _("Display of full headers is now off."));
2642 break;
2644 case 1:
2645 q_status_message1(SM_ORDER, 0, 3,
2646 _("Quotes displayed, use %s to see full headers"),
2647 F_ON(F_USE_FK, ps_global) ? "F9" : "H");
2648 break;
2650 case 2:
2651 q_status_message(SM_ORDER, 0, 3,
2652 _("Display of full headers is now on."));
2653 break;
2657 rv = 1;
2658 break;
2660 #ifdef SMIME
2661 case MC_DECRYPT:
2662 if(ps_global->smime && ps_global->smime->need_passphrase)
2663 smime_get_passphrase();
2664 break;
2666 case MC_SECURITY:
2667 display_smime_info_att(ps_global, sparms->proc.data.p);
2668 break;
2669 #endif
2671 default:
2672 alpine_panic("Unexpected command case");
2673 break;
2676 return(rv);
2681 * Returns 1 on success, 0 on error.
2684 format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
2686 int rv = 1;
2688 if((*a)->body->type != TYPEMESSAGE)
2689 return(gf_puts("[ Undisplayed Attachment of Type ", pc)
2690 && gf_puts(body_type_names((*a)->body->type), pc)
2691 && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
2693 if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
2694 HEADER_S h;
2696 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
2697 FE_DEFAULT);
2698 switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
2699 (*a)->body->nested.msg->env, &h, NULL, NULL,
2700 flags, NULL, pc)){
2701 case -1 : /* write error */
2702 return(0);
2704 case 1 : /* fetch error */
2705 if(!(gf_puts("[ Error fetching header ]", pc)
2706 && !gf_puts(NEWLINE, pc)))
2707 return(0);
2709 break;
2712 gf_puts(NEWLINE, pc);
2714 ++(*a);
2716 #ifdef SMIME
2717 if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2718 if((*a)->description){
2719 if(!(!format_editorial((*a)->description,
2720 ps_global->ttyo->screen_cols,
2721 flags, NULL, pc)
2722 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2723 rv = 0;
2726 ++(*a);
2728 #endif /* SMIME */
2730 if(((*a))->description
2731 && (*a)->body && (*a)->body->type == TYPETEXT){
2732 if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2733 rv = 0;
2735 else if(!(gf_puts("[Can't display ", pc)
2736 && gf_puts(((*a)->description && (*a)->body)
2737 ? "first non-" : "missing ", pc)
2738 && gf_puts("text segment]", pc)
2739 && gf_puts(NEWLINE, pc)))
2740 rv = 0;
2742 ++(*a);
2745 else if((*a)->body->subtype
2746 && strucmp((*a)->body->subtype, "external-body") == 0) {
2747 if(format_editorial("This part is not included and can be fetched as follows:",
2748 ps_global->ttyo->screen_cols, flags, NULL, pc)
2749 || !gf_puts(NEWLINE, pc)
2750 || format_editorial(display_parameters((*a)->body->parameter),
2751 ps_global->ttyo->screen_cols, flags, handlesp, pc))
2752 rv = 0;
2754 else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2755 rv = 0;
2757 return(rv);
2761 void
2762 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2764 STORE_S *in_store, *out_store = NULL;
2765 HANDLE_S *handles = NULL;
2766 URL_HILITE_S uh;
2767 gf_io_t gc, pc;
2768 char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2769 int cmd, indent, begins = 0;
2771 lines = detach_vcard_att(ps_global->mail_stream,
2772 msgno, a->body, a->number);
2773 if(!lines){
2774 q_status_message(SM_ORDER | SM_DING, 3, 3,
2775 _("Error accessing attachment."));
2776 return;
2779 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2780 free_list_array(&lines);
2781 q_status_message(SM_ORDER | SM_DING, 3, 3,
2782 _("Error allocating space for attachment."));
2783 return;
2786 for(ll = lines, indent = 0; ll && *ll; ll++)
2787 if((p = strindex(*ll, ':')) && p - *ll > indent)
2788 indent = p - *ll;
2790 indent += 5;
2791 for(ll = lines; ll && *ll; ll++){
2792 if((p = strindex(*ll, ':')) != NULL){
2793 if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2794 begins++;
2796 snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
2797 MIN(p - *ll, sizeof(tmp)-5), *ll);
2798 tmp[sizeof(tmp)-1] = '\0';
2799 so_puts(in_store, tmp);
2800 p++;
2802 else{
2803 p = *ll;
2804 so_puts(in_store, repeat_char(indent, SPACE));
2807 snprintf(tmp, sizeof(tmp), "%.200s", p);
2808 tmp[sizeof(tmp)-1] = '\0';
2809 so_puts(in_store,
2810 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
2811 SIZEOF_20KBUF, tmp));
2812 so_puts(in_store, "\015\012");
2815 free_list_array(&lines);
2817 so_puts(in_store, "\015\012\015\012");
2820 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2821 so_seek(in_store, 0L, 0);
2823 init_handles(&handles);
2824 gf_filter_init();
2826 if(F_ON(F_VIEW_SEL_URL, ps_global)
2827 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2828 || F_ON(F_SCAN_ADDR, ps_global))
2829 gf_link_filter(gf_line_test,
2830 gf_line_test_opt(url_hilite,
2831 gf_url_hilite_opt(&uh,&handles,0)));
2833 gf_link_filter(gf_wrap,
2834 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
2835 ps_global->ttyo->screen_cols,
2836 NULL, indent, GFW_HANDLES));
2837 gf_link_filter(gf_nvtnl_local, NULL);
2839 gf_set_so_readc(&gc, in_store);
2840 gf_set_so_writec(&pc, out_store);
2842 errstr = gf_pipe(gc, pc);
2844 gf_clear_so_readc(in_store);
2846 if(!errstr){
2847 #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.")
2848 #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.")
2849 errstr = format_editorial((begins > 1)
2850 ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
2851 ps_global->ttyo->screen_cols, 0, NULL, pc);
2854 gf_clear_so_writec(out_store);
2856 if(!errstr)
2857 cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
2858 CharStar, handles, a, flags | DA_RESIZE);
2860 free_handles(&handles);
2861 so_give(&out_store);
2863 else
2864 errstr = _("Error allocating space");
2866 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
2868 if(errstr)
2869 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2870 _("Can't format entry : %s"), errstr);
2872 so_give(&in_store);
2876 /*----------------------------------------------------------------------
2877 Display attachment information
2879 Args: msgno -- message number to get partrom
2880 a -- attachment struct for the desired part
2882 Result: a screen containing information about attachment:
2883 ----*/
2884 void
2885 display_attach_info(long int msgno, ATTACH_S *a)
2887 int i, indent, cols;
2888 char buf1[100], *folded;
2889 STORE_S *store;
2890 PARMLIST_S *plist;
2891 SCROLL_S sargs;
2893 (void) dispatch_attachment(a);
2895 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2896 q_status_message(SM_ORDER | SM_DING, 3, 3,
2897 _("Error allocating space for message."));
2898 return;
2901 cols = ps_global->ttyo->screen_cols;
2904 * 2 spaces on left
2905 * 16 for text (longest is Display Method == 14)
2906 * 2 for ": "
2908 indent = 20;
2910 /* don't try stupid folding */
2911 cols = MAX(indent+10, cols);
2913 so_puts(store, "Details about Attachment #");
2914 so_puts(store, a->number);
2915 so_puts(store, " :\n\n");
2916 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
2917 so_puts(store, buf1);
2918 so_puts(store, body_type_names(a->body->type));
2919 so_puts(store, "\n");
2920 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
2921 so_puts(store, buf1);
2922 so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
2923 so_puts(store, "\n");
2924 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
2925 so_puts(store, buf1);
2926 so_puts(store, a->body->encoding < ENCMAX
2927 ? body_encodings[a->body->encoding]
2928 : "Unknown");
2929 so_puts(store, "\n");
2930 if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
2931 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
2932 so_puts(store, buf1);
2933 i = 0;
2934 while(rfc2231_list_params(plist)){
2935 if(i++)
2936 so_puts(store, repeat_char(indent, ' '));
2938 so_puts(store, plist->attrib);
2939 so_puts(store, " = ");
2940 so_puts(store, plist->value ? plist->value : "");
2941 so_puts(store, "\n");
2944 rfc2231_free_parmlist(&plist);
2947 if(a->body->description && a->body->description[0]){
2948 char buftmp[MAILTMPLEN];
2949 unsigned char *q;
2951 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
2953 snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
2954 buftmp[sizeof(buftmp)-1] = '\0';
2955 q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
2956 folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
2958 if(folded){
2959 so_puts(store, folded);
2960 fs_give((void **) &folded);
2964 /* BUG: no a->body->language support */
2966 if(a->body->disposition.type){
2967 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
2968 so_puts(store, buf1);
2969 so_puts(store, a->body->disposition.type);
2970 so_puts(store, "\n");
2971 if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
2972 while(rfc2231_list_params(plist)){
2973 so_puts(store, repeat_char(indent, ' '));
2974 so_puts(store, plist->attrib);
2975 so_puts(store, " = ");
2976 so_puts(store, plist->value ? plist->value : "");
2977 so_puts(store, "\n");
2980 rfc2231_free_parmlist(&plist);
2984 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
2985 so_puts(store, buf1);
2986 so_puts(store, comatose((a->body->encoding == ENCBASE64)
2987 ? ((a->body->size.bytes * 3)/4)
2988 : a->body->size.bytes));
2989 so_puts(store, " bytes\n");
2990 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
2991 so_puts(store, buf1);
2992 if(a->can_display == MCD_NONE) {
2993 so_puts(store, "Can't, ");
2994 so_puts(store, (a->body->encoding < ENCOTHER)
2995 ? "Unknown Attachment Format"
2996 : "Unknown Encoding");
2998 else if(!(a->can_display & MCD_EXTERNAL)){
2999 so_puts(store, "Alpine's Internal Pager");
3001 else{
3002 int nt, free_pretty_cmd;
3003 MCAP_CMD_S *mc_cmd;
3004 char *pretty_cmd;
3006 if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
3007 a->body, "<datafile>", &nt,
3008 a->can_display & MCD_EXT_PROMPT)) != NULL){
3009 so_puts(store, "\"");
3010 if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
3011 so_puts(store, pretty_cmd);
3012 so_puts(store, "\"");
3013 if(free_pretty_cmd && pretty_cmd)
3014 fs_give((void **)&pretty_cmd);
3015 if(mc_cmd->command)
3016 fs_give((void **)&mc_cmd->command);
3017 fs_give((void **)&mc_cmd);
3021 so_puts(store, "\n");
3023 memset(&sargs, 0, sizeof(SCROLL_S));
3024 sargs.text.text = so_text(store);
3025 sargs.text.src = CharStar;
3026 sargs.text.desc = "attachment info";
3027 sargs.bar.title = _("ABOUT ATTACHMENT");
3028 sargs.help.text = h_simple_text_view;
3029 sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
3031 sargs.use_indexline_color = 1;
3033 scrolltool(&sargs);
3035 so_give(&store); /* free resources associated with store */
3036 ps_global->mangled_screen = 1;
3040 /*----------------------------------------------------------------------
3042 ----*/
3043 void
3044 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3046 char *sig;
3047 void *msgtext;
3048 ENVELOPE *outgoing;
3049 BODY *body;
3051 if(MIME_MSG_A(a)){
3052 forward_msg_att(stream, msgno, a);
3054 else{
3055 ACTION_S *role = NULL;
3056 REDRAFT_POS_S *redraft_pos = NULL;
3057 long rflags = ROLE_FORWARD;
3058 PAT_STATE dummy;
3060 outgoing = mail_newenvelope();
3061 outgoing->message_id = generate_message_id();
3062 outgoing->subject = cpystr("Forwarded attachment...");
3064 if(nonempty_patterns(rflags, &dummy)){
3066 * There is no message, but a Current Folder Type might match.
3068 * This has been changed to check against the message
3069 * containing the attachment.
3071 role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3072 if(confirm_role(rflags, &role))
3073 role = combine_inherited_role(role);
3074 else{
3075 role = NULL;
3076 cmd_cancelled("Forward");
3077 mail_free_envelope(&outgoing);
3078 return;
3082 if(role)
3083 q_status_message1(SM_ORDER, 3, 4,
3084 _("Forwarding using role \"%s\""), role->nick);
3087 * as with all text bound for the composer, build it in
3088 * a storage object of the type it understands...
3090 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3091 int impl, template_len = 0;
3093 if(role && role->template){
3094 char *filtered;
3096 impl = 1;
3097 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3098 if(filtered){
3099 if(*filtered){
3100 so_puts((STORE_S *)msgtext, filtered);
3101 if(impl == 1)
3102 template_len = strlen(filtered);
3105 fs_give((void **)&filtered);
3108 else
3109 impl = 1;
3111 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3112 if(impl == 2)
3113 redraft_pos->offset += template_len;
3115 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3117 fs_give((void **)&sig);
3119 else
3120 so_puts((STORE_S *)msgtext, NEWLINE);
3122 /*---- New Body to start with ----*/
3123 body = mail_newbody();
3124 body->type = TYPEMULTIPART;
3126 /*---- The TEXT part/body ----*/
3127 body->nested.part = mail_newbody_part();
3128 body->nested.part->body.type = TYPETEXT;
3129 body->nested.part->body.contents.text.data = msgtext;
3131 /*---- The corresponding things we're attaching ----*/
3132 body->nested.part->next = mail_newbody_part();
3133 body->nested.part->next->body.id = generate_message_id();
3134 copy_body(&body->nested.part->next->body, a->body);
3136 if(fetch_contents(stream, msgno, a->number,
3137 &body->nested.part->next->body)){
3138 pine_send(outgoing, &body, "FORWARD MESSAGE",
3139 role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3141 ps_global->mangled_screen = 1;
3142 pine_free_body(&body);
3143 free_redraft_pos(&redraft_pos);
3145 else{
3146 mail_free_body(&body);
3147 so_give((STORE_S **) &msgtext);
3148 free_redraft_pos(&redraft_pos);
3149 q_status_message(SM_ORDER | SM_DING, 4, 5,
3150 _("Error fetching message contents. Can't forward message."));
3153 else
3154 q_status_message(SM_ORDER | SM_DING, 3, 4,
3155 _("Error allocating message text"));
3157 mail_free_envelope(&outgoing);
3158 free_action(&role);
3163 void
3164 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3166 char *p, *sig = NULL;
3167 int ret;
3168 void *msgtext;
3169 ENVELOPE *outgoing;
3170 BODY *body;
3171 ACTION_S *role = NULL;
3172 REPLY_S reply;
3173 REDRAFT_POS_S *redraft_pos = NULL;
3175 outgoing = mail_newenvelope();
3176 outgoing->message_id = generate_message_id();
3178 memset((void *)&reply, 0, sizeof(reply));
3180 if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
3182 * as with all text bound for the composer, build it in
3183 * a storage object of the type it understands...
3185 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3186 int impl, template_len = 0;
3187 long rflags = ROLE_FORWARD;
3188 PAT_STATE dummy;
3190 ret = 'n';
3191 if(ps_global->full_header == 2)
3192 ret = want_to(_("Forward message as an attachment"), 'n', 0,
3193 NO_HELP, WT_SEQ_SENSITIVE);
3194 /* Setup possible role */
3195 if(nonempty_patterns(rflags, &dummy)){
3196 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3197 if(confirm_role(rflags, &role))
3198 role = combine_inherited_role(role);
3199 else{ /* cancel reply */
3200 role = NULL;
3201 cmd_cancelled("Forward");
3202 mail_free_envelope(&outgoing);
3203 so_give((STORE_S **) &msgtext);
3204 return;
3208 if(role)
3209 q_status_message1(SM_ORDER, 3, 4,
3210 _("Forwarding using role \"%s\""), role->nick);
3212 if(role && role->template){
3213 char *filtered;
3215 impl = 1;
3216 filtered = detoken(role, a->body->nested.msg->env,
3217 0, 0, 0, &redraft_pos, &impl);
3218 if(filtered){
3219 if(*filtered){
3220 so_puts((STORE_S *)msgtext, filtered);
3221 if(impl == 1)
3222 template_len = strlen(filtered);
3225 fs_give((void **)&filtered);
3228 else
3229 impl = 1;
3231 if((sig = detoken(role, a->body->nested.msg->env,
3232 2, 0, 1, &redraft_pos, &impl)) != NULL){
3233 if(impl == 2)
3234 redraft_pos->offset += template_len;
3236 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3238 fs_give((void **)&sig);
3240 else
3241 so_puts((STORE_S *)msgtext, NEWLINE);
3243 if(ret == 'y'){
3244 /*---- New Body to start with ----*/
3245 body = mail_newbody();
3246 body->type = TYPEMULTIPART;
3248 /*---- The TEXT part/body ----*/
3249 body->nested.part = mail_newbody_part();
3250 body->nested.part->body.type = TYPETEXT;
3251 body->nested.part->body.contents.text.data = msgtext;
3253 if(!forward_mime_msg(stream, msgno,
3254 p = body_partno(stream, msgno, a->body),
3255 a->body->nested.msg->env,
3256 &body->nested.part->next, msgtext))
3257 mail_free_body(&body);
3259 else{
3260 reply.forw = 1;
3261 if(a->body->nested.msg->body){
3262 char *charset;
3264 charset
3265 = parameter_val(a->body->nested.msg->body->parameter,
3266 "charset");
3268 if(charset && strucmp(charset, "us-ascii") != 0){
3269 CONV_TABLE *ct;
3272 * There is a non-ascii charset,
3273 * is there conversion happening?
3275 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3276 || !ct->table){
3277 reply.orig_charset = charset;
3278 charset = NULL;
3282 if(charset)
3283 fs_give((void **) &charset);
3286 body = forward_body(stream, a->body->nested.msg->env,
3287 a->body->nested.msg->body, msgno,
3288 p = body_partno(stream, msgno, a->body),
3289 msgtext, FWD_NONE);
3292 fs_give((void **) &p);
3294 if(body){
3295 pine_send(outgoing, &body,
3296 "FORWARD MESSAGE",
3297 role, NULL,
3298 reply.forw ? &reply : NULL,
3299 redraft_pos, NULL, NULL, 0);
3301 ps_global->mangled_screen = 1;
3302 pine_free_body(&body);
3303 free_redraft_pos(&redraft_pos);
3304 free_action(&role);
3306 else{
3307 so_give((STORE_S **) &msgtext);
3308 q_status_message(SM_ORDER | SM_DING, 4, 5,
3309 _("Error fetching message contents. Can't forward message."));
3312 else
3313 q_status_message(SM_ORDER | SM_DING, 3, 4,
3314 _("Error allocating message text"));
3316 else
3317 q_status_message1(SM_ORDER,3,4,
3318 _("Error fetching message %s. Can't forward it."),
3319 long2string(msgno));
3321 if(reply.orig_charset)
3322 fs_give((void **)&reply.orig_charset);
3324 mail_free_envelope(&outgoing);
3328 void
3329 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3331 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
3332 ENVELOPE *outgoing;
3333 BODY *body;
3334 void *msgtext;
3335 char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3336 int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3337 int rolemsg = 0, copytomsg = 0;
3338 long rflags;
3339 PAT_STATE dummy;
3340 REDRAFT_POS_S *redraft_pos = NULL;
3341 ACTION_S *role = NULL;
3343 outgoing = mail_newenvelope();
3345 dprint((4,"\n - attachment reply \n"));
3347 saved_from = (ADDRESS *) NULL;
3348 saved_to = (ADDRESS *) NULL;
3349 saved_cc = (ADDRESS *) NULL;
3350 saved_resent = (ADDRESS *) NULL;
3351 outgoing->subject = NULL;
3353 prefix = reply_quote_str(a->body->nested.msg->env);
3355 * For consistency, the first question is always "include text?"
3357 if((include_text = reply_text_query(ps_global, 1, &prefix)) >= 0
3358 && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3359 && reply_harvest(ps_global, msgno, a->number,
3360 a->body->nested.msg->env, &saved_from,
3361 &saved_to, &saved_cc, &saved_resent, &flags)){
3362 outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3363 NULL, 0);
3364 clear_cursor_pos();
3365 reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3366 saved_from, saved_to, saved_cc, saved_resent,
3367 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3368 if(errmsg){
3369 if(*errmsg){
3370 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3371 display_message(NO_OP_COMMAND);
3374 fs_give((void **)&errmsg);
3377 if(sp_expunge_count(stream)) /* current msg was expunged */
3378 goto seeyalater;
3380 /* Setup possible role */
3381 rflags = ROLE_REPLY;
3382 if(nonempty_patterns(rflags, &dummy)){
3383 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3384 if(confirm_role(rflags, &role))
3385 role = combine_inherited_role(role);
3386 else{ /* cancel reply */
3387 role = NULL;
3388 cmd_cancelled("Reply");
3389 goto seeyalater;
3393 if(role){
3394 rolemsg++;
3396 /* override fcc gotten in reply_seed */
3397 if(role->fcc && fcc)
3398 fs_give((void **) &fcc);
3401 if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3402 ADDRESS *us_in_to_and_cc, *ap;
3404 us_in_to_and_cc = (ADDRESS *) NULL;
3405 if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3406 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3407 NULL, us_in_to_and_cc, NULL,
3408 a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3409 reply_append_addr(&us_in_to_and_cc, ap);
3411 if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3412 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3413 NULL, us_in_to_and_cc, NULL,
3414 a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3415 reply_append_addr(&us_in_to_and_cc, ap);
3418 * A list of all of our addresses that appear in the To
3419 * and cc fields is in us_in_to_and_cc.
3420 * If there is exactly one address in that list then
3421 * use it for the outgoing From.
3423 if(us_in_to_and_cc && !us_in_to_and_cc->next){
3424 PINEFIELD *custom, *pf;
3425 ADDRESS *a = NULL;
3426 char *addr = NULL;
3429 * Check to see if this address is different from what
3430 * we would have used anyway. If it is, notify the user
3431 * with a status message. This is pretty hokey because we're
3432 * mimicking how pine_send would set the From address and
3433 * there is no coordination between the two.
3436 /* in case user has a custom From value */
3437 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3439 pf = (PINEFIELD *) fs_get(sizeof(*pf));
3440 memset((void *) pf, 0, sizeof(*pf));
3441 pf->name = cpystr("From");
3442 pf->addr = &a;
3443 if(set_default_hdrval(pf, custom) >= UseAsDef
3444 && pf->textbuf && pf->textbuf[0]){
3445 removing_trailing_white_space(pf->textbuf);
3446 (void)removing_double_quotes(pf->textbuf);
3447 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3448 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3449 if(addr)
3450 fs_give((void **) &addr);
3453 if(!*pf->addr)
3454 *pf->addr = generate_from();
3456 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3457 copytomsg++;
3458 if(!role){
3459 role = (ACTION_S *) fs_get(sizeof(*role));
3460 memset((void *) role, 0, sizeof(*role));
3461 role->is_a_role = 1;
3464 role->from = us_in_to_and_cc;
3465 us_in_to_and_cc = NULL;
3468 free_customs(custom);
3469 free_customs(pf);
3472 if(us_in_to_and_cc)
3473 mail_free_address(&us_in_to_and_cc);
3477 if(role){
3478 if(rolemsg && copytomsg)
3479 q_status_message1(SM_ORDER, 3, 4,
3480 _("Replying using role \"%s\" and To as From"), role->nick);
3481 else if(rolemsg)
3482 q_status_message1(SM_ORDER, 3, 4,
3483 _("Replying using role \"%s\""), role->nick);
3484 else if(copytomsg)
3485 q_status_message(SM_ORDER, 3, 4,
3486 _("Replying using incoming To as outgoing From"));
3489 outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3490 outgoing->references = reply_build_refs(a->body->nested.msg->env);
3491 outgoing->message_id = generate_message_id();
3493 if(!outgoing->to && !outgoing->cc
3494 && !outgoing->bcc && !outgoing->newsgroups)
3495 q_status_message(SM_ORDER | SM_DING, 3, 6,
3496 _("Warning: no valid addresses to reply to!"));
3499 * Now fix up the body...
3501 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3502 REPLY_S reply;
3504 memset((void *)&reply, 0, sizeof(reply));
3505 reply.forw = 1;
3506 if(a->body->nested.msg->body){
3507 char *charset;
3509 charset
3510 = parameter_val(a->body->nested.msg->body->parameter,
3511 "charset");
3513 if(charset && strucmp(charset, "us-ascii") != 0){
3514 CONV_TABLE *ct;
3517 * There is a non-ascii charset,
3518 * is there conversion happening?
3520 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3521 || !ct->table){
3522 reply.orig_charset = charset;
3523 charset = NULL;
3527 if(charset)
3528 fs_give((void **) &charset);
3531 if((body = reply_body(stream, a->body->nested.msg->env,
3532 a->body->nested.msg->body, msgno,
3533 tp = body_partno(stream, msgno, a->body),
3534 msgtext, prefix, include_text, role,
3535 1, &redraft_pos)) != NULL){
3536 /* partially formatted outgoing message */
3537 pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3538 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3540 pine_free_body(&body);
3541 ps_global->mangled_screen = 1;
3543 else
3544 q_status_message(SM_ORDER | SM_DING, 3, 4,
3545 _("Error building message body"));
3547 fs_give((void **) &tp);
3548 if(reply.orig_charset)
3549 fs_give((void **)&reply.orig_charset);
3551 else
3552 q_status_message(SM_ORDER | SM_DING, 3, 4,
3553 _("Error allocating message text"));
3556 seeyalater:
3557 mail_free_envelope(&outgoing);
3558 mail_free_address(&saved_from);
3559 mail_free_address(&saved_to);
3560 mail_free_address(&saved_cc);
3561 mail_free_address(&saved_resent);
3563 if(prefix)
3564 fs_give((void **) &prefix);
3566 if(fcc)
3567 fs_give((void **) &fcc);
3569 free_redraft_pos(&redraft_pos);
3570 free_action(&role);
3574 void
3575 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
3577 char *errstr;
3579 if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
3580 q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
3584 void
3585 pipe_attachment(long int msgno, ATTACH_S *a)
3587 char *err, *resultfilename = NULL, prompt[80], *p;
3588 int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
3589 long ku;
3590 PIPE_S *syspipe;
3591 HelpType help;
3592 char pipe_command[MAXPATH+1];
3593 unsigned flagsforhist = 1; /* raw=2 /capture=1 */
3594 static HISTORY_S *history = NULL;
3595 ESCKEY_S pipe_opt[6];
3597 if(ps_global->restricted){
3598 q_status_message(SM_ORDER | SM_DING, 0, 4,
3599 "Alpine demo can't pipe attachments");
3600 return;
3603 help = NO_HELP;
3604 pipe_command[0] = '\0';
3606 init_hist(&history, HISTSIZE);
3607 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3608 if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
3609 strncpy(pipe_command, p, sizeof(pipe_command));
3610 pipe_command[sizeof(pipe_command)-1] = '\0';
3611 if(history->hist[history->curindex]){
3612 flagsforhist = history->hist[history->curindex]->flags;
3613 raw = (flagsforhist & 0x2) ? 1 : 0;
3614 capture = (flagsforhist & 0x1) ? 1 : 0;
3618 pipe_opt[j].ch = 0;
3619 pipe_opt[j].rval = 0;
3620 pipe_opt[j].name = "";
3621 pipe_opt[j++].label = "";
3623 pipe_opt[j].ch = ctrl('W');
3624 pipe_opt[j].rval = 10;
3625 pipe_opt[j].name = "^W";
3626 pipe_opt[j++].label = NULL;
3628 pipe_opt[j].ch = ctrl('Y');
3629 pipe_opt[j].rval = 11;
3630 pipe_opt[j].name = "^Y";
3631 pipe_opt[j++].label = NULL;
3633 pipe_opt[j].ch = KEY_UP;
3634 pipe_opt[j].rval = 30;
3635 pipe_opt[j].name = "";
3636 ku = j;
3637 pipe_opt[j++].label = "";
3639 pipe_opt[j].ch = KEY_DOWN;
3640 pipe_opt[j].rval = 31;
3641 pipe_opt[j].name = "";
3642 pipe_opt[j++].label = "";
3644 pipe_opt[j].ch = -1;
3646 while(1){
3647 int flags;
3649 snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
3650 a->number, capture ? "" : "(Free Output) ");
3651 prompt[sizeof(prompt)-1] = '\0';
3652 pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
3653 pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
3656 * 2 is really 1 because there will be one real entry and
3657 * one entry of "" because of the get_prev_hist above.
3659 if(items_in_hist(history) > 2){
3660 pipe_opt[ku].name = HISTORY_UP_KEYNAME;
3661 pipe_opt[ku].label = HISTORY_KEYLABEL;
3662 pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
3663 pipe_opt[ku+1].label = HISTORY_KEYLABEL;
3665 else{
3666 pipe_opt[ku].name = "";
3667 pipe_opt[ku].label = "";
3668 pipe_opt[ku+1].name = "";
3669 pipe_opt[ku+1].label = "";
3672 flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
3673 rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
3674 sizeof(pipe_command), prompt,
3675 pipe_opt, help, &flags);
3676 if(rc == -1){
3677 q_status_message(SM_ORDER | SM_DING, 3, 4,
3678 "Internal problem encountered");
3679 break;
3681 else if(rc == 10){
3682 raw = !raw; /* flip raw text */
3684 else if(rc == 11){
3685 capture = !capture; /* flip capture output */
3687 else if(rc == 30){
3688 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3689 if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3690 strncpy(pipe_command, p, sizeof(pipe_command));
3691 pipe_command[sizeof(pipe_command)-1] = '\0';
3692 if(history->hist[history->curindex]){
3693 flagsforhist = history->hist[history->curindex]->flags;
3694 raw = (flagsforhist & 0x2) ? 1 : 0;
3695 capture = (flagsforhist & 0x1) ? 1 : 0;
3698 else
3699 Writechar(BELL, 0);
3701 else if(rc == 31){
3702 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3703 if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3704 strncpy(pipe_command, p, sizeof(pipe_command));
3705 pipe_command[sizeof(pipe_command)-1] = '\0';
3706 if(history->hist[history->curindex]){
3707 flagsforhist = history->hist[history->curindex]->flags;
3708 raw = (flagsforhist & 0x2) ? 1 : 0;
3709 capture = (flagsforhist & 0x1) ? 1 : 0;
3712 else
3713 Writechar(BELL, 0);
3715 else if(rc == 0){
3716 if(pipe_command[0] == '\0'){
3717 cmd_cancelled("Pipe command");
3718 break;
3721 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3722 save_hist(history, pipe_command, flagsforhist, NULL);
3724 flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
3725 flags |= (raw ? PIPE_RAW : 0);
3726 if(!capture){
3727 #ifndef _WINDOWS
3728 ClearScreen();
3729 fflush(stdout);
3730 clear_cursor_pos();
3731 ps_global->mangled_screen = 1;
3732 #endif
3733 flags |= PIPE_RESET;
3736 if((syspipe = open_system_pipe(pipe_command,
3737 (flags&PIPE_RESET) ? NULL : &resultfilename,
3738 NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
3739 int is_text = 0;
3740 gf_io_t pc; /* wire up a generic putchar */
3742 is_text = (a && a->body && a->body->type == TYPETEXT);
3744 gf_set_writec(&pc, syspipe, 0L, PipeStar,
3745 (is_text && !raw) ? WRITE_TO_LOCALE : 0);
3747 /*------ Write the image to a temporary file ------*/
3748 if(raw){ /* pipe raw text */
3749 FETCH_READC_S fetch_part;
3751 err = NULL;
3753 if(capture)
3754 we_cancel = busy_cue(NULL, NULL, 1);
3755 else
3756 suspend_busy_cue();
3758 gf_filter_init();
3759 fetch_readc_init(&fetch_part, ps_global->mail_stream,
3760 msgno, a->number, a->body->size.bytes, 0, 0);
3761 gf_link_filter(gf_nvtnl_local, NULL);
3762 err = gf_pipe(FETCH_READC, pc);
3764 if(capture){
3765 if(we_cancel)
3766 cancel_busy_cue(0);
3768 else
3769 resume_busy_cue(0);
3771 else{
3772 /* BUG: there's got to be a better way */
3773 if(!capture)
3774 ps_global->print = (PRINT_S *) 1;
3776 suspend_busy_cue();
3777 err = detach(ps_global->mail_stream, msgno,
3778 a->number, 0L, (long *)NULL, pc, NULL, 0);
3779 ps_global->print = (PRINT_S *) NULL;
3780 resume_busy_cue(0);
3783 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
3785 if(err)
3786 q_status_message1(SM_ORDER | SM_DING, 3, 4,
3787 _("Error detaching for pipe: %s"), err);
3789 display_output_file(resultfilename,
3790 (err)
3791 ? _("PIPE ATTACHMENT (ERROR)")
3792 : _("PIPE ATTACHMENT"),
3793 NULL, DOF_EMPTY);
3795 fs_give((void **) &resultfilename);
3797 else
3798 q_status_message(SM_ORDER | SM_DING, 3, 4,
3799 _("Error opening pipe"));
3801 break;
3803 else if(rc == 1){
3804 cmd_cancelled("Pipe");
3805 break;
3807 else if(rc == 3)
3808 help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
3814 delete_attachment(long int msgno, ATTACH_S *a)
3816 int expbits, rv = 0;
3818 if(!msgno_exceptions(ps_global->mail_stream, msgno,
3819 a->number, &expbits, FALSE)
3820 || !(expbits & MSG_EX_DELETE)){
3821 expbits |= MSG_EX_DELETE;
3822 msgno_exceptions(ps_global->mail_stream, msgno,
3823 a->number, &expbits, TRUE);
3825 q_status_message1(SM_ORDER, 0, 3,
3826 _("Part %s will be omitted only if message is Saved"),
3827 a->number);
3828 rv = 1;
3830 else
3831 q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
3832 a->number);
3834 return(rv);
3839 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
3841 int rv = 0;
3843 if(msgno_exceptions(ps_global->mail_stream, msgno,
3844 a->number, expbitsp, FALSE)
3845 && ((*expbitsp) & MSG_EX_DELETE)){
3846 (*expbitsp) ^= MSG_EX_DELETE;
3847 msgno_exceptions(ps_global->mail_stream, msgno,
3848 a->number, expbitsp, TRUE);
3849 rv = 1;
3851 else
3852 q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
3853 a->number);
3855 return(rv);
3859 /*----------------------------------------------------------------------
3860 Resolve any deferred tests for attachment displayability
3862 Args: attachment structure
3864 Returns: undefer's attachment's displayability test
3865 ----*/
3867 dispatch_attachment(ATTACH_S *a)
3869 if(a->test_deferred){
3870 a->test_deferred = 0;
3871 a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
3874 return(a->can_display);
3878 #ifdef _WINDOWS
3880 scroll_att_popup(sparms, in_handle)
3881 SCROLL_S *sparms;
3882 int in_handle;
3884 MPopup scrat_popup[20];
3885 int i = -1, n;
3887 if(in_handle){
3888 scrat_popup[++i].type = tIndex;
3889 scrat_popup[i].label.style = lNormal;
3890 scrat_popup[i].label.string = "View Selectable Item";
3891 scrat_popup[i].data.val = ctrl('L');
3894 scrat_popup[++i].type = tQueue;
3895 scrat_popup[i].label.style = lNormal;
3896 scrat_popup[i].label.string = "&Save";
3897 scrat_popup[i].data.val = 'S';
3899 scrat_popup[++i].type = tQueue;
3900 scrat_popup[i].label.style = lNormal;
3901 if(msgno_exceptions(ps_global->mail_stream,
3902 mn_m2raw(ps_global->msgmap,
3903 mn_get_cur(ps_global->msgmap)),
3904 scrat_attachp->number, &n, FALSE)
3905 && (n & MSG_EX_DELETE)){
3906 scrat_popup[i].label.string = "&Undelete";
3907 scrat_popup[i].data.val = 'U';
3909 else{
3910 scrat_popup[i].label.string = "&Delete";
3911 scrat_popup[i].data.val = 'D';
3914 if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
3915 scrat_popup[++i].type = tQueue;
3916 scrat_popup[i].label.style = lNormal;
3917 scrat_popup[i].label.string = "&Reply";
3918 scrat_popup[i].data.val = 'R';
3920 scrat_popup[++i].type = tQueue;
3921 scrat_popup[i].label.style = lNormal;
3922 scrat_popup[i].label.string = "&Forward";
3923 scrat_popup[i].data.val = 'f';
3926 scrat_popup[++i].type = tSeparator;
3928 scrat_popup[++i].type = tQueue;
3929 scrat_popup[i].label.style = lNormal;
3930 scrat_popup[i].label.string = "Attachment Index";
3931 scrat_popup[i].data.val = '<';
3933 scrat_popup[++i].type = tTail;
3935 return(mswin_popup(scrat_popup) == 0 && in_handle);
3939 void
3940 display_att_window(a)
3941 ATTACH_S *a;
3943 #if !defined(DOS) && !defined(OS2)
3944 char prefix[8];
3945 #endif
3947 if(a->body->type == TYPEMULTIPART){
3948 if(a->body->subtype){
3949 /* if(!strucmp(a->body->subtype, "digest"))
3950 display_digest_att(msgno, a, flags);
3951 else */
3952 q_status_message1(SM_ORDER, 3, 5,
3953 "Can't display Multipart/%s",
3954 a->body->subtype);
3956 else
3957 q_status_message(SM_ORDER, 3, 5,
3958 "Can't display unknown Multipart Subtype");
3960 /* else if(MIME_VCARD_A(a))
3961 display_vcard_att_window(msgno, a, flags);*/
3962 else if(a->body->type == TYPETEXT)
3963 display_text_att_window(a);
3964 else if(a->body->type == TYPEMESSAGE)
3965 display_msg_att_window(a);
3969 void
3970 display_text_att_window(a)
3971 ATTACH_S *a;
3973 STORE_S *store;
3974 long msgno;
3976 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
3978 if(store = format_text_att(msgno, a, NULL)){
3979 if (mswin_displaytext("ATTACHED TEXT",
3980 so_text(store),
3981 strlen((char *) so_text(store)),
3982 NULL, NULL, 0) >= 0)
3983 store->txt = (void *) NULL; /* free'd in mswin_displaytext */
3985 so_give(&store); /* free resources associated with store */
3987 else
3988 q_status_message(SM_ORDER | SM_DING, 3, 3,
3989 "Error allocating space for attachment.");
3993 void
3994 display_msg_att_window(a)
3995 ATTACH_S *a;
3997 STORE_S *store;
3998 gf_io_t pc;
3999 ATTACH_S *ap = a;
4000 long msgno;
4002 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4004 /* BUG, should check this return code */
4005 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
4007 /* initialize a storage object */
4008 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
4010 gf_set_so_writec(&pc, store);
4012 if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
4013 && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
4014 strlen((char *) so_text(store)),
4015 NULL, NULL, 0) >= 0)
4016 /* free'd in mswin_displaytext */
4017 store->txt = (void *) NULL;
4019 gf_clear_so_writec(store);
4021 so_give(&store);
4023 else
4024 q_status_message(SM_ORDER | SM_DING, 3, 3,
4025 "Error allocating space for message.");
4027 #endif