* Patches from MichaƂ Dardas and Mateusz Kocielski from LogicalTrust
[alpine.git] / alpine / mailpart.c
blobc1c8101bb13efbb7e56e22047319a7b917366997
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailpart.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 mailpart.c
21 The meat and pototoes of attachment processing here.
23 ====*/
25 #include "headers.h"
26 #include "mailpart.h"
27 #include "status.h"
28 #include "context.h"
29 #include "keymenu.h"
30 #include "alpine.h"
31 #include "reply.h"
32 #include "radio.h"
33 #include "takeaddr.h"
34 #include "mailview.h"
35 #include "mailindx.h"
36 #include "mailcmd.h"
37 #include "help.h"
38 #include "titlebar.h"
39 #include "signal.h"
40 #include "send.h"
41 #include "busy.h"
42 #include "smime.h"
43 #include "../pith/state.h"
44 #include "../pith/conf.h"
45 #include "../pith/store.h"
46 #include "../pith/msgno.h"
47 #include "../pith/detach.h"
48 #include "../pith/handle.h"
49 #include "../pith/filter.h"
50 #include "../pith/bitmap.h"
51 #include "../pith/charset.h"
52 #include "../pith/mimedesc.h"
53 #include "../pith/mailcap.h"
54 #include "../pith/newmail.h"
55 #include "../pith/rfc2231.h"
56 #include "../pith/flag.h"
57 #include "../pith/text.h"
58 #include "../pith/editorial.h"
59 #include "../pith/save.h"
60 #include "../pith/pipe.h"
61 #include "../pith/util.h"
62 #include "../pith/detoken.h"
63 #include "../pith/busy.h"
64 #include "../pith/mimetype.h"
65 #include "../pith/icache.h"
66 #include "../pith/list.h"
67 #include "../pith/ablookup.h"
68 #include "../pith/options.h"
69 #include "../pith/smime.h"
70 #include "../pith/ical.h"
71 #include "../pith/body.h"
74 * Information used to paint and maintain a line on the attachment
75 * screen.
77 typedef struct atdisp_line {
78 char *dstring; /* alloc'd var value string */
79 ATTACH_S *attp; /* actual attachment pointer */
80 struct atdisp_line *next, *prev;
81 } ATDISP_S;
85 * struct defining attachment screen's current state
87 typedef struct att_screen {
88 ATDISP_S *current,
89 *top_line;
90 COLOR_PAIR *titlecolor;
91 } ATT_SCREEN_S;
93 static ATT_SCREEN_S *att_screen;
96 #define next_attline(p) ((p) ? (p)->next : NULL)
97 #define prev_attline(p) ((p) ? (p)->prev : NULL)
100 #ifdef _WINDOWS
102 * local global pointer to scroll attachment popup menu
104 static ATTACH_S *scrat_attachp;
105 #endif
108 #define FETCH_READC g_fr_desc->readc
111 /* used to keep track of detached MIME segments total length */
112 static long save_att_length;
115 * Internal Prototypes
117 ATDISP_S *new_attline(ATDISP_S **);
118 void free_attline(ATDISP_S **);
119 int attachment_screen_updater(struct pine *, ATDISP_S *, ATT_SCREEN_S *);
120 void attachment_screen_redrawer(void);
121 void update_att_screen_titlebar(void);
122 ATDISP_S *first_attline(ATDISP_S *);
123 int init_att_progress(char *, MAILSTREAM *, BODY *);
124 long save_att_piped(int);
125 int save_att_percent(void);
126 void save_attachment(int, long, ATTACH_S *);
127 void export_attachment(int, long, ATTACH_S *);
128 char *write_attached_msg(long, ATTACH_S **, STORE_S *, int);
129 void save_msg_att(long, ATTACH_S *);
130 void save_digest_att(long, ATTACH_S *);
131 int save_msg_att_work(long int, ATTACH_S *, MAILSTREAM *, char *,
132 CONTEXT_S *, char *);
133 void export_msg_att(long, ATTACH_S *);
134 void export_digest_att(long, ATTACH_S *);
135 void print_attachment(int, long, ATTACH_S *);
136 int print_msg_att(long, ATTACH_S *, int);
137 void print_digest_att(long, ATTACH_S *);
138 void run_viewer(char *, BODY *, int);
139 STORE_S *format_text_att(long, ATTACH_S *, HANDLE_S **);
140 int display_text_att(long, ATTACH_S *, int);
141 int display_msg_att(long, ATTACH_S *, int);
142 void display_digest_att(long, ATTACH_S *, int);
143 int scroll_attachment(char *, STORE_S *, SourceType, HANDLE_S *, ATTACH_S *, int);
144 int process_attachment_cmd(int, MSGNO_S *, SCROLL_S *);
145 int format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
146 void display_vcard_att(long, ATTACH_S *, int);
147 void display_vcalendar_att(long, ATTACH_S *, int);
148 void display_attach_info(long, ATTACH_S *);
149 void forward_attachment(MAILSTREAM *, long, ATTACH_S *);
150 void forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
151 void reply_msg_att(MAILSTREAM *, long, ATTACH_S *);
152 void bounce_msg_att(MAILSTREAM *, long, char *, char *);
153 void pipe_attachment(long, ATTACH_S *);
154 int delete_attachment(long, ATTACH_S *);
155 int undelete_attachment(long, ATTACH_S *, int *);
156 #ifdef _WINDOWS
157 int scroll_att_popup(SCROLL_S *, int);
158 void display_text_att_window(ATTACH_S *);
159 void display_msg_att_window(ATTACH_S *);
160 #endif
163 /*----------------------------------------------------------------------
164 Provide attachments in browser for selected action
166 Args: ps -- pointer to pine structure
167 msgmap -- struct containing current message to display
169 Result:
171 Handle painting and navigation of attachment index. It would be nice
172 to someday handle message/rfc822 segments in a neat way (i.e., enable
173 forwarding, take address, etc.).
174 ----*/
175 void
176 attachment_screen(struct pine *ps)
178 UCS ch = 'x';
179 int n, cmd, dline,
180 maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
181 last_type = TYPEOTHER;
182 long msgno;
183 char *q, *last_subtype = NULL, backtag[64], *utf8str;
184 OtherMenu what;
185 ATTACH_S *a;
186 ATDISP_S *current = NULL, *ctmp = NULL;
187 ATT_SCREEN_S screen;
189 ps->prev_screen = attachment_screen;
190 ps->next_screen = SCREEN_FUN_NULL;
192 ps->some_quoting_was_suppressed = 0;
194 if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
195 q_status_message(SM_ORDER | SM_DING, 0, 3,
196 _("Screen too small to view attachment"));
197 ps->next_screen = mail_view_screen;
198 return;
201 if(mn_total_cur(ps->msgmap) > 1L){
202 q_status_message(SM_ORDER | SM_DING, 0, 3,
203 _("Can only view one message's attachments at a time."));
204 return;
206 else if(ps->atmts && ps->atmts->description && !(ps->atmts + 1)->description)
207 q_status_message1(SM_ASYNC, 0, 3,
208 _("Message %s has only one part (the message body), and no attachments."),
209 long2string(mn_get_cur(ps->msgmap)));
211 /*----- Figure max display widths -----*/
212 for(a = ps->atmts; a && a->description != NULL; a++){
213 if((n = utf8_width(a->number)) > maxnumwid)
214 maxnumwid = n;
216 if((n = utf8_width(a->size)) > maxsizewid)
217 maxsizewid = n;
221 * Then, allocate and initialize attachment line list...
223 for(a = ps->atmts; a && a->description; a++)
224 new_attline(&current)->attp = a;
226 memset(&screen, 0, sizeof(screen));
227 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
228 ps->mangled_screen = 1; /* build display */
229 ps->redrawer = attachment_screen_redrawer;
230 att_screen = &screen;
231 current = first_attline(current);
232 what = FirstMenu;
235 * Advance to next attachment if it's likely that we've already
236 * shown it to the user...
239 if (current == NULL){
240 q_status_message1(SM_ORDER | SM_DING, 0, 3,
241 _("Malformed message: %s"),
242 ps->c_client_error ? ps->c_client_error : "?");
243 ps->next_screen = mail_view_screen;
245 else if(current->attp->shown && (ctmp = next_attline(current)))
246 current = ctmp;
248 while(ps->next_screen == SCREEN_FUN_NULL){
249 ps->user_says_cancel = 0;
250 if(km_popped){
251 km_popped--;
252 if(km_popped == 0){
253 clearfooter(ps);
254 ps->mangled_body = 1;
258 if(ps->mangled_screen){
260 * build/rebuild display lines
262 if(old_cols != ps->ttyo->screen_cols){
263 int avail, s1, s2, s4, s5, dwid, sizewid, descwid;
265 avail = ps_global->ttyo->screen_cols;
267 s1 = MAX(MIN(1, avail), 0);
268 avail -= s1;
270 dwid = MAX(MIN(1, avail), 0);
271 avail -= dwid;
273 s2 = MAX(MIN(1, avail), 0);
274 avail -= s2;
276 /* Only give up a third of the display to part numbers */
277 maxnumwid = MIN(maxnumwid, (ps_global->ttyo->screen_cols/3));
278 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
279 avail -= maxnumwid;
281 s4 = MAX(MIN(3, avail), 0);
282 avail -= s4;
284 sizewid = MAX(MIN(maxsizewid, avail), 0);
285 avail -= sizewid;
287 s5 = MAX(MIN(3, avail), 0);
288 avail -= s5;
290 descwid = MAX(0, avail);
292 old_cols = ps->ttyo->screen_cols;
294 for(ctmp = first_attline(current);
295 ctmp && (a = ctmp->attp) && a->description;
296 ctmp = next_attline(ctmp)){
297 size_t len;
298 char numbuf[50];
299 char description[1000];
301 if(ctmp->dstring)
302 fs_give((void **)&ctmp->dstring);
304 len = 6 * MAX(80, ps->ttyo->screen_cols) * sizeof(char);
305 ctmp->dstring = (char *)fs_get(len + 1);
308 * description
310 q = a->body->description;
311 if(!(q && q[0]) && (MIME_MSG_A(a) && a->body->nested.msg->env))
312 q = a->body->nested.msg->env->subject;
314 if(q && q[0]){
315 char buftmp[1000];
317 strncpy(buftmp, q, sizeof(buftmp));
318 buftmp[sizeof(buftmp)-1] = '\0';
320 q = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
321 SIZEOF_20KBUF, buftmp);
324 if(q && !q[0])
325 q = NULL;
327 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 ? "\"" : "");
328 description[sizeof(description)-1] = '\0';
330 utf8_snprintf(ctmp->dstring, len+1,
331 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%-*.*w",
332 s1, s1, "",
333 dwid, dwid,
334 msgno_part_deleted(ps->mail_stream, msgno, a->number) ? "D" : "",
335 s2, s2, "",
336 maxnumwid, maxnumwid,
337 a->number
338 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
339 : "",
340 s4, s4, "",
341 sizewid, sizewid,
342 a->size ? a->size : "",
343 s5, s5, "",
344 descwid, descwid, description);
346 ctmp->dstring[len] = '\0';
350 ps->mangled_header = 1;
351 ps->mangled_footer = 1;
352 ps->mangled_body = 1;
355 /*----------- Check for new mail -----------*/
356 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
357 ps->mangled_header = 1;
360 * If an expunge of the current message happened during the
361 * new mail check we want to bail out of here. See mm_expunged.
363 if(ps->next_screen != SCREEN_FUN_NULL)
364 break;
366 if(ps->mangled_header){
367 update_att_screen_titlebar();
368 ps->mangled_header = 0;
371 if(ps->mangled_screen){
372 ClearLine(1);
373 ps->mangled_screen = 0;
376 dline = attachment_screen_updater(ps, current, &screen);
378 /*---- This displays new mail notification, or errors ---*/
379 if(km_popped){
380 FOOTER_ROWS(ps) = 3;
381 mark_status_unknown();
384 display_message(ch);
385 if(km_popped){
386 FOOTER_ROWS(ps) = 1;
387 mark_status_unknown();
390 if(ps->mangled_footer
391 || current->attp->body->type != last_type
392 || !(last_subtype && current->attp->body->subtype)
393 || strucmp(last_subtype, current->attp->body->subtype)){
395 struct key_menu *km = &att_index_keymenu;
396 bitmap_t bitmap;
398 setbitmap(bitmap);
399 ps->mangled_footer = 0;
400 last_type = current->attp->body->type;
401 last_subtype = current->attp->body->subtype;
403 snprintf(backtag, sizeof(backtag), "Msg #%ld", mn_get_cur(ps->msgmap));
404 backtag[sizeof(backtag)-1] = '\0';
405 km->keys[ATT_PARENT_KEY].label = backtag;
407 if(F_OFF(F_ENABLE_PIPE, ps))
408 clrbitn(ATT_PIPE_KEY, bitmap);
411 * If message or digest, leave Reply and Save and,
412 * conditionally, Bounce on...
414 if(MIME_MSG(last_type, last_subtype)){
415 if(F_OFF(F_ENABLE_BOUNCE, ps))
416 clrbitn(ATT_BOUNCE_KEY, bitmap);
418 km->keys[ATT_EXPORT_KEY].name = "";
419 km->keys[ATT_EXPORT_KEY].label = "";
421 else if(MIME_DGST(last_type, last_subtype)){
422 clrbitn(ATT_BOUNCE_KEY, bitmap);
423 clrbitn(ATT_REPLY_KEY, bitmap);
425 km->keys[ATT_EXPORT_KEY].name = "";
426 km->keys[ATT_EXPORT_KEY].label = "";
428 else{
429 clrbitn(ATT_BOUNCE_KEY, bitmap);
430 clrbitn(ATT_REPLY_KEY, bitmap);
432 if(last_type != TYPETEXT)
433 clrbitn(ATT_PRINT_KEY, bitmap);
435 km->keys[ATT_EXPORT_KEY].name = "E";
436 km->keys[ATT_EXPORT_KEY].label = N_("Export");
439 if(km_popped){
440 FOOTER_ROWS(ps) = 3;
441 clearfooter(ps);
444 if(F_ON(F_ARROW_NAV, ps)){
445 menu_add_binding(km, KEY_LEFT, MC_EXIT);
446 menu_add_binding(km, KEY_RIGHT, MC_VIEW_ATCH);
448 else{
449 menu_clear_binding(km, KEY_LEFT);
450 menu_clear_binding(km, KEY_RIGHT);
453 draw_keymenu(km, bitmap, ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps),
454 0, what);
455 what = SameMenu;
456 if(km_popped){
457 FOOTER_ROWS(ps) = 1;
458 mark_keymenu_dirty();
462 if(F_ON(F_SHOW_CURSOR, ps))
463 MoveCursor(dline, 0);
464 else
465 MoveCursor(MAX(0, ps->ttyo->screen_rows - FOOTER_ROWS(ps)), 0);
467 /*------ Prepare to read the command from the keyboard ----*/
468 #ifdef MOUSE
469 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
470 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
471 ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
472 ps_global->ttyo->screen_cols);
473 #endif
474 ch = READ_COMMAND(&utf8str);
475 #ifdef MOUSE
476 clear_mfunc(mouse_in_content);
477 #endif
479 cmd = menu_command(ch, &att_index_keymenu);
481 if(km_popped)
482 switch(cmd){
483 case MC_NONE :
484 case MC_OTHER :
485 case MC_RESIZE :
486 case MC_REPAINT :
487 km_popped++;
488 break;
490 default:
491 clearfooter(ps);
492 break;
495 switch(cmd){
496 case MC_HELP :
497 if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
498 km_popped = 2;
499 ps->mangled_footer = 1;
500 break;
503 helper(h_attachment_screen, _("HELP FOR ATTACHMENT INDEX"), 0);
504 ps->mangled_screen = 1;
505 break;
507 case MC_OTHER :
508 what = NextMenu;
509 ps->mangled_footer = 1;
510 break;
512 case MC_FULLHDR :
513 ps->full_header++;
514 if(ps->full_header == 1){
515 if(!(ps->quote_suppression_threshold
516 && (ps->some_quoting_was_suppressed /* || in_index != View */)))
517 ps->full_header++;
519 else if(ps->full_header > 2)
520 ps->full_header = 0;
522 switch(ps->full_header){
523 case 0:
524 q_status_message(SM_ORDER, 0, 3,
525 _("Display of full headers is now off."));
526 break;
528 case 1:
529 q_status_message1(SM_ORDER, 0, 3,
530 _("Quotes displayed, use %s to see full headers"),
531 F_ON(F_USE_FK, ps) ? "F9" : "H");
532 break;
534 case 2:
535 q_status_message(SM_ORDER, 0, 3,
536 _("Display of full headers is now on."));
537 break;
541 break;
543 case MC_EXIT : /* exit attachment screen */
544 ps->next_screen = mail_view_screen;
545 break;
547 case MC_QUIT :
548 ps->next_screen = quit_screen;
549 break;
551 case MC_MAIN :
552 ps->next_screen = main_menu_screen;
553 break;
555 case MC_INDEX :
556 ps->next_screen = mail_index_screen;
557 break;
559 case MC_NEXTITEM :
560 if((ctmp = next_attline(current)) != NULL)
561 current = ctmp;
562 else
563 q_status_message(SM_ORDER, 0, 1, _("Already on last attachment"));
565 break;
567 case MC_PREVITEM :
568 if((ctmp = prev_attline(current)) != NULL)
569 current = ctmp;
570 else
571 q_status_message(SM_ORDER, 0, 1, _("Already on first attachment"));
573 break;
575 case MC_PAGEDN : /* page forward */
576 if(next_attline(current)){
577 while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
578 if((ctmp = next_attline(current)) != NULL)
579 current = ctmp;
580 else
581 break;
583 else
584 q_status_message(SM_ORDER, 0, 1,
585 _("Already on last page of attachments"));
587 break;
589 case MC_PAGEUP : /* page backward */
590 if(prev_attline(current)){
591 while(dline-- > HEADER_ROWS(ps))
592 if((ctmp = prev_attline(current)) != NULL)
593 current = ctmp;
594 else
595 break;
597 while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
598 if((ctmp = prev_attline(current)) != NULL)
599 current = ctmp;
600 else
601 break;
603 else
604 q_status_message(SM_ORDER, 0, 1,
605 _("Already on first page of attachments"));
607 break;
609 #ifdef MOUSE
610 case MC_MOUSE:
612 MOUSEPRESS mp;
614 mouse_get_last (NULL, &mp);
615 mp.row -= HEADER_ROWS(ps);
616 ctmp = screen.top_line;
617 while (mp.row && ctmp != NULL) {
618 --mp.row;
619 ctmp = ctmp->next;
622 if (ctmp != NULL){
623 current = ctmp;
625 if (mp.doubleclick){
626 if(mp.button == M_BUTTON_LEFT)
627 display_attachment(msgno, current->attp, DA_SAVE);
629 #ifdef _WINDOWS
630 else if(mp.button == M_BUTTON_RIGHT){
631 MPopup atmt_popup[20];
632 int i = -1, exoffer = 0;
634 dline = attachment_screen_updater(ps,current,&screen);
636 if(dispatch_attachment(current->attp) != MCD_NONE){
637 atmt_popup[++i].type = tQueue;
638 atmt_popup[i].data.val = 'V';
639 atmt_popup[i].label.style = lNormal;
640 atmt_popup[i].label.string = "&View";
642 if(!(current->attp->can_display & MCD_EXTERNAL)
643 && (current->attp->body->type == TYPETEXT
644 || MIME_MSG_A(current->attp)
645 || MIME_DGST_A(current->attp))){
646 exoffer++;
647 atmt_popup[++i].type = tIndex;
648 atmt_popup[i].label.style = lNormal;
649 atmt_popup[i].label.string =
650 "View in New Window";
654 atmt_popup[++i].type = tQueue;
655 atmt_popup[i].data.val = 'S';
656 atmt_popup[i].label.style = lNormal;
657 atmt_popup[i].label.string = "&Save";
659 atmt_popup[++i].type = tQueue;
660 atmt_popup[i].label.style = lNormal;
661 if(current->dstring[1] == 'D'){
662 atmt_popup[i].data.val = 'U';
663 atmt_popup[i].label.string = "&Undelete";
665 else{
666 atmt_popup[i].data.val = 'D';
667 atmt_popup[i].label.string = "&Delete";
670 if(MIME_MSG_A(current->attp)){
671 atmt_popup[++i].type = tQueue;
672 atmt_popup[i].label.style = lNormal;
673 atmt_popup[i].label.string = "&Reply...";
674 atmt_popup[i].data.val = 'R';
676 atmt_popup[++i].type = tQueue;
677 atmt_popup[i].label.style = lNormal;
678 atmt_popup[i].label.string = "&Forward...";
679 atmt_popup[i].data.val = 'F';
682 atmt_popup[++i].type = tQueue;
683 atmt_popup[i].label.style = lNormal;
684 atmt_popup[i].label.string = "&About...";
685 atmt_popup[i].data.val = 'A';
687 atmt_popup[++i].type = tSeparator;
689 atmt_popup[++i].type = tQueue;
690 snprintf(backtag, sizeof(backtag), "View Message #%ld",
691 mn_get_cur(ps->msgmap));
692 backtag[sizeof(backtag)-1] = '\0';
693 atmt_popup[i].label.string = backtag;
694 atmt_popup[i].label.style = lNormal;
695 atmt_popup[i].data.val = '<';
697 atmt_popup[++i].type = tTail;
699 if(mswin_popup(atmt_popup) == 1 && exoffer)
700 display_att_window(current->attp);
703 else if(mp.button == M_BUTTON_RIGHT){
704 MPopup atmt_popup[2];
706 atmt_popup[0].type = tQueue;
707 snprintf(backtag, sizeof(backtag), "View Message #%ld",
708 mn_get_cur(ps->msgmap));
709 backtag[sizeof(backtag)-1] = '\0';
710 atmt_popup[0].label.string = backtag;
711 atmt_popup[0].label.style = lNormal;
712 atmt_popup[0].data.val = '<';
714 atmt_popup[1].type = tTail;
716 mswin_popup(atmt_popup);
717 #endif
720 break;
721 #endif
723 case MC_WHEREIS : /* whereis */
724 /*--- get string ---*/
725 {int rc, found = 0;
726 char *result = NULL, buf[64];
727 static char last[64], tmp[64];
728 HelpType help;
730 ps->mangled_footer = 1;
731 buf[0] = '\0';
732 snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ",
733 (last[0]) ? "[" : "",
734 (last[0]) ? last : "",
735 (last[0]) ? "]" : "");
736 tmp[sizeof(tmp)-1] = '\0';
737 help = NO_HELP;
738 while(1){
739 int flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
741 rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf),
742 tmp,NULL,help,&flags);
743 if(rc == 3)
744 help = help == NO_HELP ? h_attach_index_whereis : NO_HELP;
745 else if(rc == 0 || rc == 1 || !buf[0]){
746 if(rc == 0 && !buf[0] && last[0]){
747 strncpy(buf, last, sizeof(buf));
748 buf[sizeof(buf)-1] = '\0';
751 break;
755 if(rc == 0 && buf[0]){
756 ctmp = current;
757 while((ctmp = next_attline(ctmp)) != NULL)
758 if(srchstr(ctmp->dstring, buf)){
759 found++;
760 break;
763 if(!found){
764 ctmp = first_attline(current);
766 while(ctmp != current)
767 if(srchstr(ctmp->dstring, buf)){
768 found++;
769 break;
771 else
772 ctmp = next_attline(ctmp);
775 else
776 result = _("WhereIs cancelled");
778 if(found && ctmp){
779 strncpy(last, buf, sizeof(last));
780 last[sizeof(last)-1] = '\0';
781 result = "Word found";
782 current = ctmp;
785 q_status_message(SM_ORDER, 0, 3,
786 result ? result : _("Word not found"));
789 break;
791 case MC_DELETE :
792 if(delete_attachment(msgno, current->attp)){
793 int l = current ? strlen(current->attp->number) : 0;
795 current->dstring[1] = 'D';
797 /* Also indicate any children that will be deleted */
799 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
800 if(!strncmp(ctmp->attp->number, current->attp->number, l)
801 && ctmp->attp->number[l] == '.'){
802 ctmp->dstring[1] = 'D';
803 ps->mangled_screen = 1;
807 break;
809 case MC_UNDELETE :
810 if(undelete_attachment(msgno, current->attp, &expbits)){
811 int l = current ? strlen(current->attp->number) : 0;
813 current->dstring[1] = ' ';
815 /* And unflag anything implicitly undeleted */
816 for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
817 if(!strncmp(ctmp->attp->number, current->attp->number, l)
818 && ctmp->attp->number[l] == '.'
819 && (!msgno_exceptions(ps->mail_stream, msgno,
820 ctmp->attp->number, &expbits, FALSE)
821 || !(expbits & MSG_EX_DELETE))){
822 ctmp->dstring[1] = ' ';
823 ps->mangled_screen = 1;
827 break;
829 case MC_REPLY :
830 reply_msg_att(ps->mail_stream, msgno, current->attp);
831 break;
833 case MC_FORWARD :
834 forward_attachment(ps->mail_stream, msgno, current->attp);
835 break;
837 case MC_BOUNCE :
838 bounce_msg_att(ps->mail_stream, msgno, current->attp->number,
839 current->attp->body->nested.msg->env->subject);
840 ps->mangled_footer = 1;
841 break;
843 case MC_ABOUTATCH :
844 display_attach_info(msgno, current->attp);
845 break;
847 case MC_VIEW_ATCH : /* View command */
848 display_attachment(msgno, current->attp, DA_SAVE);
849 old_cols = -1;
850 /* fall thru to repaint */
852 case MC_REPAINT : /* redraw */
853 case MC_RESIZE :
854 ps->mangled_screen = 1;
855 break;
857 case MC_EXPORT :
858 export_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
859 ps->mangled_footer = 1;
860 break;
862 case MC_SAVETEXT : /* Save command */
863 save_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
864 ps->mangled_footer = 1;
865 break;
867 case MC_PRINTMSG : /* Save command */
868 print_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
869 ps->mangled_footer = 1;
870 break;
872 case MC_PIPE : /* Pipe command */
873 if(F_ON(F_ENABLE_PIPE, ps)){
874 pipe_attachment(msgno, current->attp);
875 ps->mangled_footer = 1;
876 break;
877 } /* fall thru to complain */
879 case MC_NONE: /* simple timeout */
880 break;
882 case MC_UTF8:
883 bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
884 break;
886 case MC_CHARUP :
887 case MC_CHARDOWN :
888 case MC_CHARRIGHT :
889 case MC_CHARLEFT :
890 case MC_GOTOBOL :
891 case MC_GOTOEOL :
892 case MC_UNKNOWN :
893 default:
894 bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
899 for(current = first_attline(current); current;){ /* clean up */
900 ctmp = current->next;
901 free_attline(&current);
902 current = ctmp;
905 if(screen.titlecolor)
906 free_color_pair(&screen.titlecolor);
910 /*----------------------------------------------------------------------
911 allocate and attach a fresh attachment line struct
913 Args: current -- display line to attache new struct to
915 Returns: newly alloc'd attachment display line
916 ----*/
917 ATDISP_S *
918 new_attline(ATDISP_S **current)
920 ATDISP_S *p;
922 p = (ATDISP_S *)fs_get(sizeof(ATDISP_S));
923 memset((void *)p, 0, sizeof(ATDISP_S));
924 if(current){
925 if(*current){
926 p->next = (*current)->next;
927 (*current)->next = p;
928 p->prev = *current;
929 if(p->next)
930 p->next->prev = p;
933 *current = p;
936 return(p);
940 /*----------------------------------------------------------------------
941 Release system resources associated with attachment display line
943 Args: p -- line to free
945 Result:
946 ----*/
947 void
948 free_attline(ATDISP_S **p)
950 if(p){
951 if((*p)->dstring)
952 fs_give((void **)&(*p)->dstring);
954 fs_give((void **)p);
959 /*----------------------------------------------------------------------
960 Manage display of the attachment screen menu body.
962 Args: ps -- pine struct pointer
963 current -- currently selected display line
964 screen -- reference points for display painting
966 Result:
969 attachment_screen_updater(struct pine *ps, ATDISP_S *current, ATT_SCREEN_S *screen)
971 int dline, return_line = HEADER_ROWS(ps);
972 ATDISP_S *top_line, *ctmp;
974 /* calculate top line of display */
975 dline = 0;
976 ctmp = top_line = first_attline(current);
978 if(((dline++)%(ps->ttyo->screen_rows-HEADER_ROWS(ps)-FOOTER_ROWS(ps)))==0)
979 top_line = ctmp;
980 while(ctmp != current && (ctmp = next_attline(ctmp)));
982 #ifdef _WINDOWS
983 /* Don't know how to manage scroll bar for attachment screen yet. */
984 scroll_setrange (0L, 0L);
985 #endif
987 /* mangled body or new page, force redraw */
988 if(ps->mangled_body || screen->top_line != top_line)
989 screen->current = NULL;
991 /* loop thru painting what's needed */
992 for(dline = 0, ctmp = top_line;
993 dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
994 dline++, ctmp = next_attline(ctmp)){
997 * only fall thru painting if something needs painting...
999 if(!(!screen->current || ctmp == screen->current || ctmp == current))
1000 continue;
1002 if(ctmp && ctmp->dstring){
1003 char *p = tmp_20k_buf;
1004 int i, col, x = 0, totlen;
1006 if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
1007 x = 2;
1008 if(ctmp == current){
1009 return_line = dline + HEADER_ROWS(ps);
1010 PutLine0(dline + HEADER_ROWS(ps), 0, "->");
1012 else
1013 PutLine0(dline + HEADER_ROWS(ps), 0, " ");
1016 * Only paint lines if we have to...
1018 if(screen->current)
1019 continue;
1021 else if(ctmp == current){
1022 return_line = dline + HEADER_ROWS(ps);
1023 StartInverse();
1026 totlen = strlen(ctmp->dstring);
1029 * Copy the value to a temp buffer expanding tabs.
1030 * Assume the caller set the widths so as not to overflow the
1031 * right margin.
1033 for(i=0,col=x; ctmp->dstring[i]; i++){
1034 if(ctmp->dstring[i] == TAB){
1035 if((p-tmp_20k_buf) < SIZEOF_20KBUF-8)
1037 *p++ = ' ';
1038 while((++col)&0x07);
1040 else{
1041 col += width_at_this_position((unsigned char *) &ctmp->dstring[i], totlen-i);
1042 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1043 *p++ = ctmp->dstring[i];
1048 if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1049 *p = '\0';
1051 PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
1053 if(ctmp == current
1054 && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
1055 EndInverse();
1057 else
1058 ClearLine(dline + HEADER_ROWS(ps));
1061 ps->mangled_body = 0;
1062 screen->top_line = top_line;
1063 screen->current = current;
1064 return(return_line);
1068 /*----------------------------------------------------------------------
1069 Redraw the attachment screen based on the global "att_screen" struct
1071 Args: none
1073 Result:
1074 ----*/
1075 void
1076 attachment_screen_redrawer(void)
1078 bitmap_t bitmap;
1080 update_att_screen_titlebar();
1081 ps_global->mangled_header = 0;
1082 ClearLine(1);
1084 ps_global->mangled_body = 1;
1085 (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
1087 setbitmap(bitmap);
1088 draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
1089 1-FOOTER_ROWS(ps_global), 0, SameMenu);
1093 void
1094 update_att_screen_titlebar(void)
1096 long raw_msgno;
1097 COLOR_PAIR *returned_color = NULL;
1098 COLOR_PAIR *titlecolor = NULL;
1099 int colormatch;
1100 SEARCHSET *ss = NULL;
1101 PAT_STATE *pstate = NULL;
1103 if(ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
1104 raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
1105 ss = mail_newsearchset();
1106 ss->first = ss->last = (unsigned long) raw_msgno;
1108 if(ss){
1109 colormatch = get_index_line_color(ps_global->mail_stream,
1110 ss, &pstate, &returned_color);
1111 mail_free_searchset(&ss);
1114 * This is a bit tricky. If there is a colormatch but
1115 * returned_color
1116 * is NULL, that means that the user explicitly wanted the
1117 * Normal color used in this index line, so that is what we
1118 * use. If no colormatch then we will use the TITLE color
1119 * instead of Normal.
1121 if(colormatch){
1122 if(returned_color)
1123 titlecolor = returned_color;
1124 else
1125 titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
1126 ps_global->VAR_NORM_BACK_COLOR);
1129 if(titlecolor
1130 && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
1131 char cbuf[MAXCOLORLEN+1];
1133 strncpy(cbuf, titlecolor->fg, sizeof(cbuf));
1134 cbuf[sizeof(cbuf)-1] = '\0';
1135 strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
1136 titlecolor->fg[MAXCOLORLEN] = '\0';
1137 strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
1138 titlecolor->bg[MAXCOLORLEN] = '\0';
1142 /* Did the color change? */
1143 if((!titlecolor && att_screen->titlecolor)
1145 (titlecolor && !att_screen->titlecolor)
1147 (titlecolor && att_screen->titlecolor
1148 && (strcmp(titlecolor->fg, att_screen->titlecolor->fg)
1149 || strcmp(titlecolor->bg, att_screen->titlecolor->bg)))){
1151 if(att_screen->titlecolor)
1152 free_color_pair(&att_screen->titlecolor);
1154 att_screen->titlecolor = titlecolor;
1155 titlecolor = NULL;
1158 if(titlecolor)
1159 free_color_pair(&titlecolor);
1162 set_titlebar(_("ATTACHMENT INDEX"), ps_global->mail_stream,
1163 ps_global->context_current, ps_global->cur_folder,
1164 ps_global->msgmap, 1, MessageNumber, 0, 0,
1165 att_screen->titlecolor);
1169 /*----------------------------------------------------------------------
1170 Seek back from the given display line to the beginning of the list
1172 Args: p -- display linked list
1174 Result:
1175 ----*/
1176 ATDISP_S *
1177 first_attline(ATDISP_S *p)
1179 while(p && p->prev)
1180 p = p->prev;
1182 return(p);
1187 init_att_progress(char *msg, MAILSTREAM *stream, struct mail_bodystruct *body)
1189 if((save_att_length = body->size.bytes) != 0){
1190 /* if there are display filters, factor in extra copy */
1191 if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
1192 save_att_length += body->size.bytes;
1194 /* if remote folder and segment not cached, factor in IMAP fetch */
1195 if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
1196 && !((body->type == TYPETEXT && body->contents.text.data)
1197 || ((body->type == TYPEMESSAGE)
1198 && body->nested.msg && body->nested.msg->text.text.data)
1199 || body->contents.text.data))
1200 save_att_length += body->size.bytes;
1202 gf_filter_init(); /* reset counters */
1203 pine_gets_bytes(1);
1204 save_att_piped(1);
1205 return(busy_cue(msg, save_att_percent, 0));
1208 return(0);
1212 long
1213 save_att_piped(int reset)
1215 static long x;
1216 long y;
1218 if(reset){
1219 x = y = 0L;
1221 else if((y = gf_bytes_piped()) >= x){
1222 x = y;
1223 y = 0;
1226 return(x + y);
1231 save_att_percent(void)
1233 int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
1234 / save_att_length);
1235 return(MIN(i, 100));
1239 /*----------------------------------------------------------------------
1240 Save the given attachment associated with the given message no
1242 Args: ps
1244 Result:
1245 ----*/
1246 void
1247 save_attachment(int qline, long int msgno, ATTACH_S *a)
1249 if(ps_global->restricted){
1250 q_status_message(SM_ORDER | SM_DING, 0, 4,
1251 "Alpine demo can't save attachments");
1252 return;
1255 if(MIME_MSG_A(a))
1256 save_msg_att(msgno, a);
1257 else if(MIME_DGST_A(a))
1258 save_digest_att(msgno, a);
1259 else if(MIME_VCARD_A(a))
1260 save_vcard_att(ps_global, qline, msgno, a);
1261 else
1262 write_attachment(qline, msgno, a, "SAVE");
1266 /*----------------------------------------------------------------------
1267 Save the given attachment associated with the given message no
1269 Args: ps
1271 Result:
1272 ----*/
1273 void
1274 export_attachment(int qline, long int msgno, ATTACH_S *a)
1276 if(ps_global->restricted){
1277 q_status_message(SM_ORDER | SM_DING, 0, 4,
1278 "Alpine demo can't export attachments");
1279 return;
1282 if(MIME_MSG_A(a))
1283 export_msg_att(msgno, a);
1284 else if(MIME_DGST_A(a))
1285 export_digest_att(msgno, a);
1286 else
1287 q_status_message1(SM_ORDER, 0, 3,
1288 _("Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."),
1289 body_type_names(a->body->type));
1293 void
1294 write_attachment(int qline, long int msgno, ATTACH_S *a, char *method)
1296 char filename[MAXPATH+1], full_filename[MAXPATH+1],
1297 title_buf[64], *err;
1298 int r, rflags = GER_NONE, we_cancel = 0, flags;
1299 static HISTORY_S *history = NULL;
1300 static ESCKEY_S att_save_opts[] = {
1301 {ctrl('T'), 10, "^T", N_("To Files")},
1302 {-1, 0, NULL, NULL},
1303 {-1, 0, NULL, NULL},
1304 {-1, 0, NULL, NULL}};
1306 /*------- Figure out suggested file name ----*/
1307 filename[0] = full_filename[0] = '\0';
1308 (void) get_filename_parameter(filename, sizeof(filename), a->body, NULL);
1310 dprint((9, "export_attachment(name: %s)\n",
1311 filename ? filename : "?"));
1313 r = 0;
1314 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1315 if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
1316 att_save_opts[++r].ch = ctrl('V');
1317 att_save_opts[r].rval = 12;
1318 att_save_opts[r].name = "^V";
1319 att_save_opts[r].label = N_("Downld Msg");
1321 #endif /* !(DOS || MAC) */
1323 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1324 att_save_opts[++r].ch = ctrl('I');
1325 att_save_opts[r].rval = 11;
1326 att_save_opts[r].name = "TAB";
1327 att_save_opts[r].label = N_("Complete");
1330 att_save_opts[++r].ch = -1;
1332 snprintf(title_buf, sizeof(title_buf), "%s ATTACHMENT", method);
1333 title_buf[sizeof(title_buf)-1] = '\0';
1335 flags = (a && a->body && a->body->type == TYPETEXT ? GE_BINARY : 0)
1336 | GE_SEQ_SENSITIVE;
1338 r = get_export_filename(ps_global, filename, NULL, full_filename,
1339 sizeof(filename), "attachment", title_buf,
1340 att_save_opts, &rflags, qline, flags, &history);
1342 if(r < 0){
1343 switch(r){
1344 case -1:
1345 cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
1346 break;
1348 case -2:
1349 q_status_message1(SM_ORDER, 0, 2,
1350 _("Can't save to file outside of %s"),
1351 ps_global->VAR_OPER_DIR);
1352 break;
1355 return;
1357 #if !defined(DOS) && !defined(MAC) && !defined(OS2)
1358 else if(r == 12){ /* Download */
1359 char cmd[MAXPATH], *tfp = NULL;
1360 PIPE_S *syspipe;
1361 gf_io_t pc;
1362 long len;
1363 STORE_S *store;
1364 char prompt_buf[256];
1366 if(ps_global->restricted){
1367 q_status_message(SM_ORDER | SM_DING, 3, 3,
1368 "Download disallowed in restricted mode");
1369 return;
1372 err = NULL;
1373 tfp = temp_nam(NULL, "pd");
1374 dprint((1, "Download attachment called!\n"));
1375 if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
1377 snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
1378 prompt_buf[sizeof(prompt_buf)-1] = '\0';
1379 we_cancel = init_att_progress(prompt_buf,
1380 ps_global->mail_stream,
1381 a->body);
1383 gf_set_so_writec(&pc, store);
1384 if((err = detach(ps_global->mail_stream, msgno,
1385 a->number, 0L, &len, pc, NULL, 0)) != NULL)
1386 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1387 "%s: Error writing attachment to \"%s\"",
1388 err, tfp);
1390 /* cancel regardless, so it doesn't get in way of xfer */
1391 cancel_busy_cue(0);
1393 gf_clear_so_writec(store);
1394 if(so_give(&store)) /* close file */
1395 err = "Error writing tempfile for download";
1397 if(!err){
1398 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
1399 ps_global->VAR_DOWNLOAD_CMD, tfp);
1400 if((syspipe = open_system_pipe(cmd, NULL, NULL,
1401 PIPE_USER | PIPE_RESET,
1402 0, pipe_callback, pipe_report_error)) != NULL)
1403 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1404 else
1405 q_status_message(SM_ORDER | SM_DING, 3, 3,
1406 err = "Error running download command");
1409 else
1410 q_status_message(SM_ORDER | SM_DING, 3, 3,
1411 err = "Error building temp file for download");
1413 if(tfp){
1414 our_unlink(tfp);
1415 fs_give((void **)&tfp);
1418 if(!err)
1419 q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
1420 a->number);
1422 return;
1424 #endif /* !(DOS || MAC) */
1426 (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
1427 rflags, full_filename);
1432 * Args stream --
1433 * msgno -- raw message number
1434 * a -- attachment struct
1435 * flags -- comes from get_export_filename
1436 * GER_OVER -- the file was truncated before we wrote
1437 * GER_APPEND -- the file was not truncated prior to our writing,
1438 * so we were appending
1439 * else -- the file didn't previously exist
1440 * file -- the full pathname of the file
1442 * Returns 1 for successful write, 0 for nothing to write, -1 for error
1445 write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
1447 char *l_string, sbuf[256], *err;
1448 int is_text, we_cancel = 0, dt_flags = 0, so_flags;
1449 long len, orig_size;
1450 gf_io_t pc;
1451 STORE_S *store;
1453 if(!(a && a->body && a->number && a->number[0] && file && file[0]
1454 && stream))
1455 return 0;
1457 is_text = (a && a->body && a->body->type == TYPETEXT);
1459 if(flags & GER_APPEND)
1460 orig_size = name_file_size(file);
1462 if(flags & GER_BINARY)
1463 dt_flags |= DT_BINARY;
1465 so_flags = (is_text & !(flags & GER_BINARY) ? WRITE_TO_LOCALE : 0)
1466 | WRITE_ACCESS ;
1468 store = so_get(FileStar, file, so_flags);
1469 if(store == NULL){
1470 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1471 /* TRANSLATORS: Error opening destination <filename>: <error text> */
1472 _("Error opening destination %s: %s"),
1473 file, error_description(errno));
1474 return -1;
1477 snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
1478 sbuf[sizeof(sbuf)-1] = '\0';
1479 we_cancel = init_att_progress(sbuf, stream, a->body);
1481 gf_set_so_writec(&pc, store);
1482 err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, dt_flags);
1483 gf_clear_so_writec(store);
1485 if(we_cancel)
1486 cancel_busy_cue(0);
1488 if(so_give(&store)) /* close file */
1489 err = error_description(errno);
1491 if(err){
1492 if(!(flags & (GER_APPEND | GER_OVER)))
1493 our_unlink(file);
1494 else
1495 our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
1497 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1498 /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
1499 _("%s: Error writing attachment to \"%s\""),
1500 err, file);
1501 return -1;
1503 else{
1504 l_string = cpystr(byte_string(len));
1505 q_status_message8(SM_ORDER, 0, 4,
1506 "Part %s, %s%s %s to \"%s\"%s%s%s",
1507 a->number,
1508 is_text
1509 ? comatose(a->body->size.lines) : l_string,
1510 is_text ? " lines" : "",
1511 flags & GER_OVER
1512 ? "overwritten"
1513 : flags & GER_APPEND ? "appended" : "written",
1514 file,
1515 (is_text || len == a->body->size.bytes)
1516 ? "" : "(decoded from ",
1517 (is_text || len == a->body->size.bytes)
1518 ? "" : byte_string(a->body->size.bytes),
1519 is_text || len == a->body->size.bytes
1520 ? "" : ")");
1521 fs_give((void **)&l_string);
1522 return 1;
1527 char *
1528 write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
1530 char *err = NULL;
1531 long start_of_append;
1532 gf_io_t pc;
1533 MESSAGECACHE *mc;
1535 if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
1536 && (*ap)->body->nested.msg->env){
1537 start_of_append = so_tell(store);
1539 gf_set_so_writec(&pc, store);
1540 if(!(ps_global->mail_stream && msgno > 0L
1541 && msgno <= ps_global->mail_stream->nmsgs
1542 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1543 mc = NULL;
1545 if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
1546 || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
1547 err = error_description(errno);
1549 gf_clear_so_writec(store);
1551 if(err)
1552 ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
1554 else
1555 err = "Can't export message. Missing Envelope data";
1557 return(err);
1561 /*----------------------------------------------------------------------
1562 Save the attachment message/rfc822 to specified folder
1564 Args:
1566 Result:
1567 ----*/
1568 void
1569 save_msg_att(long int msgno, ATTACH_S *a)
1571 char newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
1572 char date[64], nmsgs[80];
1573 CONTEXT_S *cntxt = NULL;
1574 int our_stream = 0, rv;
1575 MAILSTREAM *save_stream;
1576 MESSAGECACHE *mc;
1578 snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
1579 nmsgs[sizeof(nmsgs)-1] = '\0';
1580 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
1581 a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
1582 if(strucmp(newfolder, ps_global->inbox_name) == 0){
1583 save_folder = ps_global->VAR_INBOX_PATH;
1584 cntxt = NULL;
1586 else
1587 save_folder = newfolder;
1589 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1591 mc = (msgno > 0L && ps_global->mail_stream
1592 && msgno <= ps_global->mail_stream->nmsgs)
1593 ? mail_elt(ps_global->mail_stream, msgno) : NULL;
1594 flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
1595 if(mc && mc->day)
1596 mail_date(date, mc);
1597 else
1598 *date = '\0';
1600 if(pith_opt_save_size_changed_prompt)
1601 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1603 rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
1605 if(pith_opt_save_size_changed_prompt)
1606 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1608 if(flags)
1609 fs_give((void **) &flags);
1611 if(rv == 1)
1612 q_status_message2(SM_ORDER, 0, 4,
1613 _("Attached message (part %s) saved to \"%s\""),
1614 a->number,
1615 save_folder);
1616 else if(rv == -1)
1617 cmd_cancelled("Attached message Save");
1618 /* else whatever broke in save_fetch_append shoulda bitched */
1620 if(our_stream)
1621 mail_close(save_stream);
1626 /*----------------------------------------------------------------------
1627 Save the message/rfc822 in the given digest to the specified folder
1629 Args:
1631 Result:
1632 ----*/
1633 void
1634 save_digest_att(long int msgno, ATTACH_S *a)
1636 char newfolder[MAILTMPLEN], *save_folder,
1637 date[64], nmsgs[80];
1638 CONTEXT_S *cntxt = NULL;
1639 int our_stream = 0, rv, cnt = 0;
1640 MAILSTREAM *save_stream;
1641 ATTACH_S *ap;
1643 for(ap = a + 1;
1644 ap->description
1645 && !strncmp(a->number, ap->number, strlen(a->number));
1646 ap++)
1647 if(MIME_MSG(ap->body->type, ap->body->subtype))
1648 cnt++;
1650 snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
1651 nmsgs[sizeof(nmsgs)-1] = '\0';
1653 if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
1654 nmsgs, NULL, 0, NULL, NULL, NULL)){
1655 save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
1656 ? ps_global->VAR_INBOX_PATH : newfolder;
1658 save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1660 if(pith_opt_save_size_changed_prompt)
1661 (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1663 for(ap = a + 1;
1664 ap->description
1665 && !strncmp(a->number, ap->number, strlen(a->number));
1666 ap++)
1667 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1668 *date = '\0';
1669 rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
1670 if(rv != 1)
1671 break;
1674 if(pith_opt_save_size_changed_prompt)
1675 (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1677 if(rv == 1)
1678 q_status_message2(SM_ORDER, 0, 4,
1679 _("Attached digest (part %s) saved to \"%s\""),
1680 a->number,
1681 save_folder);
1682 else if(rv == -1)
1683 cmd_cancelled("Attached digest Save");
1684 /* else whatever broke in save_fetch_append shoulda bitched */
1686 if(our_stream)
1687 mail_close(save_stream);
1693 save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
1694 char *save_folder, CONTEXT_S *cntxt, char *date)
1696 STORE_S *so;
1697 int rv = 0;
1699 if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
1700 if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1701 *date = '\0';
1702 rv = save_fetch_append(ps_global->mail_stream, msgno,
1703 a->number,
1704 save_stream, save_folder, cntxt,
1705 a->body->size.bytes,
1706 NULL, date, so);
1708 else{
1709 dprint((1, "Can't allocate store for save: %s\n",
1710 error_description(errno)));
1711 q_status_message(SM_ORDER | SM_DING, 3, 4,
1712 _("Problem creating space for message text."));
1713 rv = 0;
1717 return(rv);
1721 /*----------------------------------------------------------------------
1722 Export the attachment message/rfc822 to specified file
1724 Args:
1726 Result:
1727 ----*/
1728 void
1729 export_msg_att(long int msgno, ATTACH_S *a)
1731 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
1732 int rv, rflags = GER_NONE, i = 1;
1733 ATTACH_S *ap = a;
1734 STORE_S *store;
1735 static HISTORY_S *history = NULL;
1736 static ESCKEY_S opts[] = {
1737 {ctrl('T'), 10, "^T", N_("To Files")},
1738 {-1, 0, NULL, NULL},
1739 {-1, 0, NULL, NULL}};
1741 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1742 opts[i].ch = ctrl('I');
1743 opts[i].rval = 11;
1744 opts[i].name = "TAB";
1745 opts[i].label = N_("Complete");
1748 filename[0] = full_filename[0] = '\0';
1750 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1751 sizeof(filename), "msg attachment",
1752 /* TRANSLATORS: Message Attachment (a screen title) */
1753 _("MSG ATTACHMENT"), opts,
1754 &rflags, -FOOTER_ROWS(ps_global),
1755 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1757 if(rv < 0){
1758 switch(rv){
1759 case -1:
1760 cmd_cancelled("Export");
1761 break;
1763 case -2:
1764 q_status_message1(SM_ORDER, 0, 2,
1765 _("Can't export to file outside of %s"),
1766 ps_global->VAR_OPER_DIR);
1767 break;
1770 return;
1773 /* With name in hand, allocate storage object and save away... */
1774 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1775 if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
1776 q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1777 else
1778 q_status_message3(SM_ORDER, 0, 4,
1779 _("Attached message (part %s) %s to \"%s\""),
1780 a->number,
1781 rflags & GER_OVER
1782 ? _("overwritten")
1783 : rflags & GER_APPEND ? _("appended") : _("written"),
1784 full_filename);
1786 if(so_give(&store))
1787 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1788 _("Error writing %s: %s"),
1789 full_filename, error_description(errno));
1791 else
1792 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1793 /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
1794 _("Error opening file \"%s\" to export message: %s"),
1795 full_filename, error_description(errno));
1799 /*----------------------------------------------------------------------
1800 Export the message/rfc822 in the given digest to the specified file
1802 Args:
1804 Result:
1805 ----*/
1806 void
1807 export_digest_att(long int msgno, ATTACH_S *a)
1809 char filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
1810 int rv, rflags = GER_NONE, i = 1;
1811 long count = 0L;
1812 ATTACH_S *ap;
1813 static HISTORY_S *history = NULL;
1814 STORE_S *store;
1815 static ESCKEY_S opts[] = {
1816 {ctrl('T'), 10, "^T", N_("To Files")},
1817 {-1, 0, NULL, NULL},
1818 {-1, 0, NULL, NULL}};
1820 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1821 opts[i].ch = ctrl('I');
1822 opts[i].rval = 11;
1823 opts[i].name = "TAB";
1824 opts[i].label = N_("Complete");
1827 filename[0] = full_filename[0] = '\0';
1829 rv = get_export_filename(ps_global, filename, NULL, full_filename,
1830 sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
1831 opts, &rflags, -FOOTER_ROWS(ps_global),
1832 GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1834 if(rv < 0){
1835 switch(rv){
1836 case -1:
1837 cmd_cancelled("Export");
1838 break;
1840 case -2:
1841 q_status_message1(SM_ORDER, 0, 2,
1842 _("Can't export to file outside of %s"),
1843 ps_global->VAR_OPER_DIR);
1844 break;
1847 return;
1850 /* With name in hand, allocate storage object and save away... */
1851 if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1852 count = 0;
1854 for(ap = a + 1;
1855 ap->description
1856 && !strncmp(a->number, ap->number, strlen(a->number))
1857 && !err;
1858 ap++){
1859 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1860 count++;
1861 err = write_attached_msg(msgno, &ap, store,
1862 !count && !(rflags & GER_APPEND));
1866 if(so_give(&store))
1867 err = error_description(errno);
1869 if(err){
1870 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1871 _("Error exporting: %s"), err);
1872 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1873 _("%s messages exported before error occurred"), err);
1875 else
1876 q_status_message4(SM_ORDER, 0, 4,
1877 "%s messages in digest (part %s) %s to \"%s\"",
1878 long2string(count),
1879 a->number,
1880 rflags & GER_OVER
1881 ? "overwritten"
1882 : rflags & GER_APPEND ? "appended" : "written",
1883 full_filename);
1885 else
1886 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1887 _("Error opening file \"%s\" to export digest: %s"),
1888 full_filename, error_description(errno));
1892 /*----------------------------------------------------------------------
1893 Print the given attachment associated with the given message no
1895 Args: ps
1897 Result:
1898 ----*/
1899 void
1900 print_attachment(int qline, long int msgno, ATTACH_S *a)
1902 char prompt[250];
1904 if(ps_global->restricted){
1905 q_status_message(SM_ORDER | SM_DING, 0, 4,
1906 "Alpine demo can't Print attachments");
1907 return;
1910 snprintf(prompt, sizeof(prompt), "attach%s %s",
1911 (a->body->type == TYPETEXT) ? "ment" : "ed message",
1912 MIME_DGST_A(a) ? "digest" : a->number);
1913 prompt[sizeof(prompt)-1] = '\0';
1914 if(open_printer(prompt) >= 0){
1915 if(MIME_MSG_A(a))
1916 (void) print_msg_att(msgno, a, 1);
1917 else if(MIME_DGST_A(a))
1918 print_digest_att(msgno, a);
1919 else
1920 (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
1922 close_printer();
1928 * Print the attachment message/rfc822 to specified file
1930 * Returns 1 on success, 0 on failure.
1933 print_msg_att(long int msgno, ATTACH_S *a, int first)
1935 ATTACH_S *ap = a;
1936 MESSAGECACHE *mc;
1938 if(!(ps_global->mail_stream && msgno > 0L
1939 && msgno <= ps_global->mail_stream->nmsgs
1940 && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1941 mc = NULL;
1943 if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
1944 && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
1945 && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
1946 ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
1947 : 1)
1948 && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
1949 return(1);
1952 q_status_message2(SM_ORDER | SM_DING, 3, 3,
1953 _("Error printing message %s, part %s"),
1954 long2string(msgno), a->number);
1955 return(0);
1959 /*----------------------------------------------------------------------
1960 Print the attachment message/rfc822 to specified file
1962 Args:
1964 Result:
1965 ----*/
1966 void
1967 print_digest_att(long int msgno, ATTACH_S *a)
1969 ATTACH_S *ap;
1970 int next = 0;
1972 for(ap = a + 1;
1973 ap->description
1974 && !strncmp(a->number, ap->number, strlen(a->number));
1975 ap++){
1976 if(MIME_MSG(ap->body->type, ap->body->subtype)){
1977 char *p = part_desc(ap->number, ap->body->nested.msg->body,
1978 0, 80, FM_NOINDENT, print_char);
1979 if(p){
1980 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1981 _("Can't print digest: %s"), p);
1982 break;
1984 else if(!print_msg_att(msgno, ap, !next))
1985 break;
1987 next++;
1993 /*----------------------------------------------------------------------
1994 Unpack and display the given attachment associated with given message no.
1996 Args: msgno -- message no attachment is part of
1997 a -- attachment to display
1999 Returns: 0 on success, non-zero (and error message queued) otherwise
2000 ----*/
2002 display_attachment(long int msgno, ATTACH_S *a, int flags)
2004 char *filename = NULL;
2005 char sender_filename[1000];
2006 char *extp = NULL;
2007 STORE_S *store;
2008 gf_io_t pc;
2009 char *err;
2010 int we_cancel = 0, rv;
2011 char prefix[70];
2012 char ext[32];
2013 char mtype[128];
2015 /*------- Display the attachment -------*/
2016 if(dispatch_attachment(a) == MCD_NONE){
2017 /*----- Can't display this type ------*/
2018 if(a->body->encoding < ENCOTHER)
2019 q_status_message4(SM_ORDER | SM_DING, 3, 5,
2020 /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
2021 _("Don't know how to display %s%s%s attachments.%s"),
2022 body_type_names(a->body->type),
2023 a->body->subtype ? "/" : "",
2024 a->body->subtype ? a->body->subtype :"",
2025 (flags & DA_SAVE) ? _(" Try Save.") : "");
2026 else
2027 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2028 _("Don't know how to unpack \"%s\" encoding"),
2029 body_encodings[(a->body->encoding <= ENCMAX)
2030 ? a->body->encoding : ENCOTHER]);
2032 return(1);
2034 else if(!(a->can_display & MCD_EXTERNAL)){
2035 if(a->body->type == TYPEMULTIPART){
2036 if(a->body->subtype){
2037 if(!strucmp(a->body->subtype, "digest"))
2038 display_digest_att(msgno, a, flags);
2039 else
2040 q_status_message1(SM_ORDER, 3, 5,
2041 _("Can't display Multipart/%s"),
2042 a->body->subtype);
2044 else
2045 q_status_message(SM_ORDER, 3, 5,
2046 _("Can't display unknown Multipart Subtype"));
2048 else if(MIME_VCARD_A(a))
2049 display_vcard_att(msgno, a, flags);
2050 else if( MIME_VCALENDAR(a->body->type, a->body->subtype))
2051 display_vcalendar_att(msgno, a, flags);
2052 else if(a->body->type == TYPETEXT){
2054 rv = display_text_att(msgno, a, flags);
2055 } while(rv == MC_FULLHDR);
2057 else if(a->body->type == TYPEMESSAGE){
2059 rv = display_msg_att(msgno, a, flags);
2060 } while(rv == MC_FULLHDR);
2063 ps_global->mangled_screen = 1;
2064 return(0);
2067 /* arrive here if MCD_EXTERNAL */
2069 if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
2070 && (!(flags & DA_DIDPROMPT)))
2071 if(want_to(_("View selected Attachment"), 'y',
2072 0, NO_HELP, WT_NORM) == 'n'){
2073 cmd_cancelled(NULL);
2074 return(1);
2077 sender_filename[0] = '\0';
2078 ext[0] = '\0';
2079 prefix[0] = '\0';
2081 if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
2082 && (a->can_display & MCD_EXT_PROMPT)){
2083 char prompt[256];
2085 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2086 a->body, &extp);
2087 snprintf(prompt, sizeof(prompt),
2088 "Attachment %s%s unrecognized. %s%s%s",
2089 a->body->subtype,
2090 strlen(a->body->subtype) > 12 ? "..." : "",
2091 (extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
2092 (extp && extp[0]) ? extp : "",
2093 (extp && extp[0]) ? ")" : "");
2095 if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
2096 cmd_cancelled(NULL);
2097 return(1);
2101 /*------ Write the image to a temporary file ------*/
2103 /* create type/subtype in mtype */
2104 strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
2105 mtype[sizeof(mtype)-1] = '\0';
2106 if(a->body->subtype){
2107 strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
2108 mtype[sizeof(mtype)-1] = '\0';
2109 strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
2110 mtype[sizeof(mtype)-1] = '\0';
2114 * If we haven't already gotten the filename parameter, get it
2115 * now. It may be used in the temporary filename and possibly
2116 * for its extension.
2118 if(!sender_filename[0])
2119 (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2120 a->body, &extp);
2122 if(check_mime_type_by_extension(extp, mtype)
2123 || (!set_mime_extension_by_type(ext, mtype) /* extension from type */
2124 && extp && extp[0])){ /* extension from filename */
2125 strncpy(ext, extp, sizeof(ext));
2126 ext[sizeof(ext)-1] = '\0';
2129 /* create a temp file */
2130 if(sender_filename){
2131 char *p, *q = NULL;
2133 /* get rid of any extension */
2134 if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
2135 *(q-1) = '\0';
2137 /* be careful about what is allowed in the filename */
2138 for(p = sender_filename; *p; p++)
2139 if(!(isascii((unsigned char) *p)
2140 && (isalnum((unsigned char) *p)
2141 || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
2142 break;
2144 if(!*p) /* filename was ok to use */
2145 snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
2148 /* didn't get it yet */
2149 if(!prefix[0]){
2150 snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
2151 ? a->body->subtype : "unk");
2154 /* We are creating a temporary name. This is just a prefix. If you
2155 * need the original name, use the save command, so if the prefix
2156 * is too long, shorten it.
2158 if (strlen(prefix) > 9){
2159 prefix[9] = '-';
2160 prefix[10] = '\0';
2163 filename = temp_nam_ext(NULL, prefix, ext);
2165 if(!filename){
2166 q_status_message1(SM_ORDER | SM_DING, 3, 5,
2167 _("Error \"%s\", Can't create temporary file"),
2168 error_description(errno));
2169 return(1);
2172 if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2173 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2174 _("Error \"%s\", Can't write file %s"),
2175 error_description(errno), filename);
2176 if(filename){
2177 our_unlink(filename);
2178 fs_give((void **)&filename);
2181 return(1);
2185 if(a->body->size.bytes){
2186 char msg_buf[128];
2188 snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2189 a->description ? "\"" : "",
2190 a->description ? a->description : "attachment number ",
2191 a->description ? "" : a->number,
2192 a->description ? "\"" : "");
2193 msg_buf[sizeof(msg_buf)-1] = '\0';
2194 we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
2195 a->body);
2198 if(a->body->type == TYPEMULTIPART){
2199 char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
2200 NULL, 0L);
2202 /* Write to store while converting newlines */
2203 while(h && *h)
2204 if(*h == '\015' && *(h+1) == '\012'){
2205 so_puts(store, NEWLINE);
2206 h += 2;
2208 else
2209 so_writec(*h++, store);
2212 gf_set_so_writec(&pc, store);
2214 err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
2216 gf_clear_so_writec(store);
2218 if(we_cancel)
2219 cancel_busy_cue(0);
2221 so_give(&store);
2223 if(err){
2224 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2225 "%s: Error saving image to temp file %s",
2226 err, filename);
2227 if(filename){
2228 our_unlink(filename);
2229 fs_give((void **)&filename);
2232 return(1);
2235 /*----- Run the viewer process ----*/
2236 run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
2237 if(filename)
2238 fs_give((void **)&filename);
2240 return(0);
2244 /*----------------------------------------------------------------------
2245 Fish the required command from mailcap and run it
2247 Args: image_file -- The name of the file to pass viewer
2248 body -- body struct containing type/subtype of part
2250 A side effect may be that scrolltool is called as well if
2251 exec_mailcap_cmd has any substantial output...
2252 ----*/
2253 void
2254 run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
2256 MCAP_CMD_S *mc_cmd = NULL;
2257 int needs_terminal = 0, we_cancel = 0;
2259 we_cancel = busy_cue("Displaying attachment", NULL, 0);
2261 if((mc_cmd = mailcap_build_command(body->type, body->subtype,
2262 body, image_file,
2263 &needs_terminal, chk_extension)) != NULL){
2264 if(we_cancel)
2265 cancel_busy_cue(-1);
2267 exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
2268 if(mc_cmd->command)
2269 fs_give((void **)&mc_cmd->command);
2270 fs_give((void **)&mc_cmd);
2272 else{
2273 if(we_cancel)
2274 cancel_busy_cue(-1);
2276 q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
2277 type_desc(body->type, body->subtype,
2278 body->parameter, NULL, 1));
2283 /*----------------------------------------------------------------------
2284 Detach and provide for browsing a text body part
2286 Args: msgno -- raw message number to get part from
2287 a -- attachment struct for the desired part
2289 Result:
2290 ----*/
2291 STORE_S *
2292 format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
2294 STORE_S *store;
2295 gf_io_t pc;
2297 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2298 if(handlesp)
2299 init_handles(handlesp);
2301 gf_set_so_writec(&pc, store);
2302 (void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
2303 gf_clear_so_writec(store);
2306 return(store);
2310 /*----------------------------------------------------------------------
2311 Detach and provide for browsing a text body part
2313 Args: msgno -- raw message number to get part from
2314 a -- attachment struct for the desired part
2316 Result:
2317 ----*/
2319 display_text_att(long int msgno, ATTACH_S *a, int flags)
2321 STORE_S *store;
2322 HANDLE_S *handles = NULL;
2323 int rv = 0;
2325 if(msgno > 0L)
2326 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2328 if((store = format_text_att(msgno, a, &handles)) != NULL){
2329 rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
2330 free_handles(&handles);
2331 so_give(&store); /* free resources associated with store */
2333 else
2334 q_status_message(SM_ORDER | SM_DING, 3, 3,
2335 _("Error allocating space for attachment."));
2337 return(rv);
2341 /*----------------------------------------------------------------------
2342 Detach and provide for browsing a body part of type "MESSAGE"
2344 Args: msgno -- message number to get partrom
2345 a -- attachment struct for the desired part
2347 Result:
2348 ----*/
2350 display_msg_att(long int msgno, ATTACH_S *a, int flags)
2352 STORE_S *store;
2353 gf_io_t pc;
2354 ATTACH_S *ap = a;
2355 HANDLE_S *handles = NULL;
2356 int rv = 0;
2358 if(msgno > 0L)
2359 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2361 /* BUG, should check this return code */
2362 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2364 /* initialize a storage object */
2365 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2366 q_status_message(SM_ORDER | SM_DING, 3, 3,
2367 _("Error allocating space for message."));
2368 return(rv);
2371 gf_set_so_writec(&pc, store);
2373 if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
2374 if(ps_global->full_header == 2)
2375 q_status_message(SM_INFO, 0, 3,
2376 _("Full header mode ON. All header text being included"));
2378 rv = scroll_attachment((a->body->subtype
2379 && !strucmp(a->body->subtype, "delivery-status"))
2380 ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
2381 store, CharStar, handles, a, flags);
2382 free_handles(&handles);
2385 gf_clear_so_writec(store);
2387 so_give(&store); /* free resources associated with store */
2388 return(rv);
2392 /*----------------------------------------------------------------------
2393 Detach and provide for browsing a multipart body part of type "DIGEST"
2395 Args: msgno -- message number to get partrom
2396 a -- attachment struct for the desired part
2398 Result:
2399 ----*/
2400 void
2401 display_digest_att(long int msgno, ATTACH_S *a, int flags)
2403 STORE_S *store;
2404 ATTACH_S *ap;
2405 HANDLE_S *handles = NULL;
2406 gf_io_t pc;
2407 SourceType src = CharStar;
2408 int bad_news = 0;
2410 if(msgno > 0L)
2411 clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2413 /* BUG, should check this return code */
2414 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2416 if(!(store = so_get(src, NULL, EDIT_ACCESS))){
2417 q_status_message(SM_ORDER | SM_DING, 3, 3,
2418 _("Error allocating space for message."));
2419 return;
2422 gf_set_so_writec(&pc, store);
2425 * While in a subtype of this message
2427 for(ap = a + 1;
2428 ap->description
2429 && !strncmp(a->number, ap->number, strlen(a->number))
2430 && !bad_news;
2431 ap++){
2432 if(ap->body->type == TYPEMESSAGE){
2433 char *errstr;
2435 if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
2436 char *tsub;
2438 tsub = cpystr(ap->body->subtype);
2439 convert_possibly_encoded_str_to_utf8((char **) &tsub);
2440 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
2441 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2443 if((errstr = format_editorial(tmp_20k_buf,
2444 ps_global->ttyo->screen_cols, 0,
2445 NULL, pc)) != NULL){
2446 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2447 _("Can't format digest: %s"), errstr);
2448 bad_news++;
2450 else if(!gf_puts(NEWLINE, pc))
2451 bad_news++;
2453 fs_give((void **) &tsub);
2455 else{
2456 if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
2457 0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
2458 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2459 _("Can't format digest: %s"), errstr);
2460 bad_news++;
2462 else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
2463 bad_news++;
2466 else if(ap->body->type == TYPETEXT
2467 && decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
2468 bad_news++;
2469 else if(!gf_puts("Unknown type in Digest", pc))
2470 bad_news++;
2473 if(!bad_news){
2474 if(ps_global->full_header == 2)
2475 q_status_message(SM_INFO, 0, 3,
2476 _("Full header mode ON. All header text being included"));
2478 scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
2481 free_handles(&handles);
2483 gf_clear_so_writec(store);
2484 so_give(&store); /* free resources associated with store */
2489 scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
2491 SCROLL_S sargs;
2493 memset(&sargs, 0, sizeof(SCROLL_S));
2494 sargs.text.text = so_text(store);
2495 sargs.text.src = src;
2496 sargs.text.desc = "attachment";
2497 sargs.text.handles = handles;
2498 sargs.bar.title = title;
2499 sargs.proc.tool = process_attachment_cmd;
2500 sargs.proc.data.p = (void *) a;
2501 sargs.help.text = h_mail_text_att_view;
2502 sargs.help.title = _("HELP FOR ATTACHED TEXT VIEW");
2503 sargs.keys.menu = &att_view_keymenu;
2504 setbitmap(sargs.keys.bitmap);
2506 /* First, fix up "back" key */
2507 if(flags & DA_FROM_VIEW){
2508 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
2510 else{
2511 att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
2514 if(!handles){
2515 clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
2516 clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
2517 clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
2520 if(F_OFF(F_ENABLE_PIPE, ps_global))
2521 clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
2523 if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
2524 clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
2527 * If message or digest, leave Reply and Save and,
2528 * conditionally, Bounce on...
2530 if(MIME_MSG_A(a)){
2531 if(F_OFF(F_ENABLE_BOUNCE, ps_global))
2532 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2534 else{
2535 clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2536 clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
2537 clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
2539 #ifdef SMIME
2540 if(!(ps_global->smime && ps_global->smime->need_passphrase))
2541 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2543 if(F_ON(F_DONT_DO_SMIME, ps_global) || !MIME_MSG_A(a)){
2544 clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2545 clrbitn(ATV_SECURITY_KEY, sargs.keys.bitmap);
2547 #endif
2549 sargs.use_indexline_color = 1;
2551 if(DA_RESIZE & flags)
2552 sargs.resize_exit = 1;
2554 #ifdef _WINDOWS
2555 scrat_attachp = a;
2556 sargs.mouse.popup = scroll_att_popup;
2557 #endif
2559 return(scrolltool(&sargs));
2562 #ifdef SMIME
2563 void
2564 display_smime_info_att(struct pine *ps, ATTACH_S *a)
2566 if(smime_check(a->body->nested.msg->body) == 0){
2567 q_status_message(SM_ORDER | SM_DING, 0, 3,
2568 _("Not a signed or encrypted message"));
2569 return;
2572 display_smime_info(ps, a->body->nested.msg->env, a->body->nested.msg->body);
2574 #endif /* SMIME */
2577 process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2579 int rv = 0, n;
2580 long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2581 #define AD(X) ((ATTACH_S *) (X)->proc.data.p)
2583 switch(cmd){
2584 case MC_EXIT :
2585 rv = 1;
2586 break;
2588 case MC_QUIT :
2589 ps_global->next_screen = quit_screen;
2590 break;
2592 case MC_MAIN :
2593 ps_global->next_screen = main_menu_screen;
2594 break;
2596 case MC_REPLY :
2597 reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
2598 break;
2600 case MC_FORWARD :
2601 forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
2602 break;
2604 case MC_BOUNCE :
2605 bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
2606 AD(sparms)->body->nested.msg->env->subject);
2607 ps_global->mangled_footer = 1;
2608 break;
2610 case MC_DELETE :
2611 delete_attachment(rawno, sparms->proc.data.p);
2612 break;
2614 case MC_UNDELETE :
2615 if(undelete_attachment(rawno, sparms->proc.data.p, &n))
2616 q_status_message1(SM_ORDER, 0, 3,
2617 "Part %s UNdeleted", AD(sparms)->number);
2619 break;
2621 case MC_SAVE :
2622 save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2623 ps_global->mangled_footer = 1;
2624 break;
2626 case MC_EXPORT :
2627 export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2628 ps_global->mangled_footer = 1;
2629 break;
2631 case MC_PRINTMSG :
2632 print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2633 ps_global->mangled_footer = 1;
2634 break;
2636 case MC_PIPE :
2637 pipe_attachment(rawno, sparms->proc.data.p);
2638 ps_global->mangled_footer = 1;
2639 break;
2641 case MC_FULLHDR :
2642 ps_global->full_header++;
2643 if(ps_global->full_header == 1){
2644 if(!(ps_global->quote_suppression_threshold
2645 && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
2646 ps_global->full_header++;
2648 else if(ps_global->full_header > 2)
2649 ps_global->full_header = 0;
2651 switch(ps_global->full_header){
2652 case 0:
2653 q_status_message(SM_ORDER, 0, 3,
2654 _("Display of full headers is now off."));
2655 break;
2657 case 1:
2658 q_status_message1(SM_ORDER, 0, 3,
2659 _("Quotes displayed, use %s to see full headers"),
2660 F_ON(F_USE_FK, ps_global) ? "F9" : "H");
2661 break;
2663 case 2:
2664 q_status_message(SM_ORDER, 0, 3,
2665 _("Display of full headers is now on."));
2666 break;
2670 rv = 1;
2671 break;
2673 #ifdef SMIME
2674 case MC_DECRYPT:
2675 if(ps_global->smime && ps_global->smime->need_passphrase)
2676 smime_get_passphrase();
2677 break;
2679 case MC_SECURITY:
2680 display_smime_info_att(ps_global, sparms->proc.data.p);
2681 break;
2682 #endif
2684 default:
2685 alpine_panic("Unexpected command case");
2686 break;
2689 return(rv);
2694 * Returns 1 on success, 0 on error.
2697 format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
2699 int rv = 1;
2701 if((*a)->body->type != TYPEMESSAGE)
2702 return(gf_puts("[ Undisplayed Attachment of Type ", pc)
2703 && gf_puts(body_type_names((*a)->body->type), pc)
2704 && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
2706 if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
2707 HEADER_S h;
2709 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
2710 FE_DEFAULT);
2711 switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
2712 (*a)->body->nested.msg->env, &h, NULL, NULL,
2713 flags, NULL, pc)){
2714 case -1 : /* write error */
2715 return(0);
2717 case 1 : /* fetch error */
2718 if(!(gf_puts("[ Error fetching header ]", pc)
2719 && !gf_puts(NEWLINE, pc)))
2720 return(0);
2722 break;
2725 gf_puts(NEWLINE, pc);
2727 if(((*a)+1)->description)
2728 ++(*a);
2729 else{
2730 if(!(gf_puts("[Can't display missing text segment]", pc)
2731 && gf_puts(NEWLINE, pc)))
2732 rv = 0;
2733 return rv;
2736 #ifdef SMIME
2737 if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2738 if((*a)->description){
2739 if(!(!format_editorial((*a)->description,
2740 ps_global->ttyo->screen_cols,
2741 flags, NULL, pc)
2742 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2743 rv = 0;
2746 if(((*a)+1)->description)
2747 ++(*a);
2748 else{
2749 if(!(gf_puts("[Can't display missing text segment]", pc)
2750 && gf_puts(NEWLINE, pc)))
2751 rv = 0;
2752 return rv;
2755 #endif /* SMIME */
2757 if(((*a))->description
2758 && (*a)->body && (*a)->body->type == TYPETEXT){
2759 if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2760 rv = 0;
2762 else if(!(gf_puts("[Can't display ", pc)
2763 && gf_puts(((*a)->description && (*a)->body)
2764 ? "first non-" : "missing ", pc)
2765 && gf_puts("text segment]", pc)
2766 && gf_puts(NEWLINE, pc)))
2767 rv = 0;
2769 if(((*a)+1)->description)
2770 ++(*a);
2771 else{
2772 if(!(gf_puts("[Can't display missing text segment]", pc)
2773 && gf_puts(NEWLINE, pc)))
2774 rv = 0;
2775 return rv;
2779 else if((*a)->body->subtype
2780 && strucmp((*a)->body->subtype, "external-body") == 0) {
2781 if(format_editorial("This part is not included and can be fetched as follows:",
2782 ps_global->ttyo->screen_cols, flags, NULL, pc)
2783 || !gf_puts(NEWLINE, pc)
2784 || format_editorial(display_parameters((*a)->body->parameter),
2785 ps_global->ttyo->screen_cols, flags, handlesp, pc))
2786 rv = 0;
2788 else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2789 rv = 0;
2791 return(rv);
2795 void
2796 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2798 STORE_S *in_store, *out_store = NULL;
2799 HANDLE_S *handles = NULL;
2800 URL_HILITE_S uh;
2801 gf_io_t gc, pc;
2802 char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2803 int cmd, indent, begins = 0;
2805 lines = detach_vcard_att(ps_global->mail_stream,
2806 msgno, a->body, a->number);
2807 if(!lines){
2808 q_status_message(SM_ORDER | SM_DING, 3, 3,
2809 _("Error accessing attachment."));
2810 return;
2813 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2814 free_list_array(&lines);
2815 q_status_message(SM_ORDER | SM_DING, 3, 3,
2816 _("Error allocating space for attachment."));
2817 return;
2820 for(ll = lines, indent = 0; ll && *ll; ll++)
2821 if((p = strindex(*ll, ':')) && p - *ll > indent)
2822 indent = p - *ll;
2824 indent += 5;
2825 for(ll = lines; ll && *ll; ll++){
2826 if((p = strindex(*ll, ':')) != NULL){
2827 if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2828 begins++;
2830 snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
2831 (int) MIN(p - *ll, sizeof(tmp)-5), *ll);
2832 tmp[sizeof(tmp)-1] = '\0';
2833 so_puts(in_store, tmp);
2834 p++;
2836 else{
2837 p = *ll;
2838 so_puts(in_store, repeat_char(indent, SPACE));
2841 snprintf(tmp, sizeof(tmp), "%.200s", p);
2842 tmp[sizeof(tmp)-1] = '\0';
2843 so_puts(in_store,
2844 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
2845 SIZEOF_20KBUF, tmp));
2846 so_puts(in_store, "\015\012");
2849 free_list_array(&lines);
2851 so_puts(in_store, "\015\012\015\012");
2854 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2855 so_seek(in_store, 0L, 0);
2857 init_handles(&handles);
2858 gf_filter_init();
2860 if(F_ON(F_VIEW_SEL_URL, ps_global)
2861 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2862 || F_ON(F_SCAN_ADDR, ps_global))
2863 gf_link_filter(gf_line_test,
2864 gf_line_test_opt(url_hilite,
2865 gf_url_hilite_opt(&uh,&handles,0)));
2867 gf_link_filter(gf_wrap,
2868 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
2869 ps_global->ttyo->screen_cols,
2870 NULL, indent, GFW_HANDLES));
2871 gf_link_filter(gf_nvtnl_local, NULL);
2873 gf_set_so_readc(&gc, in_store);
2874 gf_set_so_writec(&pc, out_store);
2876 errstr = gf_pipe(gc, pc);
2878 gf_clear_so_readc(in_store);
2880 if(!errstr){
2881 #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.")
2882 #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.")
2883 errstr = format_editorial((begins > 1)
2884 ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
2885 ps_global->ttyo->screen_cols, 0, NULL, pc);
2888 gf_clear_so_writec(out_store);
2890 if(!errstr)
2891 cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
2892 CharStar, handles, a, flags | DA_RESIZE);
2894 free_handles(&handles);
2895 so_give(&out_store);
2897 else
2898 errstr = _("Error allocating space");
2900 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
2902 if(errstr)
2903 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2904 _("Can't format entry : %s"), errstr);
2906 so_give(&in_store);
2909 void
2910 display_vevent_summary(long int msgno, ATTACH_S *a, int flags, int depth)
2912 BODY *b;
2913 VCALENDAR_S *vcal = NULL;
2914 char *b64text, *caltext;
2915 unsigned long callen;
2916 VEVENT_SUMMARY_S *vesy, *vsummary; /* vevent summary */
2917 STORE_S *in_store, *out_store = NULL;
2918 HANDLE_S *handles = NULL;
2919 URL_HILITE_S uh;
2920 gf_io_t gc, pc;
2921 char *errstr = NULL, tmp[MAILTMPLEN], *p;
2922 int cmd, i, k;
2924 b = mail_body(ps_global->mail_stream, msgno, a->number);
2925 if(b->sparep == NULL){
2926 b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
2927 b64text[callen] = '\0'; /* chop off cookie */
2928 caltext = rfc822_base64(b64text, strlen(b64text), &callen);
2929 vcal = ical_parse_text(caltext);
2930 b->sparep = create_body_sparep(iCalType, (void *) vcal);
2932 else if(get_body_sparep_type(b->sparep) == iCalType)
2933 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
2935 vsummary = ical_vevent_summary(vcal);
2937 if(vsummary == NULL){
2938 q_status_message(SM_ORDER | SM_DING, 3, 3,
2939 _("Error parsing event"));
2940 return;
2943 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2944 q_status_message(SM_ORDER | SM_DING, 3, 3,
2945 _("Error allocating space to process Calendar"));
2946 return;
2949 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
2950 q_status_message(SM_ORDER | SM_DING, 3, 3,
2951 _("Error allocating space to write Calendar"));
2952 return;
2955 gf_set_so_readc(&gc, in_store);
2956 gf_set_so_writec(&pc, out_store);
2958 for(vesy = vsummary, k = 0; vesy; vesy = vesy->next, k++){
2959 if(depth >= 0 && k != depth)
2960 continue;
2962 if(vesy->cancel){
2963 so_puts(in_store, _("This event was cancelled"));
2964 so_puts(in_store, "\015\012");
2967 if(vesy->priority){
2968 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%d %s",
2969 _("Priority: "), vesy->priority,
2970 vesy->priority == 5 ? _("(Normal)")
2971 : (vesy->priority < 5 ? _("(High)")
2972 : _("(Low)")));
2973 so_puts(in_store, tmp_20k_buf);
2974 so_puts(in_store, "\015\012");
2977 if(vesy->summary){
2978 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2979 _("Summary: "), vesy->summary);
2980 so_puts(in_store, tmp_20k_buf);
2981 so_puts(in_store, "\015\012");
2984 if(vesy->sender){
2985 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2986 _("Sender: "), vesy->sender);
2987 so_puts(in_store, tmp_20k_buf);
2988 so_puts(in_store, "\015\012");
2991 if(vesy->organizer){
2992 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2993 _("Organizer: "), vesy->organizer);
2994 so_puts(in_store, tmp_20k_buf);
2995 so_puts(in_store, "\015\012");
2998 if(vesy->location){
2999 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3000 _("Location: "), vesy->location);
3001 so_puts(in_store, tmp_20k_buf);
3002 so_puts(in_store, "\015\012");
3005 if(vesy->evstart){
3006 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3007 _("Start Date: "), vesy->evstart);
3008 so_puts(in_store, tmp_20k_buf);
3009 so_puts(in_store, "\015\012");
3012 if(vesy->duration){
3013 for(i = 0; vesy->duration[i] != NULL; i++){
3014 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3015 _("Duration: "), vesy->duration[i]);
3016 so_puts(in_store, tmp_20k_buf);
3017 so_puts(in_store, "\015\012");
3019 } else if(vesy->evend){
3020 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3021 _("End Date: "), vesy->evend);
3022 so_puts(in_store, tmp_20k_buf);
3023 so_puts(in_store, "\015\012");
3026 if(vesy->dtstamp){
3027 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3028 vcal->method ? _("Created on: ") : _("Last Revised on; "),
3029 vesy->dtstamp);
3030 so_puts(in_store, tmp_20k_buf);
3031 so_puts(in_store, "\015\012");
3034 if(vesy->description){
3035 char c;
3036 int j, empty;
3038 so_puts(in_store, "\015\012");
3040 for(i = 0; vesy->description[i] != NULL; i++){
3041 so_puts(in_store, _("Description: "));
3042 /* Check if empty description */
3043 empty = 1;
3044 for(j =0; empty == 1 && vesy->description[i][j] != '\0'; j++){
3045 c = vesy->description[i][j];
3046 if(c != '\n' && c != ' ' && c != '\t')
3047 empty = 0;
3049 if(empty){
3050 so_puts(in_store, _("[ No description provided ]"));
3051 so_puts(in_store, "\015\012");
3053 else {
3054 for(j =0; vesy->description[i][j] != '\0'; j++){
3055 c = vesy->description[i][j];
3056 if(c == '\n'){
3057 so_puts(in_store, "\015\012");
3058 continue;
3060 so_writec(c, in_store);
3063 so_puts(in_store, "\015\012");
3067 if(vesy->attendee){
3068 so_puts(in_store, "\015\012");
3069 so_puts(in_store, _("List of Attendees:"));
3070 so_puts(in_store, "\015\012");
3071 for(i = 0; vesy->attendee[i] != NULL; i++){
3072 so_puts(in_store, vesy->attendee[i]);
3073 so_puts(in_store, "\015\012");
3075 so_puts(in_store, "\015\012");
3078 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3079 _("This event was tagged as a %s entry by the sender"), vesy->class);
3080 so_puts(in_store, tmp_20k_buf);
3081 so_puts(in_store, "\015\012\015\012");
3083 if(depth < 0 && vesy->next){
3084 for(i = 0; i < ps_global->ttyo->screen_cols && i < 40; i++)
3085 tmp_20k_buf[i] = '-';
3086 tmp_20k_buf[i]= '\0';
3087 so_puts(in_store, tmp_20k_buf);
3088 so_puts(in_store, "\015\012");
3090 } /* end "for" loop */
3093 so_seek(in_store, 0L, 0);
3095 init_handles(&handles);
3096 gf_filter_init();
3098 if(F_ON(F_VIEW_SEL_URL, ps_global)
3099 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3100 || F_ON(F_SCAN_ADDR, ps_global))
3101 gf_link_filter(gf_line_test,
3102 gf_line_test_opt(url_hilite,
3103 gf_url_hilite_opt(&uh,&handles,0)));
3105 gf_link_filter(gf_wrap,
3106 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3107 ps_global->ttyo->screen_cols,
3108 NULL, 0, GFW_HANDLES));
3109 gf_link_filter(gf_nvtnl_local, NULL);
3111 gf_set_so_readc(&gc, in_store);
3112 gf_set_so_writec(&pc, out_store);
3114 errstr = gf_pipe(gc, pc);
3116 gf_clear_so_readc(in_store);
3118 gf_clear_so_writec(out_store);
3120 if(!errstr)
3121 cmd = scroll_attachment(_("CALENDAR EVENT ATTACHMENT"), out_store,
3122 CharStar, handles, a, flags | DA_RESIZE);
3124 free_handles(&handles);
3125 so_give(&out_store);
3127 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3129 if(errstr)
3130 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3131 _("Can't format entry : %s"), errstr);
3132 so_give(&in_store);
3133 free_vevent_summary(&vsummary);
3134 ps_global->mangled_screen = 1;
3138 void
3139 display_vcalendar_att(long int msgno, ATTACH_S *a, int flags)
3141 display_vevent_summary(msgno, a, flags, -1);
3145 /*----------------------------------------------------------------------
3146 Display attachment information
3148 Args: msgno -- message number to get partrom
3149 a -- attachment struct for the desired part
3151 Result: a screen containing information about attachment:
3152 ----*/
3153 void
3154 display_attach_info(long int msgno, ATTACH_S *a)
3156 int i, indent, cols;
3157 char buf1[100], *folded;
3158 STORE_S *store;
3159 PARMLIST_S *plist;
3160 SCROLL_S sargs;
3162 (void) dispatch_attachment(a);
3164 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
3165 q_status_message(SM_ORDER | SM_DING, 3, 3,
3166 _("Error allocating space for message."));
3167 return;
3170 cols = ps_global->ttyo->screen_cols;
3173 * 2 spaces on left
3174 * 16 for text (longest is Display Method == 14)
3175 * 2 for ": "
3177 indent = 20;
3179 /* don't try stupid folding */
3180 cols = MAX(indent+10, cols);
3182 so_puts(store, "Details about Attachment #");
3183 so_puts(store, a->number);
3184 so_puts(store, " :\n\n");
3185 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
3186 so_puts(store, buf1);
3187 so_puts(store, body_type_names(a->body->type));
3188 so_puts(store, "\n");
3189 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
3190 so_puts(store, buf1);
3191 so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
3192 so_puts(store, "\n");
3193 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
3194 so_puts(store, buf1);
3195 so_puts(store, a->body->encoding < ENCMAX
3196 ? body_encodings[a->body->encoding]
3197 : "Unknown");
3198 so_puts(store, "\n");
3199 if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
3200 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
3201 so_puts(store, buf1);
3202 i = 0;
3203 while(rfc2231_list_params(plist)){
3204 if(i++)
3205 so_puts(store, repeat_char(indent, ' '));
3207 so_puts(store, plist->attrib);
3208 so_puts(store, " = ");
3209 so_puts(store, plist->value ? plist->value : "");
3210 so_puts(store, "\n");
3213 rfc2231_free_parmlist(&plist);
3216 if(a->body->description && a->body->description[0]){
3217 char buftmp[MAILTMPLEN];
3218 unsigned char *q;
3220 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
3222 snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
3223 buftmp[sizeof(buftmp)-1] = '\0';
3224 q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
3225 folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
3227 if(folded){
3228 so_puts(store, folded);
3229 fs_give((void **) &folded);
3233 /* BUG: no a->body->language support */
3235 if(a->body->disposition.type){
3236 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
3237 so_puts(store, buf1);
3238 so_puts(store, a->body->disposition.type);
3239 so_puts(store, "\n");
3240 if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
3241 while(rfc2231_list_params(plist)){
3242 so_puts(store, repeat_char(indent, ' '));
3243 so_puts(store, plist->attrib);
3244 so_puts(store, " = ");
3245 so_puts(store, plist->value ? plist->value : "");
3246 so_puts(store, "\n");
3249 rfc2231_free_parmlist(&plist);
3253 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
3254 so_puts(store, buf1);
3255 so_puts(store, comatose((a->body->encoding == ENCBASE64)
3256 ? ((a->body->size.bytes * 3)/4)
3257 : a->body->size.bytes));
3258 so_puts(store, " bytes\n");
3259 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
3260 so_puts(store, buf1);
3261 if(a->can_display == MCD_NONE) {
3262 so_puts(store, "Can't, ");
3263 so_puts(store, (a->body->encoding < ENCOTHER)
3264 ? "Unknown Attachment Format"
3265 : "Unknown Encoding");
3267 else if(!(a->can_display & MCD_EXTERNAL)){
3268 so_puts(store, "Alpine's Internal Pager");
3270 else{
3271 int nt, free_pretty_cmd;
3272 MCAP_CMD_S *mc_cmd;
3273 char *pretty_cmd;
3275 if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
3276 a->body, "<datafile>", &nt,
3277 a->can_display & MCD_EXT_PROMPT)) != NULL){
3278 so_puts(store, "\"");
3279 if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
3280 so_puts(store, pretty_cmd);
3281 so_puts(store, "\"");
3282 if(free_pretty_cmd && pretty_cmd)
3283 fs_give((void **)&pretty_cmd);
3284 if(mc_cmd->command)
3285 fs_give((void **)&mc_cmd->command);
3286 fs_give((void **)&mc_cmd);
3290 so_puts(store, "\n");
3292 memset(&sargs, 0, sizeof(SCROLL_S));
3293 sargs.text.text = so_text(store);
3294 sargs.text.src = CharStar;
3295 sargs.text.desc = "attachment info";
3296 sargs.bar.title = _("ABOUT ATTACHMENT");
3297 sargs.help.text = h_simple_text_view;
3298 sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
3300 sargs.use_indexline_color = 1;
3302 scrolltool(&sargs);
3304 so_give(&store); /* free resources associated with store */
3305 ps_global->mangled_screen = 1;
3309 /*----------------------------------------------------------------------
3311 ----*/
3312 void
3313 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3315 char *sig;
3316 void *msgtext;
3317 ENVELOPE *outgoing;
3318 BODY *body;
3320 if(MIME_MSG_A(a)){
3321 forward_msg_att(stream, msgno, a);
3323 else{
3324 ACTION_S *role = NULL;
3325 REDRAFT_POS_S *redraft_pos = NULL;
3326 long rflags = ROLE_FORWARD;
3327 PAT_STATE dummy;
3329 outgoing = mail_newenvelope();
3330 outgoing->message_id = generate_message_id();
3331 outgoing->subject = cpystr("Forwarded attachment...");
3333 if(nonempty_patterns(rflags, &dummy)){
3335 * There is no message, but a Current Folder Type might match.
3337 * This has been changed to check against the message
3338 * containing the attachment.
3340 role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3341 if(confirm_role(rflags, &role))
3342 role = combine_inherited_role(role);
3343 else{
3344 role = NULL;
3345 cmd_cancelled("Forward");
3346 mail_free_envelope(&outgoing);
3347 return;
3351 if(role)
3352 q_status_message1(SM_ORDER, 3, 4,
3353 _("Forwarding using role \"%s\""), role->nick);
3356 * as with all text bound for the composer, build it in
3357 * a storage object of the type it understands...
3359 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3360 int impl, template_len = 0;
3362 if(role && role->template){
3363 char *filtered;
3365 impl = 1;
3366 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3367 if(filtered){
3368 if(*filtered){
3369 so_puts((STORE_S *)msgtext, filtered);
3370 if(impl == 1)
3371 template_len = strlen(filtered);
3374 fs_give((void **)&filtered);
3377 else
3378 impl = 1;
3380 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3381 if(impl == 2)
3382 redraft_pos->offset += template_len;
3384 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3386 fs_give((void **)&sig);
3388 else
3389 so_puts((STORE_S *)msgtext, NEWLINE);
3391 /*---- New Body to start with ----*/
3392 body = mail_newbody();
3393 body->type = TYPEMULTIPART;
3395 /*---- The TEXT part/body ----*/
3396 body->nested.part = mail_newbody_part();
3397 body->nested.part->body.type = TYPETEXT;
3398 body->nested.part->body.contents.text.data = msgtext;
3400 /*---- The corresponding things we're attaching ----*/
3401 body->nested.part->next = mail_newbody_part();
3402 body->nested.part->next->body.id = generate_message_id();
3403 copy_body(&body->nested.part->next->body, a->body);
3405 if(fetch_contents(stream, msgno, a->number,
3406 &body->nested.part->next->body)){
3407 pine_send(outgoing, &body, "FORWARD MESSAGE",
3408 role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3410 ps_global->mangled_screen = 1;
3411 pine_free_body(&body);
3412 free_redraft_pos(&redraft_pos);
3414 else{
3415 mail_free_body(&body);
3416 so_give((STORE_S **) &msgtext);
3417 free_redraft_pos(&redraft_pos);
3418 q_status_message(SM_ORDER | SM_DING, 4, 5,
3419 _("Error fetching message contents. Can't forward message."));
3422 else
3423 q_status_message(SM_ORDER | SM_DING, 3, 4,
3424 _("Error allocating message text"));
3426 mail_free_envelope(&outgoing);
3427 free_action(&role);
3432 void
3433 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3435 char *p, *sig = NULL;
3436 int ret;
3437 void *msgtext;
3438 ENVELOPE *outgoing;
3439 BODY *body;
3440 ACTION_S *role = NULL;
3441 REPLY_S reply;
3442 REDRAFT_POS_S *redraft_pos = NULL;
3444 outgoing = mail_newenvelope();
3445 outgoing->message_id = generate_message_id();
3447 memset((void *)&reply, 0, sizeof(reply));
3449 if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
3451 * as with all text bound for the composer, build it in
3452 * a storage object of the type it understands...
3454 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3455 int impl, template_len = 0;
3456 long rflags = ROLE_FORWARD;
3457 PAT_STATE dummy;
3459 ret = 'n';
3460 if(ps_global->full_header == 2)
3461 ret = want_to(_("Forward message as an attachment"), 'n', 0,
3462 NO_HELP, WT_SEQ_SENSITIVE);
3463 /* Setup possible role */
3464 if(nonempty_patterns(rflags, &dummy)){
3465 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3466 if(confirm_role(rflags, &role))
3467 role = combine_inherited_role(role);
3468 else{ /* cancel reply */
3469 role = NULL;
3470 cmd_cancelled("Forward");
3471 mail_free_envelope(&outgoing);
3472 so_give((STORE_S **) &msgtext);
3473 return;
3477 if(role)
3478 q_status_message1(SM_ORDER, 3, 4,
3479 _("Forwarding using role \"%s\""), role->nick);
3481 if(role && role->template){
3482 char *filtered;
3484 impl = 1;
3485 filtered = detoken(role, a->body->nested.msg->env,
3486 0, 0, 0, &redraft_pos, &impl);
3487 if(filtered){
3488 if(*filtered){
3489 so_puts((STORE_S *)msgtext, filtered);
3490 if(impl == 1)
3491 template_len = strlen(filtered);
3494 fs_give((void **)&filtered);
3497 else
3498 impl = 1;
3500 if((sig = detoken(role, a->body->nested.msg->env,
3501 2, 0, 1, &redraft_pos, &impl)) != NULL){
3502 if(impl == 2)
3503 redraft_pos->offset += template_len;
3505 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3507 fs_give((void **)&sig);
3509 else
3510 so_puts((STORE_S *)msgtext, NEWLINE);
3512 if(ret == 'y'){
3513 /*---- New Body to start with ----*/
3514 body = mail_newbody();
3515 body->type = TYPEMULTIPART;
3517 /*---- The TEXT part/body ----*/
3518 body->nested.part = mail_newbody_part();
3519 body->nested.part->body.type = TYPETEXT;
3520 body->nested.part->body.contents.text.data = msgtext;
3522 if(!forward_mime_msg(stream, msgno,
3523 p = body_partno(stream, msgno, a->body),
3524 a->body->nested.msg->env,
3525 &body->nested.part->next, msgtext))
3526 mail_free_body(&body);
3528 else{
3529 reply.forw = 1;
3530 if(a->body->nested.msg->body){
3531 char *charset;
3533 charset
3534 = parameter_val(a->body->nested.msg->body->parameter,
3535 "charset");
3537 if(charset && strucmp(charset, "us-ascii") != 0){
3538 CONV_TABLE *ct;
3541 * There is a non-ascii charset,
3542 * is there conversion happening?
3544 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3545 || !ct->table){
3546 reply.orig_charset = charset;
3547 charset = NULL;
3551 if(charset)
3552 fs_give((void **) &charset);
3555 body = forward_body(stream, a->body->nested.msg->env,
3556 a->body->nested.msg->body, msgno,
3557 p = body_partno(stream, msgno, a->body),
3558 msgtext, FWD_NONE);
3561 fs_give((void **) &p);
3563 if(body){
3564 pine_send(outgoing, &body,
3565 "FORWARD MESSAGE",
3566 role, NULL,
3567 reply.forw ? &reply : NULL,
3568 redraft_pos, NULL, NULL, 0);
3570 ps_global->mangled_screen = 1;
3571 pine_free_body(&body);
3572 free_redraft_pos(&redraft_pos);
3573 free_action(&role);
3575 else{
3576 so_give((STORE_S **) &msgtext);
3577 q_status_message(SM_ORDER | SM_DING, 4, 5,
3578 _("Error fetching message contents. Can't forward message."));
3581 else
3582 q_status_message(SM_ORDER | SM_DING, 3, 4,
3583 _("Error allocating message text"));
3585 else
3586 q_status_message1(SM_ORDER,3,4,
3587 _("Error fetching message %s. Can't forward it."),
3588 long2string(msgno));
3590 if(reply.orig_charset)
3591 fs_give((void **)&reply.orig_charset);
3593 mail_free_envelope(&outgoing);
3597 void
3598 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3600 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
3601 ENVELOPE *outgoing;
3602 BODY *body;
3603 void *msgtext;
3604 char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3605 int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3606 int rolemsg = 0, copytomsg = 0;
3607 long rflags;
3608 PAT_STATE dummy;
3609 REDRAFT_POS_S *redraft_pos = NULL;
3610 ACTION_S *role = NULL;
3612 outgoing = mail_newenvelope();
3614 dprint((4,"\n - attachment reply \n"));
3616 saved_from = (ADDRESS *) NULL;
3617 saved_to = (ADDRESS *) NULL;
3618 saved_cc = (ADDRESS *) NULL;
3619 saved_resent = (ADDRESS *) NULL;
3620 outgoing->subject = NULL;
3622 prefix = reply_quote_str(a->body->nested.msg->env);
3624 * For consistency, the first question is always "include text?"
3626 if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0
3627 && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3628 && reply_harvest(ps_global, msgno, a->number,
3629 a->body->nested.msg->env, &saved_from,
3630 &saved_to, &saved_cc, &saved_resent, &flags)){
3631 outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3632 NULL, 0);
3633 clear_cursor_pos();
3634 reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3635 saved_from, saved_to, saved_cc, saved_resent,
3636 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3637 if(errmsg){
3638 if(*errmsg){
3639 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3640 display_message(NO_OP_COMMAND);
3643 fs_give((void **)&errmsg);
3646 if(sp_expunge_count(stream)) /* current msg was expunged */
3647 goto seeyalater;
3649 /* Setup possible role */
3650 rflags = ROLE_REPLY;
3651 if(nonempty_patterns(rflags, &dummy)){
3652 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3653 if(confirm_role(rflags, &role))
3654 role = combine_inherited_role(role);
3655 else{ /* cancel reply */
3656 role = NULL;
3657 cmd_cancelled("Reply");
3658 goto seeyalater;
3662 if(role){
3663 rolemsg++;
3665 /* override fcc gotten in reply_seed */
3666 if(role->fcc && fcc)
3667 fs_give((void **) &fcc);
3670 if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3671 ADDRESS *us_in_to_and_cc, *ap;
3673 us_in_to_and_cc = (ADDRESS *) NULL;
3674 if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3675 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3676 NULL, us_in_to_and_cc, NULL,
3677 a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3678 reply_append_addr(&us_in_to_and_cc, ap);
3680 if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3681 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3682 NULL, us_in_to_and_cc, NULL,
3683 a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3684 reply_append_addr(&us_in_to_and_cc, ap);
3687 * A list of all of our addresses that appear in the To
3688 * and cc fields is in us_in_to_and_cc.
3689 * If there is exactly one address in that list then
3690 * use it for the outgoing From.
3692 if(us_in_to_and_cc && !us_in_to_and_cc->next){
3693 PINEFIELD *custom, *pf;
3694 ADDRESS *a = NULL;
3695 char *addr = NULL;
3698 * Check to see if this address is different from what
3699 * we would have used anyway. If it is, notify the user
3700 * with a status message. This is pretty hokey because we're
3701 * mimicking how pine_send would set the From address and
3702 * there is no coordination between the two.
3705 /* in case user has a custom From value */
3706 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3708 pf = (PINEFIELD *) fs_get(sizeof(*pf));
3709 memset((void *) pf, 0, sizeof(*pf));
3710 pf->name = cpystr("From");
3711 pf->addr = &a;
3712 if(set_default_hdrval(pf, custom) >= UseAsDef
3713 && pf->textbuf && pf->textbuf[0]){
3714 removing_trailing_white_space(pf->textbuf);
3715 (void)removing_double_quotes(pf->textbuf);
3716 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3717 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3718 if(addr)
3719 fs_give((void **) &addr);
3722 if(!*pf->addr)
3723 *pf->addr = generate_from();
3725 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3726 copytomsg++;
3727 if(!role){
3728 role = (ACTION_S *) fs_get(sizeof(*role));
3729 memset((void *) role, 0, sizeof(*role));
3730 role->is_a_role = 1;
3733 role->from = us_in_to_and_cc;
3734 us_in_to_and_cc = NULL;
3737 free_customs(custom);
3738 free_customs(pf);
3741 if(us_in_to_and_cc)
3742 mail_free_address(&us_in_to_and_cc);
3746 if(role){
3747 if(rolemsg && copytomsg)
3748 q_status_message1(SM_ORDER, 3, 4,
3749 _("Replying using role \"%s\" and To as From"), role->nick);
3750 else if(rolemsg)
3751 q_status_message1(SM_ORDER, 3, 4,
3752 _("Replying using role \"%s\""), role->nick);
3753 else if(copytomsg)
3754 q_status_message(SM_ORDER, 3, 4,
3755 _("Replying using incoming To as outgoing From"));
3758 outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3759 outgoing->references = reply_build_refs(a->body->nested.msg->env);
3760 outgoing->message_id = generate_message_id();
3762 if(!outgoing->to && !outgoing->cc
3763 && !outgoing->bcc && !outgoing->newsgroups)
3764 q_status_message(SM_ORDER | SM_DING, 3, 6,
3765 _("Warning: no valid addresses to reply to!"));
3768 * Now fix up the body...
3770 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3771 REPLY_S reply;
3773 memset((void *)&reply, 0, sizeof(reply));
3774 reply.forw = 1;
3775 if(a->body->nested.msg->body){
3776 char *charset;
3778 charset
3779 = parameter_val(a->body->nested.msg->body->parameter,
3780 "charset");
3782 if(charset && strucmp(charset, "us-ascii") != 0){
3783 CONV_TABLE *ct;
3786 * There is a non-ascii charset,
3787 * is there conversion happening?
3789 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3790 || !ct->table){
3791 reply.orig_charset = charset;
3792 charset = NULL;
3796 if(charset)
3797 fs_give((void **) &charset);
3800 if((body = reply_body(stream, a->body->nested.msg->env,
3801 a->body->nested.msg->body, msgno,
3802 tp = body_partno(stream, msgno, a->body),
3803 msgtext, prefix, include_text, role,
3804 1, &redraft_pos)) != NULL){
3805 /* partially formatted outgoing message */
3806 pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3807 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3809 pine_free_body(&body);
3810 ps_global->mangled_screen = 1;
3812 else
3813 q_status_message(SM_ORDER | SM_DING, 3, 4,
3814 _("Error building message body"));
3816 fs_give((void **) &tp);
3817 if(reply.orig_charset)
3818 fs_give((void **)&reply.orig_charset);
3820 else
3821 q_status_message(SM_ORDER | SM_DING, 3, 4,
3822 _("Error allocating message text"));
3825 seeyalater:
3826 mail_free_envelope(&outgoing);
3827 mail_free_address(&saved_from);
3828 mail_free_address(&saved_to);
3829 mail_free_address(&saved_cc);
3830 mail_free_address(&saved_resent);
3832 if(prefix)
3833 fs_give((void **) &prefix);
3835 if(fcc)
3836 fs_give((void **) &fcc);
3838 free_redraft_pos(&redraft_pos);
3839 free_action(&role);
3843 void
3844 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
3846 char *errstr;
3848 if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
3849 q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
3853 void
3854 pipe_attachment(long int msgno, ATTACH_S *a)
3856 char *err, *resultfilename = NULL, prompt[80], *p;
3857 int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
3858 long ku;
3859 PIPE_S *syspipe;
3860 HelpType help;
3861 char pipe_command[MAXPATH+1];
3862 unsigned flagsforhist = 1; /* raw=2 /capture=1 */
3863 static HISTORY_S *history = NULL;
3864 ESCKEY_S pipe_opt[6];
3866 if(ps_global->restricted){
3867 q_status_message(SM_ORDER | SM_DING, 0, 4,
3868 "Alpine demo can't pipe attachments");
3869 return;
3872 help = NO_HELP;
3873 pipe_command[0] = '\0';
3875 init_hist(&history, HISTSIZE);
3876 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3877 if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
3878 strncpy(pipe_command, p, sizeof(pipe_command));
3879 pipe_command[sizeof(pipe_command)-1] = '\0';
3880 if(history->hist[history->curindex]){
3881 flagsforhist = history->hist[history->curindex]->flags;
3882 raw = (flagsforhist & 0x2) ? 1 : 0;
3883 capture = (flagsforhist & 0x1) ? 1 : 0;
3887 pipe_opt[j].ch = 0;
3888 pipe_opt[j].rval = 0;
3889 pipe_opt[j].name = "";
3890 pipe_opt[j++].label = "";
3892 pipe_opt[j].ch = ctrl('W');
3893 pipe_opt[j].rval = 10;
3894 pipe_opt[j].name = "^W";
3895 pipe_opt[j++].label = NULL;
3897 pipe_opt[j].ch = ctrl('Y');
3898 pipe_opt[j].rval = 11;
3899 pipe_opt[j].name = "^Y";
3900 pipe_opt[j++].label = NULL;
3902 pipe_opt[j].ch = KEY_UP;
3903 pipe_opt[j].rval = 30;
3904 pipe_opt[j].name = "";
3905 ku = j;
3906 pipe_opt[j++].label = "";
3908 pipe_opt[j].ch = KEY_DOWN;
3909 pipe_opt[j].rval = 31;
3910 pipe_opt[j].name = "";
3911 pipe_opt[j++].label = "";
3913 pipe_opt[j].ch = -1;
3915 while(1){
3916 int flags;
3918 snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
3919 a->number, capture ? "" : "(Free Output) ");
3920 prompt[sizeof(prompt)-1] = '\0';
3921 pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
3922 pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
3925 * 2 is really 1 because there will be one real entry and
3926 * one entry of "" because of the get_prev_hist above.
3928 if(items_in_hist(history) > 2){
3929 pipe_opt[ku].name = HISTORY_UP_KEYNAME;
3930 pipe_opt[ku].label = HISTORY_KEYLABEL;
3931 pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
3932 pipe_opt[ku+1].label = HISTORY_KEYLABEL;
3934 else{
3935 pipe_opt[ku].name = "";
3936 pipe_opt[ku].label = "";
3937 pipe_opt[ku+1].name = "";
3938 pipe_opt[ku+1].label = "";
3941 flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
3942 rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
3943 sizeof(pipe_command), prompt,
3944 pipe_opt, help, &flags);
3945 if(rc == -1){
3946 q_status_message(SM_ORDER | SM_DING, 3, 4,
3947 "Internal problem encountered");
3948 break;
3950 else if(rc == 10){
3951 raw = !raw; /* flip raw text */
3953 else if(rc == 11){
3954 capture = !capture; /* flip capture output */
3956 else if(rc == 30){
3957 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3958 if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3959 strncpy(pipe_command, p, sizeof(pipe_command));
3960 pipe_command[sizeof(pipe_command)-1] = '\0';
3961 if(history->hist[history->curindex]){
3962 flagsforhist = history->hist[history->curindex]->flags;
3963 raw = (flagsforhist & 0x2) ? 1 : 0;
3964 capture = (flagsforhist & 0x1) ? 1 : 0;
3967 else
3968 Writechar(BELL, 0);
3970 else if(rc == 31){
3971 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3972 if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3973 strncpy(pipe_command, p, sizeof(pipe_command));
3974 pipe_command[sizeof(pipe_command)-1] = '\0';
3975 if(history->hist[history->curindex]){
3976 flagsforhist = history->hist[history->curindex]->flags;
3977 raw = (flagsforhist & 0x2) ? 1 : 0;
3978 capture = (flagsforhist & 0x1) ? 1 : 0;
3981 else
3982 Writechar(BELL, 0);
3984 else if(rc == 0){
3985 if(pipe_command[0] == '\0'){
3986 cmd_cancelled("Pipe command");
3987 break;
3990 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3991 save_hist(history, pipe_command, flagsforhist, NULL);
3993 flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
3994 flags |= (raw ? PIPE_RAW : 0);
3995 if(!capture){
3996 #ifndef _WINDOWS
3997 ClearScreen();
3998 fflush(stdout);
3999 clear_cursor_pos();
4000 ps_global->mangled_screen = 1;
4001 #endif
4002 flags |= PIPE_RESET;
4005 if((syspipe = open_system_pipe(pipe_command,
4006 (flags&PIPE_RESET) ? NULL : &resultfilename,
4007 NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
4008 int is_text = 0;
4009 gf_io_t pc; /* wire up a generic putchar */
4011 is_text = (a && a->body && a->body->type == TYPETEXT);
4013 gf_set_writec(&pc, syspipe, 0L, PipeStar,
4014 (is_text && !raw) ? WRITE_TO_LOCALE : 0);
4016 /*------ Write the image to a temporary file ------*/
4017 if(raw){ /* pipe raw text */
4018 FETCH_READC_S fetch_part;
4020 err = NULL;
4022 if(capture)
4023 we_cancel = busy_cue(NULL, NULL, 1);
4024 else
4025 suspend_busy_cue();
4027 gf_filter_init();
4028 fetch_readc_init(&fetch_part, ps_global->mail_stream,
4029 msgno, a->number, a->body->size.bytes, 0, 0);
4030 gf_link_filter(gf_nvtnl_local, NULL);
4031 err = gf_pipe(FETCH_READC, pc);
4033 if(capture){
4034 if(we_cancel)
4035 cancel_busy_cue(0);
4037 else
4038 resume_busy_cue(0);
4040 else{
4041 /* BUG: there's got to be a better way */
4042 if(!capture)
4043 ps_global->print = (PRINT_S *) 1;
4045 suspend_busy_cue();
4046 err = detach(ps_global->mail_stream, msgno,
4047 a->number, 0L, (long *)NULL, pc, NULL, 0);
4048 ps_global->print = (PRINT_S *) NULL;
4049 resume_busy_cue(0);
4052 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
4054 if(err)
4055 q_status_message1(SM_ORDER | SM_DING, 3, 4,
4056 _("Error detaching for pipe: %s"), err);
4058 display_output_file(resultfilename,
4059 (err)
4060 ? _("PIPE ATTACHMENT (ERROR)")
4061 : _("PIPE ATTACHMENT"),
4062 NULL, DOF_EMPTY);
4064 fs_give((void **) &resultfilename);
4066 else
4067 q_status_message(SM_ORDER | SM_DING, 3, 4,
4068 _("Error opening pipe"));
4070 break;
4072 else if(rc == 1){
4073 cmd_cancelled("Pipe");
4074 break;
4076 else if(rc == 3)
4077 help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
4083 delete_attachment(long int msgno, ATTACH_S *a)
4085 int expbits, rv = 0;
4087 if(!msgno_exceptions(ps_global->mail_stream, msgno,
4088 a->number, &expbits, FALSE)
4089 || !(expbits & MSG_EX_DELETE)){
4090 expbits |= MSG_EX_DELETE;
4091 msgno_exceptions(ps_global->mail_stream, msgno,
4092 a->number, &expbits, TRUE);
4094 q_status_message1(SM_ORDER, 0, 3,
4095 _("Part %s will be omitted only if message is Saved"),
4096 a->number);
4097 rv = 1;
4099 else
4100 q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
4101 a->number);
4103 return(rv);
4108 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
4110 int rv = 0;
4112 if(msgno_exceptions(ps_global->mail_stream, msgno,
4113 a->number, expbitsp, FALSE)
4114 && ((*expbitsp) & MSG_EX_DELETE)){
4115 (*expbitsp) ^= MSG_EX_DELETE;
4116 msgno_exceptions(ps_global->mail_stream, msgno,
4117 a->number, expbitsp, TRUE);
4118 rv = 1;
4120 else
4121 q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
4122 a->number);
4124 return(rv);
4128 /*----------------------------------------------------------------------
4129 Resolve any deferred tests for attachment displayability
4131 Args: attachment structure
4133 Returns: undefer's attachment's displayability test
4134 ----*/
4136 dispatch_attachment(ATTACH_S *a)
4138 if(a->test_deferred){
4139 a->test_deferred = 0;
4140 a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
4143 return(a->can_display);
4147 #ifdef _WINDOWS
4149 scroll_att_popup(sparms, in_handle)
4150 SCROLL_S *sparms;
4151 int in_handle;
4153 MPopup scrat_popup[20];
4154 int i = -1, n;
4156 if(in_handle){
4157 scrat_popup[++i].type = tIndex;
4158 scrat_popup[i].label.style = lNormal;
4159 scrat_popup[i].label.string = "View Selectable Item";
4160 scrat_popup[i].data.val = ctrl('L');
4163 scrat_popup[++i].type = tQueue;
4164 scrat_popup[i].label.style = lNormal;
4165 scrat_popup[i].label.string = "&Save";
4166 scrat_popup[i].data.val = 'S';
4168 scrat_popup[++i].type = tQueue;
4169 scrat_popup[i].label.style = lNormal;
4170 if(msgno_exceptions(ps_global->mail_stream,
4171 mn_m2raw(ps_global->msgmap,
4172 mn_get_cur(ps_global->msgmap)),
4173 scrat_attachp->number, &n, FALSE)
4174 && (n & MSG_EX_DELETE)){
4175 scrat_popup[i].label.string = "&Undelete";
4176 scrat_popup[i].data.val = 'U';
4178 else{
4179 scrat_popup[i].label.string = "&Delete";
4180 scrat_popup[i].data.val = 'D';
4183 if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
4184 scrat_popup[++i].type = tQueue;
4185 scrat_popup[i].label.style = lNormal;
4186 scrat_popup[i].label.string = "&Reply";
4187 scrat_popup[i].data.val = 'R';
4189 scrat_popup[++i].type = tQueue;
4190 scrat_popup[i].label.style = lNormal;
4191 scrat_popup[i].label.string = "&Forward";
4192 scrat_popup[i].data.val = 'f';
4195 scrat_popup[++i].type = tSeparator;
4197 scrat_popup[++i].type = tQueue;
4198 scrat_popup[i].label.style = lNormal;
4199 scrat_popup[i].label.string = "Attachment Index";
4200 scrat_popup[i].data.val = '<';
4202 scrat_popup[++i].type = tTail;
4204 return(mswin_popup(scrat_popup) == 0 && in_handle);
4208 void
4209 display_att_window(a)
4210 ATTACH_S *a;
4212 #if !defined(DOS) && !defined(OS2)
4213 char prefix[8];
4214 #endif
4216 if(a->body->type == TYPEMULTIPART){
4217 if(a->body->subtype){
4218 /* if(!strucmp(a->body->subtype, "digest"))
4219 display_digest_att(msgno, a, flags);
4220 else */
4221 q_status_message1(SM_ORDER, 3, 5,
4222 "Can't display Multipart/%s",
4223 a->body->subtype);
4225 else
4226 q_status_message(SM_ORDER, 3, 5,
4227 "Can't display unknown Multipart Subtype");
4229 /* else if(MIME_VCARD_A(a))
4230 display_vcard_att_window(msgno, a, flags);*/
4231 else if(a->body->type == TYPETEXT)
4232 display_text_att_window(a);
4233 else if(a->body->type == TYPEMESSAGE)
4234 display_msg_att_window(a);
4238 void
4239 display_text_att_window(a)
4240 ATTACH_S *a;
4242 STORE_S *store;
4243 long msgno;
4245 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4247 if(store = format_text_att(msgno, a, NULL)){
4248 if (mswin_displaytext("ATTACHED TEXT",
4249 so_text(store),
4250 strlen((char *) so_text(store)),
4251 NULL, NULL, 0) >= 0)
4252 store->txt = (void *) NULL; /* free'd in mswin_displaytext */
4254 so_give(&store); /* free resources associated with store */
4256 else
4257 q_status_message(SM_ORDER | SM_DING, 3, 3,
4258 "Error allocating space for attachment.");
4262 void
4263 display_msg_att_window(a)
4264 ATTACH_S *a;
4266 STORE_S *store;
4267 gf_io_t pc;
4268 ATTACH_S *ap = a;
4269 long msgno;
4271 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4273 /* BUG, should check this return code */
4274 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
4276 /* initialize a storage object */
4277 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
4279 gf_set_so_writec(&pc, store);
4281 if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
4282 && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
4283 strlen((char *) so_text(store)),
4284 NULL, NULL, 0) >= 0)
4285 /* free'd in mswin_displaytext */
4286 store->txt = (void *) NULL;
4288 gf_clear_so_writec(store);
4290 so_give(&store);
4292 else
4293 q_status_message(SM_ORDER | SM_DING, 3, 3,
4294 "Error allocating space for message.");
4296 #endif