* New version 2.21.999
[alpine.git] / alpine / mailpart.c
blob1e0f91339878c022eda6a98b897a5feba6e90638
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 ++(*a);
2729 #ifdef SMIME
2730 if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2731 if((*a)->description){
2732 if(!(!format_editorial((*a)->description,
2733 ps_global->ttyo->screen_cols,
2734 flags, NULL, pc)
2735 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2736 rv = 0;
2739 ++(*a);
2741 #endif /* SMIME */
2743 if(((*a))->description
2744 && (*a)->body && (*a)->body->type == TYPETEXT){
2745 if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2746 rv = 0;
2748 else if(!(gf_puts("[Can't display ", pc)
2749 && gf_puts(((*a)->description && (*a)->body)
2750 ? "first non-" : "missing ", pc)
2751 && gf_puts("text segment]", pc)
2752 && gf_puts(NEWLINE, pc)))
2753 rv = 0;
2755 ++(*a);
2758 else if((*a)->body->subtype
2759 && strucmp((*a)->body->subtype, "external-body") == 0) {
2760 if(format_editorial("This part is not included and can be fetched as follows:",
2761 ps_global->ttyo->screen_cols, flags, NULL, pc)
2762 || !gf_puts(NEWLINE, pc)
2763 || format_editorial(display_parameters((*a)->body->parameter),
2764 ps_global->ttyo->screen_cols, flags, handlesp, pc))
2765 rv = 0;
2767 else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2768 rv = 0;
2770 return(rv);
2774 void
2775 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2777 STORE_S *in_store, *out_store = NULL;
2778 HANDLE_S *handles = NULL;
2779 URL_HILITE_S uh;
2780 gf_io_t gc, pc;
2781 char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2782 int cmd, indent, begins = 0;
2784 lines = detach_vcard_att(ps_global->mail_stream,
2785 msgno, a->body, a->number);
2786 if(!lines){
2787 q_status_message(SM_ORDER | SM_DING, 3, 3,
2788 _("Error accessing attachment."));
2789 return;
2792 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2793 free_list_array(&lines);
2794 q_status_message(SM_ORDER | SM_DING, 3, 3,
2795 _("Error allocating space for attachment."));
2796 return;
2799 for(ll = lines, indent = 0; ll && *ll; ll++)
2800 if((p = strindex(*ll, ':')) && p - *ll > indent)
2801 indent = p - *ll;
2803 indent += 5;
2804 for(ll = lines; ll && *ll; ll++){
2805 if((p = strindex(*ll, ':')) != NULL){
2806 if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2807 begins++;
2809 snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
2810 (int) MIN(p - *ll, sizeof(tmp)-5), *ll);
2811 tmp[sizeof(tmp)-1] = '\0';
2812 so_puts(in_store, tmp);
2813 p++;
2815 else{
2816 p = *ll;
2817 so_puts(in_store, repeat_char(indent, SPACE));
2820 snprintf(tmp, sizeof(tmp), "%.200s", p);
2821 tmp[sizeof(tmp)-1] = '\0';
2822 so_puts(in_store,
2823 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
2824 SIZEOF_20KBUF, tmp));
2825 so_puts(in_store, "\015\012");
2828 free_list_array(&lines);
2830 so_puts(in_store, "\015\012\015\012");
2833 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2834 so_seek(in_store, 0L, 0);
2836 init_handles(&handles);
2837 gf_filter_init();
2839 if(F_ON(F_VIEW_SEL_URL, ps_global)
2840 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2841 || F_ON(F_SCAN_ADDR, ps_global))
2842 gf_link_filter(gf_line_test,
2843 gf_line_test_opt(url_hilite,
2844 gf_url_hilite_opt(&uh,&handles,0)));
2846 gf_link_filter(gf_wrap,
2847 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
2848 ps_global->ttyo->screen_cols,
2849 NULL, indent, GFW_HANDLES));
2850 gf_link_filter(gf_nvtnl_local, NULL);
2852 gf_set_so_readc(&gc, in_store);
2853 gf_set_so_writec(&pc, out_store);
2855 errstr = gf_pipe(gc, pc);
2857 gf_clear_so_readc(in_store);
2859 if(!errstr){
2860 #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.")
2861 #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.")
2862 errstr = format_editorial((begins > 1)
2863 ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
2864 ps_global->ttyo->screen_cols, 0, NULL, pc);
2867 gf_clear_so_writec(out_store);
2869 if(!errstr)
2870 cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
2871 CharStar, handles, a, flags | DA_RESIZE);
2873 free_handles(&handles);
2874 so_give(&out_store);
2876 else
2877 errstr = _("Error allocating space");
2879 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
2881 if(errstr)
2882 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2883 _("Can't format entry : %s"), errstr);
2885 so_give(&in_store);
2888 void
2889 display_vcalendar_att(long int msgno, ATTACH_S *a, int flags)
2891 BODY *b;
2892 VCALENDAR_S *vcal = NULL;
2893 char *b64text, *caltext;
2894 unsigned long callen;
2895 STORE_S *in_store, *out_store = NULL;
2896 HANDLE_S *handles = NULL;
2897 URL_HILITE_S uh;
2898 gf_io_t gc, pc;
2899 char *errstr = NULL, tmp[MAILTMPLEN], *p;
2900 int cmd, i;
2901 VEVENT_SUMMARY_S *vesy; /* vevent summary */
2903 b = mail_body (ps_global->mail_stream, msgno, a->number);
2904 if(b->sparep == NULL){
2905 b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
2906 b64text[callen] = '\0'; /* chop off cookie */
2907 caltext = rfc822_base64(b64text, strlen(b64text), &callen);
2908 vcal = ical_parse_text(caltext);
2909 b->sparep = create_body_sparep(iCalType, (void *) vcal);
2911 else if(get_body_sparep_type(b->sparep) == iCalType)
2912 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
2914 vesy = ical_vevent_summary(vcal);
2916 if(vesy == NULL){
2917 q_status_message(SM_ORDER | SM_DING, 3, 3,
2918 _("Error parsing event"));
2919 return;
2922 if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2923 q_status_message(SM_ORDER | SM_DING, 3, 3,
2924 _("Error allocating space for Calendar"));
2925 return;
2928 if(vesy->cancel){
2929 so_puts(in_store, _("This event was cancelled"));
2930 so_puts(in_store, "\015\012");
2933 if(vesy->priority){
2934 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%d %s",
2935 _("Priority: "), vesy->priority,
2936 vesy->priority == 5 ? _("(Normal)")
2937 : (vesy->priority < 5 ? _("(High)")
2938 : _("(Low)")));
2939 so_puts(in_store, tmp_20k_buf);
2940 so_puts(in_store, "\015\012");
2943 if(vesy->summary){
2944 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2945 _("Summary: "), vesy->summary);
2946 so_puts(in_store, tmp_20k_buf);
2947 so_puts(in_store, "\015\012");
2950 if(vesy->sender){
2951 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2952 _("Sender: "), vesy->sender);
2953 so_puts(in_store, tmp_20k_buf);
2954 so_puts(in_store, "\015\012");
2957 if(vesy->organizer){
2958 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2959 _("Organizer: "), vesy->organizer);
2960 so_puts(in_store, tmp_20k_buf);
2961 so_puts(in_store, "\015\012");
2964 if(vesy->location){
2965 ical_remove_escapes(&vesy->location);
2966 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2967 _("Location: "), vesy->location);
2968 so_puts(in_store, tmp_20k_buf);
2969 so_puts(in_store, "\015\012");
2972 if(vesy->evstart){
2973 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2974 _("Start Date: "), vesy->evstart);
2975 so_puts(in_store, tmp_20k_buf);
2976 so_puts(in_store, "\015\012");
2979 if(vesy->duration){
2980 for(i = 0; vesy->duration[i] != NULL; i++){
2981 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2982 _("Duration: "), vesy->duration[i]);
2983 so_puts(in_store, tmp_20k_buf);
2984 so_puts(in_store, "\015\012");
2986 } else if(vesy->evend){
2987 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
2988 _("End Date: "), vesy->evend);
2989 so_puts(in_store, tmp_20k_buf);
2990 so_puts(in_store, "\015\012");
2993 if(vesy->description){
2994 char c;
2995 int j, empty;
2997 so_puts(in_store, "\015\012");
2999 for(i = 0; vesy->description[i] != NULL; i++){
3000 so_puts(in_store, _("Description: "));
3001 /* Check if empty description */
3002 empty = 1;
3003 for(j =0; empty == 1 && vesy->description[i][j] != '\0'; j++){
3004 c = vesy->description[i][j];
3005 if(c != '\n' && c != ' ' && c != '\t')
3006 empty = 0;
3008 if(empty){
3009 so_puts(in_store, _("[ No description provided ]"));
3010 so_puts(in_store, "\015\012");
3012 else {
3013 for(j =0; vesy->description[i][j] != '\0'; j++){
3014 c = vesy->description[i][j];
3015 if(c == '\n'){
3016 so_puts(in_store, "\015\012");
3017 continue;
3019 so_writec(c, in_store);
3022 so_puts(in_store, "\015\012");
3026 if(vesy->attendee){
3027 so_puts(in_store, "\015\012");
3028 so_puts(in_store, _("List of Attendees:"));
3029 so_puts(in_store, "\015\012");
3030 for(i = 0; vesy->attendee[i] != NULL; i++){
3031 so_puts(in_store, vesy->attendee[i]);
3032 so_puts(in_store, "\015\012");
3036 so_puts(in_store, "\015\012\015\012");
3039 if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
3040 so_seek(in_store, 0L, 0);
3042 init_handles(&handles);
3043 gf_filter_init();
3045 if(F_ON(F_VIEW_SEL_URL, ps_global)
3046 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3047 || F_ON(F_SCAN_ADDR, ps_global))
3048 gf_link_filter(gf_line_test,
3049 gf_line_test_opt(url_hilite,
3050 gf_url_hilite_opt(&uh,&handles,0)));
3052 gf_link_filter(gf_wrap,
3053 gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3054 ps_global->ttyo->screen_cols,
3055 NULL, 0, GFW_HANDLES));
3056 gf_link_filter(gf_nvtnl_local, NULL);
3058 gf_set_so_readc(&gc, in_store);
3059 gf_set_so_writec(&pc, out_store);
3061 errstr = gf_pipe(gc, pc);
3063 gf_clear_so_readc(in_store);
3065 if(!errstr){
3066 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3067 _("This event was tagged as a %s entry by the sender"), vesy->class);
3068 errstr = format_editorial(tmp_20k_buf, ps_global->ttyo->screen_cols, 0, NULL, pc);
3071 gf_clear_so_writec(out_store);
3073 if(!errstr)
3074 cmd = scroll_attachment(_("CALENDAR EVENT ATTACHMENT"), out_store,
3075 CharStar, handles, a, flags | DA_RESIZE);
3077 free_handles(&handles);
3078 so_give(&out_store);
3080 else
3081 errstr = _("Error allocating space");
3083 while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3085 if(errstr)
3086 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3087 _("Can't format entry : %s"), errstr);
3089 so_give(&in_store);
3093 /*----------------------------------------------------------------------
3094 Display attachment information
3096 Args: msgno -- message number to get partrom
3097 a -- attachment struct for the desired part
3099 Result: a screen containing information about attachment:
3100 ----*/
3101 void
3102 display_attach_info(long int msgno, ATTACH_S *a)
3104 int i, indent, cols;
3105 char buf1[100], *folded;
3106 STORE_S *store;
3107 PARMLIST_S *plist;
3108 SCROLL_S sargs;
3110 (void) dispatch_attachment(a);
3112 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
3113 q_status_message(SM_ORDER | SM_DING, 3, 3,
3114 _("Error allocating space for message."));
3115 return;
3118 cols = ps_global->ttyo->screen_cols;
3121 * 2 spaces on left
3122 * 16 for text (longest is Display Method == 14)
3123 * 2 for ": "
3125 indent = 20;
3127 /* don't try stupid folding */
3128 cols = MAX(indent+10, cols);
3130 so_puts(store, "Details about Attachment #");
3131 so_puts(store, a->number);
3132 so_puts(store, " :\n\n");
3133 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
3134 so_puts(store, buf1);
3135 so_puts(store, body_type_names(a->body->type));
3136 so_puts(store, "\n");
3137 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
3138 so_puts(store, buf1);
3139 so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
3140 so_puts(store, "\n");
3141 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
3142 so_puts(store, buf1);
3143 so_puts(store, a->body->encoding < ENCMAX
3144 ? body_encodings[a->body->encoding]
3145 : "Unknown");
3146 so_puts(store, "\n");
3147 if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
3148 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
3149 so_puts(store, buf1);
3150 i = 0;
3151 while(rfc2231_list_params(plist)){
3152 if(i++)
3153 so_puts(store, repeat_char(indent, ' '));
3155 so_puts(store, plist->attrib);
3156 so_puts(store, " = ");
3157 so_puts(store, plist->value ? plist->value : "");
3158 so_puts(store, "\n");
3161 rfc2231_free_parmlist(&plist);
3164 if(a->body->description && a->body->description[0]){
3165 char buftmp[MAILTMPLEN];
3166 unsigned char *q;
3168 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
3170 snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
3171 buftmp[sizeof(buftmp)-1] = '\0';
3172 q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
3173 folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
3175 if(folded){
3176 so_puts(store, folded);
3177 fs_give((void **) &folded);
3181 /* BUG: no a->body->language support */
3183 if(a->body->disposition.type){
3184 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
3185 so_puts(store, buf1);
3186 so_puts(store, a->body->disposition.type);
3187 so_puts(store, "\n");
3188 if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
3189 while(rfc2231_list_params(plist)){
3190 so_puts(store, repeat_char(indent, ' '));
3191 so_puts(store, plist->attrib);
3192 so_puts(store, " = ");
3193 so_puts(store, plist->value ? plist->value : "");
3194 so_puts(store, "\n");
3197 rfc2231_free_parmlist(&plist);
3201 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
3202 so_puts(store, buf1);
3203 so_puts(store, comatose((a->body->encoding == ENCBASE64)
3204 ? ((a->body->size.bytes * 3)/4)
3205 : a->body->size.bytes));
3206 so_puts(store, " bytes\n");
3207 utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
3208 so_puts(store, buf1);
3209 if(a->can_display == MCD_NONE) {
3210 so_puts(store, "Can't, ");
3211 so_puts(store, (a->body->encoding < ENCOTHER)
3212 ? "Unknown Attachment Format"
3213 : "Unknown Encoding");
3215 else if(!(a->can_display & MCD_EXTERNAL)){
3216 so_puts(store, "Alpine's Internal Pager");
3218 else{
3219 int nt, free_pretty_cmd;
3220 MCAP_CMD_S *mc_cmd;
3221 char *pretty_cmd;
3223 if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
3224 a->body, "<datafile>", &nt,
3225 a->can_display & MCD_EXT_PROMPT)) != NULL){
3226 so_puts(store, "\"");
3227 if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
3228 so_puts(store, pretty_cmd);
3229 so_puts(store, "\"");
3230 if(free_pretty_cmd && pretty_cmd)
3231 fs_give((void **)&pretty_cmd);
3232 if(mc_cmd->command)
3233 fs_give((void **)&mc_cmd->command);
3234 fs_give((void **)&mc_cmd);
3238 so_puts(store, "\n");
3240 memset(&sargs, 0, sizeof(SCROLL_S));
3241 sargs.text.text = so_text(store);
3242 sargs.text.src = CharStar;
3243 sargs.text.desc = "attachment info";
3244 sargs.bar.title = _("ABOUT ATTACHMENT");
3245 sargs.help.text = h_simple_text_view;
3246 sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
3248 sargs.use_indexline_color = 1;
3250 scrolltool(&sargs);
3252 so_give(&store); /* free resources associated with store */
3253 ps_global->mangled_screen = 1;
3257 /*----------------------------------------------------------------------
3259 ----*/
3260 void
3261 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3263 char *sig;
3264 void *msgtext;
3265 ENVELOPE *outgoing;
3266 BODY *body;
3268 if(MIME_MSG_A(a)){
3269 forward_msg_att(stream, msgno, a);
3271 else{
3272 ACTION_S *role = NULL;
3273 REDRAFT_POS_S *redraft_pos = NULL;
3274 long rflags = ROLE_FORWARD;
3275 PAT_STATE dummy;
3277 outgoing = mail_newenvelope();
3278 outgoing->message_id = generate_message_id();
3279 outgoing->subject = cpystr("Forwarded attachment...");
3281 if(nonempty_patterns(rflags, &dummy)){
3283 * There is no message, but a Current Folder Type might match.
3285 * This has been changed to check against the message
3286 * containing the attachment.
3288 role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3289 if(confirm_role(rflags, &role))
3290 role = combine_inherited_role(role);
3291 else{
3292 role = NULL;
3293 cmd_cancelled("Forward");
3294 mail_free_envelope(&outgoing);
3295 return;
3299 if(role)
3300 q_status_message1(SM_ORDER, 3, 4,
3301 _("Forwarding using role \"%s\""), role->nick);
3304 * as with all text bound for the composer, build it in
3305 * a storage object of the type it understands...
3307 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3308 int impl, template_len = 0;
3310 if(role && role->template){
3311 char *filtered;
3313 impl = 1;
3314 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3315 if(filtered){
3316 if(*filtered){
3317 so_puts((STORE_S *)msgtext, filtered);
3318 if(impl == 1)
3319 template_len = strlen(filtered);
3322 fs_give((void **)&filtered);
3325 else
3326 impl = 1;
3328 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3329 if(impl == 2)
3330 redraft_pos->offset += template_len;
3332 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3334 fs_give((void **)&sig);
3336 else
3337 so_puts((STORE_S *)msgtext, NEWLINE);
3339 /*---- New Body to start with ----*/
3340 body = mail_newbody();
3341 body->type = TYPEMULTIPART;
3343 /*---- The TEXT part/body ----*/
3344 body->nested.part = mail_newbody_part();
3345 body->nested.part->body.type = TYPETEXT;
3346 body->nested.part->body.contents.text.data = msgtext;
3348 /*---- The corresponding things we're attaching ----*/
3349 body->nested.part->next = mail_newbody_part();
3350 body->nested.part->next->body.id = generate_message_id();
3351 copy_body(&body->nested.part->next->body, a->body);
3353 if(fetch_contents(stream, msgno, a->number,
3354 &body->nested.part->next->body)){
3355 pine_send(outgoing, &body, "FORWARD MESSAGE",
3356 role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3358 ps_global->mangled_screen = 1;
3359 pine_free_body(&body);
3360 free_redraft_pos(&redraft_pos);
3362 else{
3363 mail_free_body(&body);
3364 so_give((STORE_S **) &msgtext);
3365 free_redraft_pos(&redraft_pos);
3366 q_status_message(SM_ORDER | SM_DING, 4, 5,
3367 _("Error fetching message contents. Can't forward message."));
3370 else
3371 q_status_message(SM_ORDER | SM_DING, 3, 4,
3372 _("Error allocating message text"));
3374 mail_free_envelope(&outgoing);
3375 free_action(&role);
3380 void
3381 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3383 char *p, *sig = NULL;
3384 int ret;
3385 void *msgtext;
3386 ENVELOPE *outgoing;
3387 BODY *body;
3388 ACTION_S *role = NULL;
3389 REPLY_S reply;
3390 REDRAFT_POS_S *redraft_pos = NULL;
3392 outgoing = mail_newenvelope();
3393 outgoing->message_id = generate_message_id();
3395 memset((void *)&reply, 0, sizeof(reply));
3397 if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
3399 * as with all text bound for the composer, build it in
3400 * a storage object of the type it understands...
3402 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3403 int impl, template_len = 0;
3404 long rflags = ROLE_FORWARD;
3405 PAT_STATE dummy;
3407 ret = 'n';
3408 if(ps_global->full_header == 2)
3409 ret = want_to(_("Forward message as an attachment"), 'n', 0,
3410 NO_HELP, WT_SEQ_SENSITIVE);
3411 /* Setup possible role */
3412 if(nonempty_patterns(rflags, &dummy)){
3413 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3414 if(confirm_role(rflags, &role))
3415 role = combine_inherited_role(role);
3416 else{ /* cancel reply */
3417 role = NULL;
3418 cmd_cancelled("Forward");
3419 mail_free_envelope(&outgoing);
3420 so_give((STORE_S **) &msgtext);
3421 return;
3425 if(role)
3426 q_status_message1(SM_ORDER, 3, 4,
3427 _("Forwarding using role \"%s\""), role->nick);
3429 if(role && role->template){
3430 char *filtered;
3432 impl = 1;
3433 filtered = detoken(role, a->body->nested.msg->env,
3434 0, 0, 0, &redraft_pos, &impl);
3435 if(filtered){
3436 if(*filtered){
3437 so_puts((STORE_S *)msgtext, filtered);
3438 if(impl == 1)
3439 template_len = strlen(filtered);
3442 fs_give((void **)&filtered);
3445 else
3446 impl = 1;
3448 if((sig = detoken(role, a->body->nested.msg->env,
3449 2, 0, 1, &redraft_pos, &impl)) != NULL){
3450 if(impl == 2)
3451 redraft_pos->offset += template_len;
3453 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3455 fs_give((void **)&sig);
3457 else
3458 so_puts((STORE_S *)msgtext, NEWLINE);
3460 if(ret == 'y'){
3461 /*---- New Body to start with ----*/
3462 body = mail_newbody();
3463 body->type = TYPEMULTIPART;
3465 /*---- The TEXT part/body ----*/
3466 body->nested.part = mail_newbody_part();
3467 body->nested.part->body.type = TYPETEXT;
3468 body->nested.part->body.contents.text.data = msgtext;
3470 if(!forward_mime_msg(stream, msgno,
3471 p = body_partno(stream, msgno, a->body),
3472 a->body->nested.msg->env,
3473 &body->nested.part->next, msgtext))
3474 mail_free_body(&body);
3476 else{
3477 reply.forw = 1;
3478 if(a->body->nested.msg->body){
3479 char *charset;
3481 charset
3482 = parameter_val(a->body->nested.msg->body->parameter,
3483 "charset");
3485 if(charset && strucmp(charset, "us-ascii") != 0){
3486 CONV_TABLE *ct;
3489 * There is a non-ascii charset,
3490 * is there conversion happening?
3492 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3493 || !ct->table){
3494 reply.orig_charset = charset;
3495 charset = NULL;
3499 if(charset)
3500 fs_give((void **) &charset);
3503 body = forward_body(stream, a->body->nested.msg->env,
3504 a->body->nested.msg->body, msgno,
3505 p = body_partno(stream, msgno, a->body),
3506 msgtext, FWD_NONE);
3509 fs_give((void **) &p);
3511 if(body){
3512 pine_send(outgoing, &body,
3513 "FORWARD MESSAGE",
3514 role, NULL,
3515 reply.forw ? &reply : NULL,
3516 redraft_pos, NULL, NULL, 0);
3518 ps_global->mangled_screen = 1;
3519 pine_free_body(&body);
3520 free_redraft_pos(&redraft_pos);
3521 free_action(&role);
3523 else{
3524 so_give((STORE_S **) &msgtext);
3525 q_status_message(SM_ORDER | SM_DING, 4, 5,
3526 _("Error fetching message contents. Can't forward message."));
3529 else
3530 q_status_message(SM_ORDER | SM_DING, 3, 4,
3531 _("Error allocating message text"));
3533 else
3534 q_status_message1(SM_ORDER,3,4,
3535 _("Error fetching message %s. Can't forward it."),
3536 long2string(msgno));
3538 if(reply.orig_charset)
3539 fs_give((void **)&reply.orig_charset);
3541 mail_free_envelope(&outgoing);
3545 void
3546 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3548 ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
3549 ENVELOPE *outgoing;
3550 BODY *body;
3551 void *msgtext;
3552 char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3553 int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3554 int rolemsg = 0, copytomsg = 0;
3555 long rflags;
3556 PAT_STATE dummy;
3557 REDRAFT_POS_S *redraft_pos = NULL;
3558 ACTION_S *role = NULL;
3560 outgoing = mail_newenvelope();
3562 dprint((4,"\n - attachment reply \n"));
3564 saved_from = (ADDRESS *) NULL;
3565 saved_to = (ADDRESS *) NULL;
3566 saved_cc = (ADDRESS *) NULL;
3567 saved_resent = (ADDRESS *) NULL;
3568 outgoing->subject = NULL;
3570 prefix = reply_quote_str(a->body->nested.msg->env);
3572 * For consistency, the first question is always "include text?"
3574 if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0
3575 && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3576 && reply_harvest(ps_global, msgno, a->number,
3577 a->body->nested.msg->env, &saved_from,
3578 &saved_to, &saved_cc, &saved_resent, &flags)){
3579 outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3580 NULL, 0);
3581 clear_cursor_pos();
3582 reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3583 saved_from, saved_to, saved_cc, saved_resent,
3584 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3585 if(errmsg){
3586 if(*errmsg){
3587 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3588 display_message(NO_OP_COMMAND);
3591 fs_give((void **)&errmsg);
3594 if(sp_expunge_count(stream)) /* current msg was expunged */
3595 goto seeyalater;
3597 /* Setup possible role */
3598 rflags = ROLE_REPLY;
3599 if(nonempty_patterns(rflags, &dummy)){
3600 role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3601 if(confirm_role(rflags, &role))
3602 role = combine_inherited_role(role);
3603 else{ /* cancel reply */
3604 role = NULL;
3605 cmd_cancelled("Reply");
3606 goto seeyalater;
3610 if(role){
3611 rolemsg++;
3613 /* override fcc gotten in reply_seed */
3614 if(role->fcc && fcc)
3615 fs_give((void **) &fcc);
3618 if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3619 ADDRESS *us_in_to_and_cc, *ap;
3621 us_in_to_and_cc = (ADDRESS *) NULL;
3622 if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3623 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3624 NULL, us_in_to_and_cc, NULL,
3625 a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3626 reply_append_addr(&us_in_to_and_cc, ap);
3628 if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3629 if((ap=reply_cp_addr(ps_global, 0L, NULL,
3630 NULL, us_in_to_and_cc, NULL,
3631 a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3632 reply_append_addr(&us_in_to_and_cc, ap);
3635 * A list of all of our addresses that appear in the To
3636 * and cc fields is in us_in_to_and_cc.
3637 * If there is exactly one address in that list then
3638 * use it for the outgoing From.
3640 if(us_in_to_and_cc && !us_in_to_and_cc->next){
3641 PINEFIELD *custom, *pf;
3642 ADDRESS *a = NULL;
3643 char *addr = NULL;
3646 * Check to see if this address is different from what
3647 * we would have used anyway. If it is, notify the user
3648 * with a status message. This is pretty hokey because we're
3649 * mimicking how pine_send would set the From address and
3650 * there is no coordination between the two.
3653 /* in case user has a custom From value */
3654 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3656 pf = (PINEFIELD *) fs_get(sizeof(*pf));
3657 memset((void *) pf, 0, sizeof(*pf));
3658 pf->name = cpystr("From");
3659 pf->addr = &a;
3660 if(set_default_hdrval(pf, custom) >= UseAsDef
3661 && pf->textbuf && pf->textbuf[0]){
3662 removing_trailing_white_space(pf->textbuf);
3663 (void)removing_double_quotes(pf->textbuf);
3664 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3665 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3666 if(addr)
3667 fs_give((void **) &addr);
3670 if(!*pf->addr)
3671 *pf->addr = generate_from();
3673 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3674 copytomsg++;
3675 if(!role){
3676 role = (ACTION_S *) fs_get(sizeof(*role));
3677 memset((void *) role, 0, sizeof(*role));
3678 role->is_a_role = 1;
3681 role->from = us_in_to_and_cc;
3682 us_in_to_and_cc = NULL;
3685 free_customs(custom);
3686 free_customs(pf);
3689 if(us_in_to_and_cc)
3690 mail_free_address(&us_in_to_and_cc);
3694 if(role){
3695 if(rolemsg && copytomsg)
3696 q_status_message1(SM_ORDER, 3, 4,
3697 _("Replying using role \"%s\" and To as From"), role->nick);
3698 else if(rolemsg)
3699 q_status_message1(SM_ORDER, 3, 4,
3700 _("Replying using role \"%s\""), role->nick);
3701 else if(copytomsg)
3702 q_status_message(SM_ORDER, 3, 4,
3703 _("Replying using incoming To as outgoing From"));
3706 outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3707 outgoing->references = reply_build_refs(a->body->nested.msg->env);
3708 outgoing->message_id = generate_message_id();
3710 if(!outgoing->to && !outgoing->cc
3711 && !outgoing->bcc && !outgoing->newsgroups)
3712 q_status_message(SM_ORDER | SM_DING, 3, 6,
3713 _("Warning: no valid addresses to reply to!"));
3716 * Now fix up the body...
3718 if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3719 REPLY_S reply;
3721 memset((void *)&reply, 0, sizeof(reply));
3722 reply.forw = 1;
3723 if(a->body->nested.msg->body){
3724 char *charset;
3726 charset
3727 = parameter_val(a->body->nested.msg->body->parameter,
3728 "charset");
3730 if(charset && strucmp(charset, "us-ascii") != 0){
3731 CONV_TABLE *ct;
3734 * There is a non-ascii charset,
3735 * is there conversion happening?
3737 if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3738 || !ct->table){
3739 reply.orig_charset = charset;
3740 charset = NULL;
3744 if(charset)
3745 fs_give((void **) &charset);
3748 if((body = reply_body(stream, a->body->nested.msg->env,
3749 a->body->nested.msg->body, msgno,
3750 tp = body_partno(stream, msgno, a->body),
3751 msgtext, prefix, include_text, role,
3752 1, &redraft_pos)) != NULL){
3753 /* partially formatted outgoing message */
3754 pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3755 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3757 pine_free_body(&body);
3758 ps_global->mangled_screen = 1;
3760 else
3761 q_status_message(SM_ORDER | SM_DING, 3, 4,
3762 _("Error building message body"));
3764 fs_give((void **) &tp);
3765 if(reply.orig_charset)
3766 fs_give((void **)&reply.orig_charset);
3768 else
3769 q_status_message(SM_ORDER | SM_DING, 3, 4,
3770 _("Error allocating message text"));
3773 seeyalater:
3774 mail_free_envelope(&outgoing);
3775 mail_free_address(&saved_from);
3776 mail_free_address(&saved_to);
3777 mail_free_address(&saved_cc);
3778 mail_free_address(&saved_resent);
3780 if(prefix)
3781 fs_give((void **) &prefix);
3783 if(fcc)
3784 fs_give((void **) &fcc);
3786 free_redraft_pos(&redraft_pos);
3787 free_action(&role);
3791 void
3792 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
3794 char *errstr;
3796 if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
3797 q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
3801 void
3802 pipe_attachment(long int msgno, ATTACH_S *a)
3804 char *err, *resultfilename = NULL, prompt[80], *p;
3805 int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
3806 long ku;
3807 PIPE_S *syspipe;
3808 HelpType help;
3809 char pipe_command[MAXPATH+1];
3810 unsigned flagsforhist = 1; /* raw=2 /capture=1 */
3811 static HISTORY_S *history = NULL;
3812 ESCKEY_S pipe_opt[6];
3814 if(ps_global->restricted){
3815 q_status_message(SM_ORDER | SM_DING, 0, 4,
3816 "Alpine demo can't pipe attachments");
3817 return;
3820 help = NO_HELP;
3821 pipe_command[0] = '\0';
3823 init_hist(&history, HISTSIZE);
3824 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3825 if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
3826 strncpy(pipe_command, p, sizeof(pipe_command));
3827 pipe_command[sizeof(pipe_command)-1] = '\0';
3828 if(history->hist[history->curindex]){
3829 flagsforhist = history->hist[history->curindex]->flags;
3830 raw = (flagsforhist & 0x2) ? 1 : 0;
3831 capture = (flagsforhist & 0x1) ? 1 : 0;
3835 pipe_opt[j].ch = 0;
3836 pipe_opt[j].rval = 0;
3837 pipe_opt[j].name = "";
3838 pipe_opt[j++].label = "";
3840 pipe_opt[j].ch = ctrl('W');
3841 pipe_opt[j].rval = 10;
3842 pipe_opt[j].name = "^W";
3843 pipe_opt[j++].label = NULL;
3845 pipe_opt[j].ch = ctrl('Y');
3846 pipe_opt[j].rval = 11;
3847 pipe_opt[j].name = "^Y";
3848 pipe_opt[j++].label = NULL;
3850 pipe_opt[j].ch = KEY_UP;
3851 pipe_opt[j].rval = 30;
3852 pipe_opt[j].name = "";
3853 ku = j;
3854 pipe_opt[j++].label = "";
3856 pipe_opt[j].ch = KEY_DOWN;
3857 pipe_opt[j].rval = 31;
3858 pipe_opt[j].name = "";
3859 pipe_opt[j++].label = "";
3861 pipe_opt[j].ch = -1;
3863 while(1){
3864 int flags;
3866 snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
3867 a->number, capture ? "" : "(Free Output) ");
3868 prompt[sizeof(prompt)-1] = '\0';
3869 pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
3870 pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
3873 * 2 is really 1 because there will be one real entry and
3874 * one entry of "" because of the get_prev_hist above.
3876 if(items_in_hist(history) > 2){
3877 pipe_opt[ku].name = HISTORY_UP_KEYNAME;
3878 pipe_opt[ku].label = HISTORY_KEYLABEL;
3879 pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
3880 pipe_opt[ku+1].label = HISTORY_KEYLABEL;
3882 else{
3883 pipe_opt[ku].name = "";
3884 pipe_opt[ku].label = "";
3885 pipe_opt[ku+1].name = "";
3886 pipe_opt[ku+1].label = "";
3889 flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
3890 rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
3891 sizeof(pipe_command), prompt,
3892 pipe_opt, help, &flags);
3893 if(rc == -1){
3894 q_status_message(SM_ORDER | SM_DING, 3, 4,
3895 "Internal problem encountered");
3896 break;
3898 else if(rc == 10){
3899 raw = !raw; /* flip raw text */
3901 else if(rc == 11){
3902 capture = !capture; /* flip capture output */
3904 else if(rc == 30){
3905 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3906 if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3907 strncpy(pipe_command, p, sizeof(pipe_command));
3908 pipe_command[sizeof(pipe_command)-1] = '\0';
3909 if(history->hist[history->curindex]){
3910 flagsforhist = history->hist[history->curindex]->flags;
3911 raw = (flagsforhist & 0x2) ? 1 : 0;
3912 capture = (flagsforhist & 0x1) ? 1 : 0;
3915 else
3916 Writechar(BELL, 0);
3918 else if(rc == 31){
3919 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3920 if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
3921 strncpy(pipe_command, p, sizeof(pipe_command));
3922 pipe_command[sizeof(pipe_command)-1] = '\0';
3923 if(history->hist[history->curindex]){
3924 flagsforhist = history->hist[history->curindex]->flags;
3925 raw = (flagsforhist & 0x2) ? 1 : 0;
3926 capture = (flagsforhist & 0x1) ? 1 : 0;
3929 else
3930 Writechar(BELL, 0);
3932 else if(rc == 0){
3933 if(pipe_command[0] == '\0'){
3934 cmd_cancelled("Pipe command");
3935 break;
3938 flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
3939 save_hist(history, pipe_command, flagsforhist, NULL);
3941 flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
3942 flags |= (raw ? PIPE_RAW : 0);
3943 if(!capture){
3944 #ifndef _WINDOWS
3945 ClearScreen();
3946 fflush(stdout);
3947 clear_cursor_pos();
3948 ps_global->mangled_screen = 1;
3949 #endif
3950 flags |= PIPE_RESET;
3953 if((syspipe = open_system_pipe(pipe_command,
3954 (flags&PIPE_RESET) ? NULL : &resultfilename,
3955 NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
3956 int is_text = 0;
3957 gf_io_t pc; /* wire up a generic putchar */
3959 is_text = (a && a->body && a->body->type == TYPETEXT);
3961 gf_set_writec(&pc, syspipe, 0L, PipeStar,
3962 (is_text && !raw) ? WRITE_TO_LOCALE : 0);
3964 /*------ Write the image to a temporary file ------*/
3965 if(raw){ /* pipe raw text */
3966 FETCH_READC_S fetch_part;
3968 err = NULL;
3970 if(capture)
3971 we_cancel = busy_cue(NULL, NULL, 1);
3972 else
3973 suspend_busy_cue();
3975 gf_filter_init();
3976 fetch_readc_init(&fetch_part, ps_global->mail_stream,
3977 msgno, a->number, a->body->size.bytes, 0, 0);
3978 gf_link_filter(gf_nvtnl_local, NULL);
3979 err = gf_pipe(FETCH_READC, pc);
3981 if(capture){
3982 if(we_cancel)
3983 cancel_busy_cue(0);
3985 else
3986 resume_busy_cue(0);
3988 else{
3989 /* BUG: there's got to be a better way */
3990 if(!capture)
3991 ps_global->print = (PRINT_S *) 1;
3993 suspend_busy_cue();
3994 err = detach(ps_global->mail_stream, msgno,
3995 a->number, 0L, (long *)NULL, pc, NULL, 0);
3996 ps_global->print = (PRINT_S *) NULL;
3997 resume_busy_cue(0);
4000 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
4002 if(err)
4003 q_status_message1(SM_ORDER | SM_DING, 3, 4,
4004 _("Error detaching for pipe: %s"), err);
4006 display_output_file(resultfilename,
4007 (err)
4008 ? _("PIPE ATTACHMENT (ERROR)")
4009 : _("PIPE ATTACHMENT"),
4010 NULL, DOF_EMPTY);
4012 fs_give((void **) &resultfilename);
4014 else
4015 q_status_message(SM_ORDER | SM_DING, 3, 4,
4016 _("Error opening pipe"));
4018 break;
4020 else if(rc == 1){
4021 cmd_cancelled("Pipe");
4022 break;
4024 else if(rc == 3)
4025 help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
4031 delete_attachment(long int msgno, ATTACH_S *a)
4033 int expbits, rv = 0;
4035 if(!msgno_exceptions(ps_global->mail_stream, msgno,
4036 a->number, &expbits, FALSE)
4037 || !(expbits & MSG_EX_DELETE)){
4038 expbits |= MSG_EX_DELETE;
4039 msgno_exceptions(ps_global->mail_stream, msgno,
4040 a->number, &expbits, TRUE);
4042 q_status_message1(SM_ORDER, 0, 3,
4043 _("Part %s will be omitted only if message is Saved"),
4044 a->number);
4045 rv = 1;
4047 else
4048 q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
4049 a->number);
4051 return(rv);
4056 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
4058 int rv = 0;
4060 if(msgno_exceptions(ps_global->mail_stream, msgno,
4061 a->number, expbitsp, FALSE)
4062 && ((*expbitsp) & MSG_EX_DELETE)){
4063 (*expbitsp) ^= MSG_EX_DELETE;
4064 msgno_exceptions(ps_global->mail_stream, msgno,
4065 a->number, expbitsp, TRUE);
4066 rv = 1;
4068 else
4069 q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
4070 a->number);
4072 return(rv);
4076 /*----------------------------------------------------------------------
4077 Resolve any deferred tests for attachment displayability
4079 Args: attachment structure
4081 Returns: undefer's attachment's displayability test
4082 ----*/
4084 dispatch_attachment(ATTACH_S *a)
4086 if(a->test_deferred){
4087 a->test_deferred = 0;
4088 a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
4091 return(a->can_display);
4095 #ifdef _WINDOWS
4097 scroll_att_popup(sparms, in_handle)
4098 SCROLL_S *sparms;
4099 int in_handle;
4101 MPopup scrat_popup[20];
4102 int i = -1, n;
4104 if(in_handle){
4105 scrat_popup[++i].type = tIndex;
4106 scrat_popup[i].label.style = lNormal;
4107 scrat_popup[i].label.string = "View Selectable Item";
4108 scrat_popup[i].data.val = ctrl('L');
4111 scrat_popup[++i].type = tQueue;
4112 scrat_popup[i].label.style = lNormal;
4113 scrat_popup[i].label.string = "&Save";
4114 scrat_popup[i].data.val = 'S';
4116 scrat_popup[++i].type = tQueue;
4117 scrat_popup[i].label.style = lNormal;
4118 if(msgno_exceptions(ps_global->mail_stream,
4119 mn_m2raw(ps_global->msgmap,
4120 mn_get_cur(ps_global->msgmap)),
4121 scrat_attachp->number, &n, FALSE)
4122 && (n & MSG_EX_DELETE)){
4123 scrat_popup[i].label.string = "&Undelete";
4124 scrat_popup[i].data.val = 'U';
4126 else{
4127 scrat_popup[i].label.string = "&Delete";
4128 scrat_popup[i].data.val = 'D';
4131 if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
4132 scrat_popup[++i].type = tQueue;
4133 scrat_popup[i].label.style = lNormal;
4134 scrat_popup[i].label.string = "&Reply";
4135 scrat_popup[i].data.val = 'R';
4137 scrat_popup[++i].type = tQueue;
4138 scrat_popup[i].label.style = lNormal;
4139 scrat_popup[i].label.string = "&Forward";
4140 scrat_popup[i].data.val = 'f';
4143 scrat_popup[++i].type = tSeparator;
4145 scrat_popup[++i].type = tQueue;
4146 scrat_popup[i].label.style = lNormal;
4147 scrat_popup[i].label.string = "Attachment Index";
4148 scrat_popup[i].data.val = '<';
4150 scrat_popup[++i].type = tTail;
4152 return(mswin_popup(scrat_popup) == 0 && in_handle);
4156 void
4157 display_att_window(a)
4158 ATTACH_S *a;
4160 #if !defined(DOS) && !defined(OS2)
4161 char prefix[8];
4162 #endif
4164 if(a->body->type == TYPEMULTIPART){
4165 if(a->body->subtype){
4166 /* if(!strucmp(a->body->subtype, "digest"))
4167 display_digest_att(msgno, a, flags);
4168 else */
4169 q_status_message1(SM_ORDER, 3, 5,
4170 "Can't display Multipart/%s",
4171 a->body->subtype);
4173 else
4174 q_status_message(SM_ORDER, 3, 5,
4175 "Can't display unknown Multipart Subtype");
4177 /* else if(MIME_VCARD_A(a))
4178 display_vcard_att_window(msgno, a, flags);*/
4179 else if(a->body->type == TYPETEXT)
4180 display_text_att_window(a);
4181 else if(a->body->type == TYPEMESSAGE)
4182 display_msg_att_window(a);
4186 void
4187 display_text_att_window(a)
4188 ATTACH_S *a;
4190 STORE_S *store;
4191 long msgno;
4193 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4195 if(store = format_text_att(msgno, a, NULL)){
4196 if (mswin_displaytext("ATTACHED TEXT",
4197 so_text(store),
4198 strlen((char *) so_text(store)),
4199 NULL, NULL, 0) >= 0)
4200 store->txt = (void *) NULL; /* free'd in mswin_displaytext */
4202 so_give(&store); /* free resources associated with store */
4204 else
4205 q_status_message(SM_ORDER | SM_DING, 3, 3,
4206 "Error allocating space for attachment.");
4210 void
4211 display_msg_att_window(a)
4212 ATTACH_S *a;
4214 STORE_S *store;
4215 gf_io_t pc;
4216 ATTACH_S *ap = a;
4217 long msgno;
4219 msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4221 /* BUG, should check this return code */
4222 (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
4224 /* initialize a storage object */
4225 if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
4227 gf_set_so_writec(&pc, store);
4229 if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
4230 && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
4231 strlen((char *) so_text(store)),
4232 NULL, NULL, 0) >= 0)
4233 /* free'd in mswin_displaytext */
4234 store->txt = (void *) NULL;
4236 gf_clear_so_writec(store);
4238 so_give(&store);
4240 else
4241 q_status_message(SM_ORDER | SM_DING, 3, 3,
4242 "Error allocating space for message.");
4244 #endif