* new version 2.19.9993
[alpine.git] / alpine / mailpart.c
blob14bd94e1542f522d03560e9410b0480ceeea1d60
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-2014 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 "../pith/state.h"
43 #include "../pith/conf.h"
44 #include "../pith/store.h"
45 #include "../pith/msgno.h"
46 #include "../pith/detach.h"
47 #include "../pith/handle.h"
48 #include "../pith/filter.h"
49 #include "../pith/bitmap.h"
50 #include "../pith/charset.h"
51 #include "../pith/mimedesc.h"
52 #include "../pith/mailcap.h"
53 #include "../pith/newmail.h"
54 #include "../pith/rfc2231.h"
55 #include "../pith/flag.h"
56 #include "../pith/text.h"
57 #include "../pith/editorial.h"
58 #include "../pith/save.h"
59 #include "../pith/pipe.h"
60 #include "../pith/util.h"
61 #include "../pith/detoken.h"
62 #include "../pith/busy.h"
63 #include "../pith/mimetype.h"
64 #include "../pith/icache.h"
65 #include "../pith/list.h"
66 #include "../pith/ablookup.h"
67 #include "../pith/options.h"
68 #include "../pith/smime.h"
72 * Information used to paint and maintain a line on the attachment
73 * screen.
75 typedef struct atdisp_line {
76 char *dstring; /* alloc'd var value string */
77 ATTACH_S *attp; /* actual attachment pointer */
78 struct atdisp_line *next, *prev;
79 } ATDISP_S;
83 * struct defining attachment screen's current state
85 typedef struct att_screen {
86 ATDISP_S *current,
87 *top_line;
88 COLOR_PAIR *titlecolor;
89 } ATT_SCREEN_S;
91 static ATT_SCREEN_S *att_screen;
94 #define next_attline(p) ((p) ? (p)->next : NULL)
95 #define prev_attline(p) ((p) ? (p)->prev : NULL)
98 #ifdef _WINDOWS
100 * local global pointer to scroll attachment popup menu
102 static ATTACH_S *scrat_attachp;
103 #endif
106 #define FETCH_READC g_fr_desc->readc
109 /* used to keep track of detached MIME segments total length */
110 static long save_att_length;
113 * Internal Prototypes
115 ATDISP_S *new_attline(ATDISP_S **);
116 void free_attline(ATDISP_S **);
117 int attachment_screen_updater(struct pine *, ATDISP_S *, ATT_SCREEN_S *);
118 void attachment_screen_redrawer(void);
119 void update_att_screen_titlebar(void);
120 ATDISP_S *first_attline(ATDISP_S *);
121 int init_att_progress(char *, MAILSTREAM *, BODY *);
122 long save_att_piped(int);
123 int save_att_percent(void);
124 void save_attachment(int, long, ATTACH_S *);
125 void export_attachment(int, long, ATTACH_S *);
126 char *write_attached_msg(long, ATTACH_S **, STORE_S *, int);
127 void save_msg_att(long, ATTACH_S *);
128 void save_digest_att(long, ATTACH_S *);
129 int save_msg_att_work(long int, ATTACH_S *, MAILSTREAM *, char *,
130 CONTEXT_S *, char *);
131 void export_msg_att(long, ATTACH_S *);
132 void export_digest_att(long, ATTACH_S *);
133 void print_attachment(int, long, ATTACH_S *);
134 int print_msg_att(long, ATTACH_S *, int);
135 void print_digest_att(long, ATTACH_S *);
136 void run_viewer(char *, BODY *, int);
137 STORE_S *format_text_att(long, ATTACH_S *, HANDLE_S **);
138 int display_text_att(long, ATTACH_S *, int);
139 int display_msg_att(long, ATTACH_S *, int);
140 void display_digest_att(long, ATTACH_S *, int);
141 int scroll_attachment(char *, STORE_S *, SourceType, HANDLE_S *, ATTACH_S *, int);
142 int process_attachment_cmd(int, MSGNO_S *, SCROLL_S *);
143 int format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
144 void display_vcard_att(long, ATTACH_S *, int);
145 void display_attach_info(long, ATTACH_S *);
146 void forward_attachment(MAILSTREAM *, long, ATTACH_S *);
147 void forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
148 void reply_msg_att(MAILSTREAM *, long, ATTACH_S *);
149 void bounce_msg_att(MAILSTREAM *, long, char *, char *);
150 void pipe_attachment(long, ATTACH_S *);
151 int delete_attachment(long, ATTACH_S *);
152 int undelete_attachment(long, ATTACH_S *, int *);
153 #ifdef _WINDOWS
154 int scroll_att_popup(SCROLL_S *, int);
155 void display_text_att_window(ATTACH_S *);
156 void display_msg_att_window(ATTACH_S *);
157 #endif
160 /*----------------------------------------------------------------------
161 Provide attachments in browser for selected action
163 Args: ps -- pointer to pine structure
164 msgmap -- struct containing current message to display
166 Result:
168 Handle painting and navigation of attachment index. It would be nice
169 to someday handle message/rfc822 segments in a neat way (i.e., enable
170 forwarding, take address, etc.).
171 ----*/
172 void
173 attachment_screen(struct pine *ps)
175 UCS ch = 'x';
176 int n, cmd, dline,
177 maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
178 last_type = TYPEOTHER;
179 long msgno;
180 char *q, *last_subtype = NULL, backtag[64], *utf8str;
181 OtherMenu what;
182 ATTACH_S *a;
183 ATDISP_S *current = NULL, *ctmp = NULL;
184 ATT_SCREEN_S screen;
186 ps->prev_screen = attachment_screen;
187 ps->next_screen = SCREEN_FUN_NULL;
189 ps->some_quoting_was_suppressed = 0;
191 if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
192 q_status_message(SM_ORDER | SM_DING, 0, 3,
193 _("Screen too small to view attachment"));
194 ps->next_screen = mail_view_screen;
195 return;
198 if(mn_total_cur(ps->msgmap) > 1L){
199 q_status_message(SM_ORDER | SM_DING, 0, 3,
200 _("Can only view one message's attachments at a time."));
201 return;
203 else if(ps->atmts && ps->atmts->description && !(ps->atmts + 1)->description)
204 q_status_message1(SM_ASYNC, 0, 3,
205 _("Message %s has only one part (the message body), and no attachments."),
206 long2string(mn_get_cur(ps->msgmap)));
208 /*----- Figure max display widths -----*/
209 for(a = ps->atmts; a && a->description != NULL; a++){
210 if((n = utf8_width(a->number)) > maxnumwid)
211 maxnumwid = n;
213 if((n = utf8_width(a->size)) > maxsizewid)
214 maxsizewid = n;
218 * Then, allocate and initialize attachment line list...
220 for(a = ps->atmts; a && a->description; a++)
221 new_attline(&current)->attp = a;
223 memset(&screen, 0, sizeof(screen));
224 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
225 ps->mangled_screen = 1; /* build display */
226 ps->redrawer = attachment_screen_redrawer;
227 att_screen = &screen;
228 current = first_attline(current);
229 what = FirstMenu;
232 * Advance to next attachment if it's likely that we've already
233 * shown it to the user...
236 if (current == NULL){
237 q_status_message1(SM_ORDER | SM_DING, 0, 3,
238 _("Malformed message: %s"),
239 ps->c_client_error ? ps->c_client_error : "?");
240 ps->next_screen = mail_view_screen;
242 else if(current->attp->shown && (ctmp = next_attline(current)))
243 current = ctmp;
245 while(ps->next_screen == SCREEN_FUN_NULL){
246 ps->user_says_cancel = 0;
247 if(km_popped){
248 km_popped--;
249 if(km_popped == 0){
250 clearfooter(ps);
251 ps->mangled_body = 1;
255 if(ps->mangled_screen){
257 * build/rebuild display lines
259 if(old_cols != ps->ttyo->screen_cols){
260 int avail, s1, s2, s4, s5, dwid, sizewid, descwid;
262 avail = ps_global->ttyo->screen_cols;
264 s1 = MAX(MIN(1, avail), 0);
265 avail -= s1;
267 dwid = MAX(MIN(1, avail), 0);
268 avail -= dwid;
270 s2 = MAX(MIN(1, avail), 0);
271 avail -= s2;
273 /* Only give up a third of the display to part numbers */
274 maxnumwid = MIN(maxnumwid, (ps_global->ttyo->screen_cols/3));
275 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
276 avail -= maxnumwid;
278 s4 = MAX(MIN(3, avail), 0);
279 avail -= s4;
281 sizewid = MAX(MIN(maxsizewid, avail), 0);
282 avail -= sizewid;
284 s5 = MAX(MIN(3, avail), 0);
285 avail -= s5;
287 descwid = MAX(0, avail);
289 old_cols = ps->ttyo->screen_cols;
291 for(ctmp = first_attline(current);
292 ctmp && (a = ctmp->attp) && a->description;
293 ctmp = next_attline(ctmp)){
294 size_t len;
295 char numbuf[50];
296 char description[1000];
298 if(ctmp->dstring)
299 fs_give((void **)&ctmp->dstring);
301 len = 6 * MAX(80, ps->ttyo->screen_cols) * sizeof(char);
302 ctmp->dstring = (char *)fs_get(len + 1);
305 * description
307 q = a->body->description;
308 if(!(q && q[0]) && (MIME_MSG_A(a) && a->body->nested.msg->env))
309 q = a->body->nested.msg->env->subject;
311 if(q && q[0]){
312 char buftmp[1000];
314 strncpy(buftmp, q, sizeof(buftmp));
315 buftmp[sizeof(buftmp)-1] = '\0';
317 q = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
318 SIZEOF_20KBUF, buftmp);
321 if(q && !q[0])
322 q = NULL;
324 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 ? "\"" : "");
325 description[sizeof(description)-1] = '\0';
327 utf8_snprintf(ctmp->dstring, len+1,
328 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%-*.*w",
329 s1, s1, "",
330 dwid, dwid,
331 msgno_part_deleted(ps->mail_stream, msgno, a->number) ? "D" : "",
332 s2, s2, "",
333 maxnumwid, maxnumwid,
334 a->number
335 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
336 : "",
337 s4, s4, "",
338 sizewid, sizewid,
339 a->size ? a->size : "",
340 s5, s5, "",
341 descwid, descwid, description);
343 ctmp->dstring[len] = '\0';
347 ps->mangled_header = 1;
348 ps->mangled_footer = 1;
349 ps->mangled_body = 1;
352 /*----------- Check for new mail -----------*/
353 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
354 ps->mangled_header = 1;
357 * If an expunge of the current message happened during the
358 * new mail check we want to bail out of here. See mm_expunged.
360 if(ps->next_screen != SCREEN_FUN_NULL)
361 break;
363 if(ps->mangled_header){
364 update_att_screen_titlebar();
365 ps->mangled_header = 0;
368 if(ps->mangled_screen){
369 ClearLine(1);
370 ps->mangled_screen = 0;
373 dline = attachment_screen_updater(ps, current, &screen);
375 /*---- This displays new mail notification, or errors ---*/
376 if(km_popped){
377 FOOTER_ROWS(ps) = 3;
378 mark_status_unknown();
381 display_message(ch);
382 if(km_popped){
383 FOOTER_ROWS(ps) = 1;
384 mark_status_unknown();
387 if(ps->mangled_footer
388 || current->attp->body->type != last_type
389 || !(last_subtype && current->attp->body->subtype)
390 || strucmp(last_subtype, current->attp->body->subtype)){
392 struct key_menu *km = &att_index_keymenu;
393 bitmap_t bitmap;
395 setbitmap(bitmap);
396 ps->mangled_footer = 0;
397 last_type = current->attp->body->type;
398 last_subtype = current->attp->body->subtype;
400 snprintf(backtag, sizeof(backtag), "Msg #%ld", mn_get_cur(ps->msgmap));
401 backtag[sizeof(backtag)-1] = '\0';
402 km->keys[ATT_PARENT_KEY].label = backtag;
404 if(F_OFF(F_ENABLE_PIPE, ps))
405 clrbitn(ATT_PIPE_KEY, bitmap);
408 * If message or digest, leave Reply and Save and,
409 * conditionally, Bounce on...
411 if(MIME_MSG(last_type, last_subtype)){
412 if(F_OFF(F_ENABLE_BOUNCE, ps))
413 clrbitn(ATT_BOUNCE_KEY, bitmap);
415 km->keys[ATT_EXPORT_KEY].name = "";
416 km->keys[ATT_EXPORT_KEY].label = "";
418 else if(MIME_DGST(last_type, last_subtype)){
419 clrbitn(ATT_BOUNCE_KEY, bitmap);
420 clrbitn(ATT_REPLY_KEY, bitmap);
422 km->keys[ATT_EXPORT_KEY].name = "";
423 km->keys[ATT_EXPORT_KEY].label = "";
425 else{
426 clrbitn(ATT_BOUNCE_KEY, bitmap);
427 clrbitn(ATT_REPLY_KEY, bitmap);
429 if(last_type != TYPETEXT)
430 clrbitn(ATT_PRINT_KEY, bitmap);
432 km->keys[ATT_EXPORT_KEY].name = "E";
433 km->keys[ATT_EXPORT_KEY].label = N_("Export");
436 if(km_popped){
437 FOOTER_ROWS(ps) = 3;
438 clearfooter(ps);
441 if(F_ON(F_ARROW_NAV, ps)){
442 menu_add_binding(km, KEY_LEFT, MC_EXIT);
443 menu_add_binding(km, KEY_RIGHT, MC_VIEW_ATCH);
445 else{
446 menu_clear_binding(km, KEY_LEFT);
447 menu_clear_binding(km, KEY_RIGHT);
450 draw_keymenu(km, bitmap, ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps),
451 0, what);
452 what = SameMenu;
453 if(km_popped){
454 FOOTER_ROWS(ps) = 1;
455 mark_keymenu_dirty();
459 if(F_ON(F_SHOW_CURSOR, ps))
460 MoveCursor(dline, 0);
461 else
462 MoveCursor(MAX(0, ps->ttyo->screen_rows - FOOTER_ROWS(ps)), 0);
464 /*------ Prepare to read the command from the keyboard ----*/
465 #ifdef MOUSE
466 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
467 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
468 ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
469 ps_global->ttyo->screen_cols);
470 #endif
471 ch = READ_COMMAND(&utf8str);
472 #ifdef MOUSE
473 clear_mfunc(mouse_in_content);
474 #endif
476 cmd = menu_command(ch, &att_index_keymenu);
478 if(km_popped)
479 switch(cmd){
480 case MC_NONE :
481 case MC_OTHER :
482 case MC_RESIZE :
483 case MC_REPAINT :
484 km_popped++;
485 break;
487 default:
488 clearfooter(ps);
489 break;
492 switch(cmd){
493 case MC_HELP :
494 if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
495 km_popped = 2;
496 ps->mangled_footer = 1;
497 break;
500 helper(h_attachment_screen, _("HELP FOR ATTACHMENT INDEX"), 0);
501 ps->mangled_screen = 1;
502 break;
504 case MC_OTHER :
505 what = NextMenu;
506 ps->mangled_footer = 1;
507 break;
509 case MC_FULLHDR :
510 ps->full_header++;
511 if(ps->full_header == 1){
512 if(!(ps->quote_suppression_threshold
513 && (ps->some_quoting_was_suppressed /* || in_index != View */)))
514 ps->full_header++;
516 else if(ps->full_header > 2)
517 ps->full_header = 0;
519 switch(ps->full_header){
520 case 0:
521 q_status_message(SM_ORDER, 0, 3,
522 _("Display of full headers is now off."));
523 break;
525 case 1:
526 q_status_message1(SM_ORDER, 0, 3,
527 _("Quotes displayed, use %s to see full headers"),
528 F_ON(F_USE_FK, ps) ? "F9" : "H");
529 break;
531 case 2:
532 q_status_message(SM_ORDER, 0, 3,
533 _("Display of full headers is now on."));
534 break;
538 break;
540 case MC_EXIT : /* exit attachment screen */
541 ps->next_screen = mail_view_screen;
542 break;
544 case MC_QUIT :
545 ps->next_screen = quit_screen;
546 break;
548 case MC_MAIN :
549 ps->next_screen = main_menu_screen;
550 break;
552 case MC_INDEX :
553 ps->next_screen = mail_index_screen;
554 break;
556 case MC_NEXTITEM :
557 if((ctmp = next_attline(current)) != NULL)
558 current = ctmp;
559 else
560 q_status_message(SM_ORDER, 0, 1, _("Already on last attachment"));
562 break;
564 case MC_PREVITEM :
565 if((ctmp = prev_attline(current)) != NULL)
566 current = ctmp;
567 else
568 q_status_message(SM_ORDER, 0, 1, _("Already on first attachment"));
570 break;
572 case MC_PAGEDN : /* page forward */
573 if(next_attline(current)){
574 while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
575 if((ctmp = next_attline(current)) != NULL)
576 current = ctmp;
577 else
578 break;
580 else
581 q_status_message(SM_ORDER, 0, 1,
582 _("Already on last page of attachments"));
584 break;
586 case MC_PAGEUP : /* page backward */
587 if(prev_attline(current)){
588 while(dline-- > HEADER_ROWS(ps))
589 if((ctmp = prev_attline(current)) != NULL)
590 current = ctmp;
591 else
592 break;
594 while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
595 if((ctmp = prev_attline(current)) != NULL)
596 current = ctmp;
597 else
598 break;
600 else
601 q_status_message(SM_ORDER, 0, 1,
602 _("Already on first page of attachments"));
604 break;
606 #ifdef MOUSE
607 case MC_MOUSE:
609 MOUSEPRESS mp;
611 mouse_get_last (NULL, &mp);
612 mp.row -= HEADER_ROWS(ps);
613 ctmp = screen.top_line;
614 while (mp.row && ctmp != NULL) {
615 --mp.row;
616 ctmp = ctmp->next;
619 if (ctmp != NULL){
620 current = ctmp;
622 if (mp.doubleclick){
623 if(mp.button == M_BUTTON_LEFT)
624 display_attachment(msgno, current->attp, DA_SAVE);
626 #ifdef _WINDOWS
627 else if(mp.button == M_BUTTON_RIGHT){
628 MPopup atmt_popup[20];
629 int i = -1, exoffer = 0;
631 dline = attachment_screen_updater(ps,current,&screen);
633 if(dispatch_attachment(current->attp) != MCD_NONE){
634 atmt_popup[++i].type = tQueue;
635 atmt_popup[i].data.val = 'V';
636 atmt_popup[i].label.style = lNormal;
637 atmt_popup[i].label.string = "&View";
639 if(!(current->attp->can_display & MCD_EXTERNAL)
640 && (current->attp->body->type == TYPETEXT
641 || MIME_MSG_A(current->attp)
642 || MIME_DGST_A(current->attp))){
643 exoffer++;
644 atmt_popup[++i].type = tIndex;
645 atmt_popup[i].label.style = lNormal;
646 atmt_popup[i].label.string =
647 "View in New Window";
651 atmt_popup[++i].type = tQueue;
652 atmt_popup[i].data.val = 'S';
653 atmt_popup[i].label.style = lNormal;
654 atmt_popup[i].label.string = "&Save";
656 atmt_popup[++i].type = tQueue;
657 atmt_popup[i].label.style = lNormal;
658 if(current->dstring[1] == 'D'){
659 atmt_popup[i].data.val = 'U';
660 atmt_popup[i].label.string = "&Undelete";
662 else{
663 atmt_popup[i].data.val = 'D';
664 atmt_popup[i].label.string = "&Delete";
667 if(MIME_MSG_A(current->attp)){
668 atmt_popup[++i].type = tQueue;
669 atmt_popup[i].label.style = lNormal;
670 atmt_popup[i].label.string = "&Reply...";
671 atmt_popup[i].data.val = 'R';
673 atmt_popup[++i].type = tQueue;
674 atmt_popup[i].label.style = lNormal;
675 atmt_popup[i].label.string = "&Forward...";
676 atmt_popup[i].data.val = 'F';
679 atmt_popup[++i].type = tQueue;
680 atmt_popup[i].label.style = lNormal;
681 atmt_popup[i].label.string = "&About...";
682 atmt_popup[i].data.val = 'A';
684 atmt_popup[++i].type = tSeparator;
686 atmt_popup[++i].type = tQueue;
687 snprintf(backtag, sizeof(backtag), "View Message #%ld",
688 mn_get_cur(ps->msgmap));
689 backtag[sizeof(backtag)-1] = '\0';
690 atmt_popup[i].label.string = backtag;
691 atmt_popup[i].label.style = lNormal;
692 atmt_popup[i].data.val = '<';
694 atmt_popup[++i].type = tTail;
696 if(mswin_popup(atmt_popup) == 1 && exoffer)
697 display_att_window(current->attp);
700 else if(mp.button == M_BUTTON_RIGHT){
701 MPopup atmt_popup[2];
703 atmt_popup[0].type = tQueue;
704 snprintf(backtag, sizeof(backtag), "View Message #%ld",
705 mn_get_cur(ps->msgmap));
706 backtag[sizeof(backtag)-1] = '\0';
707 atmt_popup[0].label.string = backtag;
708 atmt_popup[0].label.style = lNormal;
709 atmt_popup[0].data.val = '<';
711 atmt_popup[1].type = tTail;
713 mswin_popup(atmt_popup);
714 #endif
717 break;
718 #endif
720 case MC_WHEREIS : /* whereis */
721 /*--- get string ---*/
722 {int rc, found = 0;
723 char *result = NULL, buf[64];
724 static char last[64], tmp[64];
725 HelpType help;
727 ps->mangled_footer = 1;
728 buf[0] = '\0';
729 snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ",
730 (last[0]) ? "[" : "",
731 (last[0]) ? last : "",
732 (last[0]) ? "]" : "");
733 tmp[sizeof(tmp)-1] = '\0';
734 help = NO_HELP;
735 while(1){
736 int flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
738 rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf),
739 tmp,NULL,help,&flags);
740 if(rc == 3)
741 help = help == NO_HELP ? h_attach_index_whereis : NO_HELP;
742 else if(rc == 0 || rc == 1 || !buf[0]){
743 if(rc == 0 && !buf[0] && last[0]){
744 strncpy(buf, last, sizeof(buf));
745 buf[sizeof(buf)-1] = '\0';
748 break;
752 if(rc == 0 && buf[0]){
753 ctmp = current;
754 while((ctmp = next_attline(ctmp)) != NULL)
755 if(srchstr(ctmp->dstring, buf)){
756 found++;
757 break;
760 if(!found){
761 ctmp = first_attline(current);
763 while(ctmp != current)
764 if(srchstr(ctmp->dstring, buf)){
765 found++;
766 break;
768 else
769 ctmp = next_attline(ctmp);
772 else
773 result = _("WhereIs cancelled");
775 if(found && ctmp){
776 strncpy(last, buf, sizeof(last));
777 last[sizeof(last)-1] = '\0';
778 result = "Word found";
779 current = ctmp;
782 q_status_message(SM_ORDER, 0, 3,
783 result ? result : _("Word not found"));
786 break;
788 case MC_DELETE :
789 if(delete_attachment(msgno, current->attp)){
790 int l = current ? strlen(current->attp->number) : 0;
792 current->dstring[1] = 'D';
794 /* Also indicate any children that will be deleted */
796 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
797 if(!strncmp(ctmp->attp->number, current->attp->number, l)
798 && ctmp->attp->number[l] == '.'){
799 ctmp->dstring[1] = 'D';
800 ps->mangled_screen = 1;
804 break;
806 case MC_UNDELETE :
807 if(undelete_attachment(msgno, current->attp, &expbits)){
808 int l = current ? strlen(current->attp->number) : 0;
810 current->dstring[1] = ' ';
812 /* And unflag anything implicitly undeleted */
813 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
814 if(!strncmp(ctmp->attp->number, current->attp->number, l)
815 && ctmp->attp->number[l] == '.'
816 && (!msgno_exceptions(ps->mail_stream, msgno,
817 ctmp->attp->number, &expbits, FALSE)
818 || !(expbits & MSG_EX_DELETE))){
819 ctmp->dstring[1] = ' ';
820 ps->mangled_screen = 1;
824 break;
826 case MC_REPLY :
827 reply_msg_att(ps->mail_stream, msgno, current->attp);
828 break;
830 case MC_FORWARD :
831 forward_attachment(ps->mail_stream, msgno, current->attp);
832 break;
834 case MC_BOUNCE :
835 bounce_msg_att(ps->mail_stream, msgno, current->attp->number,
836 current->attp->body->nested.msg->env->subject);
837 ps->mangled_footer = 1;
838 break;
840 case MC_ABOUTATCH :
841 display_attach_info(msgno, current->attp);
842 break;
844 case MC_VIEW_ATCH : /* View command */
845 display_attachment(msgno, current->attp, DA_SAVE);
846 old_cols = -1;
847 /* fall thru to repaint */
849 case MC_REPAINT : /* redraw */
850 case MC_RESIZE :
851 ps->mangled_screen = 1;
852 break;
854 case MC_EXPORT :
855 export_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
856 ps->mangled_footer = 1;
857 break;
859 case MC_SAVETEXT : /* Save command */
860 save_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
861 ps->mangled_footer = 1;
862 break;
864 case MC_PRINTMSG : /* Save command */
865 print_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
866 ps->mangled_footer = 1;
867 break;
869 case MC_PIPE : /* Pipe command */
870 if(F_ON(F_ENABLE_PIPE, ps)){
871 pipe_attachment(msgno, current->attp);
872 ps->mangled_footer = 1;
873 break;
874 } /* fall thru to complain */
876 case MC_NONE: /* simple timeout */
877 break;
879 case MC_UTF8:
880 bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
881 break;
883 case MC_CHARUP :
884 case MC_CHARDOWN :
885 case MC_CHARRIGHT :
886 case MC_CHARLEFT :
887 case MC_GOTOBOL :
888 case MC_GOTOEOL :
889 case MC_UNKNOWN :
890 default:
891 bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
896 for(current = first_attline(current); current;){ /* clean up */
897 ctmp = current->next;
898 free_attline(&current);
899 current = ctmp;
902 if(screen.titlecolor)
903 free_color_pair(&screen.titlecolor);
907 /*----------------------------------------------------------------------
908 allocate and attach a fresh attachment line struct
910 Args: current -- display line to attache new struct to
912 Returns: newly alloc'd attachment display line
913 ----*/
914 ATDISP_S *
915 new_attline(ATDISP_S **current)
917 ATDISP_S *p;
919 p = (ATDISP_S *)fs_get(sizeof(ATDISP_S));
920 memset((void *)p, 0, sizeof(ATDISP_S));
921 if(current){
922 if(*current){
923 p->next = (*current)->next;
924 (*current)->next = p;
925 p->prev = *current;
926 if(p->next)
927 p->next->prev = p;
930 *current = p;
933 return(p);
937 /*----------------------------------------------------------------------
938 Release system resources associated with attachment display line
940 Args: p -- line to free
942 Result:
943 ----*/
944 void
945 free_attline(ATDISP_S **p)
947 if(p){
948 if((*p)->dstring)
949 fs_give((void **)&(*p)->dstring);
951 fs_give((void **)p);
956 /*----------------------------------------------------------------------
957 Manage display of the attachment screen menu body.
959 Args: ps -- pine struct pointer
960 current -- currently selected display line
961 screen -- reference points for display painting
963 Result:
966 attachment_screen_updater(struct pine *ps, ATDISP_S *current, ATT_SCREEN_S *screen)
968 int dline, return_line = HEADER_ROWS(ps);
969 ATDISP_S *top_line, *ctmp;
971 /* calculate top line of display */
972 dline = 0;
973 ctmp = top_line = first_attline(current);
975 if(((dline++)%(ps->ttyo->screen_rows-HEADER_ROWS(ps)-FOOTER_ROWS(ps)))==0)
976 top_line = ctmp;
977 while(ctmp != current && (ctmp = next_attline(ctmp)));
979 #ifdef _WINDOWS
980 /* Don't know how to manage scroll bar for attachment screen yet. */
981 scroll_setrange (0L, 0L);
982 #endif
984 /* mangled body or new page, force redraw */
985 if(ps->mangled_body || screen->top_line != top_line)
986 screen->current = NULL;
988 /* loop thru painting what's needed */
989 for(dline = 0, ctmp = top_line;
990 dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
991 dline++, ctmp = next_attline(ctmp)){
994 * only fall thru painting if something needs painting...
996 if(!(!screen->current || ctmp == screen->current || ctmp == current))
997 continue;
999 if(ctmp && ctmp->dstring){
1000 char *p = tmp_20k_buf;
1001 int i, col, x = 0, totlen;
1003 if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
1004 x = 2;
1005 if(ctmp == current){
1006 return_line = dline + HEADER_ROWS(ps);
1007 PutLine0(dline + HEADER_ROWS(ps), 0, "->");
1009 else
1010 PutLine0(dline + HEADER_ROWS(ps), 0, " ");
1013 * Only paint lines if we have to...
1015 if(screen->current)
1016 continue;
1018 else if(ctmp == current){
1019 return_line = dline + HEADER_ROWS(ps);
1020 StartInverse();
1023 totlen = strlen(ctmp->dstring);
1026 * Copy the value to a temp buffer expanding tabs.
1027 * Assume the caller set the widths so as not to overflow the
1028 * right margin.
1030 for(i=0,col=x; ctmp->dstring[i]; i++){
1031 if(ctmp->dstring[i] == TAB){
1032 if((p-tmp_20k_buf) < SIZEOF_20KBUF-8)
1034 *p++ = ' ';
1035 while((++col)&0x07);
1037 else{
1038 col += width_at_this_position((unsigned char *) &ctmp->dstring[i], totlen-i);
1039 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1040 *p++ = ctmp->dstring[i];
1045 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1046 *p = '\0';
1048 PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
1050 if(ctmp == current
1051 && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
1052 EndInverse();
1054 else
1055 ClearLine(dline + HEADER_ROWS(ps));
1058 ps->mangled_body = 0;
1059 screen->top_line = top_line;
1060 screen->current = current;
1061 return(return_line);
1065 /*----------------------------------------------------------------------
1066 Redraw the attachment screen based on the global "att_screen" struct
1068 Args: none
1070 Result:
1071 ----*/
1072 void
1073 attachment_screen_redrawer(void)
1075 bitmap_t bitmap;
1077 update_att_screen_titlebar();
1078 ps_global->mangled_header = 0;
1079 ClearLine(1);
1081 ps_global->mangled_body = 1;
1082 (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
1084 setbitmap(bitmap);
1085 draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
1086 1-FOOTER_ROWS(ps_global), 0, SameMenu);
1090 void
1091 update_att_screen_titlebar(void)
1093 long raw_msgno;
1094 COLOR_PAIR *returned_color = NULL;
1095 COLOR_PAIR *titlecolor = NULL;
1096 int colormatch;
1097 SEARCHSET *ss = NULL;
1098 PAT_STATE *pstate = NULL;
1100 if(ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
1101 raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
1102 ss = mail_newsearchset();
1103 ss->first = ss->last = (unsigned long) raw_msgno;
1105 if(ss){
1106 colormatch = get_index_line_color(ps_global->mail_stream,
1107 ss, &pstate, &returned_color);
1108 mail_free_searchset(&ss);
1111 * This is a bit tricky. If there is a colormatch but
1112 * returned_color
1113 * is NULL, that means that the user explicitly wanted the
1114 * Normal color used in this index line, so that is what we
1115 * use. If no colormatch then we will use the TITLE color
1116 * instead of Normal.
1118 if(colormatch){
1119 if(returned_color)
1120 titlecolor = returned_color;
1121 else
1122 titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
1123 ps_global->VAR_NORM_BACK_COLOR);
1126 if(titlecolor
1127 && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
1128 char cbuf[MAXCOLORLEN+1];
1130 strncpy(cbuf, titlecolor->fg, sizeof(cbuf));
1131 cbuf[sizeof(cbuf)-1] = '\0';
1132 strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
1133 titlecolor->fg[MAXCOLORLEN] = '\0';
1134 strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
1135 titlecolor->bg[MAXCOLORLEN] = '\0';
1139 /* Did the color change? */
1140 if((!titlecolor && att_screen->titlecolor)
1142 (titlecolor && !att_screen->titlecolor)
1144 (titlecolor && att_screen->titlecolor
1145 && (strcmp(titlecolor->fg, att_screen->titlecolor->fg)
1146 || strcmp(titlecolor->bg, att_screen->titlecolor->bg)))){
1148 if(att_screen->titlecolor)
1149 free_color_pair(&att_screen->titlecolor);
1151 att_screen->titlecolor = titlecolor;
1152 titlecolor = NULL;
1155 if(titlecolor)
1156 free_color_pair(&titlecolor);
1159 set_titlebar(_("ATTACHMENT INDEX"), ps_global->mail_stream,
1160 ps_global->context_current, ps_global->cur_folder,
1161 ps_global->msgmap, 1, MessageNumber, 0, 0,
1162 att_screen->titlecolor);
1166 /*----------------------------------------------------------------------
1167 Seek back from the given display line to the beginning of the list
1169 Args: p -- display linked list
1171 Result:
1172 ----*/
1173 ATDISP_S *
1174 first_attline(ATDISP_S *p)
1176 while(p && p->prev)
1177 p = p->prev;
1179 return(p);
1184 init_att_progress(char *msg, MAILSTREAM *stream, struct mail_bodystruct *body)
1186 if((save_att_length = body->size.bytes) != 0){
1187 /* if there are display filters, factor in extra copy */
1188 if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
1189 save_att_length += body->size.bytes;
1191 /* if remote folder and segment not cached, factor in IMAP fetch */
1192 if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
1193 && !((body->type == TYPETEXT && body->contents.text.data)
1194 || ((body->type == TYPEMESSAGE)
1195 && body->nested.msg && body->nested.msg->text.text.data)
1196 || body->contents.text.data))
1197 save_att_length += body->size.bytes;
1199 gf_filter_init(); /* reset counters */
1200 pine_gets_bytes(1);
1201 save_att_piped(1);
1202 return(busy_cue(msg, save_att_percent, 0));
1205 return(0);
1209 long
1210 save_att_piped(int reset)
1212 static long x;
1213 long y;
1215 if(reset){
1216 x = y = 0L;
1218 else if((y = gf_bytes_piped()) >= x){
1219 x = y;
1220 y = 0;
1223 return(x + y);
1228 save_att_percent(void)
1230 int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
1231 / save_att_length);
1232 return(MIN(i, 100));
1236 /*----------------------------------------------------------------------
1237 Save the given attachment associated with the given message no
1239 Args: ps
1241 Result:
1242 ----*/
1243 void
1244 save_attachment(int qline, long int msgno, ATTACH_S *a)
1246 if(ps_global->restricted){
1247 q_status_message(SM_ORDER | SM_DING, 0, 4,
1248 "Alpine demo can't save attachments");
1249 return;
1252 if(MIME_MSG_A(a))
1253 save_msg_att(msgno, a);
1254 else if(MIME_DGST_A(a))
1255 save_digest_att(msgno, a);
1256 else if(MIME_VCARD_A(a))
1257 save_vcard_att(ps_global, qline, msgno, a);
1258 else
1259 write_attachment(qline, msgno, a, "SAVE");
1263 /*----------------------------------------------------------------------
1264 Save the given attachment associated with the given message no
1266 Args: ps
1268 Result:
1269 ----*/
1270 void
1271 export_attachment(int qline, long int msgno, ATTACH_S *a)
1273 if(ps_global->restricted){
1274 q_status_message(SM_ORDER | SM_DING, 0, 4,
1275 "Alpine demo can't export attachments");
1276 return;
1279 if(MIME_MSG_A(a))
1280 export_msg_att(msgno, a);
1281 else if(MIME_DGST_A(a))
1282 export_digest_att(msgno, a);
1283 else
1284 q_status_message1(SM_ORDER, 0, 3,
1285 _("Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."),
1286 body_type_names(a->body->type));
1290 void
1291 write_attachment(int qline, long int msgno, ATTACH_S *a, char *method)
1293 char filename[MAXPATH+1], full_filename[MAXPATH+1],
1294 title_buf[64], *err;
1295 int r, rflags = GER_NONE, we_cancel = 0;
1296 static HISTORY_S *history = NULL;
1297 static ESCKEY_S att_save_opts[] = {
1298 {ctrl('T'), 10, "^T", N_("To Files")},
1299 {-1, 0, NULL, NULL},
1300 {-1, 0, NULL, NULL},
1301 {-1, 0, NULL, NULL}};
1303 /*------- Figure out suggested file name ----*/
1304 filename[0] = full_filename[0] = '\0';
1305 (void) get_filename_parameter(filename, sizeof(filename), a->body, NULL);
1307 dprint((9, "export_attachment(name: %s)\n",
1308 filename ? filename : "?"));
1310 r = 0;
1311 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1312 if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
1313 att_save_opts[++r].ch = ctrl('V');
1314 att_save_opts[r].rval = 12;
1315 att_save_opts[r].name = "^V";
1316 att_save_opts[r].label = N_("Downld Msg");
1318 #endif /* !(DOS || MAC) */
1320 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1321 att_save_opts[++r].ch = ctrl('I');
1322 att_save_opts[r].rval = 11;
1323 att_save_opts[r].name = "TAB";
1324 att_save_opts[r].label = N_("Complete");
1327 att_save_opts[++r].ch = -1;
1329 snprintf(title_buf, sizeof(title_buf), "%s ATTACHMENT", method);
1330 title_buf[sizeof(title_buf)-1] = '\0';
1332 r = get_export_filename(ps_global, filename, NULL, full_filename,
1333 sizeof(filename), "attachment", title_buf,
1334 att_save_opts, &rflags, qline, GE_SEQ_SENSITIVE, &history);
1336 if(r < 0){
1337 switch(r){
1338 case -1:
1339 cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
1340 break;
1342 case -2:
1343 q_status_message1(SM_ORDER, 0, 2,
1344 _("Can't save to file outside of %s"),
1345 ps_global->VAR_OPER_DIR);
1346 break;
1349 return;
1351 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1352 else if(r == 12){ /* Download */
1353 char cmd[MAXPATH], *tfp = NULL;
1354 PIPE_S *syspipe;
1355 gf_io_t pc;
1356 long len;
1357 STORE_S *store;
1358 char prompt_buf[256];
1360 if(ps_global->restricted){
1361 q_status_message(SM_ORDER | SM_DING, 3, 3,
1362 "Download disallowed in restricted mode");
1363 return;
1366 err = NULL;
1367 tfp = temp_nam(NULL, "pd");
1368 dprint((1, "Download attachment called!\n"));
1369 if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
1371 snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
1372 prompt_buf[sizeof(prompt_buf)-1] = '\0';
1373 we_cancel = init_att_progress(prompt_buf,
1374 ps_global->mail_stream,
1375 a->body);
1377 gf_set_so_writec(&pc, store);
1378 if((err = detach(ps_global->mail_stream, msgno,
1379 a->number, 0L, &len, pc, NULL, 0)) != NULL)
1380 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1381 "%s: Error writing attachment to \"%s\"",
1382 err, tfp);
1384 /* cancel regardless, so it doesn't get in way of xfer */
1385 cancel_busy_cue(0);
1387 gf_clear_so_writec(store);
1388 if(so_give(&store)) /* close file */
1389 err = "Error writing tempfile for download";
1391 if(!err){
1392 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
1393 ps_global->VAR_DOWNLOAD_CMD, tfp);
1394 if((syspipe = open_system_pipe(cmd, NULL, NULL,
1395 PIPE_USER | PIPE_RESET,
1396 0, pipe_callback, pipe_report_error)) != NULL)
1397 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1398 else
1399 q_status_message(SM_ORDER | SM_DING, 3, 3,
1400 err = "Error running download command");
1403 else
1404 q_status_message(SM_ORDER | SM_DING, 3, 3,
1405 err = "Error building temp file for download");
1407 if(tfp){
1408 our_unlink(tfp);
1409 fs_give((void **)&tfp);
1412 if(!err)
1413 q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
1414 a->number);
1416 return;
1418 #endif /* !(DOS || MAC) */
1420 (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
1421 rflags, full_filename);
1426 * Args stream --
1427 * msgno -- raw message number
1428 * a -- attachment struct
1429 * flags -- comes from get_export_filename
1430 * GER_OVER -- the file was truncated before we wrote
1431 * GER_APPEND -- the file was not truncated prior to our writing,
1432 * so we were appending
1433 * else -- the file didn't previously exist
1434 * file -- the full pathname of the file
1436 * Returns 1 for successful write, 0 for nothing to write, -1 for error
1439 write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
1441 char *l_string, sbuf[256], *err;
1442 int is_text, we_cancel = 0;
1443 long len, orig_size;
1444 gf_io_t pc;
1445 STORE_S *store;
1447 if(!(a && a->body && a->number && a->number[0] && file && file[0]
1448 && stream))
1449 return 0;
1451 is_text = (a && a->body && a->body->type == TYPETEXT);
1453 if(flags & GER_APPEND)
1454 orig_size = name_file_size(file);
1456 store = so_get(FileStar, file, WRITE_ACCESS | (is_text ? WRITE_TO_LOCALE : 0));
1457 if(store == NULL){
1458 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1459 /* TRANSLATORS: Error opening destination <filename>: <error text> */
1460 _("Error opening destination %s: %s"),
1461 file, error_description(errno));
1462 return -1;
1465 snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
1466 sbuf[sizeof(sbuf)-1] = '\0';
1467 we_cancel = init_att_progress(sbuf, stream, a->body);
1469 gf_set_so_writec(&pc, store);
1470 err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, 0);
1471 gf_clear_so_writec(store);
1473 if(we_cancel)
1474 cancel_busy_cue(0);
1476 if(so_give(&store)) /* close file */
1477 err = error_description(errno);
1479 if(err){
1480 if(!(flags & (GER_APPEND | GER_OVER)))
1481 our_unlink(file);
1482 else
1483 our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
1485 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1486 /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
1487 _("%s: Error writing attachment to \"%s\""),
1488 err, file);
1489 return -1;
1491 else{
1492 l_string = cpystr(byte_string(len));
1493 q_status_message8(SM_ORDER, 0, 4,
1494 "Part %s, %s%s %s to \"%s\"%s%s%s",
1495 a->number,
1496 is_text
1497 ? comatose(a->body->size.lines) : l_string,
1498 is_text ? " lines" : "",
1499 flags & GER_OVER
1500 ? "overwritten"
1501 : flags & GER_APPEND ? "appended" : "written",
1502 file,
1503 (is_text || len == a->body->size.bytes)
1504 ? "" : "(decoded from ",
1505 (is_text || len == a->body->size.bytes)
1506 ? "" : byte_string(a->body->size.bytes),
1507 is_text || len == a->body->size.bytes
1508 ? "" : ")");
1509 fs_give((void **)&l_string);
1510 return 1;
1515 char *
1516 write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
1518 char *err = NULL;
1519 long start_of_append;
1520 gf_io_t pc;
1521 MESSAGECACHE *mc;
1523 if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
1524 && (*ap)->body->nested.msg->env){
1525 start_of_append = so_tell(store);
1527 gf_set_so_writec(&pc, store);
1528 if(!(ps_global->mail_stream && msgno > 0L
1529 && msgno <= ps_global->mail_stream->nmsgs
1530 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1531 mc = NULL;
1533 if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
1534 || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
1535 err = error_description(errno);
1537 gf_clear_so_writec(store);
1539 if(err)
1540 ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
1542 else
1543 err = "Can't export message. Missing Envelope data";
1545 return(err);
1549 /*----------------------------------------------------------------------
1550 Save the attachment message/rfc822 to specified folder
1552 Args:
1554 Result:
1555 ----*/
1556 void
1557 save_msg_att(long int msgno, ATTACH_S *a)
1559 char newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
1560 char date[64], nmsgs[80];
1561 CONTEXT_S *cntxt = NULL;
1562 int our_stream = 0, rv;
1563 MAILSTREAM *save_stream;
1564 MESSAGECACHE *mc;
1566 snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
1567 nmsgs[sizeof(nmsgs)-1] = '\0';
1568 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
1569 a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
1570 if(strucmp(newfolder, ps_global->inbox_name) == 0){
1571 save_folder = ps_global->VAR_INBOX_PATH;
1572 cntxt = NULL;
1574 else
1575 save_folder = newfolder;
1577 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1579 mc = (msgno > 0L && ps_global->mail_stream
1580 && msgno <= ps_global->mail_stream->nmsgs)
1581 ? mail_elt(ps_global->mail_stream, msgno) : NULL;
1582 flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
1583 if(mc && mc->day)
1584 mail_date(date, mc);
1585 else
1586 *date = '\0';
1588 if(pith_opt_save_size_changed_prompt)
1589 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1591 rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
1593 if(pith_opt_save_size_changed_prompt)
1594 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1596 if(flags)
1597 fs_give((void **) &flags);
1599 if(rv == 1)
1600 q_status_message2(SM_ORDER, 0, 4,
1601 _("Attached message (part %s) saved to \"%s\""),
1602 a->number,
1603 save_folder);
1604 else if(rv == -1)
1605 cmd_cancelled("Attached message Save");
1606 /* else whatever broke in save_fetch_append shoulda bitched */
1608 if(our_stream)
1609 mail_close(save_stream);
1614 /*----------------------------------------------------------------------
1615 Save the message/rfc822 in the given digest to the specified folder
1617 Args:
1619 Result:
1620 ----*/
1621 void
1622 save_digest_att(long int msgno, ATTACH_S *a)
1624 char newfolder[MAILTMPLEN], *save_folder,
1625 date[64], nmsgs[80];
1626 CONTEXT_S *cntxt = NULL;
1627 int our_stream = 0, rv, cnt = 0;
1628 MAILSTREAM *save_stream;
1629 ATTACH_S *ap;
1631 for(ap = a + 1;
1632 ap->description
1633 && !strncmp(a->number, ap->number, strlen(a->number));
1634 ap++)
1635 if(MIME_MSG(ap->body->type, ap->body->subtype))
1636 cnt++;
1638 snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
1639 nmsgs[sizeof(nmsgs)-1] = '\0';
1641 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
1642 nmsgs, NULL, 0, NULL, NULL, NULL)){
1643 save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
1644 ? ps_global->VAR_INBOX_PATH : newfolder;
1646 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1648 if(pith_opt_save_size_changed_prompt)
1649 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1651 for(ap = a + 1;
1652 ap->description
1653 && !strncmp(a->number, ap->number, strlen(a->number));
1654 ap++)
1655 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1656 *date = '\0';
1657 rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
1658 if(rv != 1)
1659 break;
1662 if(pith_opt_save_size_changed_prompt)
1663 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1665 if(rv == 1)
1666 q_status_message2(SM_ORDER, 0, 4,
1667 _("Attached digest (part %s) saved to \"%s\""),
1668 a->number,
1669 save_folder);
1670 else if(rv == -1)
1671 cmd_cancelled("Attached digest Save");
1672 /* else whatever broke in save_fetch_append shoulda bitched */
1674 if(our_stream)
1675 mail_close(save_stream);
1681 save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
1682 char *save_folder, CONTEXT_S *cntxt, char *date)
1684 STORE_S *so;
1685 int rv = 0;
1687 if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
1688 if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1689 *date = '\0';
1690 rv = save_fetch_append(ps_global->mail_stream, msgno,
1691 a->number,
1692 save_stream, save_folder, cntxt,
1693 a->body->size.bytes,
1694 NULL, date, so);
1696 else{
1697 dprint((1, "Can't allocate store for save: %s\n",
1698 error_description(errno)));
1699 q_status_message(SM_ORDER | SM_DING, 3, 4,
1700 _("Problem creating space for message text."));
1701 rv = 0;
1705 return(rv);
1709 /*----------------------------------------------------------------------
1710 Export the attachment message/rfc822 to specified file
1712 Args:
1714 Result:
1715 ----*/
1716 void
1717 export_msg_att(long int msgno, ATTACH_S *a)
1719 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
1720 int rv, rflags = GER_NONE, i = 1;
1721 ATTACH_S *ap = a;
1722 STORE_S *store;
1723 static HISTORY_S *history = NULL;
1724 static ESCKEY_S opts[] = {
1725 {ctrl('T'), 10, "^T", N_("To Files")},
1726 {-1, 0, NULL, NULL},
1727 {-1, 0, NULL, NULL}};
1729 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1730 opts[i].ch = ctrl('I');
1731 opts[i].rval = 11;
1732 opts[i].name = "TAB";
1733 opts[i].label = N_("Complete");
1736 filename[0] = full_filename[0] = '\0';
1738 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1739 sizeof(filename), "msg attachment",
1740 /* TRANSLATORS: Message Attachment (a screen title) */
1741 _("MSG ATTACHMENT"), opts,
1742 &rflags, -FOOTER_ROWS(ps_global),
1743 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1745 if(rv < 0){
1746 switch(rv){
1747 case -1:
1748 cmd_cancelled("Export");
1749 break;
1751 case -2:
1752 q_status_message1(SM_ORDER, 0, 2,
1753 _("Can't export to file outside of %s"),
1754 ps_global->VAR_OPER_DIR);
1755 break;
1758 return;
1761 /* With name in hand, allocate storage object and save away... */
1762 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1763 if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
1764 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1765 else
1766 q_status_message3(SM_ORDER, 0, 4,
1767 _("Attached message (part %s) %s to \"%s\""),
1768 a->number,
1769 rflags & GER_OVER
1770 ? _("overwritten")
1771 : rflags & GER_APPEND ? _("appended") : _("written"),
1772 full_filename);
1774 if(so_give(&store))
1775 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1776 _("Error writing %s: %s"),
1777 full_filename, error_description(errno));
1779 else
1780 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1781 /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
1782 _("Error opening file \"%s\" to export message: %s"),
1783 full_filename, error_description(errno));
1787 /*----------------------------------------------------------------------
1788 Export the message/rfc822 in the given digest to the specified file
1790 Args:
1792 Result:
1793 ----*/
1794 void
1795 export_digest_att(long int msgno, ATTACH_S *a)
1797 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
1798 int rv, rflags = GER_NONE, i = 1;
1799 long count = 0L;
1800 ATTACH_S *ap;
1801 static HISTORY_S *history = NULL;
1802 STORE_S *store;
1803 static ESCKEY_S opts[] = {
1804 {ctrl('T'), 10, "^T", N_("To Files")},
1805 {-1, 0, NULL, NULL},
1806 {-1, 0, NULL, NULL}};
1808 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1809 opts[i].ch = ctrl('I');
1810 opts[i].rval = 11;
1811 opts[i].name = "TAB";
1812 opts[i].label = N_("Complete");
1815 filename[0] = full_filename[0] = '\0';
1817 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1818 sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
1819 opts, &rflags, -FOOTER_ROWS(ps_global),
1820 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1822 if(rv < 0){
1823 switch(rv){
1824 case -1:
1825 cmd_cancelled("Export");
1826 break;
1828 case -2:
1829 q_status_message1(SM_ORDER, 0, 2,
1830 _("Can't export to file outside of %s"),
1831 ps_global->VAR_OPER_DIR);
1832 break;
1835 return;
1838 /* With name in hand, allocate storage object and save away... */
1839 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1840 count = 0;
1842 for(ap = a + 1;
1843 ap->description
1844 && !strncmp(a->number, ap->number, strlen(a->number))
1845 && !err;
1846 ap++){
1847 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1848 count++;
1849 err = write_attached_msg(msgno, &ap, store,
1850 !count && !(rflags & GER_APPEND));
1854 if(so_give(&store))
1855 err = error_description(errno);
1857 if(err){
1858 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1859 _("Error exporting: %s"), err);
1860 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1861 _("%s messages exported before error occurred"), err);
1863 else
1864 q_status_message4(SM_ORDER, 0, 4,
1865 "%s messages in digest (part %s) %s to \"%s\"",
1866 long2string(count),
1867 a->number,
1868 rflags & GER_OVER
1869 ? "overwritten"
1870 : rflags & GER_APPEND ? "appended" : "written",
1871 full_filename);
1873 else
1874 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1875 _("Error opening file \"%s\" to export digest: %s"),
1876 full_filename, error_description(errno));
1880 /*----------------------------------------------------------------------
1881 Print the given attachment associated with the given message no
1883 Args: ps
1885 Result:
1886 ----*/
1887 void
1888 print_attachment(int qline, long int msgno, ATTACH_S *a)
1890 char prompt[250];
1892 if(ps_global->restricted){
1893 q_status_message(SM_ORDER | SM_DING, 0, 4,
1894 "Alpine demo can't Print attachments");
1895 return;
1898 snprintf(prompt, sizeof(prompt), "attach%s %s",
1899 (a->body->type == TYPETEXT) ? "ment" : "ed message",
1900 MIME_DGST_A(a) ? "digest" : a->number);
1901 prompt[sizeof(prompt)-1] = '\0';
1902 if(open_printer(prompt) >= 0){
1903 if(MIME_MSG_A(a))
1904 (void) print_msg_att(msgno, a, 1);
1905 else if(MIME_DGST_A(a))
1906 print_digest_att(msgno, a);
1907 else
1908 (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
1910 close_printer();
1916 * Print the attachment message/rfc822 to specified file
1918 * Returns 1 on success, 0 on failure.
1921 print_msg_att(long int msgno, ATTACH_S *a, int first)
1923 ATTACH_S *ap = a;
1924 MESSAGECACHE *mc;
1926 if(!(ps_global->mail_stream && msgno > 0L
1927 && msgno <= ps_global->mail_stream->nmsgs
1928 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1929 mc = NULL;
1931 if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
1932 && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
1933 && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
1934 ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
1935 : 1)
1936 && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
1937 return(1);
1940 q_status_message2(SM_ORDER | SM_DING, 3, 3,
1941 _("Error printing message %s, part %s"),
1942 long2string(msgno), a->number);
1943 return(0);
1947 /*----------------------------------------------------------------------
1948 Print the attachment message/rfc822 to specified file
1950 Args:
1952 Result:
1953 ----*/
1954 void
1955 print_digest_att(long int msgno, ATTACH_S *a)
1957 ATTACH_S *ap;
1958 int next = 0;
1960 for(ap = a + 1;
1961 ap->description
1962 && !strncmp(a->number, ap->number, strlen(a->number));
1963 ap++){
1964 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1965 char *p = part_desc(ap->number, ap->body->nested.msg->body,
1966 0, 80, FM_NOINDENT, print_char);
1967 if(p){
1968 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1969 _("Can't print digest: %s"), p);
1970 break;
1972 else if(!print_msg_att(msgno, ap, !next))
1973 break;
1975 next++;
1981 /*----------------------------------------------------------------------
1982 Unpack and display the given attachment associated with given message no.
1984 Args: msgno -- message no attachment is part of
1985 a -- attachment to display
1987 Returns: 0 on success, non-zero (and error message queued) otherwise
1988 ----*/
1990 display_attachment(long int msgno, ATTACH_S *a, int flags)
1992 char *filename = NULL;
1993 char sender_filename[1000];
1994 char *extp = NULL;
1995 STORE_S *store;
1996 gf_io_t pc;
1997 char *err;
1998 int we_cancel = 0, rv;
1999 char prefix[70];
2000 char ext[32];
2001 char mtype[128];
2003 /*------- Display the attachment -------*/
2004 if(dispatch_attachment(a) == MCD_NONE){
2005 /*----- Can't display this type ------*/
2006 if(a->body->encoding < ENCOTHER)
2007 q_status_message4(SM_ORDER | SM_DING, 3, 5,
2008 /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
2009 _("Don't know how to display %s%s%s attachments.%s"),
2010 body_type_names(a->body->type),
2011 a->body->subtype ? "/" : "",
2012 a->body->subtype ? a->body->subtype :"",
2013 (flags & DA_SAVE) ? _(" Try Save.") : "");
2014 else
2015 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2016 _("Don't know how to unpack \"%s\" encoding"),
2017 body_encodings[(a->body->encoding <= ENCMAX)
2018 ? a->body->encoding : ENCOTHER]);
2020 return(1);
2022 else if(!(a->can_display & MCD_EXTERNAL)){
2023 if(a->body->type == TYPEMULTIPART){
2024 if(a->body->subtype){
2025 if(!strucmp(a->body->subtype, "digest"))
2026 display_digest_att(msgno, a, flags);
2027 else
2028 q_status_message1(SM_ORDER, 3, 5,
2029 _("Can't display Multipart/%s"),
2030 a->body->subtype);
2032 else
2033 q_status_message(SM_ORDER, 3, 5,
2034 _("Can't display unknown Multipart Subtype"));
2036 else if(MIME_VCARD_A(a))
2037 display_vcard_att(msgno, a, flags);
2038 else if(a->body->type == TYPETEXT){
2040 rv = display_text_att(msgno, a, flags);
2041 } while(rv == MC_FULLHDR);
2043 else if(a->body->type == TYPEMESSAGE){
2045 rv = display_msg_att(msgno, a, flags);
2046 } while(rv == MC_FULLHDR);
2049 ps_global->mangled_screen = 1;
2050 return(0);
2053 /* arrive here if MCD_EXTERNAL */
2055 if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
2056 && (!(flags & DA_DIDPROMPT)))
2057 if(want_to(_("View selected Attachment"), 'y',
2058 0, NO_HELP, WT_NORM) == 'n'){
2059 cmd_cancelled(NULL);
2060 return(1);
2063 sender_filename[0] = '\0';
2064 ext[0] = '\0';
2065 prefix[0] = '\0';
2067 if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
2068 && (a->can_display & MCD_EXT_PROMPT)){
2069 char prompt[256];
2071 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2072 a->body, &extp);
2073 snprintf(prompt, sizeof(prompt),
2074 "Attachment %s%s unrecognized. %s%s%s",
2075 a->body->subtype,
2076 strlen(a->body->subtype) > 12 ? "..." : "",
2077 (extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
2078 (extp && extp[0]) ? extp : "",
2079 (extp && extp[0]) ? ")" : "");
2081 if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
2082 cmd_cancelled(NULL);
2083 return(1);
2087 /*------ Write the image to a temporary file ------*/
2089 /* create type/subtype in mtype */
2090 strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
2091 mtype[sizeof(mtype)-1] = '\0';
2092 if(a->body->subtype){
2093 strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
2094 mtype[sizeof(mtype)-1] = '\0';
2095 strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
2096 mtype[sizeof(mtype)-1] = '\0';
2100 * If we haven't already gotten the filename parameter, get it
2101 * now. It may be used in the temporary filename and possibly
2102 * for its extension.
2104 if(!sender_filename[0])
2105 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2106 a->body, &extp);
2108 if(!set_mime_extension_by_type(ext, mtype)){ /* extension from type */
2109 if(extp && extp[0]){ /* extension from filename */
2110 strncpy(ext, extp, sizeof(ext));
2111 ext[sizeof(ext)-1] = '\0';
2115 /* create a temp file */
2116 if(sender_filename){
2117 char *p, *q = NULL;
2119 /* get rid of any extension */
2120 if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
2121 *(q-1) = '\0';
2123 /* be careful about what is allowed in the filename */
2124 for(p = sender_filename; *p; p++)
2125 if(!(isascii((unsigned char) *p)
2126 && (isalnum((unsigned char) *p)
2127 || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
2128 break;
2130 if(!*p) /* filename was ok to use */
2131 snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
2134 /* didn't get it yet */
2135 if(!prefix[0]){
2136 snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
2137 ? a->body->subtype : "unk");
2140 /* We are creating a temporary name. This is just a prefix. If you
2141 * need the original name, use the save command, so if the prefix
2142 * is too long, shorten it.
2144 if (strlen(prefix) > 9){
2145 prefix[9] = '-';
2146 prefix[10] = '\0';
2149 filename = temp_nam_ext(NULL, prefix, ext);
2151 if(!filename){
2152 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2153 _("Error \"%s\", Can't create temporary file"),
2154 error_description(errno));
2155 return(1);
2158 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2159 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2160 _("Error \"%s\", Can't write file %s"),
2161 error_description(errno), filename);
2162 if(filename){
2163 our_unlink(filename);
2164 fs_give((void **)&filename);
2167 return(1);
2171 if(a->body->size.bytes){
2172 char msg_buf[128];
2174 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2175 a->description ? "\"" : "",
2176 a->description ? a->description : "attachment number ",
2177 a->description ? "" : a->number,
2178 a->description ? "\"" : "");
2179 msg_buf[sizeof(msg_buf)-1] = '\0';
2180 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
2181 a->body);
2184 if(a->body->type == TYPEMULTIPART){
2185 char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
2186 NULL, 0L);
2188 /* Write to store while converting newlines */
2189 while(h && *h)
2190 if(*h == '\015' && *(h+1) == '\012'){
2191 so_puts(store, NEWLINE);
2192 h += 2;
2194 else
2195 so_writec(*h++, store);
2198 gf_set_so_writec(&pc, store);
2200 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
2202 gf_clear_so_writec(store);
2204 if(we_cancel)
2205 cancel_busy_cue(0);
2207 so_give(&store);
2209 if(err){
2210 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2211 "%s: Error saving image to temp file %s",
2212 err, filename);
2213 if(filename){
2214 our_unlink(filename);
2215 fs_give((void **)&filename);
2218 return(1);
2221 /*----- Run the viewer process ----*/
2222 run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
2223 if(filename)
2224 fs_give((void **)&filename);
2226 return(0);
2230 /*----------------------------------------------------------------------
2231 Fish the required command from mailcap and run it
2233 Args: image_file -- The name of the file to pass viewer
2234 body -- body struct containing type/subtype of part
2236 A side effect may be that scrolltool is called as well if
2237 exec_mailcap_cmd has any substantial output...
2238 ----*/
2239 void
2240 run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
2242 MCAP_CMD_S *mc_cmd = NULL;
2243 int needs_terminal = 0, we_cancel = 0;
2245 we_cancel = busy_cue("Displaying attachment", NULL, 0);
2247 if((mc_cmd = mailcap_build_command(body->type, body->subtype,
2248 body, image_file,
2249 &needs_terminal, chk_extension)) != NULL){
2250 if(we_cancel)
2251 cancel_busy_cue(-1);
2253 exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
2254 if(mc_cmd->command)
2255 fs_give((void **)&mc_cmd->command);
2256 fs_give((void **)&mc_cmd);
2258 else{
2259 if(we_cancel)
2260 cancel_busy_cue(-1);
2262 q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
2263 type_desc(body->type, body->subtype,
2264 body->parameter, NULL, 1));
2269 /*----------------------------------------------------------------------
2270 Detach and provide for browsing a text body part
2272 Args: msgno -- raw message number to get part from
2273 a -- attachment struct for the desired part
2275 Result:
2276 ----*/
2277 STORE_S *
2278 format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
2280 STORE_S *store;
2281 gf_io_t pc;
2283 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2284 if(handlesp)
2285 init_handles(handlesp);
2287 gf_set_so_writec(&pc, store);
2288 (void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
2289 gf_clear_so_writec(store);
2292 return(store);
2296 /*----------------------------------------------------------------------
2297 Detach and provide for browsing a text body part
2299 Args: msgno -- raw message number to get part from
2300 a -- attachment struct for the desired part
2302 Result:
2303 ----*/
2305 display_text_att(long int msgno, ATTACH_S *a, int flags)
2307 STORE_S *store;
2308 HANDLE_S *handles = NULL;
2309 int rv = 0;
2311 if(msgno > 0L)
2312 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2314 if((store = format_text_att(msgno, a, &handles)) != NULL){
2315 rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
2316 free_handles(&handles);
2317 so_give(&store); /* free resources associated with store */
2319 else
2320 q_status_message(SM_ORDER | SM_DING, 3, 3,
2321 _("Error allocating space for attachment."));
2323 return(rv);
2327 /*----------------------------------------------------------------------
2328 Detach and provide for browsing a body part of type "MESSAGE"
2330 Args: msgno -- message number to get partrom
2331 a -- attachment struct for the desired part
2333 Result:
2334 ----*/
2336 display_msg_att(long int msgno, ATTACH_S *a, int flags)
2338 STORE_S *store;
2339 gf_io_t pc;
2340 ATTACH_S *ap = a;
2341 HANDLE_S *handles = NULL;
2342 int rv = 0;
2344 if(msgno > 0L)
2345 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2347 /* BUG, should check this return code */
2348 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2350 /* initialize a storage object */
2351 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2352 q_status_message(SM_ORDER | SM_DING, 3, 3,
2353 _("Error allocating space for message."));
2354 return(rv);
2357 gf_set_so_writec(&pc, store);
2359 if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
2360 if(ps_global->full_header == 2)
2361 q_status_message(SM_INFO, 0, 3,
2362 _("Full header mode ON. All header text being included"));
2364 rv = scroll_attachment((a->body->subtype
2365 && !strucmp(a->body->subtype, "delivery-status"))
2366 ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
2367 store, CharStar, handles, a, flags);
2368 free_handles(&handles);
2371 gf_clear_so_writec(store);
2373 so_give(&store); /* free resources associated with store */
2374 return(rv);
2378 /*----------------------------------------------------------------------
2379 Detach and provide for browsing a multipart body part of type "DIGEST"
2381 Args: msgno -- message number to get partrom
2382 a -- attachment struct for the desired part
2384 Result:
2385 ----*/
2386 void
2387 display_digest_att(long int msgno, ATTACH_S *a, int flags)
2389 STORE_S *store;
2390 ATTACH_S *ap;
2391 HANDLE_S *handles = NULL;
2392 gf_io_t pc;
2393 SourceType src = CharStar;
2394 int bad_news = 0;
2396 if(msgno > 0L)
2397 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2399 /* BUG, should check this return code */
2400 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2402 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
2403 q_status_message(SM_ORDER | SM_DING, 3, 3,
2404 _("Error allocating space for message."));
2405 return;
2408 gf_set_so_writec(&pc, store);
2411 * While in a subtype of this message
2413 for(ap = a + 1;
2414 ap->description
2415 && !strncmp(a->number, ap->number, strlen(a->number))
2416 && !bad_news;
2417 ap++){
2418 if(ap->body->type == TYPEMESSAGE){
2419 char *errstr;
2421 if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
2422 char *tsub;
2424 tsub = cpystr(ap->body->subtype);
2425 convert_possibly_encoded_str_to_utf8((char **) &tsub);
2426 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
2427 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2429 if((errstr = format_editorial(tmp_20k_buf,
2430 ps_global->ttyo->screen_cols, 0,
2431 NULL, pc)) != NULL){
2432 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2433 _("Can't format digest: %s"), errstr);
2434 bad_news++;
2436 else if(!gf_puts(NEWLINE, pc))
2437 bad_news++;
2439 fs_give((void **) &tsub);
2441 else{
2442 if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
2443 0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
2444 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2445 _("Can't format digest: %s"), errstr);
2446 bad_news++;
2448 else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
2449 bad_news++;
2452 else if(ap->body->type == TYPETEXT
2453 && decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
2454 bad_news++;
2455 else if(!gf_puts("Unknown type in Digest", pc))
2456 bad_news++;
2459 if(!bad_news){
2460 if(ps_global->full_header == 2)
2461 q_status_message(SM_INFO, 0, 3,
2462 _("Full header mode ON. All header text being included"));
2464 scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
2467 free_handles(&handles);
2469 gf_clear_so_writec(store);
2470 so_give(&store); /* free resources associated with store */
2475 scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
2477 SCROLL_S sargs;
2479 memset(&sargs, 0, sizeof(SCROLL_S));
2480 sargs.text.text = so_text(store);
2481 sargs.text.src = src;
2482 sargs.text.desc = "attachment";
2483 sargs.text.handles = handles;
2484 sargs.bar.title = title;
2485 sargs.proc.tool = process_attachment_cmd;
2486 sargs.proc.data.p = (void *) a;
2487 sargs.help.text = h_mail_text_att_view;
2488 sargs.help.title = _("HELP FOR ATTACHED TEXT VIEW");
2489 sargs.keys.menu = &att_view_keymenu;
2490 setbitmap(sargs.keys.bitmap);
2492 /* First, fix up "back" key */
2493 if(flags & DA_FROM_VIEW){
2494 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
2496 else{
2497 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
2500 if(!handles){
2501 clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
2502 clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
2503 clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
2506 if(F_OFF(F_ENABLE_PIPE, ps_global))
2507 clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
2509 if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
2510 clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
2513 * If message or digest, leave Reply and Save and,
2514 * conditionally, Bounce on...
2516 if(MIME_MSG_A(a)){
2517 if(F_OFF(F_ENABLE_BOUNCE, ps_global))
2518 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2520 else{
2521 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2522 clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
2523 clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
2526 sargs.use_indexline_color = 1;
2528 if(DA_RESIZE & flags)
2529 sargs.resize_exit = 1;
2531 #ifdef _WINDOWS
2532 scrat_attachp = a;
2533 sargs.mouse.popup = scroll_att_popup;
2534 #endif
2536 return(scrolltool(&sargs));
2541 process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2543 int rv = 0, n;
2544 long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2545 #define AD(X) ((ATTACH_S *) (X)->proc.data.p)
2547 switch(cmd){
2548 case MC_EXIT :
2549 rv = 1;
2550 break;
2552 case MC_QUIT :
2553 ps_global->next_screen = quit_screen;
2554 break;
2556 case MC_MAIN :
2557 ps_global->next_screen = main_menu_screen;
2558 break;
2560 case MC_REPLY :
2561 reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
2562 break;
2564 case MC_FORWARD :
2565 forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
2566 break;
2568 case MC_BOUNCE :
2569 bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
2570 AD(sparms)->body->nested.msg->env->subject);
2571 ps_global->mangled_footer = 1;
2572 break;
2574 case MC_DELETE :
2575 delete_attachment(rawno, sparms->proc.data.p);
2576 break;
2578 case MC_UNDELETE :
2579 if(undelete_attachment(rawno, sparms->proc.data.p, &n))
2580 q_status_message1(SM_ORDER, 0, 3,
2581 "Part %s UNdeleted", AD(sparms)->number);
2583 break;
2585 case MC_SAVE :
2586 save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2587 ps_global->mangled_footer = 1;
2588 break;
2590 case MC_EXPORT :
2591 export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2592 ps_global->mangled_footer = 1;
2593 break;
2595 case MC_PRINTMSG :
2596 print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2597 ps_global->mangled_footer = 1;
2598 break;
2600 case MC_PIPE :
2601 pipe_attachment(rawno, sparms->proc.data.p);
2602 ps_global->mangled_footer = 1;
2603 break;
2605 case MC_FULLHDR :
2606 ps_global->full_header++;
2607 if(ps_global->full_header == 1){
2608 if(!(ps_global->quote_suppression_threshold
2609 && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
2610 ps_global->full_header++;
2612 else if(ps_global->full_header > 2)
2613 ps_global->full_header = 0;
2615 switch(ps_global->full_header){
2616 case 0:
2617 q_status_message(SM_ORDER, 0, 3,
2618 _("Display of full headers is now off."));
2619 break;
2621 case 1:
2622 q_status_message1(SM_ORDER, 0, 3,
2623 _("Quotes displayed, use %s to see full headers"),
2624 F_ON(F_USE_FK, ps_global) ? "F9" : "H");
2625 break;
2627 case 2:
2628 q_status_message(SM_ORDER, 0, 3,
2629 _("Display of full headers is now on."));
2630 break;
2634 rv = 1;
2635 break;
2637 default:
2638 alpine_panic("Unexpected command case");
2639 break;
2642 return(rv);
2647 * Returns 1 on success, 0 on error.
2650 format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
2652 int rv = 1;
2654 if((*a)->body->type != TYPEMESSAGE)
2655 return(gf_puts("[ Undisplayed Attachment of Type ", pc)
2656 && gf_puts(body_type_names((*a)->body->type), pc)
2657 && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
2659 if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
2660 HEADER_S h;
2662 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
2663 FE_DEFAULT);
2664 switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
2665 (*a)->body->nested.msg->env, &h, NULL, NULL,
2666 flags, NULL, pc)){
2667 case -1 : /* write error */
2668 return(0);
2670 case 1 : /* fetch error */
2671 if(!(gf_puts("[ Error fetching header ]", pc)
2672 && !gf_puts(NEWLINE, pc)))
2673 return(0);
2675 break;
2678 gf_puts(NEWLINE, pc);
2680 ++(*a);
2682 #ifdef SMIME
2683 if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2684 if((*a)->description){
2685 if(!(!format_editorial((*a)->description,
2686 ps_global->ttyo->screen_cols,
2687 flags, NULL, pc)
2688 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2689 rv = 0;
2692 ++(*a);
2694 #endif /* SMIME */
2696 if(((*a))->description
2697 && (*a)->body && (*a)->body->type == TYPETEXT){
2698 if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2699 rv = 0;
2701 else if(!(gf_puts("[Can't display ", pc)
2702 && gf_puts(((*a)->description && (*a)->body)
2703 ? "first non-" : "missing ", pc)
2704 && gf_puts("text segment]", pc)
2705 && gf_puts(NEWLINE, pc)))
2706 rv = 0;
2708 ++(*a);
2711 else if((*a)->body->subtype
2712 && strucmp((*a)->body->subtype, "external-body") == 0) {
2713 if(format_editorial("This part is not included and can be fetched as follows:",
2714 ps_global->ttyo->screen_cols, flags, NULL, pc)
2715 || !gf_puts(NEWLINE, pc)
2716 || format_editorial(display_parameters((*a)->body->parameter),
2717 ps_global->ttyo->screen_cols, flags, handlesp, pc))
2718 rv = 0;
2720 else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2721 rv = 0;
2723 return(rv);
2727 void
2728 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2730 STORE_S *in_store, *out_store = NULL;
2731 HANDLE_S *handles = NULL;
2732 URL_HILITE_S uh;
2733 gf_io_t gc, pc;
2734 char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2735 int cmd, indent, begins = 0;
2737 lines = detach_vcard_att(ps_global->mail_stream,
2738 msgno, a->body, a->number);
2739 if(!lines){
2740 q_status_message(SM_ORDER | SM_DING, 3, 3,
2741 _("Error accessing attachment."));
2742 return;
2745 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2746 free_list_array(&lines);
2747 q_status_message(SM_ORDER | SM_DING, 3, 3,
2748 _("Error allocating space for attachment."));
2749 return;
2752 for(ll = lines, indent = 0; ll && *ll; ll++)
2753 if((p = strindex(*ll, ':')) && p - *ll > indent)
2754 indent = p - *ll;
2756 indent += 5;
2757 for(ll = lines; ll && *ll; ll++){
2758 if((p = strindex(*ll, ':')) != NULL){
2759 if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2760 begins++;
2762 snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
2763 MIN(p - *ll, sizeof(tmp)-5), *ll);
2764 tmp[sizeof(tmp)-1] = '\0';
2765 so_puts(in_store, tmp);
2766 p++;
2768 else{
2769 p = *ll;
2770 so_puts(in_store, repeat_char(indent, SPACE));
2773 snprintf(tmp, sizeof(tmp), "%.200s", p);
2774 tmp[sizeof(tmp)-1] = '\0';
2775 so_puts(in_store,
2776 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
2777 SIZEOF_20KBUF, tmp));
2778 so_puts(in_store, "\015\012");
2781 free_list_array(&lines);
2783 so_puts(in_store, "\015\012\015\012");
2786 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2787 so_seek(in_store, 0L, 0);
2789 init_handles(&handles);
2790 gf_filter_init();
2792 if(F_ON(F_VIEW_SEL_URL, ps_global)
2793 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2794 || F_ON(F_SCAN_ADDR, ps_global))
2795 gf_link_filter(gf_line_test,
2796 gf_line_test_opt(url_hilite,
2797 gf_url_hilite_opt(&uh,&handles,0)));
2799 gf_link_filter(gf_wrap,
2800 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
2801 ps_global->ttyo->screen_cols,
2802 NULL, indent, GFW_HANDLES));
2803 gf_link_filter(gf_nvtnl_local, NULL);
2805 gf_set_so_readc(&gc, in_store);
2806 gf_set_so_writec(&pc, out_store);
2808 errstr = gf_pipe(gc, pc);
2810 gf_clear_so_readc(in_store);
2812 if(!errstr){
2813 #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.")
2814 #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.")
2815 errstr = format_editorial((begins > 1)
2816 ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
2817 ps_global->ttyo->screen_cols, 0, NULL, pc);
2820 gf_clear_so_writec(out_store);
2822 if(!errstr)
2823 cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
2824 CharStar, handles, a, flags | DA_RESIZE);
2826 free_handles(&handles);
2827 so_give(&out_store);
2829 else
2830 errstr = _("Error allocating space");
2832 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
2834 if(errstr)
2835 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2836 _("Can't format entry : %s"), errstr);
2838 so_give(&in_store);
2842 /*----------------------------------------------------------------------
2843 Display attachment information
2845 Args: msgno -- message number to get partrom
2846 a -- attachment struct for the desired part
2848 Result: a screen containing information about attachment:
2849 ----*/
2850 void
2851 display_attach_info(long int msgno, ATTACH_S *a)
2853 int i, indent, cols;
2854 char buf1[100], *folded;
2855 STORE_S *store;
2856 PARMLIST_S *plist;
2857 SCROLL_S sargs;
2859 (void) dispatch_attachment(a);
2861 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2862 q_status_message(SM_ORDER | SM_DING, 3, 3,
2863 _("Error allocating space for message."));
2864 return;
2867 cols = ps_global->ttyo->screen_cols;
2870 * 2 spaces on left
2871 * 16 for text (longest is Display Method == 14)
2872 * 2 for ": "
2874 indent = 20;
2876 /* don't try stupid folding */
2877 cols = MAX(indent+10, cols);
2879 so_puts(store, "Details about Attachment #");
2880 so_puts(store, a->number);
2881 so_puts(store, " :\n\n");
2882 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
2883 so_puts(store, buf1);
2884 so_puts(store, body_type_names(a->body->type));
2885 so_puts(store, "\n");
2886 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
2887 so_puts(store, buf1);
2888 so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
2889 so_puts(store, "\n");
2890 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
2891 so_puts(store, buf1);
2892 so_puts(store, a->body->encoding < ENCMAX
2893 ? body_encodings[a->body->encoding]
2894 : "Unknown");
2895 so_puts(store, "\n");
2896 if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
2897 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
2898 so_puts(store, buf1);
2899 i = 0;
2900 while(rfc2231_list_params(plist)){
2901 if(i++)
2902 so_puts(store, repeat_char(indent, ' '));
2904 so_puts(store, plist->attrib);
2905 so_puts(store, " = ");
2906 so_puts(store, plist->value ? plist->value : "");
2907 so_puts(store, "\n");
2910 rfc2231_free_parmlist(&plist);
2913 if(a->body->description && a->body->description[0]){
2914 char buftmp[MAILTMPLEN];
2915 unsigned char *q;
2917 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
2919 snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
2920 buftmp[sizeof(buftmp)-1] = '\0';
2921 q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
2922 folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
2924 if(folded){
2925 so_puts(store, folded);
2926 fs_give((void **) &folded);
2930 /* BUG: no a->body->language support */
2932 if(a->body->disposition.type){
2933 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
2934 so_puts(store, buf1);
2935 so_puts(store, a->body->disposition.type);
2936 so_puts(store, "\n");
2937 if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
2938 while(rfc2231_list_params(plist)){
2939 so_puts(store, repeat_char(indent, ' '));
2940 so_puts(store, plist->attrib);
2941 so_puts(store, " = ");
2942 so_puts(store, plist->value ? plist->value : "");
2943 so_puts(store, "\n");
2946 rfc2231_free_parmlist(&plist);
2950 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
2951 so_puts(store, buf1);
2952 so_puts(store, comatose((a->body->encoding == ENCBASE64)
2953 ? ((a->body->size.bytes * 3)/4)
2954 : a->body->size.bytes));
2955 so_puts(store, " bytes\n");
2956 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
2957 so_puts(store, buf1);
2958 if(a->can_display == MCD_NONE) {
2959 so_puts(store, "Can't, ");
2960 so_puts(store, (a->body->encoding < ENCOTHER)
2961 ? "Unknown Attachment Format"
2962 : "Unknown Encoding");
2964 else if(!(a->can_display & MCD_EXTERNAL)){
2965 so_puts(store, "Alpine's Internal Pager");
2967 else{
2968 int nt, free_pretty_cmd;
2969 MCAP_CMD_S *mc_cmd;
2970 char *pretty_cmd;
2972 if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
2973 a->body, "<datafile>", &nt,
2974 a->can_display & MCD_EXT_PROMPT)) != NULL){
2975 so_puts(store, "\"");
2976 if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
2977 so_puts(store, pretty_cmd);
2978 so_puts(store, "\"");
2979 if(free_pretty_cmd && pretty_cmd)
2980 fs_give((void **)&pretty_cmd);
2981 if(mc_cmd->command)
2982 fs_give((void **)&mc_cmd->command);
2983 fs_give((void **)&mc_cmd);
2987 so_puts(store, "\n");
2989 memset(&sargs, 0, sizeof(SCROLL_S));
2990 sargs.text.text = so_text(store);
2991 sargs.text.src = CharStar;
2992 sargs.text.desc = "attachment info";
2993 sargs.bar.title = _("ABOUT ATTACHMENT");
2994 sargs.help.text = h_simple_text_view;
2995 sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
2997 sargs.use_indexline_color = 1;
2999 scrolltool(&sargs);
3001 so_give(&store); /* free resources associated with store */
3002 ps_global->mangled_screen = 1;
3006 /*----------------------------------------------------------------------
3008 ----*/
3009 void
3010 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3012 char *sig;
3013 void *msgtext;
3014 ENVELOPE *outgoing;
3015 BODY *body;
3017 if(MIME_MSG_A(a)){
3018 forward_msg_att(stream, msgno, a);
3020 else{
3021 ACTION_S *role = NULL;
3022 REDRAFT_POS_S *redraft_pos = NULL;
3023 long rflags = ROLE_FORWARD;
3024 PAT_STATE dummy;
3026 outgoing = mail_newenvelope();
3027 outgoing->message_id = generate_message_id();
3028 outgoing->subject = cpystr("Forwarded attachment...");
3030 if(nonempty_patterns(rflags, &dummy)){
3032 * There is no message, but a Current Folder Type might match.
3034 * This has been changed to check against the message
3035 * containing the attachment.
3037 role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3038 if(confirm_role(rflags, &role))
3039 role = combine_inherited_role(role);
3040 else{
3041 role = NULL;
3042 cmd_cancelled("Forward");
3043 mail_free_envelope(&outgoing);
3044 return;
3048 if(role)
3049 q_status_message1(SM_ORDER, 3, 4,
3050 _("Forwarding using role \"%s\""), role->nick);
3053 * as with all text bound for the composer, build it in
3054 * a storage object of the type it understands...
3056 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3057 int impl, template_len = 0;
3059 if(role && role->template){
3060 char *filtered;
3062 impl = 1;
3063 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3064 if(filtered){
3065 if(*filtered){
3066 so_puts((STORE_S *)msgtext, filtered);
3067 if(impl == 1)
3068 template_len = strlen(filtered);
3071 fs_give((void **)&filtered);
3074 else
3075 impl = 1;
3077 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3078 if(impl == 2)
3079 redraft_pos->offset += template_len;
3081 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3083 fs_give((void **)&sig);
3085 else
3086 so_puts((STORE_S *)msgtext, NEWLINE);
3088 /*---- New Body to start with ----*/
3089 body = mail_newbody();
3090 body->type = TYPEMULTIPART;
3092 /*---- The TEXT part/body ----*/
3093 body->nested.part = mail_newbody_part();
3094 body->nested.part->body.type = TYPETEXT;
3095 body->nested.part->body.contents.text.data = msgtext;
3097 /*---- The corresponding things we're attaching ----*/
3098 body->nested.part->next = mail_newbody_part();
3099 body->nested.part->next->body.id = generate_message_id();
3100 copy_body(&body->nested.part->next->body, a->body);
3102 if(fetch_contents(stream, msgno, a->number,
3103 &body->nested.part->next->body)){
3104 pine_send(outgoing, &body, "FORWARD MESSAGE",
3105 role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3107 ps_global->mangled_screen = 1;
3108 pine_free_body(&body);
3109 free_redraft_pos(&redraft_pos);
3111 else{
3112 mail_free_body(&body);
3113 so_give((STORE_S **) &msgtext);
3114 free_redraft_pos(&redraft_pos);
3115 q_status_message(SM_ORDER | SM_DING, 4, 5,
3116 _("Error fetching message contents. Can't forward message."));
3119 else
3120 q_status_message(SM_ORDER | SM_DING, 3, 4,
3121 _("Error allocating message text"));
3123 mail_free_envelope(&outgoing);
3124 free_action(&role);
3129 void
3130 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3132 char *p, *sig = NULL;
3133 int ret;
3134 void *msgtext;
3135 ENVELOPE *outgoing;
3136 BODY *body;
3137 ACTION_S *role = NULL;
3138 REPLY_S reply;
3139 REDRAFT_POS_S *redraft_pos = NULL;
3141 outgoing = mail_newenvelope();
3142 outgoing->message_id = generate_message_id();
3144 memset((void *)&reply, 0, sizeof(reply));
3146 if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
3148 * as with all text bound for the composer, build it in
3149 * a storage object of the type it understands...
3151 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3152 int impl, template_len = 0;
3153 long rflags = ROLE_FORWARD;
3154 PAT_STATE dummy;
3156 ret = 'n';
3157 if(ps_global->full_header == 2)
3158 ret = want_to(_("Forward message as an attachment"), 'n', 0,
3159 NO_HELP, WT_SEQ_SENSITIVE);
3160 /* Setup possible role */
3161 if(nonempty_patterns(rflags, &dummy)){
3162 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3163 if(confirm_role(rflags, &role))
3164 role = combine_inherited_role(role);
3165 else{ /* cancel reply */
3166 role = NULL;
3167 cmd_cancelled("Forward");
3168 mail_free_envelope(&outgoing);
3169 so_give((STORE_S **) &msgtext);
3170 return;
3174 if(role)
3175 q_status_message1(SM_ORDER, 3, 4,
3176 _("Forwarding using role \"%s\""), role->nick);
3178 if(role && role->template){
3179 char *filtered;
3181 impl = 1;
3182 filtered = detoken(role, a->body->nested.msg->env,
3183 0, 0, 0, &redraft_pos, &impl);
3184 if(filtered){
3185 if(*filtered){
3186 so_puts((STORE_S *)msgtext, filtered);
3187 if(impl == 1)
3188 template_len = strlen(filtered);
3191 fs_give((void **)&filtered);
3194 else
3195 impl = 1;
3197 if((sig = detoken(role, a->body->nested.msg->env,
3198 2, 0, 1, &redraft_pos, &impl)) != NULL){
3199 if(impl == 2)
3200 redraft_pos->offset += template_len;
3202 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3204 fs_give((void **)&sig);
3206 else
3207 so_puts((STORE_S *)msgtext, NEWLINE);
3209 if(ret == 'y'){
3210 /*---- New Body to start with ----*/
3211 body = mail_newbody();
3212 body->type = TYPEMULTIPART;
3214 /*---- The TEXT part/body ----*/
3215 body->nested.part = mail_newbody_part();
3216 body->nested.part->body.type = TYPETEXT;
3217 body->nested.part->body.contents.text.data = msgtext;
3219 if(!forward_mime_msg(stream, msgno,
3220 p = body_partno(stream, msgno, a->body),
3221 a->body->nested.msg->env,
3222 &body->nested.part->next, msgtext))
3223 mail_free_body(&body);
3225 else{
3226 reply.forw = 1;
3227 if(a->body->nested.msg->body){
3228 char *charset;
3230 charset
3231 = parameter_val(a->body->nested.msg->body->parameter,
3232 "charset");
3234 if(charset && strucmp(charset, "us-ascii") != 0){
3235 CONV_TABLE *ct;
3238 * There is a non-ascii charset,
3239 * is there conversion happening?
3241 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3242 || !ct->table){
3243 reply.orig_charset = charset;
3244 charset = NULL;
3248 if(charset)
3249 fs_give((void **) &charset);
3252 body = forward_body(stream, a->body->nested.msg->env,
3253 a->body->nested.msg->body, msgno,
3254 p = body_partno(stream, msgno, a->body),
3255 msgtext, FWD_NONE);
3258 fs_give((void **) &p);
3260 if(body){
3261 pine_send(outgoing, &body,
3262 "FORWARD MESSAGE",
3263 role, NULL,
3264 reply.forw ? &reply : NULL,
3265 redraft_pos, NULL, NULL, 0);
3267 ps_global->mangled_screen = 1;
3268 pine_free_body(&body);
3269 free_redraft_pos(&redraft_pos);
3270 free_action(&role);
3272 else{
3273 so_give((STORE_S **) &msgtext);
3274 q_status_message(SM_ORDER | SM_DING, 4, 5,
3275 _("Error fetching message contents. Can't forward message."));
3278 else
3279 q_status_message(SM_ORDER | SM_DING, 3, 4,
3280 _("Error allocating message text"));
3282 else
3283 q_status_message1(SM_ORDER,3,4,
3284 _("Error fetching message %s. Can't forward it."),
3285 long2string(msgno));
3287 if(reply.orig_charset)
3288 fs_give((void **)&reply.orig_charset);
3290 mail_free_envelope(&outgoing);
3294 void
3295 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3297 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
3298 ENVELOPE *outgoing;
3299 BODY *body;
3300 void *msgtext;
3301 char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3302 int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3303 int rolemsg = 0, copytomsg = 0;
3304 long rflags;
3305 PAT_STATE dummy;
3306 REDRAFT_POS_S *redraft_pos = NULL;
3307 ACTION_S *role = NULL;
3309 outgoing = mail_newenvelope();
3311 dprint((4,"\n - attachment reply \n"));
3313 saved_from = (ADDRESS *) NULL;
3314 saved_to = (ADDRESS *) NULL;
3315 saved_cc = (ADDRESS *) NULL;
3316 saved_resent = (ADDRESS *) NULL;
3317 outgoing->subject = NULL;
3319 prefix = reply_quote_str(a->body->nested.msg->env);
3321 * For consistency, the first question is always "include text?"
3323 if((include_text = reply_text_query(ps_global, 1, &prefix)) >= 0
3324 && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3325 && reply_harvest(ps_global, msgno, a->number,
3326 a->body->nested.msg->env, &saved_from,
3327 &saved_to, &saved_cc, &saved_resent, &flags)){
3328 outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3329 NULL, 0);
3330 clear_cursor_pos();
3331 reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3332 saved_from, saved_to, saved_cc, saved_resent,
3333 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3334 if(errmsg){
3335 if(*errmsg){
3336 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3337 display_message(NO_OP_COMMAND);
3340 fs_give((void **)&errmsg);
3343 if(sp_expunge_count(stream)) /* current msg was expunged */
3344 goto seeyalater;
3346 /* Setup possible role */
3347 rflags = ROLE_REPLY;
3348 if(nonempty_patterns(rflags, &dummy)){
3349 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3350 if(confirm_role(rflags, &role))
3351 role = combine_inherited_role(role);
3352 else{ /* cancel reply */
3353 role = NULL;
3354 cmd_cancelled("Reply");
3355 goto seeyalater;
3359 if(role){
3360 rolemsg++;
3362 /* override fcc gotten in reply_seed */
3363 if(role->fcc && fcc)
3364 fs_give((void **) &fcc);
3367 if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3368 ADDRESS *us_in_to_and_cc, *ap;
3370 us_in_to_and_cc = (ADDRESS *) NULL;
3371 if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3372 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3373 NULL, us_in_to_and_cc, NULL,
3374 a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3375 reply_append_addr(&us_in_to_and_cc, ap);
3377 if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3378 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3379 NULL, us_in_to_and_cc, NULL,
3380 a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3381 reply_append_addr(&us_in_to_and_cc, ap);
3384 * A list of all of our addresses that appear in the To
3385 * and cc fields is in us_in_to_and_cc.
3386 * If there is exactly one address in that list then
3387 * use it for the outgoing From.
3389 if(us_in_to_and_cc && !us_in_to_and_cc->next){
3390 PINEFIELD *custom, *pf;
3391 ADDRESS *a = NULL;
3392 char *addr = NULL;
3395 * Check to see if this address is different from what
3396 * we would have used anyway. If it is, notify the user
3397 * with a status message. This is pretty hokey because we're
3398 * mimicking how pine_send would set the From address and
3399 * there is no coordination between the two.
3402 /* in case user has a custom From value */
3403 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3405 pf = (PINEFIELD *) fs_get(sizeof(*pf));
3406 memset((void *) pf, 0, sizeof(*pf));
3407 pf->name = cpystr("From");
3408 pf->addr = &a;
3409 if(set_default_hdrval(pf, custom) >= UseAsDef
3410 && pf->textbuf && pf->textbuf[0]){
3411 removing_trailing_white_space(pf->textbuf);
3412 (void)removing_double_quotes(pf->textbuf);
3413 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3414 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3415 if(addr)
3416 fs_give((void **) &addr);
3419 if(!*pf->addr)
3420 *pf->addr = generate_from();
3422 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3423 copytomsg++;
3424 if(!role){
3425 role = (ACTION_S *) fs_get(sizeof(*role));
3426 memset((void *) role, 0, sizeof(*role));
3427 role->is_a_role = 1;
3430 role->from = us_in_to_and_cc;
3431 us_in_to_and_cc = NULL;
3434 free_customs(custom);
3435 free_customs(pf);
3438 if(us_in_to_and_cc)
3439 mail_free_address(&us_in_to_and_cc);
3443 if(role){
3444 if(rolemsg && copytomsg)
3445 q_status_message1(SM_ORDER, 3, 4,
3446 _("Replying using role \"%s\" and To as From"), role->nick);
3447 else if(rolemsg)
3448 q_status_message1(SM_ORDER, 3, 4,
3449 _("Replying using role \"%s\""), role->nick);
3450 else if(copytomsg)
3451 q_status_message(SM_ORDER, 3, 4,
3452 _("Replying using incoming To as outgoing From"));
3455 outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3456 outgoing->references = reply_build_refs(a->body->nested.msg->env);
3457 outgoing->message_id = generate_message_id();
3459 if(!outgoing->to && !outgoing->cc
3460 && !outgoing->bcc && !outgoing->newsgroups)
3461 q_status_message(SM_ORDER | SM_DING, 3, 6,
3462 _("Warning: no valid addresses to reply to!"));
3465 * Now fix up the body...
3467 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3468 REPLY_S reply;
3470 memset((void *)&reply, 0, sizeof(reply));
3471 reply.forw = 1;
3472 if(a->body->nested.msg->body){
3473 char *charset;
3475 charset
3476 = parameter_val(a->body->nested.msg->body->parameter,
3477 "charset");
3479 if(charset && strucmp(charset, "us-ascii") != 0){
3480 CONV_TABLE *ct;
3483 * There is a non-ascii charset,
3484 * is there conversion happening?
3486 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3487 || !ct->table){
3488 reply.orig_charset = charset;
3489 charset = NULL;
3493 if(charset)
3494 fs_give((void **) &charset);
3497 if((body = reply_body(stream, a->body->nested.msg->env,
3498 a->body->nested.msg->body, msgno,
3499 tp = body_partno(stream, msgno, a->body),
3500 msgtext, prefix, include_text, role,
3501 1, &redraft_pos)) != NULL){
3502 /* partially formatted outgoing message */
3503 pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3504 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3506 pine_free_body(&body);
3507 ps_global->mangled_screen = 1;
3509 else
3510 q_status_message(SM_ORDER | SM_DING, 3, 4,
3511 _("Error building message body"));
3513 fs_give((void **) &tp);
3514 if(reply.orig_charset)
3515 fs_give((void **)&reply.orig_charset);
3517 else
3518 q_status_message(SM_ORDER | SM_DING, 3, 4,
3519 _("Error allocating message text"));
3522 seeyalater:
3523 mail_free_envelope(&outgoing);
3524 mail_free_address(&saved_from);
3525 mail_free_address(&saved_to);
3526 mail_free_address(&saved_cc);
3527 mail_free_address(&saved_resent);
3529 if(prefix)
3530 fs_give((void **) &prefix);
3532 if(fcc)
3533 fs_give((void **) &fcc);
3535 free_redraft_pos(&redraft_pos);
3536 free_action(&role);
3540 void
3541 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
3543 char *errstr;
3545 if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
3546 q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
3550 void
3551 pipe_attachment(long int msgno, ATTACH_S *a)
3553 char *err, *resultfilename = NULL, prompt[80], *p;
3554 int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
3555 long ku;
3556 PIPE_S *syspipe;
3557 HelpType help;
3558 char pipe_command[MAXPATH+1];
3559 unsigned flagsforhist = 1; /* raw=2 /capture=1 */
3560 static HISTORY_S *history = NULL;
3561 ESCKEY_S pipe_opt[6];
3563 if(ps_global->restricted){
3564 q_status_message(SM_ORDER | SM_DING, 0, 4,
3565 "Alpine demo can't pipe attachments");
3566 return;
3569 help = NO_HELP;
3570 pipe_command[0] = '\0';
3572 init_hist(&history, HISTSIZE);
3573 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3574 if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
3575 strncpy(pipe_command, p, sizeof(pipe_command));
3576 pipe_command[sizeof(pipe_command)-1] = '\0';
3577 if(history->hist[history->curindex]){
3578 flagsforhist = history->hist[history->curindex]->flags;
3579 raw = (flagsforhist & 0x2) ? 1 : 0;
3580 capture = (flagsforhist & 0x1) ? 1 : 0;
3584 pipe_opt[j].ch = 0;
3585 pipe_opt[j].rval = 0;
3586 pipe_opt[j].name = "";
3587 pipe_opt[j++].label = "";
3589 pipe_opt[j].ch = ctrl('W');
3590 pipe_opt[j].rval = 10;
3591 pipe_opt[j].name = "^W";
3592 pipe_opt[j++].label = NULL;
3594 pipe_opt[j].ch = ctrl('Y');
3595 pipe_opt[j].rval = 11;
3596 pipe_opt[j].name = "^Y";
3597 pipe_opt[j++].label = NULL;
3599 pipe_opt[j].ch = KEY_UP;
3600 pipe_opt[j].rval = 30;
3601 pipe_opt[j].name = "";
3602 ku = j;
3603 pipe_opt[j++].label = "";
3605 pipe_opt[j].ch = KEY_DOWN;
3606 pipe_opt[j].rval = 31;
3607 pipe_opt[j].name = "";
3608 pipe_opt[j++].label = "";
3610 pipe_opt[j].ch = -1;
3612 while(1){
3613 int flags;
3615 snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
3616 a->number, capture ? "" : "(Free Output) ");
3617 prompt[sizeof(prompt)-1] = '\0';
3618 pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
3619 pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
3622 * 2 is really 1 because there will be one real entry and
3623 * one entry of "" because of the get_prev_hist above.
3625 if(items_in_hist(history) > 2){
3626 pipe_opt[ku].name = HISTORY_UP_KEYNAME;
3627 pipe_opt[ku].label = HISTORY_KEYLABEL;
3628 pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
3629 pipe_opt[ku+1].label = HISTORY_KEYLABEL;
3631 else{
3632 pipe_opt[ku].name = "";
3633 pipe_opt[ku].label = "";
3634 pipe_opt[ku+1].name = "";
3635 pipe_opt[ku+1].label = "";
3638 flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
3639 rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
3640 sizeof(pipe_command), prompt,
3641 pipe_opt, help, &flags);
3642 if(rc == -1){
3643 q_status_message(SM_ORDER | SM_DING, 3, 4,
3644 "Internal problem encountered");
3645 break;
3647 else if(rc == 10){
3648 raw = !raw; /* flip raw text */
3650 else if(rc == 11){
3651 capture = !capture; /* flip capture output */
3653 else if(rc == 30){
3654 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3655 if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3656 strncpy(pipe_command, p, sizeof(pipe_command));
3657 pipe_command[sizeof(pipe_command)-1] = '\0';
3658 if(history->hist[history->curindex]){
3659 flagsforhist = history->hist[history->curindex]->flags;
3660 raw = (flagsforhist & 0x2) ? 1 : 0;
3661 capture = (flagsforhist & 0x1) ? 1 : 0;
3664 else
3665 Writechar(BELL, 0);
3667 else if(rc == 31){
3668 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3669 if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3670 strncpy(pipe_command, p, sizeof(pipe_command));
3671 pipe_command[sizeof(pipe_command)-1] = '\0';
3672 if(history->hist[history->curindex]){
3673 flagsforhist = history->hist[history->curindex]->flags;
3674 raw = (flagsforhist & 0x2) ? 1 : 0;
3675 capture = (flagsforhist & 0x1) ? 1 : 0;
3678 else
3679 Writechar(BELL, 0);
3681 else if(rc == 0){
3682 if(pipe_command[0] == '\0'){
3683 cmd_cancelled("Pipe command");
3684 break;
3687 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3688 save_hist(history, pipe_command, flagsforhist, NULL);
3690 flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
3691 flags |= (raw ? PIPE_RAW : 0);
3692 if(!capture){
3693 #ifndef _WINDOWS
3694 ClearScreen();
3695 fflush(stdout);
3696 clear_cursor_pos();
3697 ps_global->mangled_screen = 1;
3698 #endif
3699 flags |= PIPE_RESET;
3702 if((syspipe = open_system_pipe(pipe_command,
3703 (flags&PIPE_RESET) ? NULL : &resultfilename,
3704 NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
3705 int is_text = 0;
3706 gf_io_t pc; /* wire up a generic putchar */
3708 is_text = (a && a->body && a->body->type == TYPETEXT);
3710 gf_set_writec(&pc, syspipe, 0L, PipeStar,
3711 (is_text && !raw) ? WRITE_TO_LOCALE : 0);
3713 /*------ Write the image to a temporary file ------*/
3714 if(raw){ /* pipe raw text */
3715 FETCH_READC_S fetch_part;
3717 err = NULL;
3719 if(capture)
3720 we_cancel = busy_cue(NULL, NULL, 1);
3721 else
3722 suspend_busy_cue();
3724 gf_filter_init();
3725 fetch_readc_init(&fetch_part, ps_global->mail_stream,
3726 msgno, a->number, a->body->size.bytes, 0, 0);
3727 gf_link_filter(gf_nvtnl_local, NULL);
3728 err = gf_pipe(FETCH_READC, pc);
3730 if(capture){
3731 if(we_cancel)
3732 cancel_busy_cue(0);
3734 else
3735 resume_busy_cue(0);
3737 else{
3738 /* BUG: there's got to be a better way */
3739 if(!capture)
3740 ps_global->print = (PRINT_S *) 1;
3742 suspend_busy_cue();
3743 err = detach(ps_global->mail_stream, msgno,
3744 a->number, 0L, (long *)NULL, pc, NULL, 0);
3745 ps_global->print = (PRINT_S *) NULL;
3746 resume_busy_cue(0);
3749 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
3751 if(err)
3752 q_status_message1(SM_ORDER | SM_DING, 3, 4,
3753 _("Error detaching for pipe: %s"), err);
3755 display_output_file(resultfilename,
3756 (err)
3757 ? _("PIPE ATTACHMENT (ERROR)")
3758 : _("PIPE ATTACHMENT"),
3759 NULL, DOF_EMPTY);
3761 fs_give((void **) &resultfilename);
3763 else
3764 q_status_message(SM_ORDER | SM_DING, 3, 4,
3765 _("Error opening pipe"));
3767 break;
3769 else if(rc == 1){
3770 cmd_cancelled("Pipe");
3771 break;
3773 else if(rc == 3)
3774 help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
3780 delete_attachment(long int msgno, ATTACH_S *a)
3782 int expbits, rv = 0;
3784 if(!msgno_exceptions(ps_global->mail_stream, msgno,
3785 a->number, &expbits, FALSE)
3786 || !(expbits & MSG_EX_DELETE)){
3787 expbits |= MSG_EX_DELETE;
3788 msgno_exceptions(ps_global->mail_stream, msgno,
3789 a->number, &expbits, TRUE);
3791 q_status_message1(SM_ORDER, 0, 3,
3792 _("Part %s will be omitted only if message is Saved"),
3793 a->number);
3794 rv = 1;
3796 else
3797 q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
3798 a->number);
3800 return(rv);
3805 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
3807 int rv = 0;
3809 if(msgno_exceptions(ps_global->mail_stream, msgno,
3810 a->number, expbitsp, FALSE)
3811 && ((*expbitsp) & MSG_EX_DELETE)){
3812 (*expbitsp) ^= MSG_EX_DELETE;
3813 msgno_exceptions(ps_global->mail_stream, msgno,
3814 a->number, expbitsp, TRUE);
3815 rv = 1;
3817 else
3818 q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
3819 a->number);
3821 return(rv);
3825 /*----------------------------------------------------------------------
3826 Resolve any deferred tests for attachment displayability
3828 Args: attachment structure
3830 Returns: undefer's attachment's displayability test
3831 ----*/
3833 dispatch_attachment(ATTACH_S *a)
3835 if(a->test_deferred){
3836 a->test_deferred = 0;
3837 a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
3840 return(a->can_display);
3844 #ifdef _WINDOWS
3846 scroll_att_popup(sparms, in_handle)
3847 SCROLL_S *sparms;
3848 int in_handle;
3850 MPopup scrat_popup[20];
3851 int i = -1, n;
3853 if(in_handle){
3854 scrat_popup[++i].type = tIndex;
3855 scrat_popup[i].label.style = lNormal;
3856 scrat_popup[i].label.string = "View Selectable Item";
3857 scrat_popup[i].data.val = ctrl('L');
3860 scrat_popup[++i].type = tQueue;
3861 scrat_popup[i].label.style = lNormal;
3862 scrat_popup[i].label.string = "&Save";
3863 scrat_popup[i].data.val = 'S';
3865 scrat_popup[++i].type = tQueue;
3866 scrat_popup[i].label.style = lNormal;
3867 if(msgno_exceptions(ps_global->mail_stream,
3868 mn_m2raw(ps_global->msgmap,
3869 mn_get_cur(ps_global->msgmap)),
3870 scrat_attachp->number, &n, FALSE)
3871 && (n & MSG_EX_DELETE)){
3872 scrat_popup[i].label.string = "&Undelete";
3873 scrat_popup[i].data.val = 'U';
3875 else{
3876 scrat_popup[i].label.string = "&Delete";
3877 scrat_popup[i].data.val = 'D';
3880 if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
3881 scrat_popup[++i].type = tQueue;
3882 scrat_popup[i].label.style = lNormal;
3883 scrat_popup[i].label.string = "&Reply";
3884 scrat_popup[i].data.val = 'R';
3886 scrat_popup[++i].type = tQueue;
3887 scrat_popup[i].label.style = lNormal;
3888 scrat_popup[i].label.string = "&Forward";
3889 scrat_popup[i].data.val = 'f';
3892 scrat_popup[++i].type = tSeparator;
3894 scrat_popup[++i].type = tQueue;
3895 scrat_popup[i].label.style = lNormal;
3896 scrat_popup[i].label.string = "Attachment Index";
3897 scrat_popup[i].data.val = '<';
3899 scrat_popup[++i].type = tTail;
3901 return(mswin_popup(scrat_popup) == 0 && in_handle);
3905 void
3906 display_att_window(a)
3907 ATTACH_S *a;
3909 #if !defined(DOS) && !defined(OS2)
3910 char prefix[8];
3911 #endif
3913 if(a->body->type == TYPEMULTIPART){
3914 if(a->body->subtype){
3915 /* if(!strucmp(a->body->subtype, "digest"))
3916 display_digest_att(msgno, a, flags);
3917 else */
3918 q_status_message1(SM_ORDER, 3, 5,
3919 "Can't display Multipart/%s",
3920 a->body->subtype);
3922 else
3923 q_status_message(SM_ORDER, 3, 5,
3924 "Can't display unknown Multipart Subtype");
3926 /* else if(MIME_VCARD_A(a))
3927 display_vcard_att_window(msgno, a, flags);*/
3928 else if(a->body->type == TYPETEXT)
3929 display_text_att_window(a);
3930 else if(a->body->type == TYPEMESSAGE)
3931 display_msg_att_window(a);
3935 void
3936 display_text_att_window(a)
3937 ATTACH_S *a;
3939 STORE_S *store;
3940 long msgno;
3942 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
3944 if(store = format_text_att(msgno, a, NULL)){
3945 if (mswin_displaytext("ATTACHED TEXT",
3946 so_text(store),
3947 strlen((char *) so_text(store)),
3948 NULL, NULL, 0) >= 0)
3949 store->txt = (void *) NULL; /* free'd in mswin_displaytext */
3951 so_give(&store); /* free resources associated with store */
3953 else
3954 q_status_message(SM_ORDER | SM_DING, 3, 3,
3955 "Error allocating space for attachment.");
3959 void
3960 display_msg_att_window(a)
3961 ATTACH_S *a;
3963 STORE_S *store;
3964 gf_io_t pc;
3965 ATTACH_S *ap = a;
3966 long msgno;
3968 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
3970 /* BUG, should check this return code */
3971 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
3973 /* initialize a storage object */
3974 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
3976 gf_set_so_writec(&pc, store);
3978 if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
3979 && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
3980 strlen((char *) so_text(store)),
3981 NULL, NULL, 0) >= 0)
3982 /* free'd in mswin_displaytext */
3983 store->txt = (void *) NULL;
3985 gf_clear_so_writec(store);
3987 so_give(&store);
3989 else
3990 q_status_message(SM_ORDER | SM_DING, 3, 3,
3991 "Error allocating space for message.");
3993 #endif