* When Alpine opens an attachment, it sometimes changes the extension
[alpine.git] / alpine / mailpart.c
blob651786cfef9128402d9a64d7bdcb3fbc100b8d13
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-2016 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, flags;
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 flags = (a && a->body && a->body->type == TYPETEXT ? GE_BINARY : 0)
1334 | GE_SEQ_SENSITIVE;
1336 r = get_export_filename(ps_global, filename, NULL, full_filename,
1337 sizeof(filename), "attachment", title_buf,
1338 att_save_opts, &rflags, qline, flags, &history);
1340 if(r < 0){
1341 switch(r){
1342 case -1:
1343 cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
1344 break;
1346 case -2:
1347 q_status_message1(SM_ORDER, 0, 2,
1348 _("Can't save to file outside of %s"),
1349 ps_global->VAR_OPER_DIR);
1350 break;
1353 return;
1355 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1356 else if(r == 12){ /* Download */
1357 char cmd[MAXPATH], *tfp = NULL;
1358 PIPE_S *syspipe;
1359 gf_io_t pc;
1360 long len;
1361 STORE_S *store;
1362 char prompt_buf[256];
1364 if(ps_global->restricted){
1365 q_status_message(SM_ORDER | SM_DING, 3, 3,
1366 "Download disallowed in restricted mode");
1367 return;
1370 err = NULL;
1371 tfp = temp_nam(NULL, "pd");
1372 dprint((1, "Download attachment called!\n"));
1373 if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
1375 snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
1376 prompt_buf[sizeof(prompt_buf)-1] = '\0';
1377 we_cancel = init_att_progress(prompt_buf,
1378 ps_global->mail_stream,
1379 a->body);
1381 gf_set_so_writec(&pc, store);
1382 if((err = detach(ps_global->mail_stream, msgno,
1383 a->number, 0L, &len, pc, NULL, 0)) != NULL)
1384 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1385 "%s: Error writing attachment to \"%s\"",
1386 err, tfp);
1388 /* cancel regardless, so it doesn't get in way of xfer */
1389 cancel_busy_cue(0);
1391 gf_clear_so_writec(store);
1392 if(so_give(&store)) /* close file */
1393 err = "Error writing tempfile for download";
1395 if(!err){
1396 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
1397 ps_global->VAR_DOWNLOAD_CMD, tfp);
1398 if((syspipe = open_system_pipe(cmd, NULL, NULL,
1399 PIPE_USER | PIPE_RESET,
1400 0, pipe_callback, pipe_report_error)) != NULL)
1401 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1402 else
1403 q_status_message(SM_ORDER | SM_DING, 3, 3,
1404 err = "Error running download command");
1407 else
1408 q_status_message(SM_ORDER | SM_DING, 3, 3,
1409 err = "Error building temp file for download");
1411 if(tfp){
1412 our_unlink(tfp);
1413 fs_give((void **)&tfp);
1416 if(!err)
1417 q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
1418 a->number);
1420 return;
1422 #endif /* !(DOS || MAC) */
1424 (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
1425 rflags, full_filename);
1430 * Args stream --
1431 * msgno -- raw message number
1432 * a -- attachment struct
1433 * flags -- comes from get_export_filename
1434 * GER_OVER -- the file was truncated before we wrote
1435 * GER_APPEND -- the file was not truncated prior to our writing,
1436 * so we were appending
1437 * else -- the file didn't previously exist
1438 * file -- the full pathname of the file
1440 * Returns 1 for successful write, 0 for nothing to write, -1 for error
1443 write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
1445 char *l_string, sbuf[256], *err;
1446 int is_text, we_cancel = 0, dt_flags = 0, so_flags;
1447 long len, orig_size;
1448 gf_io_t pc;
1449 STORE_S *store;
1451 if(!(a && a->body && a->number && a->number[0] && file && file[0]
1452 && stream))
1453 return 0;
1455 is_text = (a && a->body && a->body->type == TYPETEXT);
1457 if(flags & GER_APPEND)
1458 orig_size = name_file_size(file);
1460 if(flags & GER_BINARY)
1461 dt_flags |= DT_BINARY;
1463 so_flags = (is_text & !(flags & GER_BINARY) ? WRITE_TO_LOCALE : 0)
1464 | WRITE_ACCESS ;
1466 store = so_get(FileStar, file, so_flags);
1467 if(store == NULL){
1468 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1469 /* TRANSLATORS: Error opening destination <filename>: <error text> */
1470 _("Error opening destination %s: %s"),
1471 file, error_description(errno));
1472 return -1;
1475 snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
1476 sbuf[sizeof(sbuf)-1] = '\0';
1477 we_cancel = init_att_progress(sbuf, stream, a->body);
1479 gf_set_so_writec(&pc, store);
1480 err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, dt_flags);
1481 gf_clear_so_writec(store);
1483 if(we_cancel)
1484 cancel_busy_cue(0);
1486 if(so_give(&store)) /* close file */
1487 err = error_description(errno);
1489 if(err){
1490 if(!(flags & (GER_APPEND | GER_OVER)))
1491 our_unlink(file);
1492 else
1493 our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
1495 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1496 /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
1497 _("%s: Error writing attachment to \"%s\""),
1498 err, file);
1499 return -1;
1501 else{
1502 l_string = cpystr(byte_string(len));
1503 q_status_message8(SM_ORDER, 0, 4,
1504 "Part %s, %s%s %s to \"%s\"%s%s%s",
1505 a->number,
1506 is_text
1507 ? comatose(a->body->size.lines) : l_string,
1508 is_text ? " lines" : "",
1509 flags & GER_OVER
1510 ? "overwritten"
1511 : flags & GER_APPEND ? "appended" : "written",
1512 file,
1513 (is_text || len == a->body->size.bytes)
1514 ? "" : "(decoded from ",
1515 (is_text || len == a->body->size.bytes)
1516 ? "" : byte_string(a->body->size.bytes),
1517 is_text || len == a->body->size.bytes
1518 ? "" : ")");
1519 fs_give((void **)&l_string);
1520 return 1;
1525 char *
1526 write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
1528 char *err = NULL;
1529 long start_of_append;
1530 gf_io_t pc;
1531 MESSAGECACHE *mc;
1533 if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
1534 && (*ap)->body->nested.msg->env){
1535 start_of_append = so_tell(store);
1537 gf_set_so_writec(&pc, store);
1538 if(!(ps_global->mail_stream && msgno > 0L
1539 && msgno <= ps_global->mail_stream->nmsgs
1540 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1541 mc = NULL;
1543 if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
1544 || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
1545 err = error_description(errno);
1547 gf_clear_so_writec(store);
1549 if(err)
1550 ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
1552 else
1553 err = "Can't export message. Missing Envelope data";
1555 return(err);
1559 /*----------------------------------------------------------------------
1560 Save the attachment message/rfc822 to specified folder
1562 Args:
1564 Result:
1565 ----*/
1566 void
1567 save_msg_att(long int msgno, ATTACH_S *a)
1569 char newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
1570 char date[64], nmsgs[80];
1571 CONTEXT_S *cntxt = NULL;
1572 int our_stream = 0, rv;
1573 MAILSTREAM *save_stream;
1574 MESSAGECACHE *mc;
1576 snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
1577 nmsgs[sizeof(nmsgs)-1] = '\0';
1578 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
1579 a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
1580 if(strucmp(newfolder, ps_global->inbox_name) == 0){
1581 save_folder = ps_global->VAR_INBOX_PATH;
1582 cntxt = NULL;
1584 else
1585 save_folder = newfolder;
1587 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1589 mc = (msgno > 0L && ps_global->mail_stream
1590 && msgno <= ps_global->mail_stream->nmsgs)
1591 ? mail_elt(ps_global->mail_stream, msgno) : NULL;
1592 flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
1593 if(mc && mc->day)
1594 mail_date(date, mc);
1595 else
1596 *date = '\0';
1598 if(pith_opt_save_size_changed_prompt)
1599 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1601 rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
1603 if(pith_opt_save_size_changed_prompt)
1604 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1606 if(flags)
1607 fs_give((void **) &flags);
1609 if(rv == 1)
1610 q_status_message2(SM_ORDER, 0, 4,
1611 _("Attached message (part %s) saved to \"%s\""),
1612 a->number,
1613 save_folder);
1614 else if(rv == -1)
1615 cmd_cancelled("Attached message Save");
1616 /* else whatever broke in save_fetch_append shoulda bitched */
1618 if(our_stream)
1619 mail_close(save_stream);
1624 /*----------------------------------------------------------------------
1625 Save the message/rfc822 in the given digest to the specified folder
1627 Args:
1629 Result:
1630 ----*/
1631 void
1632 save_digest_att(long int msgno, ATTACH_S *a)
1634 char newfolder[MAILTMPLEN], *save_folder,
1635 date[64], nmsgs[80];
1636 CONTEXT_S *cntxt = NULL;
1637 int our_stream = 0, rv, cnt = 0;
1638 MAILSTREAM *save_stream;
1639 ATTACH_S *ap;
1641 for(ap = a + 1;
1642 ap->description
1643 && !strncmp(a->number, ap->number, strlen(a->number));
1644 ap++)
1645 if(MIME_MSG(ap->body->type, ap->body->subtype))
1646 cnt++;
1648 snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
1649 nmsgs[sizeof(nmsgs)-1] = '\0';
1651 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
1652 nmsgs, NULL, 0, NULL, NULL, NULL)){
1653 save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
1654 ? ps_global->VAR_INBOX_PATH : newfolder;
1656 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1658 if(pith_opt_save_size_changed_prompt)
1659 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1661 for(ap = a + 1;
1662 ap->description
1663 && !strncmp(a->number, ap->number, strlen(a->number));
1664 ap++)
1665 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1666 *date = '\0';
1667 rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
1668 if(rv != 1)
1669 break;
1672 if(pith_opt_save_size_changed_prompt)
1673 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1675 if(rv == 1)
1676 q_status_message2(SM_ORDER, 0, 4,
1677 _("Attached digest (part %s) saved to \"%s\""),
1678 a->number,
1679 save_folder);
1680 else if(rv == -1)
1681 cmd_cancelled("Attached digest Save");
1682 /* else whatever broke in save_fetch_append shoulda bitched */
1684 if(our_stream)
1685 mail_close(save_stream);
1691 save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
1692 char *save_folder, CONTEXT_S *cntxt, char *date)
1694 STORE_S *so;
1695 int rv = 0;
1697 if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
1698 if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1699 *date = '\0';
1700 rv = save_fetch_append(ps_global->mail_stream, msgno,
1701 a->number,
1702 save_stream, save_folder, cntxt,
1703 a->body->size.bytes,
1704 NULL, date, so);
1706 else{
1707 dprint((1, "Can't allocate store for save: %s\n",
1708 error_description(errno)));
1709 q_status_message(SM_ORDER | SM_DING, 3, 4,
1710 _("Problem creating space for message text."));
1711 rv = 0;
1715 return(rv);
1719 /*----------------------------------------------------------------------
1720 Export the attachment message/rfc822 to specified file
1722 Args:
1724 Result:
1725 ----*/
1726 void
1727 export_msg_att(long int msgno, ATTACH_S *a)
1729 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
1730 int rv, rflags = GER_NONE, i = 1;
1731 ATTACH_S *ap = a;
1732 STORE_S *store;
1733 static HISTORY_S *history = NULL;
1734 static ESCKEY_S opts[] = {
1735 {ctrl('T'), 10, "^T", N_("To Files")},
1736 {-1, 0, NULL, NULL},
1737 {-1, 0, NULL, NULL}};
1739 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1740 opts[i].ch = ctrl('I');
1741 opts[i].rval = 11;
1742 opts[i].name = "TAB";
1743 opts[i].label = N_("Complete");
1746 filename[0] = full_filename[0] = '\0';
1748 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1749 sizeof(filename), "msg attachment",
1750 /* TRANSLATORS: Message Attachment (a screen title) */
1751 _("MSG ATTACHMENT"), opts,
1752 &rflags, -FOOTER_ROWS(ps_global),
1753 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1755 if(rv < 0){
1756 switch(rv){
1757 case -1:
1758 cmd_cancelled("Export");
1759 break;
1761 case -2:
1762 q_status_message1(SM_ORDER, 0, 2,
1763 _("Can't export to file outside of %s"),
1764 ps_global->VAR_OPER_DIR);
1765 break;
1768 return;
1771 /* With name in hand, allocate storage object and save away... */
1772 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1773 if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
1774 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1775 else
1776 q_status_message3(SM_ORDER, 0, 4,
1777 _("Attached message (part %s) %s to \"%s\""),
1778 a->number,
1779 rflags & GER_OVER
1780 ? _("overwritten")
1781 : rflags & GER_APPEND ? _("appended") : _("written"),
1782 full_filename);
1784 if(so_give(&store))
1785 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1786 _("Error writing %s: %s"),
1787 full_filename, error_description(errno));
1789 else
1790 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1791 /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
1792 _("Error opening file \"%s\" to export message: %s"),
1793 full_filename, error_description(errno));
1797 /*----------------------------------------------------------------------
1798 Export the message/rfc822 in the given digest to the specified file
1800 Args:
1802 Result:
1803 ----*/
1804 void
1805 export_digest_att(long int msgno, ATTACH_S *a)
1807 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
1808 int rv, rflags = GER_NONE, i = 1;
1809 long count = 0L;
1810 ATTACH_S *ap;
1811 static HISTORY_S *history = NULL;
1812 STORE_S *store;
1813 static ESCKEY_S opts[] = {
1814 {ctrl('T'), 10, "^T", N_("To Files")},
1815 {-1, 0, NULL, NULL},
1816 {-1, 0, NULL, NULL}};
1818 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1819 opts[i].ch = ctrl('I');
1820 opts[i].rval = 11;
1821 opts[i].name = "TAB";
1822 opts[i].label = N_("Complete");
1825 filename[0] = full_filename[0] = '\0';
1827 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1828 sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
1829 opts, &rflags, -FOOTER_ROWS(ps_global),
1830 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1832 if(rv < 0){
1833 switch(rv){
1834 case -1:
1835 cmd_cancelled("Export");
1836 break;
1838 case -2:
1839 q_status_message1(SM_ORDER, 0, 2,
1840 _("Can't export to file outside of %s"),
1841 ps_global->VAR_OPER_DIR);
1842 break;
1845 return;
1848 /* With name in hand, allocate storage object and save away... */
1849 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1850 count = 0;
1852 for(ap = a + 1;
1853 ap->description
1854 && !strncmp(a->number, ap->number, strlen(a->number))
1855 && !err;
1856 ap++){
1857 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1858 count++;
1859 err = write_attached_msg(msgno, &ap, store,
1860 !count && !(rflags & GER_APPEND));
1864 if(so_give(&store))
1865 err = error_description(errno);
1867 if(err){
1868 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1869 _("Error exporting: %s"), err);
1870 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1871 _("%s messages exported before error occurred"), err);
1873 else
1874 q_status_message4(SM_ORDER, 0, 4,
1875 "%s messages in digest (part %s) %s to \"%s\"",
1876 long2string(count),
1877 a->number,
1878 rflags & GER_OVER
1879 ? "overwritten"
1880 : rflags & GER_APPEND ? "appended" : "written",
1881 full_filename);
1883 else
1884 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1885 _("Error opening file \"%s\" to export digest: %s"),
1886 full_filename, error_description(errno));
1890 /*----------------------------------------------------------------------
1891 Print the given attachment associated with the given message no
1893 Args: ps
1895 Result:
1896 ----*/
1897 void
1898 print_attachment(int qline, long int msgno, ATTACH_S *a)
1900 char prompt[250];
1902 if(ps_global->restricted){
1903 q_status_message(SM_ORDER | SM_DING, 0, 4,
1904 "Alpine demo can't Print attachments");
1905 return;
1908 snprintf(prompt, sizeof(prompt), "attach%s %s",
1909 (a->body->type == TYPETEXT) ? "ment" : "ed message",
1910 MIME_DGST_A(a) ? "digest" : a->number);
1911 prompt[sizeof(prompt)-1] = '\0';
1912 if(open_printer(prompt) >= 0){
1913 if(MIME_MSG_A(a))
1914 (void) print_msg_att(msgno, a, 1);
1915 else if(MIME_DGST_A(a))
1916 print_digest_att(msgno, a);
1917 else
1918 (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
1920 close_printer();
1926 * Print the attachment message/rfc822 to specified file
1928 * Returns 1 on success, 0 on failure.
1931 print_msg_att(long int msgno, ATTACH_S *a, int first)
1933 ATTACH_S *ap = a;
1934 MESSAGECACHE *mc;
1936 if(!(ps_global->mail_stream && msgno > 0L
1937 && msgno <= ps_global->mail_stream->nmsgs
1938 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1939 mc = NULL;
1941 if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
1942 && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
1943 && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
1944 ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
1945 : 1)
1946 && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
1947 return(1);
1950 q_status_message2(SM_ORDER | SM_DING, 3, 3,
1951 _("Error printing message %s, part %s"),
1952 long2string(msgno), a->number);
1953 return(0);
1957 /*----------------------------------------------------------------------
1958 Print the attachment message/rfc822 to specified file
1960 Args:
1962 Result:
1963 ----*/
1964 void
1965 print_digest_att(long int msgno, ATTACH_S *a)
1967 ATTACH_S *ap;
1968 int next = 0;
1970 for(ap = a + 1;
1971 ap->description
1972 && !strncmp(a->number, ap->number, strlen(a->number));
1973 ap++){
1974 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1975 char *p = part_desc(ap->number, ap->body->nested.msg->body,
1976 0, 80, FM_NOINDENT, print_char);
1977 if(p){
1978 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1979 _("Can't print digest: %s"), p);
1980 break;
1982 else if(!print_msg_att(msgno, ap, !next))
1983 break;
1985 next++;
1991 /*----------------------------------------------------------------------
1992 Unpack and display the given attachment associated with given message no.
1994 Args: msgno -- message no attachment is part of
1995 a -- attachment to display
1997 Returns: 0 on success, non-zero (and error message queued) otherwise
1998 ----*/
2000 display_attachment(long int msgno, ATTACH_S *a, int flags)
2002 char *filename = NULL;
2003 char sender_filename[1000];
2004 char *extp = NULL;
2005 STORE_S *store;
2006 gf_io_t pc;
2007 char *err;
2008 int we_cancel = 0, rv;
2009 char prefix[70];
2010 char ext[32];
2011 char mtype[128];
2013 /*------- Display the attachment -------*/
2014 if(dispatch_attachment(a) == MCD_NONE){
2015 /*----- Can't display this type ------*/
2016 if(a->body->encoding < ENCOTHER)
2017 q_status_message4(SM_ORDER | SM_DING, 3, 5,
2018 /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
2019 _("Don't know how to display %s%s%s attachments.%s"),
2020 body_type_names(a->body->type),
2021 a->body->subtype ? "/" : "",
2022 a->body->subtype ? a->body->subtype :"",
2023 (flags & DA_SAVE) ? _(" Try Save.") : "");
2024 else
2025 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2026 _("Don't know how to unpack \"%s\" encoding"),
2027 body_encodings[(a->body->encoding <= ENCMAX)
2028 ? a->body->encoding : ENCOTHER]);
2030 return(1);
2032 else if(!(a->can_display & MCD_EXTERNAL)){
2033 if(a->body->type == TYPEMULTIPART){
2034 if(a->body->subtype){
2035 if(!strucmp(a->body->subtype, "digest"))
2036 display_digest_att(msgno, a, flags);
2037 else
2038 q_status_message1(SM_ORDER, 3, 5,
2039 _("Can't display Multipart/%s"),
2040 a->body->subtype);
2042 else
2043 q_status_message(SM_ORDER, 3, 5,
2044 _("Can't display unknown Multipart Subtype"));
2046 else if(MIME_VCARD_A(a))
2047 display_vcard_att(msgno, a, flags);
2048 else if(a->body->type == TYPETEXT){
2050 rv = display_text_att(msgno, a, flags);
2051 } while(rv == MC_FULLHDR);
2053 else if(a->body->type == TYPEMESSAGE){
2055 rv = display_msg_att(msgno, a, flags);
2056 } while(rv == MC_FULLHDR);
2059 ps_global->mangled_screen = 1;
2060 return(0);
2063 /* arrive here if MCD_EXTERNAL */
2065 if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
2066 && (!(flags & DA_DIDPROMPT)))
2067 if(want_to(_("View selected Attachment"), 'y',
2068 0, NO_HELP, WT_NORM) == 'n'){
2069 cmd_cancelled(NULL);
2070 return(1);
2073 sender_filename[0] = '\0';
2074 ext[0] = '\0';
2075 prefix[0] = '\0';
2077 if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
2078 && (a->can_display & MCD_EXT_PROMPT)){
2079 char prompt[256];
2081 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2082 a->body, &extp);
2083 snprintf(prompt, sizeof(prompt),
2084 "Attachment %s%s unrecognized. %s%s%s",
2085 a->body->subtype,
2086 strlen(a->body->subtype) > 12 ? "..." : "",
2087 (extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
2088 (extp && extp[0]) ? extp : "",
2089 (extp && extp[0]) ? ")" : "");
2091 if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
2092 cmd_cancelled(NULL);
2093 return(1);
2097 /*------ Write the image to a temporary file ------*/
2099 /* create type/subtype in mtype */
2100 strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
2101 mtype[sizeof(mtype)-1] = '\0';
2102 if(a->body->subtype){
2103 strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
2104 mtype[sizeof(mtype)-1] = '\0';
2105 strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
2106 mtype[sizeof(mtype)-1] = '\0';
2110 * If we haven't already gotten the filename parameter, get it
2111 * now. It may be used in the temporary filename and possibly
2112 * for its extension.
2114 if(!sender_filename[0])
2115 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2116 a->body, &extp);
2118 if(check_mime_type_by_extension(extp, mtype)
2119 || (!set_mime_extension_by_type(ext, mtype) /* extension from type */
2120 && extp && extp[0])){ /* extension from filename */
2121 strncpy(ext, extp, sizeof(ext));
2122 ext[sizeof(ext)-1] = '\0';
2125 /* create a temp file */
2126 if(sender_filename){
2127 char *p, *q = NULL;
2129 /* get rid of any extension */
2130 if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
2131 *(q-1) = '\0';
2133 /* be careful about what is allowed in the filename */
2134 for(p = sender_filename; *p; p++)
2135 if(!(isascii((unsigned char) *p)
2136 && (isalnum((unsigned char) *p)
2137 || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
2138 break;
2140 if(!*p) /* filename was ok to use */
2141 snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
2144 /* didn't get it yet */
2145 if(!prefix[0]){
2146 snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
2147 ? a->body->subtype : "unk");
2150 /* We are creating a temporary name. This is just a prefix. If you
2151 * need the original name, use the save command, so if the prefix
2152 * is too long, shorten it.
2154 if (strlen(prefix) > 9){
2155 prefix[9] = '-';
2156 prefix[10] = '\0';
2159 filename = temp_nam_ext(NULL, prefix, ext);
2161 if(!filename){
2162 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2163 _("Error \"%s\", Can't create temporary file"),
2164 error_description(errno));
2165 return(1);
2168 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2169 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2170 _("Error \"%s\", Can't write file %s"),
2171 error_description(errno), filename);
2172 if(filename){
2173 our_unlink(filename);
2174 fs_give((void **)&filename);
2177 return(1);
2181 if(a->body->size.bytes){
2182 char msg_buf[128];
2184 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2185 a->description ? "\"" : "",
2186 a->description ? a->description : "attachment number ",
2187 a->description ? "" : a->number,
2188 a->description ? "\"" : "");
2189 msg_buf[sizeof(msg_buf)-1] = '\0';
2190 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
2191 a->body);
2194 if(a->body->type == TYPEMULTIPART){
2195 char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
2196 NULL, 0L);
2198 /* Write to store while converting newlines */
2199 while(h && *h)
2200 if(*h == '\015' && *(h+1) == '\012'){
2201 so_puts(store, NEWLINE);
2202 h += 2;
2204 else
2205 so_writec(*h++, store);
2208 gf_set_so_writec(&pc, store);
2210 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
2212 gf_clear_so_writec(store);
2214 if(we_cancel)
2215 cancel_busy_cue(0);
2217 so_give(&store);
2219 if(err){
2220 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2221 "%s: Error saving image to temp file %s",
2222 err, filename);
2223 if(filename){
2224 our_unlink(filename);
2225 fs_give((void **)&filename);
2228 return(1);
2231 /*----- Run the viewer process ----*/
2232 run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
2233 if(filename)
2234 fs_give((void **)&filename);
2236 return(0);
2240 /*----------------------------------------------------------------------
2241 Fish the required command from mailcap and run it
2243 Args: image_file -- The name of the file to pass viewer
2244 body -- body struct containing type/subtype of part
2246 A side effect may be that scrolltool is called as well if
2247 exec_mailcap_cmd has any substantial output...
2248 ----*/
2249 void
2250 run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
2252 MCAP_CMD_S *mc_cmd = NULL;
2253 int needs_terminal = 0, we_cancel = 0;
2255 we_cancel = busy_cue("Displaying attachment", NULL, 0);
2257 if((mc_cmd = mailcap_build_command(body->type, body->subtype,
2258 body, image_file,
2259 &needs_terminal, chk_extension)) != NULL){
2260 if(we_cancel)
2261 cancel_busy_cue(-1);
2263 exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
2264 if(mc_cmd->command)
2265 fs_give((void **)&mc_cmd->command);
2266 fs_give((void **)&mc_cmd);
2268 else{
2269 if(we_cancel)
2270 cancel_busy_cue(-1);
2272 q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
2273 type_desc(body->type, body->subtype,
2274 body->parameter, NULL, 1));
2279 /*----------------------------------------------------------------------
2280 Detach and provide for browsing a text body part
2282 Args: msgno -- raw message number to get part from
2283 a -- attachment struct for the desired part
2285 Result:
2286 ----*/
2287 STORE_S *
2288 format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
2290 STORE_S *store;
2291 gf_io_t pc;
2293 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2294 if(handlesp)
2295 init_handles(handlesp);
2297 gf_set_so_writec(&pc, store);
2298 (void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
2299 gf_clear_so_writec(store);
2302 return(store);
2306 /*----------------------------------------------------------------------
2307 Detach and provide for browsing a text body part
2309 Args: msgno -- raw message number to get part from
2310 a -- attachment struct for the desired part
2312 Result:
2313 ----*/
2315 display_text_att(long int msgno, ATTACH_S *a, int flags)
2317 STORE_S *store;
2318 HANDLE_S *handles = NULL;
2319 int rv = 0;
2321 if(msgno > 0L)
2322 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2324 if((store = format_text_att(msgno, a, &handles)) != NULL){
2325 rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
2326 free_handles(&handles);
2327 so_give(&store); /* free resources associated with store */
2329 else
2330 q_status_message(SM_ORDER | SM_DING, 3, 3,
2331 _("Error allocating space for attachment."));
2333 return(rv);
2337 /*----------------------------------------------------------------------
2338 Detach and provide for browsing a body part of type "MESSAGE"
2340 Args: msgno -- message number to get partrom
2341 a -- attachment struct for the desired part
2343 Result:
2344 ----*/
2346 display_msg_att(long int msgno, ATTACH_S *a, int flags)
2348 STORE_S *store;
2349 gf_io_t pc;
2350 ATTACH_S *ap = a;
2351 HANDLE_S *handles = NULL;
2352 int rv = 0;
2354 if(msgno > 0L)
2355 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2357 /* BUG, should check this return code */
2358 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2360 /* initialize a storage object */
2361 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2362 q_status_message(SM_ORDER | SM_DING, 3, 3,
2363 _("Error allocating space for message."));
2364 return(rv);
2367 gf_set_so_writec(&pc, store);
2369 if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
2370 if(ps_global->full_header == 2)
2371 q_status_message(SM_INFO, 0, 3,
2372 _("Full header mode ON. All header text being included"));
2374 rv = scroll_attachment((a->body->subtype
2375 && !strucmp(a->body->subtype, "delivery-status"))
2376 ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
2377 store, CharStar, handles, a, flags);
2378 free_handles(&handles);
2381 gf_clear_so_writec(store);
2383 so_give(&store); /* free resources associated with store */
2384 return(rv);
2388 /*----------------------------------------------------------------------
2389 Detach and provide for browsing a multipart body part of type "DIGEST"
2391 Args: msgno -- message number to get partrom
2392 a -- attachment struct for the desired part
2394 Result:
2395 ----*/
2396 void
2397 display_digest_att(long int msgno, ATTACH_S *a, int flags)
2399 STORE_S *store;
2400 ATTACH_S *ap;
2401 HANDLE_S *handles = NULL;
2402 gf_io_t pc;
2403 SourceType src = CharStar;
2404 int bad_news = 0;
2406 if(msgno > 0L)
2407 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2409 /* BUG, should check this return code */
2410 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2412 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
2413 q_status_message(SM_ORDER | SM_DING, 3, 3,
2414 _("Error allocating space for message."));
2415 return;
2418 gf_set_so_writec(&pc, store);
2421 * While in a subtype of this message
2423 for(ap = a + 1;
2424 ap->description
2425 && !strncmp(a->number, ap->number, strlen(a->number))
2426 && !bad_news;
2427 ap++){
2428 if(ap->body->type == TYPEMESSAGE){
2429 char *errstr;
2431 if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
2432 char *tsub;
2434 tsub = cpystr(ap->body->subtype);
2435 convert_possibly_encoded_str_to_utf8((char **) &tsub);
2436 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
2437 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2439 if((errstr = format_editorial(tmp_20k_buf,
2440 ps_global->ttyo->screen_cols, 0,
2441 NULL, pc)) != NULL){
2442 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2443 _("Can't format digest: %s"), errstr);
2444 bad_news++;
2446 else if(!gf_puts(NEWLINE, pc))
2447 bad_news++;
2449 fs_give((void **) &tsub);
2451 else{
2452 if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
2453 0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
2454 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2455 _("Can't format digest: %s"), errstr);
2456 bad_news++;
2458 else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
2459 bad_news++;
2462 else if(ap->body->type == TYPETEXT
2463 && decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
2464 bad_news++;
2465 else if(!gf_puts("Unknown type in Digest", pc))
2466 bad_news++;
2469 if(!bad_news){
2470 if(ps_global->full_header == 2)
2471 q_status_message(SM_INFO, 0, 3,
2472 _("Full header mode ON. All header text being included"));
2474 scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
2477 free_handles(&handles);
2479 gf_clear_so_writec(store);
2480 so_give(&store); /* free resources associated with store */
2485 scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
2487 SCROLL_S sargs;
2489 memset(&sargs, 0, sizeof(SCROLL_S));
2490 sargs.text.text = so_text(store);
2491 sargs.text.src = src;
2492 sargs.text.desc = "attachment";
2493 sargs.text.handles = handles;
2494 sargs.bar.title = title;
2495 sargs.proc.tool = process_attachment_cmd;
2496 sargs.proc.data.p = (void *) a;
2497 sargs.help.text = h_mail_text_att_view;
2498 sargs.help.title = _("HELP FOR ATTACHED TEXT VIEW");
2499 sargs.keys.menu = &att_view_keymenu;
2500 setbitmap(sargs.keys.bitmap);
2502 /* First, fix up "back" key */
2503 if(flags & DA_FROM_VIEW){
2504 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
2506 else{
2507 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
2510 if(!handles){
2511 clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
2512 clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
2513 clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
2516 if(F_OFF(F_ENABLE_PIPE, ps_global))
2517 clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
2519 if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
2520 clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
2523 * If message or digest, leave Reply and Save and,
2524 * conditionally, Bounce on...
2526 if(MIME_MSG_A(a)){
2527 if(F_OFF(F_ENABLE_BOUNCE, ps_global))
2528 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2530 else{
2531 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2532 clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
2533 clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
2535 #ifdef SMIME
2536 if(!(ps_global->smime && ps_global->smime->need_passphrase))
2537 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2539 if(F_ON(F_DONT_DO_SMIME, ps_global) || !MIME_MSG_A(a)){
2540 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2541 clrbitn(ATV_SECURITY_KEY, sargs.keys.bitmap);
2543 #endif
2545 sargs.use_indexline_color = 1;
2547 if(DA_RESIZE & flags)
2548 sargs.resize_exit = 1;
2550 #ifdef _WINDOWS
2551 scrat_attachp = a;
2552 sargs.mouse.popup = scroll_att_popup;
2553 #endif
2555 return(scrolltool(&sargs));
2558 #ifdef SMIME
2559 void
2560 display_smime_info_att(struct pine *ps, ATTACH_S *a)
2562 if(smime_check(a->body->nested.msg->body) == 0){
2563 q_status_message(SM_ORDER | SM_DING, 0, 3,
2564 _("Not a signed or encrypted message"));
2565 return;
2568 display_smime_info(ps, a->body->nested.msg->env, a->body->nested.msg->body);
2570 #endif /* SMIME */
2573 process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2575 int rv = 0, n;
2576 long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2577 #define AD(X) ((ATTACH_S *) (X)->proc.data.p)
2579 switch(cmd){
2580 case MC_EXIT :
2581 rv = 1;
2582 break;
2584 case MC_QUIT :
2585 ps_global->next_screen = quit_screen;
2586 break;
2588 case MC_MAIN :
2589 ps_global->next_screen = main_menu_screen;
2590 break;
2592 case MC_REPLY :
2593 reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
2594 break;
2596 case MC_FORWARD :
2597 forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
2598 break;
2600 case MC_BOUNCE :
2601 bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
2602 AD(sparms)->body->nested.msg->env->subject);
2603 ps_global->mangled_footer = 1;
2604 break;
2606 case MC_DELETE :
2607 delete_attachment(rawno, sparms->proc.data.p);
2608 break;
2610 case MC_UNDELETE :
2611 if(undelete_attachment(rawno, sparms->proc.data.p, &n))
2612 q_status_message1(SM_ORDER, 0, 3,
2613 "Part %s UNdeleted", AD(sparms)->number);
2615 break;
2617 case MC_SAVE :
2618 save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2619 ps_global->mangled_footer = 1;
2620 break;
2622 case MC_EXPORT :
2623 export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2624 ps_global->mangled_footer = 1;
2625 break;
2627 case MC_PRINTMSG :
2628 print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2629 ps_global->mangled_footer = 1;
2630 break;
2632 case MC_PIPE :
2633 pipe_attachment(rawno, sparms->proc.data.p);
2634 ps_global->mangled_footer = 1;
2635 break;
2637 case MC_FULLHDR :
2638 ps_global->full_header++;
2639 if(ps_global->full_header == 1){
2640 if(!(ps_global->quote_suppression_threshold
2641 && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
2642 ps_global->full_header++;
2644 else if(ps_global->full_header > 2)
2645 ps_global->full_header = 0;
2647 switch(ps_global->full_header){
2648 case 0:
2649 q_status_message(SM_ORDER, 0, 3,
2650 _("Display of full headers is now off."));
2651 break;
2653 case 1:
2654 q_status_message1(SM_ORDER, 0, 3,
2655 _("Quotes displayed, use %s to see full headers"),
2656 F_ON(F_USE_FK, ps_global) ? "F9" : "H");
2657 break;
2659 case 2:
2660 q_status_message(SM_ORDER, 0, 3,
2661 _("Display of full headers is now on."));
2662 break;
2666 rv = 1;
2667 break;
2669 #ifdef SMIME
2670 case MC_DECRYPT:
2671 if(ps_global->smime && ps_global->smime->need_passphrase)
2672 smime_get_passphrase();
2673 break;
2675 case MC_SECURITY:
2676 display_smime_info_att(ps_global, sparms->proc.data.p);
2677 break;
2678 #endif
2680 default:
2681 alpine_panic("Unexpected command case");
2682 break;
2685 return(rv);
2690 * Returns 1 on success, 0 on error.
2693 format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
2695 int rv = 1;
2697 if((*a)->body->type != TYPEMESSAGE)
2698 return(gf_puts("[ Undisplayed Attachment of Type ", pc)
2699 && gf_puts(body_type_names((*a)->body->type), pc)
2700 && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
2702 if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
2703 HEADER_S h;
2705 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
2706 FE_DEFAULT);
2707 switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
2708 (*a)->body->nested.msg->env, &h, NULL, NULL,
2709 flags, NULL, pc)){
2710 case -1 : /* write error */
2711 return(0);
2713 case 1 : /* fetch error */
2714 if(!(gf_puts("[ Error fetching header ]", pc)
2715 && !gf_puts(NEWLINE, pc)))
2716 return(0);
2718 break;
2721 gf_puts(NEWLINE, pc);
2723 ++(*a);
2725 #ifdef SMIME
2726 if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2727 if((*a)->description){
2728 if(!(!format_editorial((*a)->description,
2729 ps_global->ttyo->screen_cols,
2730 flags, NULL, pc)
2731 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2732 rv = 0;
2735 ++(*a);
2737 #endif /* SMIME */
2739 if(((*a))->description
2740 && (*a)->body && (*a)->body->type == TYPETEXT){
2741 if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2742 rv = 0;
2744 else if(!(gf_puts("[Can't display ", pc)
2745 && gf_puts(((*a)->description && (*a)->body)
2746 ? "first non-" : "missing ", pc)
2747 && gf_puts("text segment]", pc)
2748 && gf_puts(NEWLINE, pc)))
2749 rv = 0;
2751 ++(*a);
2754 else if((*a)->body->subtype
2755 && strucmp((*a)->body->subtype, "external-body") == 0) {
2756 if(format_editorial("This part is not included and can be fetched as follows:",
2757 ps_global->ttyo->screen_cols, flags, NULL, pc)
2758 || !gf_puts(NEWLINE, pc)
2759 || format_editorial(display_parameters((*a)->body->parameter),
2760 ps_global->ttyo->screen_cols, flags, handlesp, pc))
2761 rv = 0;
2763 else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2764 rv = 0;
2766 return(rv);
2770 void
2771 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2773 STORE_S *in_store, *out_store = NULL;
2774 HANDLE_S *handles = NULL;
2775 URL_HILITE_S uh;
2776 gf_io_t gc, pc;
2777 char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2778 int cmd, indent, begins = 0;
2780 lines = detach_vcard_att(ps_global->mail_stream,
2781 msgno, a->body, a->number);
2782 if(!lines){
2783 q_status_message(SM_ORDER | SM_DING, 3, 3,
2784 _("Error accessing attachment."));
2785 return;
2788 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2789 free_list_array(&lines);
2790 q_status_message(SM_ORDER | SM_DING, 3, 3,
2791 _("Error allocating space for attachment."));
2792 return;
2795 for(ll = lines, indent = 0; ll && *ll; ll++)
2796 if((p = strindex(*ll, ':')) && p - *ll > indent)
2797 indent = p - *ll;
2799 indent += 5;
2800 for(ll = lines; ll && *ll; ll++){
2801 if((p = strindex(*ll, ':')) != NULL){
2802 if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2803 begins++;
2805 snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
2806 (int) MIN(p - *ll, sizeof(tmp)-5), *ll);
2807 tmp[sizeof(tmp)-1] = '\0';
2808 so_puts(in_store, tmp);
2809 p++;
2811 else{
2812 p = *ll;
2813 so_puts(in_store, repeat_char(indent, SPACE));
2816 snprintf(tmp, sizeof(tmp), "%.200s", p);
2817 tmp[sizeof(tmp)-1] = '\0';
2818 so_puts(in_store,
2819 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
2820 SIZEOF_20KBUF, tmp));
2821 so_puts(in_store, "\015\012");
2824 free_list_array(&lines);
2826 so_puts(in_store, "\015\012\015\012");
2829 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2830 so_seek(in_store, 0L, 0);
2832 init_handles(&handles);
2833 gf_filter_init();
2835 if(F_ON(F_VIEW_SEL_URL, ps_global)
2836 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2837 || F_ON(F_SCAN_ADDR, ps_global))
2838 gf_link_filter(gf_line_test,
2839 gf_line_test_opt(url_hilite,
2840 gf_url_hilite_opt(&uh,&handles,0)));
2842 gf_link_filter(gf_wrap,
2843 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
2844 ps_global->ttyo->screen_cols,
2845 NULL, indent, GFW_HANDLES));
2846 gf_link_filter(gf_nvtnl_local, NULL);
2848 gf_set_so_readc(&gc, in_store);
2849 gf_set_so_writec(&pc, out_store);
2851 errstr = gf_pipe(gc, pc);
2853 gf_clear_so_readc(in_store);
2855 if(!errstr){
2856 #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.")
2857 #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.")
2858 errstr = format_editorial((begins > 1)
2859 ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
2860 ps_global->ttyo->screen_cols, 0, NULL, pc);
2863 gf_clear_so_writec(out_store);
2865 if(!errstr)
2866 cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
2867 CharStar, handles, a, flags | DA_RESIZE);
2869 free_handles(&handles);
2870 so_give(&out_store);
2872 else
2873 errstr = _("Error allocating space");
2875 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
2877 if(errstr)
2878 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2879 _("Can't format entry : %s"), errstr);
2881 so_give(&in_store);
2885 /*----------------------------------------------------------------------
2886 Display attachment information
2888 Args: msgno -- message number to get partrom
2889 a -- attachment struct for the desired part
2891 Result: a screen containing information about attachment:
2892 ----*/
2893 void
2894 display_attach_info(long int msgno, ATTACH_S *a)
2896 int i, indent, cols;
2897 char buf1[100], *folded;
2898 STORE_S *store;
2899 PARMLIST_S *plist;
2900 SCROLL_S sargs;
2902 (void) dispatch_attachment(a);
2904 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2905 q_status_message(SM_ORDER | SM_DING, 3, 3,
2906 _("Error allocating space for message."));
2907 return;
2910 cols = ps_global->ttyo->screen_cols;
2913 * 2 spaces on left
2914 * 16 for text (longest is Display Method == 14)
2915 * 2 for ": "
2917 indent = 20;
2919 /* don't try stupid folding */
2920 cols = MAX(indent+10, cols);
2922 so_puts(store, "Details about Attachment #");
2923 so_puts(store, a->number);
2924 so_puts(store, " :\n\n");
2925 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
2926 so_puts(store, buf1);
2927 so_puts(store, body_type_names(a->body->type));
2928 so_puts(store, "\n");
2929 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
2930 so_puts(store, buf1);
2931 so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
2932 so_puts(store, "\n");
2933 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
2934 so_puts(store, buf1);
2935 so_puts(store, a->body->encoding < ENCMAX
2936 ? body_encodings[a->body->encoding]
2937 : "Unknown");
2938 so_puts(store, "\n");
2939 if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
2940 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
2941 so_puts(store, buf1);
2942 i = 0;
2943 while(rfc2231_list_params(plist)){
2944 if(i++)
2945 so_puts(store, repeat_char(indent, ' '));
2947 so_puts(store, plist->attrib);
2948 so_puts(store, " = ");
2949 so_puts(store, plist->value ? plist->value : "");
2950 so_puts(store, "\n");
2953 rfc2231_free_parmlist(&plist);
2956 if(a->body->description && a->body->description[0]){
2957 char buftmp[MAILTMPLEN];
2958 unsigned char *q;
2960 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
2962 snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
2963 buftmp[sizeof(buftmp)-1] = '\0';
2964 q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
2965 folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
2967 if(folded){
2968 so_puts(store, folded);
2969 fs_give((void **) &folded);
2973 /* BUG: no a->body->language support */
2975 if(a->body->disposition.type){
2976 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
2977 so_puts(store, buf1);
2978 so_puts(store, a->body->disposition.type);
2979 so_puts(store, "\n");
2980 if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
2981 while(rfc2231_list_params(plist)){
2982 so_puts(store, repeat_char(indent, ' '));
2983 so_puts(store, plist->attrib);
2984 so_puts(store, " = ");
2985 so_puts(store, plist->value ? plist->value : "");
2986 so_puts(store, "\n");
2989 rfc2231_free_parmlist(&plist);
2993 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
2994 so_puts(store, buf1);
2995 so_puts(store, comatose((a->body->encoding == ENCBASE64)
2996 ? ((a->body->size.bytes * 3)/4)
2997 : a->body->size.bytes));
2998 so_puts(store, " bytes\n");
2999 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
3000 so_puts(store, buf1);
3001 if(a->can_display == MCD_NONE) {
3002 so_puts(store, "Can't, ");
3003 so_puts(store, (a->body->encoding < ENCOTHER)
3004 ? "Unknown Attachment Format"
3005 : "Unknown Encoding");
3007 else if(!(a->can_display & MCD_EXTERNAL)){
3008 so_puts(store, "Alpine's Internal Pager");
3010 else{
3011 int nt, free_pretty_cmd;
3012 MCAP_CMD_S *mc_cmd;
3013 char *pretty_cmd;
3015 if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
3016 a->body, "<datafile>", &nt,
3017 a->can_display & MCD_EXT_PROMPT)) != NULL){
3018 so_puts(store, "\"");
3019 if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
3020 so_puts(store, pretty_cmd);
3021 so_puts(store, "\"");
3022 if(free_pretty_cmd && pretty_cmd)
3023 fs_give((void **)&pretty_cmd);
3024 if(mc_cmd->command)
3025 fs_give((void **)&mc_cmd->command);
3026 fs_give((void **)&mc_cmd);
3030 so_puts(store, "\n");
3032 memset(&sargs, 0, sizeof(SCROLL_S));
3033 sargs.text.text = so_text(store);
3034 sargs.text.src = CharStar;
3035 sargs.text.desc = "attachment info";
3036 sargs.bar.title = _("ABOUT ATTACHMENT");
3037 sargs.help.text = h_simple_text_view;
3038 sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
3040 sargs.use_indexline_color = 1;
3042 scrolltool(&sargs);
3044 so_give(&store); /* free resources associated with store */
3045 ps_global->mangled_screen = 1;
3049 /*----------------------------------------------------------------------
3051 ----*/
3052 void
3053 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3055 char *sig;
3056 void *msgtext;
3057 ENVELOPE *outgoing;
3058 BODY *body;
3060 if(MIME_MSG_A(a)){
3061 forward_msg_att(stream, msgno, a);
3063 else{
3064 ACTION_S *role = NULL;
3065 REDRAFT_POS_S *redraft_pos = NULL;
3066 long rflags = ROLE_FORWARD;
3067 PAT_STATE dummy;
3069 outgoing = mail_newenvelope();
3070 outgoing->message_id = generate_message_id();
3071 outgoing->subject = cpystr("Forwarded attachment...");
3073 if(nonempty_patterns(rflags, &dummy)){
3075 * There is no message, but a Current Folder Type might match.
3077 * This has been changed to check against the message
3078 * containing the attachment.
3080 role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3081 if(confirm_role(rflags, &role))
3082 role = combine_inherited_role(role);
3083 else{
3084 role = NULL;
3085 cmd_cancelled("Forward");
3086 mail_free_envelope(&outgoing);
3087 return;
3091 if(role)
3092 q_status_message1(SM_ORDER, 3, 4,
3093 _("Forwarding using role \"%s\""), role->nick);
3096 * as with all text bound for the composer, build it in
3097 * a storage object of the type it understands...
3099 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3100 int impl, template_len = 0;
3102 if(role && role->template){
3103 char *filtered;
3105 impl = 1;
3106 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3107 if(filtered){
3108 if(*filtered){
3109 so_puts((STORE_S *)msgtext, filtered);
3110 if(impl == 1)
3111 template_len = strlen(filtered);
3114 fs_give((void **)&filtered);
3117 else
3118 impl = 1;
3120 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3121 if(impl == 2)
3122 redraft_pos->offset += template_len;
3124 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3126 fs_give((void **)&sig);
3128 else
3129 so_puts((STORE_S *)msgtext, NEWLINE);
3131 /*---- New Body to start with ----*/
3132 body = mail_newbody();
3133 body->type = TYPEMULTIPART;
3135 /*---- The TEXT part/body ----*/
3136 body->nested.part = mail_newbody_part();
3137 body->nested.part->body.type = TYPETEXT;
3138 body->nested.part->body.contents.text.data = msgtext;
3140 /*---- The corresponding things we're attaching ----*/
3141 body->nested.part->next = mail_newbody_part();
3142 body->nested.part->next->body.id = generate_message_id();
3143 copy_body(&body->nested.part->next->body, a->body);
3145 if(fetch_contents(stream, msgno, a->number,
3146 &body->nested.part->next->body)){
3147 pine_send(outgoing, &body, "FORWARD MESSAGE",
3148 role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3150 ps_global->mangled_screen = 1;
3151 pine_free_body(&body);
3152 free_redraft_pos(&redraft_pos);
3154 else{
3155 mail_free_body(&body);
3156 so_give((STORE_S **) &msgtext);
3157 free_redraft_pos(&redraft_pos);
3158 q_status_message(SM_ORDER | SM_DING, 4, 5,
3159 _("Error fetching message contents. Can't forward message."));
3162 else
3163 q_status_message(SM_ORDER | SM_DING, 3, 4,
3164 _("Error allocating message text"));
3166 mail_free_envelope(&outgoing);
3167 free_action(&role);
3172 void
3173 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3175 char *p, *sig = NULL;
3176 int ret;
3177 void *msgtext;
3178 ENVELOPE *outgoing;
3179 BODY *body;
3180 ACTION_S *role = NULL;
3181 REPLY_S reply;
3182 REDRAFT_POS_S *redraft_pos = NULL;
3184 outgoing = mail_newenvelope();
3185 outgoing->message_id = generate_message_id();
3187 memset((void *)&reply, 0, sizeof(reply));
3189 if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
3191 * as with all text bound for the composer, build it in
3192 * a storage object of the type it understands...
3194 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3195 int impl, template_len = 0;
3196 long rflags = ROLE_FORWARD;
3197 PAT_STATE dummy;
3199 ret = 'n';
3200 if(ps_global->full_header == 2)
3201 ret = want_to(_("Forward message as an attachment"), 'n', 0,
3202 NO_HELP, WT_SEQ_SENSITIVE);
3203 /* Setup possible role */
3204 if(nonempty_patterns(rflags, &dummy)){
3205 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3206 if(confirm_role(rflags, &role))
3207 role = combine_inherited_role(role);
3208 else{ /* cancel reply */
3209 role = NULL;
3210 cmd_cancelled("Forward");
3211 mail_free_envelope(&outgoing);
3212 so_give((STORE_S **) &msgtext);
3213 return;
3217 if(role)
3218 q_status_message1(SM_ORDER, 3, 4,
3219 _("Forwarding using role \"%s\""), role->nick);
3221 if(role && role->template){
3222 char *filtered;
3224 impl = 1;
3225 filtered = detoken(role, a->body->nested.msg->env,
3226 0, 0, 0, &redraft_pos, &impl);
3227 if(filtered){
3228 if(*filtered){
3229 so_puts((STORE_S *)msgtext, filtered);
3230 if(impl == 1)
3231 template_len = strlen(filtered);
3234 fs_give((void **)&filtered);
3237 else
3238 impl = 1;
3240 if((sig = detoken(role, a->body->nested.msg->env,
3241 2, 0, 1, &redraft_pos, &impl)) != NULL){
3242 if(impl == 2)
3243 redraft_pos->offset += template_len;
3245 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3247 fs_give((void **)&sig);
3249 else
3250 so_puts((STORE_S *)msgtext, NEWLINE);
3252 if(ret == 'y'){
3253 /*---- New Body to start with ----*/
3254 body = mail_newbody();
3255 body->type = TYPEMULTIPART;
3257 /*---- The TEXT part/body ----*/
3258 body->nested.part = mail_newbody_part();
3259 body->nested.part->body.type = TYPETEXT;
3260 body->nested.part->body.contents.text.data = msgtext;
3262 if(!forward_mime_msg(stream, msgno,
3263 p = body_partno(stream, msgno, a->body),
3264 a->body->nested.msg->env,
3265 &body->nested.part->next, msgtext))
3266 mail_free_body(&body);
3268 else{
3269 reply.forw = 1;
3270 if(a->body->nested.msg->body){
3271 char *charset;
3273 charset
3274 = parameter_val(a->body->nested.msg->body->parameter,
3275 "charset");
3277 if(charset && strucmp(charset, "us-ascii") != 0){
3278 CONV_TABLE *ct;
3281 * There is a non-ascii charset,
3282 * is there conversion happening?
3284 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3285 || !ct->table){
3286 reply.orig_charset = charset;
3287 charset = NULL;
3291 if(charset)
3292 fs_give((void **) &charset);
3295 body = forward_body(stream, a->body->nested.msg->env,
3296 a->body->nested.msg->body, msgno,
3297 p = body_partno(stream, msgno, a->body),
3298 msgtext, FWD_NONE);
3301 fs_give((void **) &p);
3303 if(body){
3304 pine_send(outgoing, &body,
3305 "FORWARD MESSAGE",
3306 role, NULL,
3307 reply.forw ? &reply : NULL,
3308 redraft_pos, NULL, NULL, 0);
3310 ps_global->mangled_screen = 1;
3311 pine_free_body(&body);
3312 free_redraft_pos(&redraft_pos);
3313 free_action(&role);
3315 else{
3316 so_give((STORE_S **) &msgtext);
3317 q_status_message(SM_ORDER | SM_DING, 4, 5,
3318 _("Error fetching message contents. Can't forward message."));
3321 else
3322 q_status_message(SM_ORDER | SM_DING, 3, 4,
3323 _("Error allocating message text"));
3325 else
3326 q_status_message1(SM_ORDER,3,4,
3327 _("Error fetching message %s. Can't forward it."),
3328 long2string(msgno));
3330 if(reply.orig_charset)
3331 fs_give((void **)&reply.orig_charset);
3333 mail_free_envelope(&outgoing);
3337 void
3338 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3340 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
3341 ENVELOPE *outgoing;
3342 BODY *body;
3343 void *msgtext;
3344 char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3345 int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3346 int rolemsg = 0, copytomsg = 0;
3347 long rflags;
3348 PAT_STATE dummy;
3349 REDRAFT_POS_S *redraft_pos = NULL;
3350 ACTION_S *role = NULL;
3352 outgoing = mail_newenvelope();
3354 dprint((4,"\n - attachment reply \n"));
3356 saved_from = (ADDRESS *) NULL;
3357 saved_to = (ADDRESS *) NULL;
3358 saved_cc = (ADDRESS *) NULL;
3359 saved_resent = (ADDRESS *) NULL;
3360 outgoing->subject = NULL;
3362 prefix = reply_quote_str(a->body->nested.msg->env);
3364 * For consistency, the first question is always "include text?"
3366 if((include_text = reply_text_query(ps_global, 1, &prefix)) >= 0
3367 && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3368 && reply_harvest(ps_global, msgno, a->number,
3369 a->body->nested.msg->env, &saved_from,
3370 &saved_to, &saved_cc, &saved_resent, &flags)){
3371 outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3372 NULL, 0);
3373 clear_cursor_pos();
3374 reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3375 saved_from, saved_to, saved_cc, saved_resent,
3376 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3377 if(errmsg){
3378 if(*errmsg){
3379 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3380 display_message(NO_OP_COMMAND);
3383 fs_give((void **)&errmsg);
3386 if(sp_expunge_count(stream)) /* current msg was expunged */
3387 goto seeyalater;
3389 /* Setup possible role */
3390 rflags = ROLE_REPLY;
3391 if(nonempty_patterns(rflags, &dummy)){
3392 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3393 if(confirm_role(rflags, &role))
3394 role = combine_inherited_role(role);
3395 else{ /* cancel reply */
3396 role = NULL;
3397 cmd_cancelled("Reply");
3398 goto seeyalater;
3402 if(role){
3403 rolemsg++;
3405 /* override fcc gotten in reply_seed */
3406 if(role->fcc && fcc)
3407 fs_give((void **) &fcc);
3410 if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3411 ADDRESS *us_in_to_and_cc, *ap;
3413 us_in_to_and_cc = (ADDRESS *) NULL;
3414 if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3415 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3416 NULL, us_in_to_and_cc, NULL,
3417 a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3418 reply_append_addr(&us_in_to_and_cc, ap);
3420 if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3421 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3422 NULL, us_in_to_and_cc, NULL,
3423 a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3424 reply_append_addr(&us_in_to_and_cc, ap);
3427 * A list of all of our addresses that appear in the To
3428 * and cc fields is in us_in_to_and_cc.
3429 * If there is exactly one address in that list then
3430 * use it for the outgoing From.
3432 if(us_in_to_and_cc && !us_in_to_and_cc->next){
3433 PINEFIELD *custom, *pf;
3434 ADDRESS *a = NULL;
3435 char *addr = NULL;
3438 * Check to see if this address is different from what
3439 * we would have used anyway. If it is, notify the user
3440 * with a status message. This is pretty hokey because we're
3441 * mimicking how pine_send would set the From address and
3442 * there is no coordination between the two.
3445 /* in case user has a custom From value */
3446 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3448 pf = (PINEFIELD *) fs_get(sizeof(*pf));
3449 memset((void *) pf, 0, sizeof(*pf));
3450 pf->name = cpystr("From");
3451 pf->addr = &a;
3452 if(set_default_hdrval(pf, custom) >= UseAsDef
3453 && pf->textbuf && pf->textbuf[0]){
3454 removing_trailing_white_space(pf->textbuf);
3455 (void)removing_double_quotes(pf->textbuf);
3456 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3457 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3458 if(addr)
3459 fs_give((void **) &addr);
3462 if(!*pf->addr)
3463 *pf->addr = generate_from();
3465 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3466 copytomsg++;
3467 if(!role){
3468 role = (ACTION_S *) fs_get(sizeof(*role));
3469 memset((void *) role, 0, sizeof(*role));
3470 role->is_a_role = 1;
3473 role->from = us_in_to_and_cc;
3474 us_in_to_and_cc = NULL;
3477 free_customs(custom);
3478 free_customs(pf);
3481 if(us_in_to_and_cc)
3482 mail_free_address(&us_in_to_and_cc);
3486 if(role){
3487 if(rolemsg && copytomsg)
3488 q_status_message1(SM_ORDER, 3, 4,
3489 _("Replying using role \"%s\" and To as From"), role->nick);
3490 else if(rolemsg)
3491 q_status_message1(SM_ORDER, 3, 4,
3492 _("Replying using role \"%s\""), role->nick);
3493 else if(copytomsg)
3494 q_status_message(SM_ORDER, 3, 4,
3495 _("Replying using incoming To as outgoing From"));
3498 outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3499 outgoing->references = reply_build_refs(a->body->nested.msg->env);
3500 outgoing->message_id = generate_message_id();
3502 if(!outgoing->to && !outgoing->cc
3503 && !outgoing->bcc && !outgoing->newsgroups)
3504 q_status_message(SM_ORDER | SM_DING, 3, 6,
3505 _("Warning: no valid addresses to reply to!"));
3508 * Now fix up the body...
3510 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3511 REPLY_S reply;
3513 memset((void *)&reply, 0, sizeof(reply));
3514 reply.forw = 1;
3515 if(a->body->nested.msg->body){
3516 char *charset;
3518 charset
3519 = parameter_val(a->body->nested.msg->body->parameter,
3520 "charset");
3522 if(charset && strucmp(charset, "us-ascii") != 0){
3523 CONV_TABLE *ct;
3526 * There is a non-ascii charset,
3527 * is there conversion happening?
3529 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3530 || !ct->table){
3531 reply.orig_charset = charset;
3532 charset = NULL;
3536 if(charset)
3537 fs_give((void **) &charset);
3540 if((body = reply_body(stream, a->body->nested.msg->env,
3541 a->body->nested.msg->body, msgno,
3542 tp = body_partno(stream, msgno, a->body),
3543 msgtext, prefix, include_text, role,
3544 1, &redraft_pos)) != NULL){
3545 /* partially formatted outgoing message */
3546 pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3547 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3549 pine_free_body(&body);
3550 ps_global->mangled_screen = 1;
3552 else
3553 q_status_message(SM_ORDER | SM_DING, 3, 4,
3554 _("Error building message body"));
3556 fs_give((void **) &tp);
3557 if(reply.orig_charset)
3558 fs_give((void **)&reply.orig_charset);
3560 else
3561 q_status_message(SM_ORDER | SM_DING, 3, 4,
3562 _("Error allocating message text"));
3565 seeyalater:
3566 mail_free_envelope(&outgoing);
3567 mail_free_address(&saved_from);
3568 mail_free_address(&saved_to);
3569 mail_free_address(&saved_cc);
3570 mail_free_address(&saved_resent);
3572 if(prefix)
3573 fs_give((void **) &prefix);
3575 if(fcc)
3576 fs_give((void **) &fcc);
3578 free_redraft_pos(&redraft_pos);
3579 free_action(&role);
3583 void
3584 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
3586 char *errstr;
3588 if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
3589 q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
3593 void
3594 pipe_attachment(long int msgno, ATTACH_S *a)
3596 char *err, *resultfilename = NULL, prompt[80], *p;
3597 int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
3598 long ku;
3599 PIPE_S *syspipe;
3600 HelpType help;
3601 char pipe_command[MAXPATH+1];
3602 unsigned flagsforhist = 1; /* raw=2 /capture=1 */
3603 static HISTORY_S *history = NULL;
3604 ESCKEY_S pipe_opt[6];
3606 if(ps_global->restricted){
3607 q_status_message(SM_ORDER | SM_DING, 0, 4,
3608 "Alpine demo can't pipe attachments");
3609 return;
3612 help = NO_HELP;
3613 pipe_command[0] = '\0';
3615 init_hist(&history, HISTSIZE);
3616 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3617 if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
3618 strncpy(pipe_command, p, sizeof(pipe_command));
3619 pipe_command[sizeof(pipe_command)-1] = '\0';
3620 if(history->hist[history->curindex]){
3621 flagsforhist = history->hist[history->curindex]->flags;
3622 raw = (flagsforhist & 0x2) ? 1 : 0;
3623 capture = (flagsforhist & 0x1) ? 1 : 0;
3627 pipe_opt[j].ch = 0;
3628 pipe_opt[j].rval = 0;
3629 pipe_opt[j].name = "";
3630 pipe_opt[j++].label = "";
3632 pipe_opt[j].ch = ctrl('W');
3633 pipe_opt[j].rval = 10;
3634 pipe_opt[j].name = "^W";
3635 pipe_opt[j++].label = NULL;
3637 pipe_opt[j].ch = ctrl('Y');
3638 pipe_opt[j].rval = 11;
3639 pipe_opt[j].name = "^Y";
3640 pipe_opt[j++].label = NULL;
3642 pipe_opt[j].ch = KEY_UP;
3643 pipe_opt[j].rval = 30;
3644 pipe_opt[j].name = "";
3645 ku = j;
3646 pipe_opt[j++].label = "";
3648 pipe_opt[j].ch = KEY_DOWN;
3649 pipe_opt[j].rval = 31;
3650 pipe_opt[j].name = "";
3651 pipe_opt[j++].label = "";
3653 pipe_opt[j].ch = -1;
3655 while(1){
3656 int flags;
3658 snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
3659 a->number, capture ? "" : "(Free Output) ");
3660 prompt[sizeof(prompt)-1] = '\0';
3661 pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
3662 pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
3665 * 2 is really 1 because there will be one real entry and
3666 * one entry of "" because of the get_prev_hist above.
3668 if(items_in_hist(history) > 2){
3669 pipe_opt[ku].name = HISTORY_UP_KEYNAME;
3670 pipe_opt[ku].label = HISTORY_KEYLABEL;
3671 pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
3672 pipe_opt[ku+1].label = HISTORY_KEYLABEL;
3674 else{
3675 pipe_opt[ku].name = "";
3676 pipe_opt[ku].label = "";
3677 pipe_opt[ku+1].name = "";
3678 pipe_opt[ku+1].label = "";
3681 flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
3682 rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
3683 sizeof(pipe_command), prompt,
3684 pipe_opt, help, &flags);
3685 if(rc == -1){
3686 q_status_message(SM_ORDER | SM_DING, 3, 4,
3687 "Internal problem encountered");
3688 break;
3690 else if(rc == 10){
3691 raw = !raw; /* flip raw text */
3693 else if(rc == 11){
3694 capture = !capture; /* flip capture output */
3696 else if(rc == 30){
3697 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3698 if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3699 strncpy(pipe_command, p, sizeof(pipe_command));
3700 pipe_command[sizeof(pipe_command)-1] = '\0';
3701 if(history->hist[history->curindex]){
3702 flagsforhist = history->hist[history->curindex]->flags;
3703 raw = (flagsforhist & 0x2) ? 1 : 0;
3704 capture = (flagsforhist & 0x1) ? 1 : 0;
3707 else
3708 Writechar(BELL, 0);
3710 else if(rc == 31){
3711 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3712 if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3713 strncpy(pipe_command, p, sizeof(pipe_command));
3714 pipe_command[sizeof(pipe_command)-1] = '\0';
3715 if(history->hist[history->curindex]){
3716 flagsforhist = history->hist[history->curindex]->flags;
3717 raw = (flagsforhist & 0x2) ? 1 : 0;
3718 capture = (flagsforhist & 0x1) ? 1 : 0;
3721 else
3722 Writechar(BELL, 0);
3724 else if(rc == 0){
3725 if(pipe_command[0] == '\0'){
3726 cmd_cancelled("Pipe command");
3727 break;
3730 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3731 save_hist(history, pipe_command, flagsforhist, NULL);
3733 flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
3734 flags |= (raw ? PIPE_RAW : 0);
3735 if(!capture){
3736 #ifndef _WINDOWS
3737 ClearScreen();
3738 fflush(stdout);
3739 clear_cursor_pos();
3740 ps_global->mangled_screen = 1;
3741 #endif
3742 flags |= PIPE_RESET;
3745 if((syspipe = open_system_pipe(pipe_command,
3746 (flags&PIPE_RESET) ? NULL : &resultfilename,
3747 NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
3748 int is_text = 0;
3749 gf_io_t pc; /* wire up a generic putchar */
3751 is_text = (a && a->body && a->body->type == TYPETEXT);
3753 gf_set_writec(&pc, syspipe, 0L, PipeStar,
3754 (is_text && !raw) ? WRITE_TO_LOCALE : 0);
3756 /*------ Write the image to a temporary file ------*/
3757 if(raw){ /* pipe raw text */
3758 FETCH_READC_S fetch_part;
3760 err = NULL;
3762 if(capture)
3763 we_cancel = busy_cue(NULL, NULL, 1);
3764 else
3765 suspend_busy_cue();
3767 gf_filter_init();
3768 fetch_readc_init(&fetch_part, ps_global->mail_stream,
3769 msgno, a->number, a->body->size.bytes, 0, 0);
3770 gf_link_filter(gf_nvtnl_local, NULL);
3771 err = gf_pipe(FETCH_READC, pc);
3773 if(capture){
3774 if(we_cancel)
3775 cancel_busy_cue(0);
3777 else
3778 resume_busy_cue(0);
3780 else{
3781 /* BUG: there's got to be a better way */
3782 if(!capture)
3783 ps_global->print = (PRINT_S *) 1;
3785 suspend_busy_cue();
3786 err = detach(ps_global->mail_stream, msgno,
3787 a->number, 0L, (long *)NULL, pc, NULL, 0);
3788 ps_global->print = (PRINT_S *) NULL;
3789 resume_busy_cue(0);
3792 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
3794 if(err)
3795 q_status_message1(SM_ORDER | SM_DING, 3, 4,
3796 _("Error detaching for pipe: %s"), err);
3798 display_output_file(resultfilename,
3799 (err)
3800 ? _("PIPE ATTACHMENT (ERROR)")
3801 : _("PIPE ATTACHMENT"),
3802 NULL, DOF_EMPTY);
3804 fs_give((void **) &resultfilename);
3806 else
3807 q_status_message(SM_ORDER | SM_DING, 3, 4,
3808 _("Error opening pipe"));
3810 break;
3812 else if(rc == 1){
3813 cmd_cancelled("Pipe");
3814 break;
3816 else if(rc == 3)
3817 help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
3823 delete_attachment(long int msgno, ATTACH_S *a)
3825 int expbits, rv = 0;
3827 if(!msgno_exceptions(ps_global->mail_stream, msgno,
3828 a->number, &expbits, FALSE)
3829 || !(expbits & MSG_EX_DELETE)){
3830 expbits |= MSG_EX_DELETE;
3831 msgno_exceptions(ps_global->mail_stream, msgno,
3832 a->number, &expbits, TRUE);
3834 q_status_message1(SM_ORDER, 0, 3,
3835 _("Part %s will be omitted only if message is Saved"),
3836 a->number);
3837 rv = 1;
3839 else
3840 q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
3841 a->number);
3843 return(rv);
3848 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
3850 int rv = 0;
3852 if(msgno_exceptions(ps_global->mail_stream, msgno,
3853 a->number, expbitsp, FALSE)
3854 && ((*expbitsp) & MSG_EX_DELETE)){
3855 (*expbitsp) ^= MSG_EX_DELETE;
3856 msgno_exceptions(ps_global->mail_stream, msgno,
3857 a->number, expbitsp, TRUE);
3858 rv = 1;
3860 else
3861 q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
3862 a->number);
3864 return(rv);
3868 /*----------------------------------------------------------------------
3869 Resolve any deferred tests for attachment displayability
3871 Args: attachment structure
3873 Returns: undefer's attachment's displayability test
3874 ----*/
3876 dispatch_attachment(ATTACH_S *a)
3878 if(a->test_deferred){
3879 a->test_deferred = 0;
3880 a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
3883 return(a->can_display);
3887 #ifdef _WINDOWS
3889 scroll_att_popup(sparms, in_handle)
3890 SCROLL_S *sparms;
3891 int in_handle;
3893 MPopup scrat_popup[20];
3894 int i = -1, n;
3896 if(in_handle){
3897 scrat_popup[++i].type = tIndex;
3898 scrat_popup[i].label.style = lNormal;
3899 scrat_popup[i].label.string = "View Selectable Item";
3900 scrat_popup[i].data.val = ctrl('L');
3903 scrat_popup[++i].type = tQueue;
3904 scrat_popup[i].label.style = lNormal;
3905 scrat_popup[i].label.string = "&Save";
3906 scrat_popup[i].data.val = 'S';
3908 scrat_popup[++i].type = tQueue;
3909 scrat_popup[i].label.style = lNormal;
3910 if(msgno_exceptions(ps_global->mail_stream,
3911 mn_m2raw(ps_global->msgmap,
3912 mn_get_cur(ps_global->msgmap)),
3913 scrat_attachp->number, &n, FALSE)
3914 && (n & MSG_EX_DELETE)){
3915 scrat_popup[i].label.string = "&Undelete";
3916 scrat_popup[i].data.val = 'U';
3918 else{
3919 scrat_popup[i].label.string = "&Delete";
3920 scrat_popup[i].data.val = 'D';
3923 if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
3924 scrat_popup[++i].type = tQueue;
3925 scrat_popup[i].label.style = lNormal;
3926 scrat_popup[i].label.string = "&Reply";
3927 scrat_popup[i].data.val = 'R';
3929 scrat_popup[++i].type = tQueue;
3930 scrat_popup[i].label.style = lNormal;
3931 scrat_popup[i].label.string = "&Forward";
3932 scrat_popup[i].data.val = 'f';
3935 scrat_popup[++i].type = tSeparator;
3937 scrat_popup[++i].type = tQueue;
3938 scrat_popup[i].label.style = lNormal;
3939 scrat_popup[i].label.string = "Attachment Index";
3940 scrat_popup[i].data.val = '<';
3942 scrat_popup[++i].type = tTail;
3944 return(mswin_popup(scrat_popup) == 0 && in_handle);
3948 void
3949 display_att_window(a)
3950 ATTACH_S *a;
3952 #if !defined(DOS) && !defined(OS2)
3953 char prefix[8];
3954 #endif
3956 if(a->body->type == TYPEMULTIPART){
3957 if(a->body->subtype){
3958 /* if(!strucmp(a->body->subtype, "digest"))
3959 display_digest_att(msgno, a, flags);
3960 else */
3961 q_status_message1(SM_ORDER, 3, 5,
3962 "Can't display Multipart/%s",
3963 a->body->subtype);
3965 else
3966 q_status_message(SM_ORDER, 3, 5,
3967 "Can't display unknown Multipart Subtype");
3969 /* else if(MIME_VCARD_A(a))
3970 display_vcard_att_window(msgno, a, flags);*/
3971 else if(a->body->type == TYPETEXT)
3972 display_text_att_window(a);
3973 else if(a->body->type == TYPEMESSAGE)
3974 display_msg_att_window(a);
3978 void
3979 display_text_att_window(a)
3980 ATTACH_S *a;
3982 STORE_S *store;
3983 long msgno;
3985 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
3987 if(store = format_text_att(msgno, a, NULL)){
3988 if (mswin_displaytext("ATTACHED TEXT",
3989 so_text(store),
3990 strlen((char *) so_text(store)),
3991 NULL, NULL, 0) >= 0)
3992 store->txt = (void *) NULL; /* free'd in mswin_displaytext */
3994 so_give(&store); /* free resources associated with store */
3996 else
3997 q_status_message(SM_ORDER | SM_DING, 3, 3,
3998 "Error allocating space for attachment.");
4002 void
4003 display_msg_att_window(a)
4004 ATTACH_S *a;
4006 STORE_S *store;
4007 gf_io_t pc;
4008 ATTACH_S *ap = a;
4009 long msgno;
4011 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4013 /* BUG, should check this return code */
4014 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
4016 /* initialize a storage object */
4017 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
4019 gf_set_so_writec(&pc, store);
4021 if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
4022 && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
4023 strlen((char *) so_text(store)),
4024 NULL, NULL, 0) >= 0)
4025 /* free'd in mswin_displaytext */
4026 store->txt = (void *) NULL;
4028 gf_clear_so_writec(store);
4030 so_give(&store);
4032 else
4033 q_status_message(SM_ORDER | SM_DING, 3, 3,
4034 "Error allocating space for message.");
4036 #endif