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
4 
5 /*
6  * ========================================================================
7  * Copyright 2013-2021 Eduardo Chappa
8  * Copyright 2006-2008 University of Washington
9  *
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
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * ========================================================================
17  */
18 
19 /*======================================================================
20      mailpart.c
21      The meat and pototoes of attachment processing here.
22 
23   ====*/
24 
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"
72 #include "../pith/init.h"
73 
74 /*
75  * Information used to paint and maintain a line on the attachment
76  * screen.
77  */
78 typedef struct atdisp_line {
79     char	     *dstring;			/* alloc'd var value string  */
80     ATTACH_S	     *attp;			/* actual attachment pointer */
81     struct atdisp_line *next, *prev;
82 } ATDISP_S;
83 
84 
85 /*
86  * struct defining attachment screen's current state
87  */
88 typedef struct att_screen {
89     ATDISP_S   *current,
90 	       *top_line;
91     COLOR_PAIR *titlecolor;
92 } ATT_SCREEN_S;
93 
94 static ATT_SCREEN_S *att_screen;
95 
96 
97 #define	next_attline(p)	((p) ? (p)->next : NULL)
98 #define	prev_attline(p)	((p) ? (p)->prev : NULL)
99 
100 
101 #ifdef	_WINDOWS
102 /*
103  * local global pointer to scroll attachment popup menu
104  */
105 static ATTACH_S *scrat_attachp;
106 #endif
107 
108 
109 #define	FETCH_READC   g_fr_desc->readc
110 
111 
112 /* used to keep track of detached MIME segments total length */
113 static long	save_att_length;
114 
115 /*
116  * Internal Prototypes
117  */
118 ATDISP_S   *new_attline(ATDISP_S **);
119 void	    free_attline(ATDISP_S **);
120 int	    attachment_screen_updater(struct pine *, ATDISP_S *, ATT_SCREEN_S *);
121 void	    attachment_screen_redrawer(void);
122 void        update_att_screen_titlebar(void);
123 ATDISP_S   *first_attline(ATDISP_S *);
124 int	    init_att_progress(char *, MAILSTREAM *, BODY *);
125 long	    save_att_piped(int);
126 int	    save_att_percent(void);
127 void	    save_attachment(int, long, ATTACH_S *);
128 void	    export_attachment(int, long, ATTACH_S *);
129 char	   *write_attached_msg(long, ATTACH_S **, STORE_S *, int);
130 void	    save_msg_att(long, ATTACH_S *);
131 void	    save_digest_att(long, ATTACH_S *);
132 int         save_msg_att_work(long int, ATTACH_S *, MAILSTREAM *, char *,
133 			      CONTEXT_S *, char *);
134 void	    export_msg_att(long, ATTACH_S *);
135 void	    export_digest_att(long, ATTACH_S *);
136 void	    print_attachment(int, long, ATTACH_S *);
137 int	    print_msg_att(long, ATTACH_S *, int);
138 void	    print_digest_att(long, ATTACH_S *);
139 void	    run_viewer(char *, BODY *, int);
140 STORE_S	   *format_text_att(long, ATTACH_S *, HANDLE_S **);
141 int	    display_text_att(long, ATTACH_S *, int);
142 int	    display_msg_att(long, ATTACH_S *, int);
143 void	    display_digest_att(long, ATTACH_S *, int);
144 int	    scroll_attachment(char *, STORE_S *, SourceType, HANDLE_S *, ATTACH_S *, int);
145 int	    process_attachment_cmd(int, MSGNO_S *, SCROLL_S *);
146 int	    format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
147 void	    display_vcard_att(long, ATTACH_S *, int);
148 void	    display_vcalendar_att(long, ATTACH_S *, int);
149 void	    display_attach_info(long, ATTACH_S *);
150 int	    display_html_external_attachment(long int, ATTACH_S *, int);
151 void	    forward_attachment(MAILSTREAM *, long, ATTACH_S *);
152 void	    forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
153 void	    reply_msg_att(MAILSTREAM *, long, ATTACH_S *);
154 void	    bounce_msg_att(MAILSTREAM *, long, char *, char *);
155 void	    pipe_attachment(long, ATTACH_S *);
156 int	    delete_attachment(long, ATTACH_S *);
157 int	    undelete_attachment(long, ATTACH_S *, int *);
158 #ifdef	_WINDOWS
159 int	    scroll_att_popup(SCROLL_S *, int);
160 void	    display_text_att_window(ATTACH_S *);
161 void	    display_msg_att_window(ATTACH_S *);
162 #endif
163 
164 
165 /*----------------------------------------------------------------------
166    Provide attachments in browser for selected action
167 
168   Args: ps -- pointer to pine structure
169 	msgmap -- struct containing current message to display
170 
171   Result:
172 
173   Handle painting and navigation of attachment index.  It would be nice
174   to someday handle message/rfc822 segments in a neat way (i.e., enable
175   forwarding, take address, etc.).
176  ----*/
177 void
attachment_screen(struct pine * ps)178 attachment_screen(struct pine *ps)
179 {
180     UCS           ch = 'x';
181     int		  n, cmd, dline,
182 		  maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
183 		  last_type = TYPEOTHER;
184     long	  msgno;
185     char	 *q, *last_subtype = NULL, backtag[64], *utf8str;
186     OtherMenu     what;
187     ATTACH_S	 *a;
188     ATDISP_S	 *current = NULL, *ctmp = NULL;
189     ATT_SCREEN_S  screen;
190 
191     ps->prev_screen = attachment_screen;
192     ps->next_screen = SCREEN_FUN_NULL;
193 
194     ps->some_quoting_was_suppressed = 0;
195 
196     if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
197 	q_status_message(SM_ORDER | SM_DING, 0, 3,
198 			 _("Screen too small to view attachment"));
199 	ps->next_screen = mail_view_screen;
200 	return;
201     }
202 
203     if(mn_total_cur(ps->msgmap) > 1L){
204 	q_status_message(SM_ORDER | SM_DING, 0, 3,
205 			 _("Can only view one message's attachments at a time."));
206 	return;
207     }
208     else if(ps->atmts && ps->atmts->description && !(ps->atmts + 1)->description)
209       q_status_message1(SM_ASYNC, 0, 3,
210     _("Message %s has only one part (the message body), and no attachments."),
211 	long2string(mn_get_cur(ps->msgmap)));
212 
213     /*----- Figure max display widths -----*/
214     for(a = ps->atmts; a && a->description != NULL; a++){
215 	if((n = utf8_width(a->number)) > maxnumwid)
216 	  maxnumwid = n;
217 
218 	if((n = utf8_width(a->size)) > maxsizewid)
219 	  maxsizewid = n;
220     }
221 
222     /*
223      * Then, allocate and initialize attachment line list...
224      */
225     for(a = ps->atmts; a && a->description; a++)
226       new_attline(&current)->attp = a;
227 
228     memset(&screen, 0, sizeof(screen));
229     msgno	       = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
230     ps->mangled_screen = 1;			/* build display */
231     ps->redrawer       = attachment_screen_redrawer;
232     att_screen	       = &screen;
233     current	       = first_attline(current);
234     what	       = FirstMenu;
235 
236     /*
237      * Advance to next attachment if it's likely that we've already
238      * shown it to the user...
239      */
240 
241     if (current == NULL){
242       q_status_message1(SM_ORDER | SM_DING, 0, 3,
243                        _("Malformed message: %s"),
244 			ps->c_client_error ? ps->c_client_error : "?");
245       ps->next_screen = mail_view_screen;
246     }
247     else if(current->attp->shown && (ctmp = next_attline(current)))
248       current = ctmp;
249 
250     while(ps->next_screen == SCREEN_FUN_NULL){
251 	ps->user_says_cancel = 0;
252 	if(km_popped){
253 	    km_popped--;
254 	    if(km_popped == 0){
255 		clearfooter(ps);
256 		ps->mangled_body = 1;
257 	    }
258 	}
259 
260 	if(ps->mangled_screen){
261 	    /*
262 	     * build/rebuild display lines
263 	     */
264 	    if(old_cols != ps->ttyo->screen_cols){
265 		int avail, s1, s2, s4, s5, dwid, sizewid, descwid;
266 
267 		avail = ps_global->ttyo->screen_cols;
268 
269 		s1 = MAX(MIN(1, avail), 0);
270 		avail -= s1;
271 
272 		dwid = MAX(MIN(1, avail), 0);
273 		avail -= dwid;
274 
275 		s2 = MAX(MIN(1, avail), 0);
276 		avail -= s2;
277 
278 		/* Only give up a third of the display to part numbers */
279 		maxnumwid = MIN(maxnumwid, (ps_global->ttyo->screen_cols/3));
280 		maxnumwid = MAX(MIN(maxnumwid, avail), 0);
281 		avail -= maxnumwid;
282 
283 		s4 = MAX(MIN(3, avail), 0);
284 		avail -= s4;
285 
286 		sizewid = MAX(MIN(maxsizewid, avail), 0);
287 		avail -= sizewid;
288 
289 		s5 = MAX(MIN(3, avail), 0);
290 		avail -= s5;
291 
292 		descwid = MAX(0, avail);
293 
294 		old_cols = ps->ttyo->screen_cols;
295 
296 		for(ctmp = first_attline(current);
297 		    ctmp && (a = ctmp->attp) && a->description;
298 		    ctmp = next_attline(ctmp)){
299 		    size_t len;
300 		    char numbuf[50];
301 		    char description[1000];
302 
303 		    if(ctmp->dstring)
304 		      fs_give((void **)&ctmp->dstring);
305 
306 		    len = 6 * MAX(80, ps->ttyo->screen_cols) * sizeof(char);
307 		    ctmp->dstring = (char *)fs_get(len + 1);
308 
309 		    /*
310 		     * description
311 		     */
312 		    q = a->body->description;
313 		    if(!(q && q[0]) && (MIME_MSG_A(a) && a->body->nested.msg->env))
314 		      q = a->body->nested.msg->env->subject;
315 
316 		    if(q && q[0]){
317 			char buftmp[1000];
318 
319 			strncpy(buftmp, q, sizeof(buftmp));
320 			buftmp[sizeof(buftmp)-1] = '\0';
321 
322 			q = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
323 							    SIZEOF_20KBUF, buftmp);
324 		    }
325 
326 		    if(q && !q[0])
327 		      q = NULL;
328 
329 		    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 ? "\"" : "");
330 		    description[sizeof(description)-1] = '\0';
331 
332 		    utf8_snprintf(ctmp->dstring, len+1,
333 			"%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%-*.*w",
334 			s1, s1, "",
335 			dwid, dwid,
336 			msgno_part_deleted(ps->mail_stream, msgno, a->number) ? "D" : "",
337 			s2, s2, "",
338 			maxnumwid, maxnumwid,
339 			a->number
340 			    ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
341 			    : "",
342 			s4, s4, "",
343 			sizewid, sizewid,
344 			a->size ? a->size : "",
345 			s5, s5, "",
346 			descwid, descwid, description);
347 
348 		    ctmp->dstring[len] = '\0';
349 		}
350 	    }
351 
352 	    ps->mangled_header = 1;
353 	    ps->mangled_footer = 1;
354 	    ps->mangled_body   = 1;
355 	}
356 
357 	/*----------- Check for new mail -----------*/
358         if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
359           ps->mangled_header = 1;
360 
361 	/*
362 	 * If an expunge of the current message happened during the
363 	 * new mail check we want to bail out of here. See mm_expunged.
364 	 */
365 	if(ps->next_screen != SCREEN_FUN_NULL)
366 	  break;
367 
368 	if(ps->mangled_header){
369 	    update_att_screen_titlebar();
370 	    ps->mangled_header = 0;
371 	}
372 
373 	if(ps->mangled_screen){
374 	    ClearLine(1);
375 	    ps->mangled_screen = 0;
376 	}
377 
378 	dline = attachment_screen_updater(ps, current, &screen);
379 
380 	/*---- This displays new mail notification, or errors ---*/
381 	if(km_popped){
382 	    FOOTER_ROWS(ps) = 3;
383 	    mark_status_unknown();
384 	}
385 
386         display_message(ch);
387 	if(km_popped){
388 	    FOOTER_ROWS(ps) = 1;
389 	    mark_status_unknown();
390 	}
391 
392 	if(ps->mangled_footer
393 	   || current->attp->body->type != last_type
394 	   || !(last_subtype && current->attp->body->subtype)
395 	   || strucmp(last_subtype, current->attp->body->subtype)){
396 
397 	    struct key_menu *km = &att_index_keymenu;
398 	    bitmap_t	     bitmap;
399 
400 	    setbitmap(bitmap);
401 	    ps->mangled_footer = 0;
402 	    last_type	       = current->attp->body->type;
403 	    last_subtype       = current->attp->body->subtype;
404 
405 	    snprintf(backtag, sizeof(backtag), "Msg #%ld", mn_get_cur(ps->msgmap));
406 	    backtag[sizeof(backtag)-1] = '\0';
407 	    km->keys[ATT_PARENT_KEY].label = backtag;
408 
409 	    if(F_OFF(F_ENABLE_PIPE, ps))
410 	      clrbitn(ATT_PIPE_KEY, bitmap);
411 
412 	    /*
413 	     * If message or digest, leave Reply and Save and,
414 	     * conditionally, Bounce on...
415 	     */
416 	    if(MIME_MSG(last_type, last_subtype)){
417 		if(F_OFF(F_ENABLE_BOUNCE, ps))
418 		  clrbitn(ATT_BOUNCE_KEY, bitmap);
419 
420 		km->keys[ATT_EXPORT_KEY].name  = "";
421 		km->keys[ATT_EXPORT_KEY].label = "";
422 	    }
423 	    else if(MIME_DGST(last_type, last_subtype)){
424 		clrbitn(ATT_BOUNCE_KEY, bitmap);
425 		clrbitn(ATT_REPLY_KEY, bitmap);
426 
427 		km->keys[ATT_EXPORT_KEY].name  = "";
428 		km->keys[ATT_EXPORT_KEY].label = "";
429 	    }
430 	    else{
431 		clrbitn(ATT_BOUNCE_KEY, bitmap);
432 		clrbitn(ATT_REPLY_KEY, bitmap);
433 
434 		if(last_type != TYPETEXT)
435 		  clrbitn(ATT_PRINT_KEY, bitmap);
436 
437 		km->keys[ATT_EXPORT_KEY].name  = "E";
438 		km->keys[ATT_EXPORT_KEY].label = N_("Export");
439 	    }
440 
441 	    if(km_popped){
442 		FOOTER_ROWS(ps) = 3;
443 		clearfooter(ps);
444 	    }
445 
446 	    if(F_ON(F_ARROW_NAV, ps)){
447 		menu_add_binding(km, KEY_LEFT, MC_EXIT);
448 		menu_add_binding(km, KEY_RIGHT, MC_VIEW_ATCH);
449 	    }
450 	    else{
451 		menu_clear_binding(km, KEY_LEFT);
452 		menu_clear_binding(km, KEY_RIGHT);
453 	    }
454 
455 	    draw_keymenu(km, bitmap, ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps),
456 			 0, what);
457 	    what = SameMenu;
458 	    if(km_popped){
459 		FOOTER_ROWS(ps) = 1;
460 		mark_keymenu_dirty();
461 	    }
462 	}
463 
464 	if(F_ON(F_SHOW_CURSOR, ps))
465 	  MoveCursor(dline, 0);
466 	else
467 	  MoveCursor(MAX(0, ps->ttyo->screen_rows - FOOTER_ROWS(ps)), 0);
468 
469         /*------ Prepare to read the command from the keyboard ----*/
470 #ifdef	MOUSE
471 	mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
472 	register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
473 		       ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
474 		       ps_global->ttyo->screen_cols);
475 #endif
476 	ch = READ_COMMAND(&utf8str);
477 #ifdef	MOUSE
478 	clear_mfunc(mouse_in_content);
479 #endif
480 
481 	cmd = menu_command(ch, &att_index_keymenu);
482 
483 	if(km_popped)
484 	  switch(cmd){
485 	    case MC_NONE :
486 	    case MC_OTHER :
487 	    case MC_RESIZE :
488 	    case MC_REPAINT :
489 	      km_popped++;
490 	      break;
491 
492 	    default:
493 	      clearfooter(ps);
494 	      break;
495 	  }
496 
497 	switch(cmd){
498 	  case MC_HELP :
499 	    if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
500 		km_popped = 2;
501 		ps->mangled_footer = 1;
502 		break;
503 	    }
504 
505 	    helper(h_attachment_screen, _("HELP FOR ATTACHMENT INDEX"), 0);
506 	    ps->mangled_screen = 1;
507 	    break;
508 
509 	  case MC_OTHER :
510 	    what = NextMenu;
511 	    ps->mangled_footer = 1;
512 	    break;
513 
514 	  case MC_FULLHDR :
515 	    ps->full_header++;
516 	    if(ps->full_header == 1){
517 		if(!(ps->quote_suppression_threshold
518 		     && (ps->some_quoting_was_suppressed /* || in_index != View */)))
519 		  ps->full_header++;
520 	    }
521 	    else if(ps->full_header > 2)
522 	      ps->full_header = 0;
523 
524 	    switch(ps->full_header){
525 	      case 0:
526 		q_status_message(SM_ORDER, 0, 3,
527 				 _("Display of full headers is now off."));
528 		break;
529 
530 	      case 1:
531 		q_status_message1(SM_ORDER, 0, 3,
532 			      _("Quotes displayed, use %s to see full headers"),
533 			      F_ON(F_USE_FK, ps) ? "F9" : "H");
534 		break;
535 
536 	      case 2:
537 		q_status_message(SM_ORDER, 0, 3,
538 				 _("Display of full headers is now on."));
539 		break;
540 
541 	    }
542 
543 	    break;
544 
545 	  case MC_EXTERNAL:
546 	       display_html_external_attachment(msgno, current->attp,
547 			DA_EXTERNAL | DA_SAVE | (F_OFF(F_EXTERNAL_INLINE_IMAGES, ps_global) ? DA_ALLIMAGES : 0));
548 	    break;
549 
550 	  case MC_EXIT :			/* exit attachment screen */
551 	    ps->next_screen = mail_view_screen;
552 	    break;
553 
554 	  case MC_QUIT :
555 	    ps->next_screen = quit_screen;
556 	    break;
557 
558 	  case MC_MAIN :
559 	    ps->next_screen = main_menu_screen;
560 	    break;
561 
562 	  case MC_INDEX :
563 	    ps->next_screen = mail_index_screen;
564 	    break;
565 
566 	  case MC_NEXTITEM :
567 	    if((ctmp = next_attline(current)) != NULL)
568 	      current = ctmp;
569 	    else
570 	      q_status_message(SM_ORDER, 0, 1, _("Already on last attachment"));
571 
572 	    break;
573 
574 	  case MC_PREVITEM :
575 	    if((ctmp = prev_attline(current)) != NULL)
576 	      current = ctmp;
577 	    else
578 	      q_status_message(SM_ORDER, 0, 1, _("Already on first attachment"));
579 
580 	    break;
581 
582 	  case MC_PAGEDN :				/* page forward */
583 	    if(next_attline(current)){
584 		while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
585 		  if((ctmp = next_attline(current)) != NULL)
586 		    current = ctmp;
587 		  else
588 		    break;
589 	    }
590 	    else
591 	      q_status_message(SM_ORDER, 0, 1,
592 			       _("Already on last page of attachments"));
593 
594 	    break;
595 
596 	  case MC_PAGEUP :			/* page backward */
597 	    if(prev_attline(current)){
598 		while(dline-- > HEADER_ROWS(ps))
599 		  if((ctmp = prev_attline(current)) != NULL)
600 		    current = ctmp;
601 		  else
602 		    break;
603 
604 		while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
605 		  if((ctmp = prev_attline(current)) != NULL)
606 		    current = ctmp;
607 		  else
608 		    break;
609 	    }
610 	    else
611 	      q_status_message(SM_ORDER, 0, 1,
612 			       _("Already on first page of attachments"));
613 
614 	    break;
615 
616 #ifdef MOUSE
617 	  case MC_MOUSE:
618 	    {
619 		MOUSEPRESS mp;
620 
621 		mouse_get_last (NULL, &mp);
622 		mp.row -= HEADER_ROWS(ps);
623 		ctmp = screen.top_line;
624 		while (mp.row && ctmp != NULL) {
625 		    --mp.row;
626 		    ctmp = ctmp->next;
627 		}
628 
629 		if (ctmp != NULL){
630 		    current = ctmp;
631 
632 		    if (mp.doubleclick){
633 			if(mp.button == M_BUTTON_LEFT)
634 			  display_attachment(msgno, current->attp, DA_SAVE);
635 		    }
636 #ifdef	_WINDOWS
637 		    else if(mp.button == M_BUTTON_RIGHT){
638 			MPopup atmt_popup[20];
639 			int    i = -1, exoffer = 0;
640 
641 			dline = attachment_screen_updater(ps,current,&screen);
642 
643 			if(dispatch_attachment(current->attp) != MCD_NONE){
644 			    atmt_popup[++i].type   = tQueue;
645 			    atmt_popup[i].data.val   = 'V';
646 			    atmt_popup[i].label.style    = lNormal;
647 			    atmt_popup[i].label.string    = "&View";
648 
649 			    if(!(current->attp->can_display & MCD_EXTERNAL)
650 				&& (current->attp->body->type == TYPETEXT
651 				    || MIME_MSG_A(current->attp)
652 				    || MIME_DGST_A(current->attp))){
653 				exoffer++;
654 				atmt_popup[++i].type	   = tIndex;
655 				atmt_popup[i].label.style  = lNormal;
656 				atmt_popup[i].label.string =
657 							  "View in New Window";
658 			    }
659 			}
660 
661 			atmt_popup[++i].type	   = tQueue;
662 			atmt_popup[i].data.val	   = 'S';
663 			atmt_popup[i].label.style  = lNormal;
664 			atmt_popup[i].label.string = "&Save";
665 
666 			atmt_popup[++i].type	  = tQueue;
667 			atmt_popup[i].label.style = lNormal;
668 			if(current->dstring[1] == 'D'){
669 			    atmt_popup[i].data.val     = 'U';
670 			    atmt_popup[i].label.string = "&Undelete";
671 			}
672 			else{
673 			    atmt_popup[i].data.val     = 'D';
674 			    atmt_popup[i].label.string = "&Delete";
675 			}
676 
677 			if(MIME_MSG_A(current->attp)){
678 			    atmt_popup[++i].type       = tQueue;
679 			    atmt_popup[i].label.style  = lNormal;
680 			    atmt_popup[i].label.string = "&Reply...";
681 			    atmt_popup[i].data.val     = 'R';
682 
683 			    atmt_popup[++i].type       = tQueue;
684 			    atmt_popup[i].label.style  = lNormal;
685 			    atmt_popup[i].label.string = "&Forward...";
686 			    atmt_popup[i].data.val   = 'F';
687 			}
688 
689 			atmt_popup[++i].type	   = tQueue;
690 			atmt_popup[i].label.style  = lNormal;
691 			atmt_popup[i].label.string = "&About...";
692 			atmt_popup[i].data.val	   = 'A';
693 
694 			atmt_popup[++i].type = tSeparator;
695 
696 			atmt_popup[++i].type	   = tQueue;
697 			snprintf(backtag, sizeof(backtag), "View Message #%ld",
698 				mn_get_cur(ps->msgmap));
699 			backtag[sizeof(backtag)-1] = '\0';
700 			atmt_popup[i].label.string = backtag;
701 			atmt_popup[i].label.style  = lNormal;
702 			atmt_popup[i].data.val	   = '<';
703 
704 			atmt_popup[++i].type = tTail;
705 
706 			if(mswin_popup(atmt_popup) == 1 && exoffer)
707 			  display_att_window(current->attp);
708 		    }
709 		}
710 		else if(mp.button == M_BUTTON_RIGHT){
711 		    MPopup atmt_popup[2];
712 
713 		    atmt_popup[0].type = tQueue;
714 		    snprintf(backtag, sizeof(backtag), "View Message #%ld",
715 			    mn_get_cur(ps->msgmap));
716 		    backtag[sizeof(backtag)-1] = '\0';
717 		    atmt_popup[0].label.string = backtag;
718 		    atmt_popup[0].label.style  = lNormal;
719 		    atmt_popup[0].data.val     = '<';
720 
721 		    atmt_popup[1].type = tTail;
722 
723 		    mswin_popup(atmt_popup);
724 #endif
725 		}
726 	    }
727 	    break;
728 #endif
729 
730 	  case MC_WHEREIS :			/* whereis */
731 	    /*--- get string  ---*/
732 	    {int   rc, found = 0;
733 	     char *result = NULL, buf[64];
734 	     static char last[64], tmp[64];
735 	     HelpType help;
736 
737 	     ps->mangled_footer = 1;
738 	     buf[0] = '\0';
739 	     snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ",
740 		     (last[0]) ? "[" : "",
741 		     (last[0]) ? last : "",
742 		     (last[0]) ? "]" : "");
743 	     tmp[sizeof(tmp)-1] = '\0';
744 	     help = NO_HELP;
745 	     while(1){
746 		 int flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
747 
748 		 rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf),
749 					 tmp,NULL,help,&flags);
750 		 if(rc == 3)
751 		   help = help == NO_HELP ? h_attach_index_whereis : NO_HELP;
752 		 else if(rc == 0 || rc == 1 || !buf[0]){
753 		     if(rc == 0 && !buf[0] && last[0]){
754 			 strncpy(buf, last, sizeof(buf));
755 			 buf[sizeof(buf)-1] = '\0';
756 		     }
757 
758 		     break;
759 		 }
760 	     }
761 
762 	     if(rc == 0 && buf[0]){
763 		 ctmp = current;
764 		 while((ctmp = next_attline(ctmp)) != NULL)
765 		   if(srchstr(ctmp->dstring, buf)){
766 		       found++;
767 		       break;
768 		   }
769 
770 		 if(!found){
771 		     ctmp = first_attline(current);
772 
773 		     while(ctmp != current)
774 		       if(srchstr(ctmp->dstring, buf)){
775 			   found++;
776 			   break;
777 		       }
778 		       else
779 			 ctmp = next_attline(ctmp);
780 		 }
781 	     }
782 	     else
783 	       result = _("WhereIs cancelled");
784 
785 	     if(found && ctmp){
786 		 strncpy(last, buf, sizeof(last));
787 		 last[sizeof(last)-1] = '\0';
788 		 result  = "Word found";
789 		 current = ctmp;
790 	     }
791 
792 	     q_status_message(SM_ORDER, 0, 3,
793 			      result ? result : _("Word not found"));
794 	    }
795 
796 	    break;
797 
798 	  case MC_DELETE :
799 	    if(delete_attachment(msgno, current->attp)){
800 		int l = current ? strlen(current->attp->number) : 0;
801 
802 		current->dstring[1] = 'D';
803 
804 		/* Also indicate any children that will be deleted */
805 
806 		for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
807 		  if(!strncmp(ctmp->attp->number, current->attp->number, l)
808 		     && ctmp->attp->number[l] == '.'){
809 		      ctmp->dstring[1] = 'D';
810 		      ps->mangled_screen = 1;
811 		  }
812 	    }
813 
814 	    break;
815 
816 	  case MC_UNDELETE :
817 	    if(undelete_attachment(msgno, current->attp, &expbits)){
818 		int l = current ? strlen(current->attp->number) : 0;
819 
820 		current->dstring[1] = ' ';
821 
822 		/* And unflag anything implicitly undeleted */
823 		for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
824 		  if(!strncmp(ctmp->attp->number, current->attp->number, l)
825 		     && ctmp->attp->number[l] == '.'
826 		     && (!msgno_exceptions(ps->mail_stream, msgno,
827 					   ctmp->attp->number, &expbits, FALSE)
828 			 || !(expbits & MSG_EX_DELETE))){
829 		      ctmp->dstring[1] = ' ';
830 		      ps->mangled_screen = 1;
831 		  }
832 	    }
833 
834 	    break;
835 
836 	  case MC_REPLY :
837 	    reply_msg_att(ps->mail_stream, msgno, current->attp);
838 	    break;
839 
840 	  case MC_FORWARD :
841 	    forward_attachment(ps->mail_stream, msgno, current->attp);
842 	    break;
843 
844 	  case MC_BOUNCE :
845 	    bounce_msg_att(ps->mail_stream, msgno, current->attp->number,
846 			   current->attp->body->nested.msg->env->subject);
847 	    ps->mangled_footer = 1;
848 	    break;
849 
850 	  case MC_ABOUTATCH :
851 	    display_attach_info(msgno, current->attp);
852 	    break;
853 
854 	  case MC_VIEW_ATCH :			/* View command */
855 	    display_attachment(msgno, current->attp, DA_SAVE);
856 	    old_cols = -1;
857 	    /* fall thru to repaint */
858 
859 	  case MC_REPAINT :			/* redraw */
860           case MC_RESIZE :
861 	    ps->mangled_screen = 1;
862 	    break;
863 
864 	  case MC_EXPORT :
865 	    export_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
866 	    ps->mangled_footer = 1;
867 	    break;
868 
869 	  case MC_SAVETEXT :			/* Save command */
870 	    save_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
871 	    ps->mangled_footer = 1;
872 	    break;
873 
874 	  case MC_PRINTMSG :			/* Save command */
875 	    print_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
876 	    ps->mangled_footer = 1;
877 	    break;
878 
879 	  case MC_PIPE :			/* Pipe command */
880 	    if(F_ON(F_ENABLE_PIPE, ps)){
881 		pipe_attachment(msgno, current->attp);
882 		ps->mangled_footer = 1;
883 		break;
884 	    }					/* fall thru to complain */
885 
886 	  case MC_NONE:				/* simple timeout */
887 	    break;
888 
889 	  case MC_UTF8:
890 	    bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
891 	    break;
892 
893 	  case MC_CHARUP :
894 	  case MC_CHARDOWN :
895 	  case MC_CHARRIGHT :
896 	  case MC_CHARLEFT :
897 	  case MC_GOTOBOL :
898 	  case MC_GOTOEOL :
899 	  case MC_UNKNOWN :
900 	  default:
901 	    bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
902 
903 	}
904     }
905 
906     for(current = first_attline(current); current;){	/* clean up */
907 	ctmp = current->next;
908 	free_attline(&current);
909 	current = ctmp;
910     }
911 
912     if(screen.titlecolor)
913       free_color_pair(&screen.titlecolor);
914 }
915 
916 
917 /*----------------------------------------------------------------------
918   allocate and attach a fresh attachment line struct
919 
920   Args: current -- display line to attach new struct to
921 
922   Returns: newly alloc'd attachment display line
923   ----*/
924 ATDISP_S *
new_attline(ATDISP_S ** current)925 new_attline(ATDISP_S **current)
926 {
927     ATDISP_S *p;
928 
929     p = (ATDISP_S *)fs_get(sizeof(ATDISP_S));
930     memset((void *)p, 0, sizeof(ATDISP_S));
931     if(current){
932 	if(*current){
933 	    p->next	     = (*current)->next;
934 	    (*current)->next = p;
935 	    p->prev	     = *current;
936 	    if(p->next)
937 	      p->next->prev = p;
938 	}
939 
940 	*current = p;
941     }
942 
943     return(p);
944 }
945 
946 
947 /*----------------------------------------------------------------------
948   Release system resources associated with attachment display line
949 
950   Args: p -- line to free
951 
952   Result:
953   ----*/
954 void
free_attline(ATDISP_S ** p)955 free_attline(ATDISP_S **p)
956 {
957     if(p){
958 	if((*p)->dstring)
959 	  fs_give((void **)&(*p)->dstring);
960 
961 	fs_give((void **)p);
962     }
963 }
964 
965 
966 /*----------------------------------------------------------------------
967   Manage display of the attachment screen menu body.
968 
969   Args: ps -- pine struct pointer
970 	current -- currently selected display line
971 	screen -- reference points for display painting
972 
973   Result:
974  */
975 int
attachment_screen_updater(struct pine * ps,ATDISP_S * current,ATT_SCREEN_S * screen)976 attachment_screen_updater(struct pine *ps, ATDISP_S *current, ATT_SCREEN_S *screen)
977 {
978     int	      dline, return_line = HEADER_ROWS(ps);
979     ATDISP_S *top_line, *ctmp;
980 
981     /* calculate top line of display */
982     dline = 0;
983     ctmp = top_line = first_attline(current);
984     do
985       if(((dline++)%(ps->ttyo->screen_rows-HEADER_ROWS(ps)-FOOTER_ROWS(ps)))==0)
986 	top_line = ctmp;
987     while(ctmp != current && (ctmp = next_attline(ctmp)));
988 
989 #ifdef _WINDOWS
990     /* Don't know how to manage scroll bar for attachment screen yet. */
991     scroll_setrange (0L, 0L);
992 #endif
993 
994     /* mangled body or new page, force redraw */
995     if(ps->mangled_body || screen->top_line != top_line)
996       screen->current = NULL;
997 
998     /* loop thru painting what's needed */
999     for(dline = 0, ctmp = top_line;
1000 	dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
1001 	dline++, ctmp = next_attline(ctmp)){
1002 
1003 	/*
1004 	 * only fall thru painting if something needs painting...
1005 	 */
1006 	if(!(!screen->current || ctmp == screen->current || ctmp == current))
1007 	  continue;
1008 
1009 	if(F_ON(F_ENABLE_DEL_WHEN_WRITING, ps_global))
1010 	   ClearLine(dline + HEADER_ROWS(ps));
1011 	if(ctmp && ctmp->dstring){
1012 	    char *p = tmp_20k_buf;
1013 	    int   i, col, x = 0, totlen;
1014 
1015 	    if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
1016 		x = 2;
1017 		if(ctmp == current){
1018 		    return_line = dline + HEADER_ROWS(ps);
1019 		    PutLine0(dline + HEADER_ROWS(ps), 0, "->");
1020 		}
1021 		else
1022 		  PutLine0(dline + HEADER_ROWS(ps), 0, "  ");
1023 
1024 		/*
1025 		 * Only paint lines if we have to...
1026 		 */
1027 		if(screen->current)
1028 		  continue;
1029 	    }
1030 	    else if(ctmp == current){
1031 		return_line = dline + HEADER_ROWS(ps);
1032 		StartInverse();
1033 	    }
1034 
1035 	    totlen = strlen(ctmp->dstring);
1036 
1037 	    /*
1038 	     * Copy the value to a temp buffer expanding tabs.
1039 	     * Assume the caller set the widths so as not to overflow the
1040 	     * right margin.
1041 	     */
1042 	    for(i=0,col=x; ctmp->dstring[i]; i++){
1043 		if(ctmp->dstring[i] == TAB){
1044 		    if((p-tmp_20k_buf) < SIZEOF_20KBUF-8)
1045 		      do
1046 		        *p++ = ' ';
1047 		      while((++col)&0x07);
1048 		}
1049 		else{
1050 		    col += width_at_this_position((unsigned char *) &ctmp->dstring[i], totlen-i);
1051 		    if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1052 		      *p++ = ctmp->dstring[i];
1053 
1054 		}
1055 	    }
1056 
1057 	    if((p-tmp_20k_buf) < SIZEOF_20KBUF)
1058 	      *p = '\0';
1059 
1060 	    PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
1061 
1062 	    if(ctmp == current
1063 	       && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
1064 	      EndInverse();
1065 	}
1066 	else if(F_OFF(F_ENABLE_DEL_WHEN_WRITING, ps_global))
1067 	  ClearLine(dline + HEADER_ROWS(ps));
1068     }
1069 
1070     ps->mangled_body = 0;
1071     screen->top_line = top_line;
1072     screen->current  = current;
1073     return(return_line);
1074 }
1075 
1076 
1077 /*----------------------------------------------------------------------
1078   Redraw the attachment screen based on the global "att_screen" struct
1079 
1080   Args: none
1081 
1082   Result:
1083   ----*/
1084 void
attachment_screen_redrawer(void)1085 attachment_screen_redrawer(void)
1086 {
1087     bitmap_t	 bitmap;
1088 
1089     update_att_screen_titlebar();
1090     ps_global->mangled_header = 0;
1091     ClearLine(1);
1092 
1093     ps_global->mangled_body = 1;
1094     (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
1095 
1096     setbitmap(bitmap);
1097     draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
1098 		 1-FOOTER_ROWS(ps_global), 0, SameMenu);
1099 }
1100 
1101 
1102 void
update_att_screen_titlebar(void)1103 update_att_screen_titlebar(void)
1104 {
1105     long        raw_msgno;
1106     COLOR_PAIR *returned_color = NULL;
1107     COLOR_PAIR *titlecolor = NULL;
1108     int         colormatch;
1109     SEARCHSET  *ss = NULL;
1110     PAT_STATE  *pstate = NULL;
1111 
1112     if(ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
1113 	raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
1114 	ss = mail_newsearchset();
1115 	ss->first = ss->last = (unsigned long) raw_msgno;
1116 
1117 	if(ss){
1118 	    colormatch = get_index_line_color(ps_global->mail_stream,
1119 					      ss, &pstate, &returned_color);
1120 	    mail_free_searchset(&ss);
1121 
1122 	    /*
1123 	     * This is a bit tricky. If there is a colormatch but
1124 	     * returned_color
1125 	     * is NULL, that means that the user explicitly wanted the
1126 	     * Normal color used in this index line, so that is what we
1127 	     * use. If no colormatch then we will use the TITLE color
1128 	     * instead of Normal.
1129 	     */
1130 	    if(colormatch){
1131 		if(returned_color)
1132 		  titlecolor = returned_color;
1133 		else
1134 		  titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
1135 					      ps_global->VAR_NORM_BACK_COLOR);
1136 	    }
1137 
1138 	    if(titlecolor
1139 	       && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
1140 		char cbuf[MAXCOLORLEN+1];
1141 
1142 		strncpy(cbuf, titlecolor->fg, sizeof(cbuf));
1143 		cbuf[sizeof(cbuf)-1] = '\0';
1144 		strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
1145 		titlecolor->fg[MAXCOLORLEN] = '\0';
1146 		strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
1147 		titlecolor->bg[MAXCOLORLEN] = '\0';
1148 	    }
1149 	}
1150 
1151 	/* Did the color change? */
1152 	if((!titlecolor && att_screen->titlecolor)
1153 	   ||
1154 	   (titlecolor && !att_screen->titlecolor)
1155 	   ||
1156 	   (titlecolor && att_screen->titlecolor
1157 	    && (strcmp(titlecolor->fg, att_screen->titlecolor->fg)
1158 		|| strcmp(titlecolor->bg, att_screen->titlecolor->bg)))){
1159 
1160 	    if(att_screen->titlecolor)
1161 	      free_color_pair(&att_screen->titlecolor);
1162 
1163 	    att_screen->titlecolor = titlecolor;
1164 	    titlecolor = NULL;
1165 	}
1166 
1167 	if(titlecolor)
1168 	  free_color_pair(&titlecolor);
1169     }
1170 
1171     set_titlebar(_("ATTACHMENT INDEX"), ps_global->mail_stream,
1172 		 ps_global->context_current, ps_global->cur_folder,
1173 		 ps_global->msgmap, 1, MessageNumber, 0, 0,
1174 		 att_screen->titlecolor);
1175 }
1176 
1177 
1178 /*----------------------------------------------------------------------
1179   Seek back from the given display line to the beginning of the list
1180 
1181   Args: p -- display linked list
1182 
1183   Result:
1184   ----*/
1185 ATDISP_S *
first_attline(ATDISP_S * p)1186 first_attline(ATDISP_S *p)
1187 {
1188     while(p && p->prev)
1189       p = p->prev;
1190 
1191     return(p);
1192 }
1193 
1194 
1195 int
init_att_progress(char * msg,MAILSTREAM * stream,struct mail_bodystruct * body)1196 init_att_progress(char *msg, MAILSTREAM *stream, struct mail_bodystruct *body)
1197 {
1198     if((save_att_length = body->size.bytes) != 0){
1199 	/* if there are display filters, factor in extra copy */
1200 	if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
1201 	  save_att_length += body->size.bytes;
1202 
1203 	/* if remote folder and segment not cached, factor in IMAP fetch */
1204 	if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
1205 	   && !((body->type == TYPETEXT && body->contents.text.data)
1206 		|| ((body->type == TYPEMESSAGE)
1207 		    && body->nested.msg && body->nested.msg->text.text.data)
1208 		|| body->contents.text.data))
1209 	  save_att_length += body->size.bytes;
1210 
1211 	gf_filter_init();			/* reset counters */
1212 	pine_gets_bytes(1);
1213 	save_att_piped(1);
1214 	return(busy_cue(msg, save_att_percent, 0));
1215     }
1216 
1217     return(0);
1218 }
1219 
1220 
1221 long
save_att_piped(int reset)1222 save_att_piped(int reset)
1223 {
1224     static long x;
1225     long	y;
1226 
1227     if(reset){
1228 	x = y = 0L;
1229     }
1230     else if((y = gf_bytes_piped()) >= x){
1231 	x = y;
1232 	y = 0;
1233     }
1234 
1235     return(x + y);
1236 }
1237 
1238 
1239 int
save_att_percent(void)1240 save_att_percent(void)
1241 {
1242     int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
1243 							   / save_att_length);
1244     return(MIN(i, 100));
1245 }
1246 
1247 
1248 /*----------------------------------------------------------------------
1249   Save the given attachment associated with the given message no
1250 
1251   Args: ps
1252 
1253   Result:
1254   ----*/
1255 void
save_attachment(int qline,long int msgno,ATTACH_S * a)1256 save_attachment(int qline, long int msgno, ATTACH_S *a)
1257 {
1258     if(ps_global->restricted){
1259         q_status_message(SM_ORDER | SM_DING, 0, 4,
1260 			 "Alpine demo can't save attachments");
1261         return;
1262     }
1263 
1264     if(MIME_MSG_A(a))
1265       save_msg_att(msgno, a);
1266     else if(MIME_DGST_A(a))
1267       save_digest_att(msgno, a);
1268     else if(MIME_VCARD_A(a))
1269       save_vcard_att(ps_global, qline, msgno, a);
1270     else
1271       write_attachment(qline, msgno, a, "SAVE");
1272 }
1273 
1274 
1275 /*----------------------------------------------------------------------
1276   Save the given attachment associated with the given message no
1277 
1278   Args: ps
1279 
1280   Result:
1281   ----*/
1282 void
export_attachment(int qline,long int msgno,ATTACH_S * a)1283 export_attachment(int qline, long int msgno, ATTACH_S *a)
1284 {
1285     if(ps_global->restricted){
1286         q_status_message(SM_ORDER | SM_DING, 0, 4,
1287 			 "Alpine demo can't export attachments");
1288         return;
1289     }
1290 
1291     if(MIME_MSG_A(a))
1292       export_msg_att(msgno, a);
1293     else if(MIME_DGST_A(a))
1294       export_digest_att(msgno, a);
1295     else
1296       q_status_message1(SM_ORDER, 0, 3,
1297      _("Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."),
1298 	 body_type_names(a->body->type));
1299 }
1300 
1301 
1302 void
write_attachment(int qline,long int msgno,ATTACH_S * a,char * method)1303 write_attachment(int qline, long int msgno, ATTACH_S *a, char *method)
1304 {
1305     char	filename[MAXPATH+1], full_filename[MAXPATH+1],
1306 	        title_buf[64], *err;
1307     int         r, rflags = GER_NONE, we_cancel = 0, flags;
1308     static HISTORY_S *history = NULL;
1309     static ESCKEY_S att_save_opts[] = {
1310 	{ctrl('T'), 10, "^T", N_("To Files")},
1311 	{-1, 0, NULL, NULL},
1312 	{-1, 0, NULL, NULL},
1313 	{-1, 0, NULL, NULL}};
1314 
1315     /*-------  Figure out suggested file name ----*/
1316     filename[0] = full_filename[0] = '\0';
1317     (void) get_filename_parameter(filename, sizeof(filename), a->body, NULL);
1318 
1319     dprint((9, "export_attachment(name: %s)\n",
1320 	   filename ? filename : "?"));
1321 
1322     r = 0;
1323 #if	!defined(DOS) && !defined(MAC) && !defined(OS2)
1324     if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
1325 	att_save_opts[++r].ch  = ctrl('V');
1326 	att_save_opts[r].rval  = 12;
1327 	att_save_opts[r].name  = "^V";
1328 	att_save_opts[r].label = N_("Downld Msg");
1329     }
1330 #endif	/* !(DOS || MAC) */
1331 
1332     if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1333 	att_save_opts[++r].ch  =  ctrl('I');
1334 	att_save_opts[r].rval  = 11;
1335 	att_save_opts[r].name  = "TAB";
1336 	att_save_opts[r].label = N_("Complete");
1337     }
1338 
1339     att_save_opts[++r].ch = -1;
1340 
1341     snprintf(title_buf, sizeof(title_buf), "%s ATTACHMENT", method);
1342     title_buf[sizeof(title_buf)-1] = '\0';
1343 
1344     flags = (a && a->body && a->body->type == TYPETEXT ? GE_BINARY : 0)
1345 	    | GE_SEQ_SENSITIVE;
1346 
1347     r = get_export_filename(ps_global, filename, NULL, full_filename,
1348 			    sizeof(filename), "attachment", title_buf,
1349 			    att_save_opts, &rflags, qline, flags, &history);
1350 
1351     if(r < 0){
1352 	switch(r){
1353 	  case -1:
1354 	    cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
1355 	    break;
1356 
1357 	  case -2:
1358 	    q_status_message1(SM_ORDER, 0, 2,
1359 			      _("Can't save to file outside of %s"),
1360 			      ps_global->VAR_OPER_DIR);
1361 	    break;
1362 	}
1363 
1364 	return;
1365     }
1366 #if	!defined(DOS) && !defined(MAC) && !defined(OS2)
1367     else if(r == 12){			/* Download */
1368 	char     cmd[MAXPATH], *tfp = NULL;
1369 	PIPE_S  *syspipe;
1370 	gf_io_t  pc;
1371 	long     len;
1372 	STORE_S *store;
1373 	char     prompt_buf[256];
1374 
1375 	if(ps_global->restricted){
1376 	    q_status_message(SM_ORDER | SM_DING, 3, 3,
1377 			     "Download disallowed in restricted mode");
1378 	    return;
1379 	}
1380 
1381 	err = NULL;
1382 	tfp = temp_nam(NULL, "pd");
1383 	dprint((1, "Download attachment called!\n"));
1384 	if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
1385 
1386 	    snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
1387 	    prompt_buf[sizeof(prompt_buf)-1] = '\0';
1388 	    we_cancel = init_att_progress(prompt_buf,
1389 					  ps_global->mail_stream,
1390 					  a->body);
1391 
1392 	    gf_set_so_writec(&pc, store);
1393 	    if((err = detach(ps_global->mail_stream, msgno,
1394 			    a->number, 0L, &len, pc, NULL, 0)) != NULL)
1395 	      q_status_message2(SM_ORDER | SM_DING, 3, 5,
1396 			       "%s: Error writing attachment to \"%s\"",
1397 				err, tfp);
1398 
1399 	    /* cancel regardless, so it doesn't get in way of xfer */
1400 	    cancel_busy_cue(0);
1401 
1402 	    gf_clear_so_writec(store);
1403 	    if(so_give(&store))		/* close file */
1404 	      err = "Error writing tempfile for download";
1405 
1406 	    if(!err){
1407 		build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
1408 				 ps_global->VAR_DOWNLOAD_CMD, tfp);
1409 		if((syspipe = open_system_pipe(cmd, NULL, NULL,
1410 					      PIPE_USER | PIPE_RESET,
1411 					      0, pipe_callback, pipe_report_error)) != NULL)
1412 		  (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1413 		else
1414 		  q_status_message(SM_ORDER | SM_DING, 3, 3,
1415 				   err = "Error running download command");
1416 	    }
1417 	}
1418 	else
1419 	  q_status_message(SM_ORDER | SM_DING, 3, 3,
1420 			   err = "Error building temp file for download");
1421 
1422 	if(tfp){
1423 	    our_unlink(tfp);
1424 	    fs_give((void **)&tfp);
1425 	}
1426 
1427 	if(!err)
1428 	  q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
1429 			    a->number);
1430 
1431 	return;
1432     }
1433 #endif	/* !(DOS || MAC) */
1434 
1435     (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
1436 				    rflags, full_filename);
1437 }
1438 
1439 
1440 /*
1441  * Args  stream --
1442  *       msgno  -- raw message number
1443  *       a      -- attachment struct
1444  *       flags  -- comes from get_export_filename
1445  *             GER_OVER -- the file was truncated before we wrote
1446  *           GER_APPEND -- the file was not truncated prior to our writing,
1447  *                         so we were appending
1448  *                 else -- the file didn't previously exist
1449  *       file   -- the full pathname of the file
1450  *
1451  * Returns  1 for successful write, 0 for nothing to write, -1 for error
1452  */
1453 int
write_attachment_to_file(MAILSTREAM * stream,long int msgno,ATTACH_S * a,int flags,char * file)1454 write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
1455 {
1456     char       *l_string, sbuf[256], *err;
1457     int         is_text, we_cancel = 0, dt_flags = 0, so_flags;
1458     long        len, orig_size;
1459     gf_io_t     pc;
1460     STORE_S    *store;
1461 
1462     if(!(a && a->body && a->number && a->number[0] && file && file[0]
1463 	 && stream))
1464       return 0;
1465 
1466     is_text = (a && a->body && a->body->type == TYPETEXT);
1467 
1468     if(flags & GER_APPEND)
1469       orig_size = name_file_size(file);
1470 
1471     if(flags & GER_BINARY)
1472       dt_flags |= DT_BINARY;
1473 
1474     so_flags = (is_text & !(flags & GER_BINARY) ? WRITE_TO_LOCALE : 0)
1475 		| WRITE_ACCESS ;
1476 
1477     store = so_get(FileStar, file, so_flags);
1478     if(store == NULL){
1479 	q_status_message2(SM_ORDER | SM_DING, 3, 5,
1480 			  /* TRANSLATORS: Error opening destination <filename>: <error text> */
1481 			  _("Error opening destination %s: %s"),
1482 			  file, error_description(errno));
1483 	return -1;
1484     }
1485 
1486     snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
1487     sbuf[sizeof(sbuf)-1] = '\0';
1488     we_cancel = init_att_progress(sbuf, stream, a->body);
1489 
1490     gf_set_so_writec(&pc, store);
1491     err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, dt_flags);
1492     gf_clear_so_writec(store);
1493 
1494     if(we_cancel)
1495       cancel_busy_cue(0);
1496 
1497     if(so_give(&store))			/* close file */
1498       err = error_description(errno);
1499 
1500     if(err){
1501 	if(!(flags & (GER_APPEND | GER_OVER)))
1502 	  our_unlink(file);
1503 	else
1504 	  our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
1505 
1506 	q_status_message2(SM_ORDER | SM_DING, 3, 5,
1507 			  /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
1508 			  _("%s: Error writing attachment to \"%s\""),
1509 			  err, file);
1510 	return -1;
1511     }
1512     else{
1513         l_string = cpystr(byte_string(len));
1514         q_status_message8(SM_ORDER, 0, 4,
1515 	     "Part %s, %s%s %s to \"%s\"%s%s%s",
1516 			  a->number,
1517 			  is_text
1518 			    ? comatose(a->body->size.lines) : l_string,
1519 			  is_text ? " lines" : "",
1520 			  flags & GER_OVER
1521 			      ? "overwritten"
1522 			      : flags & GER_APPEND ? "appended" : "written",
1523 			  file,
1524 			  (is_text || len == a->body->size.bytes)
1525 			    ? "" : "(decoded from ",
1526                           (is_text || len == a->body->size.bytes)
1527 			    ? "" : byte_string(a->body->size.bytes),
1528 			  is_text || len == a->body->size.bytes
1529 			    ? "" : ")");
1530         fs_give((void **)&l_string);
1531 	return 1;
1532     }
1533 }
1534 
1535 
1536 char *
write_attached_msg(long int msgno,ATTACH_S ** ap,STORE_S * store,int newfile)1537 write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
1538 {
1539     char      *err = NULL;
1540     long      start_of_append;
1541     gf_io_t   pc;
1542     MESSAGECACHE *mc;
1543 
1544     if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
1545        && (*ap)->body->nested.msg->env){
1546 	start_of_append = so_tell(store);
1547 
1548 	gf_set_so_writec(&pc, store);
1549 	if(!(ps_global->mail_stream && msgno > 0L
1550 	   && msgno <= ps_global->mail_stream->nmsgs
1551 	   && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1552 	  mc = NULL;
1553 
1554 	if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
1555 	   || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
1556 	  err = error_description(errno);
1557 
1558 	gf_clear_so_writec(store);
1559 
1560 	if(err)
1561 	  ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
1562     }
1563     else
1564       err = "Can't export message. Missing Envelope data";
1565 
1566     return(err);
1567 }
1568 
1569 
1570 /*----------------------------------------------------------------------
1571   Save the attachment message/rfc822 to specified folder
1572 
1573   Args:
1574 
1575   Result:
1576   ----*/
1577 void
save_msg_att(long int msgno,ATTACH_S * a)1578 save_msg_att(long int msgno, ATTACH_S *a)
1579 {
1580     char	  newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
1581     char          date[64], nmsgs[80];
1582     CONTEXT_S    *cntxt = NULL;
1583     int		  our_stream = 0, rv;
1584     MAILSTREAM   *save_stream;
1585     MESSAGECACHE *mc;
1586 
1587     snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
1588     nmsgs[sizeof(nmsgs)-1] = '\0';
1589     if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
1590 		   a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
1591 	if(strucmp(newfolder, ps_global->inbox_name) == 0){
1592 	    save_folder = ps_global->VAR_INBOX_PATH;
1593 	    cntxt = NULL;
1594 	}
1595 	else
1596 	  save_folder = newfolder;
1597 
1598 	save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1599 
1600 	mc = (msgno > 0L && ps_global->mail_stream
1601 	      && msgno <= ps_global->mail_stream->nmsgs)
1602 	      ? mail_elt(ps_global->mail_stream, msgno) : NULL;
1603 	flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
1604 	if(mc && mc->day)
1605 	  mail_date(date, mc);
1606 	else
1607 	  *date = '\0';
1608 
1609 	if(pith_opt_save_size_changed_prompt)
1610 	  (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1611 
1612 	rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
1613 
1614 	if(pith_opt_save_size_changed_prompt)
1615 	  (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1616 
1617 	if(flags)
1618 	  fs_give((void **) &flags);
1619 
1620 	if(rv == 1)
1621 	  q_status_message2(SM_ORDER, 0, 4,
1622 		       _("Attached message (part %s) saved to \"%s\""),
1623 			    a->number,
1624 			    save_folder);
1625 	else if(rv == -1)
1626 	  cmd_cancelled("Attached message Save");
1627 	/* else whatever broke in save_fetch_append shoulda bitched */
1628 
1629 	if(our_stream)
1630 	  mail_close(save_stream);
1631     }
1632 }
1633 
1634 
1635 /*----------------------------------------------------------------------
1636   Save the message/rfc822 in the given digest to the specified folder
1637 
1638   Args:
1639 
1640   Result:
1641   ----*/
1642 void
save_digest_att(long int msgno,ATTACH_S * a)1643 save_digest_att(long int msgno, ATTACH_S *a)
1644 {
1645     char	 newfolder[MAILTMPLEN], *save_folder,
1646 		 date[64], nmsgs[80];
1647     CONTEXT_S   *cntxt = NULL;
1648     int		 our_stream = 0, rv, cnt = 0;
1649     MAILSTREAM  *save_stream;
1650     ATTACH_S    *ap;
1651 
1652     for(ap = a + 1;
1653 	ap->description
1654 	  && !strncmp(a->number, ap->number, strlen(a->number));
1655 	ap++)
1656       if(MIME_MSG(ap->body->type, ap->body->subtype))
1657 	cnt++;
1658 
1659     snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
1660     nmsgs[sizeof(nmsgs)-1] = '\0';
1661 
1662     if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
1663 		   nmsgs, NULL, 0, NULL, NULL, NULL)){
1664 	save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
1665 			? ps_global->VAR_INBOX_PATH : newfolder;
1666 
1667 	save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
1668 
1669 	if(pith_opt_save_size_changed_prompt)
1670 	  (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
1671 
1672 	for(ap = a + 1;
1673 	    ap->description
1674 	      && !strncmp(a->number, ap->number, strlen(a->number));
1675 	    ap++)
1676 	  if(MIME_MSG(ap->body->type, ap->body->subtype)){
1677 	      *date = '\0';
1678 	      rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
1679 	      if(rv != 1)
1680 		break;
1681 	  }
1682 
1683 	if(pith_opt_save_size_changed_prompt)
1684 	  (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
1685 
1686 	if(rv == 1)
1687 	  q_status_message2(SM_ORDER, 0, 4,
1688 			    _("Attached digest (part %s) saved to \"%s\""),
1689 			    a->number,
1690 			    save_folder);
1691 	else if(rv == -1)
1692 	  cmd_cancelled("Attached digest Save");
1693 	/* else whatever broke in save_fetch_append shoulda bitched */
1694 
1695 	if(our_stream)
1696 	  mail_close(save_stream);
1697     }
1698 }
1699 
1700 
1701 int
save_msg_att_work(long int msgno,ATTACH_S * a,MAILSTREAM * save_stream,char * save_folder,CONTEXT_S * cntxt,char * date)1702 save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
1703 		  char *save_folder, CONTEXT_S *cntxt, char *date)
1704 {
1705     STORE_S      *so;
1706     int           rv = 0;
1707 
1708     if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
1709 	if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
1710 	    *date = '\0';
1711 	    rv = save_fetch_append(ps_global->mail_stream, msgno,
1712 				   a->number,
1713 				   save_stream, save_folder, cntxt,
1714 				   a->body->size.bytes,
1715 				   NULL, date, so);
1716 	}
1717 	else{
1718 	    dprint((1, "Can't allocate store for save: %s\n",
1719 		     error_description(errno)));
1720 	    q_status_message(SM_ORDER | SM_DING, 3, 4,
1721 			   _("Problem creating space for message text."));
1722 	    rv = 0;
1723 	}
1724     }
1725 
1726     return(rv);
1727 }
1728 
1729 
1730 /*----------------------------------------------------------------------
1731   Export the attachment message/rfc822 to specified file
1732 
1733   Args:
1734 
1735   Result:
1736   ----*/
1737 void
export_msg_att(long int msgno,ATTACH_S * a)1738 export_msg_att(long int msgno, ATTACH_S *a)
1739 {
1740     char      filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
1741     int	      rv, rflags = GER_NONE, i = 1;
1742     ATTACH_S *ap = a;
1743     STORE_S  *store;
1744     static HISTORY_S *history = NULL;
1745     static ESCKEY_S opts[] = {
1746 	{ctrl('T'), 10, "^T", N_("To Files")},
1747 	{-1, 0, NULL, NULL},
1748 	{-1, 0, NULL, NULL}};
1749 
1750     if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1751 	opts[i].ch    =  ctrl('I');
1752 	opts[i].rval  = 11;
1753 	opts[i].name  = "TAB";
1754 	opts[i].label = N_("Complete");
1755     }
1756 
1757     filename[0] = full_filename[0] = '\0';
1758 
1759     rv = get_export_filename(ps_global, filename, NULL, full_filename,
1760 			     sizeof(filename), "msg attachment",
1761 			     /* TRANSLATORS: Message Attachment (a screen title) */
1762 			     _("MSG ATTACHMENT"), opts,
1763 			     &rflags, -FOOTER_ROWS(ps_global),
1764 			     GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1765 
1766     if(rv < 0){
1767 	switch(rv){
1768 	  case -1:
1769 	    cmd_cancelled("Export");
1770 	    break;
1771 
1772 	  case -2:
1773 	    q_status_message1(SM_ORDER, 0, 2,
1774 			      _("Can't export to file outside of %s"),
1775 			      ps_global->VAR_OPER_DIR);
1776 	    break;
1777 	}
1778 
1779 	return;
1780     }
1781 
1782     /* With name in hand, allocate storage object and save away... */
1783     if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1784 	if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
1785 	  q_status_message(SM_ORDER | SM_DING, 3, 4, err);
1786 	else
1787           q_status_message3(SM_ORDER, 0, 4,
1788 			  _("Attached message (part %s) %s to \"%s\""),
1789 			    a->number,
1790 			    rflags & GER_OVER
1791 			      ? _("overwritten")
1792 			      : rflags & GER_APPEND ? _("appended") : _("written"),
1793 			    full_filename);
1794 
1795 	if(so_give(&store))
1796 	  q_status_message2(SM_ORDER | SM_DING, 3, 4,
1797 			   _("Error writing %s: %s"),
1798 			   full_filename, error_description(errno));
1799     }
1800     else
1801       q_status_message2(SM_ORDER | SM_DING, 3, 4,
1802 		    /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
1803 		    _("Error opening file \"%s\" to export message: %s"),
1804 			full_filename, error_description(errno));
1805 }
1806 
1807 
1808 /*----------------------------------------------------------------------
1809   Export the message/rfc822 in the given digest to the specified file
1810 
1811   Args:
1812 
1813   Result:
1814   ----*/
1815 void
export_digest_att(long int msgno,ATTACH_S * a)1816 export_digest_att(long int msgno, ATTACH_S *a)
1817 {
1818     char      filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
1819     int	      rv, rflags = GER_NONE, i = 1;
1820     long      count = 0L;
1821     ATTACH_S *ap;
1822     static HISTORY_S *history = NULL;
1823     STORE_S  *store;
1824     static ESCKEY_S opts[] = {
1825 	{ctrl('T'), 10, "^T", N_("To Files")},
1826 	{-1, 0, NULL, NULL},
1827 	{-1, 0, NULL, NULL}};
1828 
1829     if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1830 	opts[i].ch    =  ctrl('I');
1831 	opts[i].rval  = 11;
1832 	opts[i].name  = "TAB";
1833 	opts[i].label = N_("Complete");
1834     }
1835 
1836     filename[0] = full_filename[0] = '\0';
1837 
1838     rv = get_export_filename(ps_global, filename, NULL, full_filename,
1839 			     sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
1840 			     opts, &rflags, -FOOTER_ROWS(ps_global),
1841 			     GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
1842 
1843     if(rv < 0){
1844 	switch(rv){
1845 	  case -1:
1846 	    cmd_cancelled("Export");
1847 	    break;
1848 
1849 	  case -2:
1850 	    q_status_message1(SM_ORDER, 0, 2,
1851 			      _("Can't export to file outside of %s"),
1852 			      ps_global->VAR_OPER_DIR);
1853 	    break;
1854 	}
1855 
1856 	return;
1857     }
1858 
1859     /* With name in hand, allocate storage object and save away... */
1860     if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
1861 	count = 0;
1862 
1863 	for(ap = a + 1;
1864 	    ap->description
1865 	      && !strncmp(a->number, ap->number, strlen(a->number))
1866 	      && !err;
1867 	    ap++){
1868 	    if(MIME_MSG(ap->body->type, ap->body->subtype)){
1869 		count++;
1870 		err = write_attached_msg(msgno, &ap, store,
1871 					 !count && !(rflags & GER_APPEND));
1872 	    }
1873 	}
1874 
1875 	if(so_give(&store))
1876 	  err = error_description(errno);
1877 
1878 	if(err){
1879 	    q_status_message1(SM_ORDER | SM_DING, 3, 3,
1880 			      _("Error exporting: %s"), err);
1881 	    q_status_message1(SM_ORDER | SM_DING, 3, 3,
1882 			_("%s messages exported before error occurred"), err);
1883 	}
1884 	else
1885           q_status_message4(SM_ORDER, 0, 4,
1886 		"%s messages in digest (part %s) %s to \"%s\"",
1887 			    long2string(count),
1888 			    a->number,
1889 			    rflags & GER_OVER
1890 			      ? "overwritten"
1891 			      : rflags & GER_APPEND ? "appended" : "written",
1892 			    full_filename);
1893     }
1894     else
1895       q_status_message2(SM_ORDER | SM_DING, 3, 4,
1896 		    _("Error opening file \"%s\" to export digest: %s"),
1897 			full_filename, error_description(errno));
1898 }
1899 
1900 
1901 /*----------------------------------------------------------------------
1902   Print the given attachment associated with the given message no
1903 
1904   Args: ps
1905 
1906   Result:
1907   ----*/
1908 void
print_attachment(int qline,long int msgno,ATTACH_S * a)1909 print_attachment(int qline, long int msgno, ATTACH_S *a)
1910 {
1911     char prompt[250];
1912 
1913     if(ps_global->restricted){
1914         q_status_message(SM_ORDER | SM_DING, 0, 4,
1915 			 "Alpine demo can't Print attachments");
1916         return;
1917     }
1918 
1919     snprintf(prompt, sizeof(prompt), "attach%s %s",
1920 	    (a->body->type == TYPETEXT) ? "ment" : "ed message",
1921 	    MIME_DGST_A(a) ? "digest" : a->number);
1922     prompt[sizeof(prompt)-1] = '\0';
1923     if(open_printer(prompt) >= 0){
1924 	if(MIME_MSG_A(a))
1925 	  (void) print_msg_att(msgno, a, 1);
1926 	else if(MIME_DGST_A(a))
1927 	  print_digest_att(msgno, a);
1928 	else
1929 	  (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
1930 
1931 	close_printer();
1932     }
1933 }
1934 
1935 
1936 /*
1937  * Print the attachment message/rfc822 to specified file
1938  *
1939  * Returns 1 on success, 0 on failure.
1940  */
1941 int
print_msg_att(long int msgno,ATTACH_S * a,int first)1942 print_msg_att(long int msgno, ATTACH_S *a, int first)
1943 {
1944     ATTACH_S *ap = a;
1945     MESSAGECACHE *mc;
1946 
1947     if(!(ps_global->mail_stream && msgno > 0L
1948        && msgno <= ps_global->mail_stream->nmsgs
1949        && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
1950       mc = NULL;
1951 
1952     if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
1953        && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
1954        && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
1955 	     ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
1956 	     : 1)
1957        && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
1958       return(1);
1959 
1960 
1961     q_status_message2(SM_ORDER | SM_DING, 3, 3,
1962 		      _("Error printing message %s, part %s"),
1963 		      long2string(msgno), a->number);
1964     return(0);
1965 }
1966 
1967 
1968 /*----------------------------------------------------------------------
1969   Print the attachment message/rfc822 to specified file
1970 
1971   Args:
1972 
1973   Result:
1974   ----*/
1975 void
print_digest_att(long int msgno,ATTACH_S * a)1976 print_digest_att(long int msgno, ATTACH_S *a)
1977 {
1978     ATTACH_S *ap;
1979     int	      next = 0;
1980 
1981     for(ap = a + 1;
1982 	ap->description
1983 	  && !strncmp(a->number, ap->number, strlen(a->number));
1984 	ap++){
1985 	if(MIME_MSG(ap->body->type, ap->body->subtype)){
1986 	    char *p = part_desc(ap->number, ap->body->nested.msg->body,
1987 				0, 80, FM_NOINDENT, print_char);
1988 	    if(p){
1989 		q_status_message1(SM_ORDER | SM_DING, 3, 3,
1990 				  _("Can't print digest: %s"), p);
1991 		break;
1992 	    }
1993 	    else if(!print_msg_att(msgno, ap, !next))
1994 	      break;
1995 
1996 	    next++;
1997 	}
1998     }
1999 }
2000 
2001 int
display_html_external_attachment(long int msgno,ATTACH_S * a,int flags)2002 display_html_external_attachment(long int msgno, ATTACH_S *a, int flags)
2003 {
2004     char    dir_path[MAXPATH+1];
2005     char    *filename = NULL;
2006     char    *file_path;		/* file:///some/path/ */
2007     STORE_S *store;
2008     gf_io_t  pc;
2009     char    *err;
2010     int      we_cancel = 0, saved, errs;
2011     char    *tool;
2012     ATTACH_S *att;
2013     unsigned long rawno;
2014 
2015     if(a->body == NULL){
2016 	q_status_message(SM_ORDER | SM_DING, 3, 5, _("Attachment has no body!"));
2017 	return 1;
2018     } else if (a->body->type != TYPETEXT
2019 		|| a->body->subtype == NULL
2020 		|| strucmp(a->body->subtype, "HTML")){
2021 	q_status_message(SM_ORDER | SM_DING, 3, 5, _("Not a TEXT/HTML attachment"));
2022 	return 1;
2023     }
2024 
2025     /* zero these variables, just in case. Do not try freeing them. They have short lives */
2026     for(att = ps_global->atmts; att->description != NULL; att++){
2027 	att->cid_tmpfile = NULL;
2028 	att->tmpdir = NULL;
2029     }
2030 
2031     /* setup the environment first */
2032     if(!ps_global->html_dir){
2033 	if(!html_directory_path(ps_global->VAR_HTML_DIRECTORY, dir_path, MAXPATH)){
2034 	    q_status_message1(SM_ORDER | SM_DING, 3, 5,
2035 		_("Error creating full path for %s"), ps_global->VAR_HTML_DIRECTORY);
2036 	    return 1;
2037 	} else if (init_html_directory(dir_path) < 0){
2038 		q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error initializing %s"), dir_path);
2039 		return 1;
2040 	}
2041 	ps_global->html_dir = cpystr(dir_path);
2042     }
2043     else{
2044 	strncpy(dir_path, ps_global->html_dir, sizeof(dir_path));
2045 	dir_path[sizeof(dir_path)-1] = '\0';
2046     }
2047 
2048     if(create_random_dir(dir_path, sizeof(dir_path)) < 0){
2049 	q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error creating temp dir in %s"), dir_path);
2050 	return 1;
2051     }
2052 
2053     a->tmpdir = cpystr(dir_path);
2054     add_html_log(&ps_global->html_dir_list, a->tmpdir);
2055 
2056     /* Process the text/html part */
2057     filename = temp_nam_ext(a->tmpdir, "tmp-html-", HTML_EXT);
2058 
2059     if(!filename){
2060         q_status_message1(SM_ORDER | SM_DING, 3, 5,
2061                           _("Error \"%s\", Can't create temporary file"),
2062                           error_description(errno));
2063         return(1);
2064     }
2065 
2066     if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2067         q_status_message2(SM_ORDER | SM_DING, 3, 5,
2068                           _("Error \"%s\", Can't write file %s"),
2069                           error_description(errno), filename);
2070 	if(filename){
2071 	    our_unlink(filename);
2072 	    fs_give((void **)&filename);
2073 	}
2074         return(1);
2075     }
2076 
2077     if(a->body->size.bytes){
2078 	char msg_buf[128];
2079 
2080 	snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2081 		a->description ? "\"" : "",
2082 		a->description ? a->description : "attachment number ",
2083 		a->description ? "" : a->number,
2084 		a->description ? "\"" : "");
2085 	msg_buf[sizeof(msg_buf)-1] = '\0';
2086 	we_cancel = init_att_progress(msg_buf, ps_global->mail_stream, a->body);
2087     }
2088 
2089     gf_set_so_writec(&pc, store);
2090 
2091     err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL,
2092 			DT_EXTERNAL | ((flags & DA_ALLIMAGES) ? DT_ALLIMAGES : 0));
2093 
2094     gf_clear_so_writec(store);
2095 
2096     if(we_cancel)
2097       cancel_busy_cue(0);
2098 
2099     so_give(&store);
2100 
2101     /*----- Download all needed inline attachments ------*/
2102     saved = errs = 0;
2103     rawno = mn_m2raw(ps_global->msgmap, msgno);
2104     for (att = ps_global->atmts; rawno > 0 && att->description != NULL; att++){
2105         if(att->cid_tmpfile){
2106 	    if(write_attachment_to_file(ps_global->mail_stream, rawno,
2107 					att, GER_NONE, att->cid_tmpfile) == 1)
2108                   saved++;
2109                 else
2110                   errs++;
2111 	    fs_give((void **) &att->cid_tmpfile);
2112 	}
2113 	if(att->tmpdir)
2114 	   fs_give((void **) &att->tmpdir);
2115     }
2116 
2117     if(err){
2118 	q_status_message2(SM_ORDER | SM_DING, 3, 5,
2119 		     "%s: Error saving image to temp file %s",
2120 		     err, filename);
2121 	if(filename){
2122 	    our_unlink(filename);
2123 	    fs_give((void **)&filename);
2124 	}
2125 	return(1);
2126     }
2127 
2128     tool = get_url_external_handler("http://", 1);
2129     if(tool == NULL) tool = get_url_external_handler("http://", 0);
2130     if(tool == NULL) tool = get_url_external_handler("https://", 1);
2131     if(tool == NULL) tool = get_url_external_handler("https://", 0);
2132 
2133     file_path = fs_get((strlen(filename) + strlen("file://") + 1)*sizeof(char));
2134     sprintf(file_path, "file://%s", filename);
2135 
2136     /*----- Run the viewer process ----*/
2137     if(do_url_launch(tool, file_path) == 0)
2138 	q_status_message(SM_ORDER, 3, 3, "Opened message in external browser");
2139     else
2140 	q_status_message(SM_ORDER|SM_DING, 3, 5, "Failed to open message in external browser");
2141 
2142     if(filename)
2143       fs_give((void **)&filename);
2144 
2145     if(file_path)
2146       fs_give((void **)&file_path);
2147 
2148     ps_global->mangled_screen = 1;
2149 
2150     return(0);
2151 }
2152 
2153 
2154 /*----------------------------------------------------------------------
2155   Unpack and display the given attachment associated with given message no.
2156 
2157   Args: msgno -- message no attachment is part of
2158 	a -- attachment to display
2159 
2160   Returns: 0 on success, non-zero (and error message queued) otherwise
2161   ----*/
2162 int
display_attachment(long int msgno,ATTACH_S * a,int flags)2163 display_attachment(long int msgno, ATTACH_S *a, int flags)
2164 {
2165     char    *filename = NULL;
2166     char     sender_filename[1000];
2167     char    *extp = NULL;
2168     STORE_S *store;
2169     gf_io_t  pc;
2170     char    *err;
2171     int      we_cancel = 0, rv;
2172     char     prefix[70 + 1000];	/* 1000 = sizeof(sender_filename) */
2173     char     ext[32];
2174     char     mtype[128];
2175 
2176     if(flags & DA_EXTERNAL)
2177 	return display_html_external_attachment(msgno, a, flags);
2178 
2179     /*------- Display the attachment -------*/
2180     if(dispatch_attachment(a) == MCD_NONE){
2181         /*----- Can't display this type ------*/
2182 	if(a->body->encoding < ENCOTHER)
2183 	  q_status_message4(SM_ORDER | SM_DING, 3, 5,
2184 	     /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
2185 	     _("Don't know how to display %s%s%s attachments.%s"),
2186 			    body_type_names(a->body->type),
2187 			    a->body->subtype ? "/" : "",
2188 			    a->body->subtype ? a->body->subtype :"",
2189 			    (flags & DA_SAVE) ? _(" Try Save.") : "");
2190 	else
2191 	  q_status_message1(SM_ORDER | SM_DING, 3, 5,
2192 			    _("Don't know how to unpack \"%s\" encoding"),
2193 			    body_encodings[(a->body->encoding <= ENCMAX)
2194 					     ? a->body->encoding : ENCOTHER]);
2195 
2196         return(1);
2197     }
2198     else if(!(a->can_display & MCD_EXTERNAL)){
2199 	if(a->body->type == TYPEMULTIPART){
2200 	    if(a->body->subtype){
2201 		if(!strucmp(a->body->subtype, "digest"))
2202 		  display_digest_att(msgno, a, flags);
2203 		else
2204 		  q_status_message1(SM_ORDER, 3, 5,
2205 				   _("Can't display Multipart/%s"),
2206 				   a->body->subtype);
2207 	    }
2208 	    else
2209 	      q_status_message(SM_ORDER, 3, 5,
2210 			       _("Can't display unknown Multipart Subtype"));
2211 	}
2212 	else if(MIME_VCARD_A(a))
2213 	  display_vcard_att(msgno, a, flags);
2214 	else if( MIME_VCALENDAR(a->body->type, a->body->subtype))
2215 	  display_vcalendar_att(msgno, a, flags);
2216 	else if(a->body->type == TYPETEXT){
2217 	  do{
2218 	    rv = display_text_att(msgno, a, flags);
2219 	  } while(rv == MC_FULLHDR);
2220 	}
2221 	else if(a->body->type == TYPEMESSAGE){
2222 	  do{
2223 	    rv = display_msg_att(msgno, a, flags);
2224 	  } while(rv == MC_FULLHDR);
2225 	}
2226 
2227 	ps_global->mangled_screen = 1;
2228 	return(0);
2229     }
2230 
2231     /* arrive here if MCD_EXTERNAL */
2232 
2233     if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
2234        && (!(flags & DA_DIDPROMPT)))
2235       if(want_to(_("View selected Attachment"), 'y',
2236 		 0, NO_HELP, WT_NORM) == 'n'){
2237 	  cmd_cancelled(NULL);
2238 	  return(1);
2239       }
2240 
2241     sender_filename[0] = '\0';
2242     ext[0] = '\0';
2243     prefix[0] = '\0';
2244 
2245     if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
2246        && (a->can_display & MCD_EXT_PROMPT)){
2247 	char prompt[256];
2248 
2249 	(void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2250 				      a->body, &extp);
2251 	snprintf(prompt, sizeof(prompt),
2252 		"Attachment %s%s unrecognized. %s%s%s",
2253 		a->body->subtype,
2254 		strlen(a->body->subtype) > 12 ? "..." : "",
2255 		(extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
2256 		(extp && extp[0]) ? extp : "",
2257 		(extp && extp[0]) ? ")" : "");
2258 
2259 	if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
2260 	    cmd_cancelled(NULL);
2261 	    return(1);
2262 	}
2263     }
2264 
2265     /*------ Write the image to a temporary file ------*/
2266 
2267     /* create type/subtype in mtype */
2268     strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
2269     mtype[sizeof(mtype)-1] = '\0';
2270     if(a->body->subtype){
2271 	strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
2272 	mtype[sizeof(mtype)-1] = '\0';
2273 	strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
2274 	mtype[sizeof(mtype)-1] = '\0';
2275     }
2276 
2277     /*
2278      * If we haven't already gotten the filename parameter, get it
2279      * now. It may be used in the temporary filename and possibly
2280      * for its extension.
2281      */
2282     if(!sender_filename[0])
2283       (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
2284 				    a->body, &extp);
2285 
2286     if(check_mime_type_by_extension(extp, mtype)
2287 	|| (!set_mime_extension_by_type(ext, mtype) /* extension from type */
2288 	    && extp && extp[0])){		/* extension from filename */
2289 	strncpy(ext, extp, sizeof(ext));
2290 	ext[sizeof(ext)-1] = '\0';
2291     }
2292 
2293     /* create a temp file */
2294     if(sender_filename){
2295 	char *p, *q = NULL;
2296 
2297 	/* get rid of any extension */
2298 	if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
2299 	  *(q-1) = '\0';
2300 
2301 	/* be careful about what is allowed in the filename */
2302 	for(p = sender_filename; *p; p++)
2303 	  if(!(isascii((unsigned char) *p)
2304 	       && (isalnum((unsigned char) *p)
2305 		   || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
2306 	    break;
2307 
2308 	if(!*p)			/* filename was ok to use */
2309 	  snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
2310     }
2311 
2312     /* didn't get it yet */
2313     if(!prefix[0]){
2314 	snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
2315 		 ? a->body->subtype : "unk");
2316     }
2317 
2318     /* We are creating a temporary name. This is just a prefix. If you
2319      * need the original name, use the save command, so if the prefix
2320      * is too long, shorten it.
2321      */
2322     if (strlen(prefix) > 9){
2323 	prefix[9]  = '-';
2324 	prefix[10] = '\0';
2325     }
2326 
2327     filename = temp_nam_ext(NULL, prefix, ext);
2328 
2329     if(!filename){
2330         q_status_message1(SM_ORDER | SM_DING, 3, 5,
2331                           _("Error \"%s\", Can't create temporary file"),
2332                           error_description(errno));
2333         return(1);
2334     }
2335 
2336     if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
2337         q_status_message2(SM_ORDER | SM_DING, 3, 5,
2338                           _("Error \"%s\", Can't write file %s"),
2339                           error_description(errno), filename);
2340 	if(filename){
2341 	    our_unlink(filename);
2342 	    fs_give((void **)&filename);
2343 	}
2344 
2345         return(1);
2346     }
2347 
2348 
2349     if(a->body->size.bytes){
2350 	char msg_buf[128];
2351 
2352 	snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
2353 		a->description ? "\"" : "",
2354 		a->description ? a->description : "attachment number ",
2355 		a->description ? "" : a->number,
2356 		a->description ? "\"" : "");
2357 	msg_buf[sizeof(msg_buf)-1] = '\0';
2358 	we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
2359 				      a->body);
2360     }
2361 
2362     if(a->body->type == TYPEMULTIPART){
2363 	char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
2364 				  NULL, 0L);
2365 
2366 	/* Write to store while converting newlines */
2367 	while(h && *h)
2368 	  if(*h == '\015' && *(h+1) == '\012'){
2369 	      so_puts(store, NEWLINE);
2370 	      h += 2;
2371 	  }
2372 	  else
2373 	    so_writec(*h++, store);
2374     }
2375 
2376     gf_set_so_writec(&pc, store);
2377 
2378     err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
2379 
2380     gf_clear_so_writec(store);
2381 
2382     if(we_cancel)
2383       cancel_busy_cue(0);
2384 
2385     so_give(&store);
2386 
2387     if(err){
2388 	q_status_message2(SM_ORDER | SM_DING, 3, 5,
2389 		     "%s: Error saving image to temp file %s",
2390 		     err, filename);
2391 	if(filename){
2392 	    our_unlink(filename);
2393 	    fs_give((void **)&filename);
2394 	}
2395 
2396 	return(1);
2397     }
2398 
2399     /*----- Run the viewer process ----*/
2400     run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
2401     if(filename)
2402       fs_give((void **)&filename);
2403 
2404     return(0);
2405 }
2406 
2407 
2408 /*----------------------------------------------------------------------
2409    Fish the required command from mailcap and run it
2410 
2411   Args: image_file -- The name of the file to pass viewer
2412 	body -- body struct containing type/subtype of part
2413 
2414 A side effect may be that scrolltool is called as well if
2415 exec_mailcap_cmd has any substantial output...
2416  ----*/
2417 void
run_viewer(char * image_file,struct mail_bodystruct * body,int chk_extension)2418 run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
2419 {
2420     MCAP_CMD_S *mc_cmd   = NULL;
2421     int   needs_terminal = 0, we_cancel = 0;
2422 
2423     we_cancel = busy_cue("Displaying attachment", NULL, 0);
2424 
2425     if((mc_cmd = mailcap_build_command(body->type, body->subtype,
2426 				      body, image_file,
2427 				      &needs_terminal, chk_extension)) != NULL){
2428 	if(we_cancel)
2429 	  cancel_busy_cue(-1);
2430 
2431 	exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
2432 	if(mc_cmd->command)
2433 	  fs_give((void **)&mc_cmd->command);
2434 	fs_give((void **)&mc_cmd);
2435     }
2436     else{
2437 	if(we_cancel)
2438 	  cancel_busy_cue(-1);
2439 
2440 	q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
2441 			  type_desc(body->type, body->subtype,
2442 				    body->parameter, NULL, 1));
2443     }
2444 }
2445 
2446 
2447 /*----------------------------------------------------------------------
2448   Detach and provide for browsing a text body part
2449 
2450   Args: msgno -- raw message number to get part from
2451 	 a -- attachment struct for the desired part
2452 
2453   Result:
2454  ----*/
2455 STORE_S *
format_text_att(long int msgno,ATTACH_S * a,HANDLE_S ** handlesp)2456 format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
2457 {
2458     STORE_S	*store;
2459     gf_io_t	 pc;
2460 
2461     if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2462 	if(handlesp)
2463 	  init_handles(handlesp);
2464 
2465 	gf_set_so_writec(&pc, store);
2466 	(void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
2467 	gf_clear_so_writec(store);
2468     }
2469 
2470     return(store);
2471 }
2472 
2473 
2474 /*----------------------------------------------------------------------
2475   Detach and provide for browsing a text body part
2476 
2477   Args: msgno -- raw message number to get part from
2478 	 a -- attachment struct for the desired part
2479 
2480   Result:
2481  ----*/
2482 int
display_text_att(long int msgno,ATTACH_S * a,int flags)2483 display_text_att(long int msgno, ATTACH_S *a, int flags)
2484 {
2485     STORE_S  *store;
2486     HANDLE_S *handles = NULL;
2487     int       rv = 0;
2488 
2489     if(msgno > 0L)
2490       clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2491 
2492     if((store = format_text_att(msgno, a, &handles)) != NULL){
2493 	rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
2494 	free_handles(&handles);
2495 	so_give(&store);	/* free resources associated with store */
2496     }
2497     else
2498       q_status_message(SM_ORDER | SM_DING, 3, 3,
2499 		       _("Error allocating space for attachment."));
2500 
2501     return(rv);
2502 }
2503 
2504 
2505 /*----------------------------------------------------------------------
2506   Detach and provide for browsing a body part of type "MESSAGE"
2507 
2508   Args: msgno -- message number to get partrom
2509 	 a -- attachment struct for the desired part
2510 
2511   Result:
2512  ----*/
2513 int
display_msg_att(long int msgno,ATTACH_S * a,int flags)2514 display_msg_att(long int msgno, ATTACH_S *a, int flags)
2515 {
2516     STORE_S	*store;
2517     gf_io_t	 pc;
2518     ATTACH_S	*ap = a;
2519     HANDLE_S *handles = NULL;
2520     int          rv = 0;
2521 
2522     if(msgno > 0L)
2523       clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2524 
2525     /* BUG, should check this return code */
2526     (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2527 
2528     /* initialize a storage object */
2529     if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2530 	q_status_message(SM_ORDER | SM_DING, 3, 3,
2531 			 _("Error allocating space for message."));
2532 	return(rv);
2533     }
2534 
2535     gf_set_so_writec(&pc, store);
2536 
2537     if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
2538 	if(ps_global->full_header == 2)
2539 	  q_status_message(SM_INFO, 0, 3,
2540 		      _("Full header mode ON.  All header text being included"));
2541 
2542 	rv = scroll_attachment((a->body->subtype
2543 			   && !strucmp(a->body->subtype, "delivery-status"))
2544 			     ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
2545 			  store, CharStar, handles, a, flags);
2546 	free_handles(&handles);
2547     }
2548 
2549     gf_clear_so_writec(store);
2550 
2551     so_give(&store);	/* free resources associated with store */
2552     return(rv);
2553 }
2554 
2555 
2556 /*----------------------------------------------------------------------
2557   Detach and provide for browsing a multipart body part of type "DIGEST"
2558 
2559   Args: msgno -- message number to get partrom
2560 	 a -- attachment struct for the desired part
2561 
2562   Result:
2563  ----*/
2564 void
display_digest_att(long int msgno,ATTACH_S * a,int flags)2565 display_digest_att(long int msgno, ATTACH_S *a, int flags)
2566 {
2567     STORE_S     *store;
2568     ATTACH_S	*ap;
2569     HANDLE_S	*handles = NULL;
2570     gf_io_t      pc;
2571     SourceType	 src = CharStar;
2572     int		 bad_news = 0;
2573 
2574     if(msgno > 0L)
2575       clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
2576 
2577     /* BUG, should check this return code */
2578     (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
2579 
2580     if(!(store = so_get(src, NULL, EDIT_ACCESS))){
2581 	q_status_message(SM_ORDER | SM_DING, 3, 3,
2582 			 _("Error allocating space for message."));
2583 	return;
2584     }
2585 
2586     gf_set_so_writec(&pc, store);
2587 
2588     /*
2589      * While in a subtype of this message
2590      */
2591     for(ap = a + 1;
2592 	ap->description
2593 	  && !strncmp(a->number, ap->number, strlen(a->number))
2594 	  && !bad_news;
2595 	ap++){
2596 	if(ap->body->type == TYPEMESSAGE){
2597 	    char *errstr;
2598 
2599 	    if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
2600 		char *tsub;
2601 
2602 		tsub = cpystr(ap->body->subtype);
2603 		convert_possibly_encoded_str_to_utf8((char **) &tsub);
2604 		snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
2605 		tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2606 
2607 		if((errstr = format_editorial(tmp_20k_buf,
2608 					     ps_global->ttyo->screen_cols, 0,
2609 					     NULL, pc)) != NULL){
2610 		    q_status_message1(SM_ORDER | SM_DING, 3, 3,
2611 				      _("Can't format digest: %s"), errstr);
2612 		    bad_news++;
2613 		}
2614 		else if(!gf_puts(NEWLINE, pc))
2615 		  bad_news++;
2616 
2617 		fs_give((void **) &tsub);
2618 	    }
2619 	    else{
2620 		if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
2621 				      0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
2622 		    q_status_message1(SM_ORDER | SM_DING, 3, 3,
2623 				      _("Can't format digest: %s"), errstr);
2624 		    bad_news++;
2625 		}
2626 		else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
2627 		  bad_news++;
2628 	    }
2629 	}
2630 	else if(ap->body->type == TYPETEXT
2631 		&& decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
2632 	  bad_news++;
2633 	else if(!gf_puts("Unknown type in Digest", pc))
2634 	  bad_news++;
2635     }
2636 
2637     if(!bad_news){
2638 	if(ps_global->full_header == 2)
2639 	  q_status_message(SM_INFO, 0, 3,
2640 		      _("Full header mode ON.  All header text being included"));
2641 
2642 	scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
2643     }
2644 
2645     free_handles(&handles);
2646 
2647     gf_clear_so_writec(store);
2648     so_give(&store);	/* free resources associated with store */
2649 }
2650 
2651 
2652 int
scroll_attachment(char * title,STORE_S * store,SourceType src,HANDLE_S * handles,ATTACH_S * a,int flags)2653 scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
2654 {
2655     SCROLL_S  sargs;
2656 
2657     memset(&sargs, 0, sizeof(SCROLL_S));
2658     sargs.text.text    = so_text(store);
2659     sargs.text.src     = src;
2660     sargs.text.desc    = "attachment";
2661     sargs.text.handles = handles;
2662     sargs.bar.title    = title;
2663     sargs.proc.tool    = process_attachment_cmd;
2664     sargs.proc.data.p  = (void *) a;
2665     sargs.help.text    = h_mail_text_att_view;
2666     sargs.help.title   = _("HELP FOR ATTACHED TEXT VIEW");
2667     sargs.keys.menu    = &att_view_keymenu;
2668     setbitmap(sargs.keys.bitmap);
2669 
2670     /* First, fix up "back" key */
2671     if(flags & DA_FROM_VIEW){
2672 	att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
2673     }
2674     else{
2675 	att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
2676     }
2677 
2678     if(!handles){
2679 	clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
2680 	clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
2681 	clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
2682     }
2683 
2684     if(F_OFF(F_ENABLE_PIPE, ps_global))
2685       clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
2686 
2687     if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
2688       clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
2689 
2690     /*
2691      * If message or digest, leave Reply and Save and,
2692      * conditionally, Bounce on...
2693      */
2694     if(MIME_MSG_A(a)){
2695 	if(F_OFF(F_ENABLE_BOUNCE, ps_global))
2696 	  clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2697     }
2698     else{
2699 	clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
2700 	clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
2701 	clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
2702     }
2703 #ifdef SMIME
2704     if(!(ps_global->smime && ps_global->smime->need_passphrase))
2705 	clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2706 
2707     if(F_ON(F_DONT_DO_SMIME, ps_global) || !MIME_MSG_A(a)){
2708 	clrbitn(ATV_DECRYPT_KEY, sargs.keys.bitmap);
2709 	clrbitn(ATV_SECURITY_KEY, sargs.keys.bitmap);
2710     }
2711 #endif
2712 
2713     sargs.use_indexline_color = 1;
2714 
2715     if(DA_RESIZE & flags)
2716       sargs.resize_exit = 1;
2717 
2718 #ifdef	_WINDOWS
2719     scrat_attachp    = a;
2720     sargs.mouse.popup = scroll_att_popup;
2721 #endif
2722 
2723     return(scrolltool(&sargs));
2724 }
2725 
2726 #ifdef SMIME
2727 void
display_smime_info_att(struct pine * ps,ATTACH_S * a)2728 display_smime_info_att(struct pine *ps, ATTACH_S *a)
2729 {
2730   if(smime_check(a->body->nested.msg->body) == 0){
2731       q_status_message(SM_ORDER | SM_DING, 0, 3,
2732                          _("Not a signed or encrypted message"));
2733       return;
2734   }
2735 
2736   display_smime_info(ps, a->body->nested.msg->env, a->body->nested.msg->body);
2737 }
2738 #endif /* SMIME */
2739 
2740 int
process_attachment_cmd(int cmd,MSGNO_S * msgmap,SCROLL_S * sparms)2741 process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2742 {
2743     int rv = 0, n;
2744     long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2745 #define	AD(X)	((ATTACH_S *) (X)->proc.data.p)
2746 
2747     switch(cmd){
2748       case MC_EXIT :
2749 	rv = 1;
2750 	break;
2751 
2752       case MC_QUIT :
2753 	ps_global->next_screen = quit_screen;
2754 	break;
2755 
2756       case MC_MAIN :
2757 	ps_global->next_screen = main_menu_screen;
2758 	break;
2759 
2760       case MC_REPLY :
2761 	reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
2762 	break;
2763 
2764       case MC_FORWARD :
2765 	forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
2766 	break;
2767 
2768       case MC_BOUNCE :
2769 	bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
2770 		       AD(sparms)->body->nested.msg->env->subject);
2771 	ps_global->mangled_footer = 1;
2772 	break;
2773 
2774       case MC_DELETE :
2775 	delete_attachment(rawno, sparms->proc.data.p);
2776 	break;
2777 
2778       case MC_UNDELETE :
2779 	if(undelete_attachment(rawno, sparms->proc.data.p, &n))
2780 	  q_status_message1(SM_ORDER, 0, 3,
2781 			    "Part %s UNdeleted", AD(sparms)->number);
2782 
2783 	break;
2784 
2785       case MC_SAVE :
2786 	save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2787 	ps_global->mangled_footer = 1;
2788 	break;
2789 
2790       case MC_EXPORT :
2791 	export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2792 	ps_global->mangled_footer = 1;
2793 	break;
2794 
2795       case MC_PRINTMSG :
2796 	print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
2797 	ps_global->mangled_footer = 1;
2798 	break;
2799 
2800       case MC_PIPE :
2801 	pipe_attachment(rawno, sparms->proc.data.p);
2802 	ps_global->mangled_footer = 1;
2803 	break;
2804 
2805       case MC_FULLHDR :
2806 	ps_global->full_header++;
2807 	if(ps_global->full_header == 1){
2808 	    if(!(ps_global->quote_suppression_threshold
2809 	         && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
2810 	      ps_global->full_header++;
2811 	}
2812 	else if(ps_global->full_header > 2)
2813 	  ps_global->full_header = 0;
2814 
2815 	switch(ps_global->full_header){
2816 	  case 0:
2817 	    q_status_message(SM_ORDER, 0, 3,
2818 			     _("Display of full headers is now off."));
2819 	    break;
2820 
2821 	  case 1:
2822 	    q_status_message1(SM_ORDER, 0, 3,
2823 			  _("Quotes displayed, use %s to see full headers"),
2824 			  F_ON(F_USE_FK, ps_global) ? "F9" : "H");
2825 	    break;
2826 
2827 	  case 2:
2828 	    q_status_message(SM_ORDER, 0, 3,
2829 			     _("Display of full headers is now on."));
2830 	    break;
2831 
2832 	}
2833 
2834 	rv = 1;
2835 	break;
2836 
2837 #ifdef SMIME
2838       case MC_DECRYPT:
2839         if(ps_global->smime && ps_global->smime->need_passphrase)
2840           smime_get_passphrase();
2841         break;
2842 
2843       case MC_SECURITY:
2844 	display_smime_info_att(ps_global, sparms->proc.data.p);
2845         break;
2846 #endif
2847 
2848       default:
2849 	alpine_panic("Unexpected command case");
2850 	break;
2851     }
2852 
2853     return(rv);
2854 }
2855 
2856 
2857 /*
2858  * Returns 1 on success, 0 on error.
2859  */
2860 int
format_msg_att(long int msgno,ATTACH_S ** a,HANDLE_S ** handlesp,gf_io_t pc,int flags)2861 format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
2862 {
2863     int rv = 1;
2864 
2865     if((*a)->body->type != TYPEMESSAGE)
2866       return(gf_puts("[ Undisplayed Attachment of Type ", pc)
2867 	     && gf_puts(body_type_names((*a)->body->type), pc)
2868 	     && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
2869 
2870     if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
2871 	HEADER_S h;
2872 
2873 	HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
2874 		FE_DEFAULT);
2875 	switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
2876 			     (*a)->body->nested.msg->env, &h, NULL, NULL,
2877 			     flags, NULL, pc)){
2878 	  case -1 :			/* write error */
2879 	    return(0);
2880 
2881 	  case 1 :			/* fetch error */
2882 	    if(!(gf_puts("[ Error fetching header ]",  pc)
2883 		 && !gf_puts(NEWLINE, pc)))
2884 	      return(0);
2885 
2886 	    break;
2887 	}
2888 
2889 	gf_puts(NEWLINE, pc);
2890 
2891 	if(((*a)+1)->description)
2892 	   ++(*a);
2893 	else{
2894 	   if(!(gf_puts("[Can't display missing text segment]", pc)
2895 		&& gf_puts(NEWLINE, pc)))
2896 	      rv = 0;
2897 	   return rv;
2898 	}
2899 
2900 #ifdef SMIME
2901 	if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
2902 	    if((*a)->description){
2903 		if(!(!format_editorial((*a)->description,
2904 				       ps_global->ttyo->screen_cols,
2905 				       flags, NULL, pc)
2906 		     && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
2907 		  rv = 0;
2908 	    }
2909 
2910 	    if(((*a)+1)->description)
2911 	      ++(*a);
2912 	    else{
2913 	      if(!(gf_puts("[Can't display missing text segment]", pc)
2914 		 && gf_puts(NEWLINE, pc)))
2915 		 rv = 0;
2916 	      return rv;
2917 	    }
2918 	}
2919 #endif /* SMIME */
2920 
2921 	if(((*a))->description
2922 	   && (*a)->body && (*a)->body->type == TYPETEXT){
2923 	    if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2924 	      rv = 0;
2925 	}
2926 	else if(!(gf_puts("[Can't display ", pc)
2927 		  && gf_puts(((*a)->description && (*a)->body)
2928 			       ? "first non-" : "missing ", pc)
2929 		  && gf_puts("text segment]", pc)
2930 		  && gf_puts(NEWLINE, pc)))
2931 	  rv = 0;
2932 
2933 	if(((*a)+1)->description)
2934 	   ++(*a);
2935 	else{
2936 	   if(!(gf_puts("[Can't display missing text segment]", pc)
2937 		&& gf_puts(NEWLINE, pc)))
2938 	      rv = 0;
2939 	   return rv;
2940 	}
2941 
2942     }
2943     else if((*a)->body->subtype
2944 	    && strucmp((*a)->body->subtype, "external-body") == 0) {
2945 	if(format_editorial("This part is not included and can be fetched as follows:",
2946 			    ps_global->ttyo->screen_cols, flags, NULL, pc)
2947 	   || !gf_puts(NEWLINE, pc)
2948 	   || format_editorial(display_parameters((*a)->body->parameter),
2949 			       ps_global->ttyo->screen_cols, flags, handlesp, pc))
2950 	  rv = 0;
2951     }
2952     else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
2953       rv = 0;
2954 
2955     return(rv);
2956 }
2957 
2958 
2959 void
display_vcard_att(long int msgno,ATTACH_S * a,int flags)2960 display_vcard_att(long int msgno, ATTACH_S *a, int flags)
2961 {
2962     STORE_S   *in_store, *out_store = NULL;
2963     HANDLE_S  *handles = NULL;
2964     URL_HILITE_S uh;
2965     gf_io_t    gc, pc;
2966     char     **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
2967     int	       cmd = MC_RESIZE, indent, begins = 0;
2968 
2969     lines = detach_vcard_att(ps_global->mail_stream,
2970 			     msgno, a->body, a->number);
2971     if(!lines){
2972 	q_status_message(SM_ORDER | SM_DING, 3, 3,
2973 			 _("Error accessing attachment."));
2974 	return;
2975     }
2976 
2977     if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
2978 	free_list_array(&lines);
2979 	q_status_message(SM_ORDER | SM_DING, 3, 3,
2980 			 _("Error allocating space for attachment."));
2981 	return;
2982     }
2983 
2984     for(ll = lines, indent = 0; ll && *ll; ll++)
2985       if((p = strindex(*ll, ':')) && p - *ll > indent)
2986 	indent = p - *ll;
2987 
2988     indent += 5;
2989     for(ll = lines; ll && *ll; ll++){
2990 	if((p = strindex(*ll, ':')) != NULL){
2991 	    if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
2992 	      begins++;
2993 
2994 	    snprintf(tmp, sizeof(tmp), "  %-*.*s : ", indent - 5,
2995 		    (int) MIN(p - *ll, sizeof(tmp)-5), *ll);
2996 	    tmp[sizeof(tmp)-1] = '\0';
2997 	    so_puts(in_store, tmp);
2998 	    p++;
2999 	}
3000 	else{
3001 	    p = *ll;
3002 	    so_puts(in_store, repeat_char(indent, SPACE));
3003 	}
3004 
3005 	snprintf(tmp, sizeof(tmp), "%.200s", p);
3006 	tmp[sizeof(tmp)-1] = '\0';
3007 	so_puts(in_store,
3008 		(char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
3009 					       SIZEOF_20KBUF, tmp));
3010 	so_puts(in_store, "\015\012");
3011     }
3012 
3013     free_list_array(&lines);
3014 
3015     so_puts(in_store, "\015\012\015\012");
3016 
3017     do{
3018 	if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
3019 	    so_seek(in_store, 0L, 0);
3020 
3021 	    init_handles(&handles);
3022 	    gf_filter_init();
3023 
3024 	    if(F_ON(F_VIEW_SEL_URL, ps_global)
3025 	       || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3026 	       || F_ON(F_SCAN_ADDR, ps_global))
3027 	      gf_link_filter(gf_line_test,
3028 			     gf_line_test_opt(url_hilite,
3029 					      gf_url_hilite_opt(&uh,&handles,0)));
3030 
3031 	    gf_link_filter(gf_wrap,
3032 			   gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3033 					      ps_global->ttyo->screen_cols,
3034 					      NULL, indent, GFW_HANDLES));
3035 	    gf_link_filter(gf_nvtnl_local, NULL);
3036 
3037 	    gf_set_so_readc(&gc, in_store);
3038 	    gf_set_so_writec(&pc, out_store);
3039 
3040 	    errstr = gf_pipe(gc, pc);
3041 
3042 	    gf_clear_so_readc(in_store);
3043 
3044 	    if(!errstr){
3045 #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.")
3046 #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.")
3047 		errstr = format_editorial((begins > 1)
3048 					    ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
3049 					  ps_global->ttyo->screen_cols, 0, NULL, pc);
3050 	    }
3051 
3052 	    gf_clear_so_writec(out_store);
3053 
3054 	    if(!errstr)
3055 	      cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
3056 				      CharStar, handles, a, flags | DA_RESIZE);
3057 
3058 	    free_handles(&handles);
3059 	    so_give(&out_store);
3060 	}
3061 	else
3062 	  errstr = _("Error allocating space");
3063     }
3064     while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3065 
3066     if(errstr)
3067       q_status_message1(SM_ORDER | SM_DING, 3, 3,
3068 			_("Can't format entry : %s"), errstr);
3069 
3070     so_give(&in_store);
3071 }
3072 
3073 void
display_vevent_summary(long int msgno,ATTACH_S * a,int flags,int depth)3074 display_vevent_summary(long int msgno, ATTACH_S *a, int flags, int depth)
3075 {
3076     BODY *b;
3077     VCALENDAR_S *vcal = NULL;
3078     char *b64text, *caltext;
3079     unsigned long callen;
3080     VEVENT_SUMMARY_S *vesy, *vsummary; /* vevent summary */
3081     STORE_S   *in_store, *out_store = NULL;
3082     HANDLE_S  *handles = NULL;
3083     URL_HILITE_S uh;
3084     gf_io_t    gc, pc;
3085     char   *errstr = NULL, tmp[MAILTMPLEN], *p;
3086     int	   cmd, i, k;
3087 
3088     b = mail_body(ps_global->mail_stream, msgno, a->number);
3089     if(b->sparep == NULL){
3090        b64text = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
3091        b64text[callen] = '\0';       /* chop off cookie */
3092        caltext = rfc822_base64(b64text, strlen(b64text), &callen);
3093        vcal = ical_parse_text(caltext);
3094        b->sparep = create_body_sparep(iCalType, (void *) vcal);
3095     }
3096     else if(get_body_sparep_type(b->sparep) == iCalType)
3097        vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
3098 
3099     vsummary = ical_vevent_summary(vcal);
3100 
3101     if(vsummary == NULL){
3102 	q_status_message(SM_ORDER | SM_DING, 3, 3,
3103 			 _("Error parsing event"));
3104 	return;
3105     }
3106 
3107     if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
3108 	q_status_message(SM_ORDER | SM_DING, 3, 3,
3109 			 _("Error allocating space to process Calendar"));
3110 	return;
3111     }
3112 
3113     gf_set_so_readc(&gc, in_store);
3114 
3115     for(vesy = vsummary, k = 0; vesy; vesy = vesy->next, k++){
3116        if(depth >= 0 && k !=  depth)
3117 	 continue;
3118 
3119        if(vesy->cancel){
3120          so_puts(in_store, _("This event was cancelled"));
3121          so_puts(in_store, "\015\012");
3122        }
3123 
3124        if(vesy->priority){
3125          utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%d %s",
3126 	   _("Priority: "), vesy->priority,
3127 	 vesy->priority == 5 ? _("(Normal)")
3128 			    : (vesy->priority < 5 ? _("(High)")
3129 						  : _("(Low)")));
3130          so_puts(in_store, tmp_20k_buf);
3131          so_puts(in_store, "\015\012");
3132        }
3133 
3134        if(vesy->summary){
3135          utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3136 	   _("Summary: "), vesy->summary);
3137          so_puts(in_store, tmp_20k_buf);
3138          so_puts(in_store, "\015\012");
3139        }
3140 
3141        if(vesy->sender){
3142          utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3143 	   _("Sender: "), vesy->sender);
3144          so_puts(in_store, tmp_20k_buf);
3145          so_puts(in_store, "\015\012");
3146        }
3147 
3148        if(vesy->organizer){
3149          utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3150 	   _("Organizer: "), vesy->organizer);
3151          so_puts(in_store, tmp_20k_buf);
3152          so_puts(in_store, "\015\012");
3153        }
3154 
3155        if(vesy->location){
3156          utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3157 	   _("Location: "), vesy->location);
3158          so_puts(in_store, tmp_20k_buf);
3159          so_puts(in_store, "\015\012");
3160        }
3161 
3162        if(vesy->evstart){
3163          utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3164 	   _("Start Date: "), vesy->evstart);
3165          so_puts(in_store, tmp_20k_buf);
3166          so_puts(in_store, "\015\012");
3167        }
3168 
3169        if(vesy->duration){
3170          for(i = 0; vesy->duration[i] != NULL; i++){
3171             utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3172 	      _("Duration: "), vesy->duration[i]);
3173             so_puts(in_store, tmp_20k_buf);
3174             so_puts(in_store, "\015\012");
3175          }
3176        } else if(vesy->evend){
3177          utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3178 	   _("End Date: "), vesy->evend);
3179          so_puts(in_store, tmp_20k_buf);
3180          so_puts(in_store, "\015\012");
3181        }
3182 
3183        if(vesy->dtstamp){
3184          utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
3185 	   vcal->method ? _("Created on: ") : _("Last Revised on; "),
3186 	   vesy->dtstamp);
3187          so_puts(in_store, tmp_20k_buf);
3188          so_puts(in_store, "\015\012");
3189        }
3190 
3191        if(vesy->description){
3192          char c;
3193          int j, empty;
3194 
3195          so_puts(in_store, "\015\012");
3196 
3197          for(i = 0; vesy->description[i] != NULL; i++){
3198 	    so_puts(in_store, _("Description: "));
3199 	   /* Check if empty description */
3200 	   empty = 1;
3201 	   for(j =0; empty == 1 && vesy->description[i][j] != '\0'; j++){
3202 	      c = vesy->description[i][j];
3203 	      if(c != '\n' && c != ' ' && c != '\t')
3204 	        empty = 0;
3205 	   }
3206 	   if(empty){
3207 	     so_puts(in_store, _("[ No description provided ]"));
3208 	     so_puts(in_store, "\015\012");
3209 	   }
3210 	   else {
3211 	     for(j =0; vesy->description[i][j] != '\0'; j++){
3212 	        c = vesy->description[i][j];
3213 	        if(c == '\n'){
3214 	          so_puts(in_store, "\015\012");
3215 	          continue;
3216 	        }
3217 	        so_writec(c, in_store);
3218 	     }
3219 	   }
3220            so_puts(in_store, "\015\012");
3221          }
3222        }
3223 
3224        if(vesy->attendee){
3225          so_puts(in_store, "\015\012");
3226          so_puts(in_store, _("List of Attendees:"));
3227          so_puts(in_store, "\015\012");
3228          for(i = 0; vesy->attendee[i] != NULL; i++){
3229             so_puts(in_store, vesy->attendee[i]);
3230             so_puts(in_store, "\015\012");
3231          }
3232          so_puts(in_store, "\015\012");
3233        }
3234 
3235        utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3236 		_("This event was tagged as a %s entry by the sender"), vesy->class);
3237        so_puts(in_store, tmp_20k_buf);
3238        so_puts(in_store, "\015\012\015\012");
3239 
3240        if(depth < 0 && vesy->next){
3241 	 for(i = 0; i < ps_global->ttyo->screen_cols && i < 40; i++)
3242 	    tmp_20k_buf[i] = '-';
3243          tmp_20k_buf[i]= '\0';
3244          so_puts(in_store, tmp_20k_buf);
3245         so_puts(in_store, "\015\012");
3246        }
3247     } /* end "for" loop */
3248 
3249     do{
3250 	so_seek(in_store, 0L, 0);
3251 	if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
3252 	   q_status_message(SM_ORDER | SM_DING, 3, 3,
3253 			 _("Error allocating space to write Calendar"));
3254 	   return;
3255 	}
3256 
3257 	gf_set_so_writec(&pc, out_store);
3258 
3259 	init_handles(&handles);
3260 	gf_filter_init();
3261 
3262 	if(F_ON(F_VIEW_SEL_URL, ps_global)
3263 	       || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
3264 	       || F_ON(F_SCAN_ADDR, ps_global))
3265 	      gf_link_filter(gf_line_test,
3266 			     gf_line_test_opt(url_hilite,
3267 					      gf_url_hilite_opt(&uh,&handles,0)));
3268 
3269 	gf_link_filter(gf_wrap,
3270 			   gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
3271 					      ps_global->ttyo->screen_cols,
3272 					      NULL, 0, GFW_HANDLES));
3273 	gf_link_filter(gf_nvtnl_local, NULL);
3274 
3275 	gf_set_so_readc(&gc, in_store);
3276 	gf_set_so_writec(&pc, out_store);
3277 
3278 	errstr = gf_pipe(gc, pc);
3279 
3280 	gf_clear_so_readc(in_store);
3281 
3282 	gf_clear_so_writec(out_store);
3283 
3284 	if(!errstr)
3285 	   cmd = scroll_attachment(_("CALENDAR EVENT ATTACHMENT"), out_store,
3286 				      CharStar, handles, a, flags | DA_RESIZE);
3287 
3288 	free_handles(&handles);
3289 	so_give(&out_store);
3290     }
3291     while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
3292 
3293     if(errstr)
3294       q_status_message1(SM_ORDER | SM_DING, 3, 3,
3295 			_("Can't format entry : %s"), errstr);
3296     so_give(&in_store);
3297     free_vevent_summary(&vsummary);
3298     ps_global->mangled_screen = 1;
3299 }
3300 
3301 
3302 void
display_vcalendar_att(long int msgno,ATTACH_S * a,int flags)3303 display_vcalendar_att(long int msgno, ATTACH_S *a, int flags)
3304 {
3305     display_vevent_summary(msgno, a, flags, -1);
3306 }
3307 
3308 
3309 /*----------------------------------------------------------------------
3310   Display attachment information
3311 
3312   Args: msgno -- message number to get partrom
3313 	 a -- attachment struct for the desired part
3314 
3315   Result: a screen containing information about attachment:
3316  ----*/
3317 void
display_attach_info(long int msgno,ATTACH_S * a)3318 display_attach_info(long int msgno, ATTACH_S *a)
3319 {
3320     int		i, indent, cols;
3321     char        buf1[100], *folded;
3322     STORE_S    *store;
3323     PARMLIST_S *plist;
3324     SCROLL_S	sargs;
3325 
3326     (void) dispatch_attachment(a);
3327 
3328     if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
3329 	q_status_message(SM_ORDER | SM_DING, 3, 3,
3330 			 _("Error allocating space for message."));
3331 	return;
3332     }
3333 
3334     cols = ps_global->ttyo->screen_cols;
3335 
3336     /*
3337      * 2 spaces on left
3338      * 16 for text (longest is Display Method == 14)
3339      * 2 for ": "
3340      */
3341     indent = 20;
3342 
3343     /* don't try stupid folding */
3344     cols = MAX(indent+10, cols);
3345 
3346     so_puts(store, "Details about Attachment #");
3347     so_puts(store, a->number);
3348     so_puts(store, " :\n\n");
3349     utf8_snprintf(buf1, sizeof(buf1), "  %-*.*w: ", indent-4, indent-4, "Type");
3350     so_puts(store, buf1);
3351     so_puts(store, body_type_names(a->body->type));
3352     so_puts(store, "\n");
3353     utf8_snprintf(buf1, sizeof(buf1), "  %-*.*w: ", indent-4, indent-4, "Subtype");
3354     so_puts(store, buf1);
3355     so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
3356     so_puts(store, "\n");
3357     utf8_snprintf(buf1, sizeof(buf1), "  %-*.*w: ", indent-4, indent-4, "Encoding");
3358     so_puts(store, buf1);
3359     so_puts(store, a->body->encoding < ENCMAX
3360 			 ? body_encodings[a->body->encoding]
3361 			 : "Unknown");
3362     so_puts(store, "\n");
3363     if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
3364 	utf8_snprintf(buf1, sizeof(buf1), "  %-*.*w: ", indent-4, indent-4, "Parameters");
3365 	so_puts(store, buf1);
3366 	i = 0;
3367 	while(rfc2231_list_params(plist)){
3368 	    if(i++)
3369 	      so_puts(store, repeat_char(indent, ' '));
3370 
3371 	    so_puts(store, plist->attrib);
3372 	    so_puts(store, " = ");
3373 	    so_puts(store, plist->value ? plist->value : "");
3374 	    so_puts(store, "\n");
3375 	}
3376 
3377 	rfc2231_free_parmlist(&plist);
3378     }
3379 
3380     if(a->body->description && a->body->description[0]){
3381 	char buftmp[MAILTMPLEN];
3382 	unsigned char *q;
3383 
3384 	utf8_snprintf(buf1, sizeof(buf1), "  %-*.*w: ", indent-4, indent-4, "Description");
3385 
3386 	snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
3387 	buftmp[sizeof(buftmp)-1] = '\0';
3388 	q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
3389 	folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
3390 
3391 	if(folded){
3392 	  so_puts(store, folded);
3393 	  fs_give((void **) &folded);
3394 	}
3395     }
3396 
3397     /* BUG: no a->body->language support */
3398 
3399     if(a->body->disposition.type){
3400 	utf8_snprintf(buf1, sizeof(buf1), "  %-*.*w: ", indent-4, indent-4, "Disposition");
3401 	so_puts(store, buf1);
3402 	so_puts(store, a->body->disposition.type);
3403 	so_puts(store, "\n");
3404 	if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
3405 	    while(rfc2231_list_params(plist)){
3406 	        so_puts(store, repeat_char(indent, ' '));
3407 		so_puts(store, plist->attrib);
3408 		so_puts(store, " = ");
3409 		so_puts(store, plist->value ? plist->value : "");
3410 		so_puts(store, "\n");
3411 	    }
3412 
3413 	    rfc2231_free_parmlist(&plist);
3414 	}
3415     }
3416 
3417     utf8_snprintf(buf1, sizeof(buf1), "  %-*.*w: ", indent-4, indent-4, "Approx. Size");
3418     so_puts(store, buf1);
3419     so_puts(store, comatose((a->body->encoding == ENCBASE64)
3420 			      ? ((a->body->size.bytes * 3)/4)
3421 			      : a->body->size.bytes));
3422     so_puts(store, " bytes\n");
3423     utf8_snprintf(buf1, sizeof(buf1), "  %-*.*w: ", indent-4, indent-4, "Display Method");
3424     so_puts(store, buf1);
3425     if(a->can_display == MCD_NONE) {
3426 	so_puts(store, "Can't, ");
3427 	so_puts(store, (a->body->encoding < ENCOTHER)
3428 			 ? "Unknown Attachment Format"
3429 			 : "Unknown Encoding");
3430     }
3431     else if(!(a->can_display & MCD_EXTERNAL)){
3432 	so_puts(store, "Alpine's Internal Pager");
3433     }
3434     else{
3435 	int   nt, free_pretty_cmd;
3436 	MCAP_CMD_S *mc_cmd;
3437 	char *pretty_cmd;
3438 
3439 	if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
3440 				       a->body, "<datafile>", &nt,
3441 				       a->can_display & MCD_EXT_PROMPT)) != NULL){
3442 	    so_puts(store, "\"");
3443 	    if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
3444 	      so_puts(store, pretty_cmd);
3445 	    so_puts(store, "\"");
3446 	    if(free_pretty_cmd && pretty_cmd)
3447 	      fs_give((void **)&pretty_cmd);
3448 	    if(mc_cmd->command)
3449 	      fs_give((void **)&mc_cmd->command);
3450 	    fs_give((void **)&mc_cmd);
3451 	}
3452     }
3453 
3454     so_puts(store, "\n");
3455 
3456     memset(&sargs, 0, sizeof(SCROLL_S));
3457     sargs.text.text  = so_text(store);
3458     sargs.text.src   = CharStar;
3459     sargs.text.desc  = "attachment info";
3460     sargs.bar.title  = _("ABOUT ATTACHMENT");
3461     sargs.help.text  = h_simple_text_view;
3462     sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
3463 
3464     sargs.use_indexline_color = 1;
3465 
3466     scrolltool(&sargs);
3467 
3468     so_give(&store);	/* free resources associated with store */
3469     ps_global->mangled_screen = 1;
3470 }
3471 
3472 
3473 /*----------------------------------------------------------------------
3474 
3475  ----*/
3476 void
forward_attachment(MAILSTREAM * stream,long int msgno,ATTACH_S * a)3477 forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3478 {
3479     char     *sig;
3480     void     *msgtext;
3481     ENVELOPE *outgoing;
3482     BODY     *body;
3483 
3484     if(MIME_MSG_A(a)){
3485 	forward_msg_att(stream, msgno, a);
3486     }
3487     else{
3488 	ACTION_S      *role = NULL;
3489 	REDRAFT_POS_S *redraft_pos = NULL;
3490 	long           rflags = ROLE_FORWARD;
3491 	PAT_STATE      dummy;
3492 
3493 	outgoing              = mail_newenvelope();
3494 	outgoing->subject     = cpystr("Forwarded attachment...");
3495 
3496 	if(nonempty_patterns(rflags, &dummy)){
3497 	    /*
3498 	     * There is no message, but a Current Folder Type might match.
3499 	     *
3500 	     * This has been changed to check against the message
3501 	     * containing the attachment.
3502 	     */
3503 	    role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
3504 	    if(confirm_role(rflags, &role))
3505 	      role = combine_inherited_role(role);
3506 	    else{
3507 		role = NULL;
3508 		cmd_cancelled("Forward");
3509 		mail_free_envelope(&outgoing);
3510 		return;
3511 	    }
3512 	}
3513 
3514 	if(role)
3515 	  q_status_message1(SM_ORDER, 3, 4,
3516 			    _("Forwarding using role \"%s\""), role->nick);
3517 
3518 	outgoing->message_id  = generate_message_id(role);
3519 	/*
3520 	 * as with all text bound for the composer, build it in
3521 	 * a storage object of the type it understands...
3522 	 */
3523 	if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3524 	    int   impl, template_len = 0;
3525 
3526 	    if(role && role->template){
3527 		char *filtered;
3528 
3529 		impl = 1;
3530 		filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
3531 		if(filtered){
3532 		    if(*filtered){
3533 			so_puts((STORE_S *)msgtext, filtered);
3534 			if(impl == 1)
3535 			  template_len = strlen(filtered);
3536 		    }
3537 
3538 		    fs_give((void **)&filtered);
3539 		}
3540 	    }
3541 	    else
3542 	      impl = 1;
3543 
3544 	    if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
3545 		if(impl == 2)
3546 		  redraft_pos->offset += template_len;
3547 
3548 		so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3549 
3550 		fs_give((void **)&sig);
3551 	    }
3552 	    else
3553 	      so_puts((STORE_S *)msgtext, NEWLINE);
3554 
3555 	    /*---- New Body to start with ----*/
3556 	    body       = mail_newbody();
3557 	    body->type = TYPEMULTIPART;
3558 
3559 	    /*---- The TEXT part/body ----*/
3560 	    body->nested.part			       = mail_newbody_part();
3561 	    body->nested.part->body.type	       = TYPETEXT;
3562 	    body->nested.part->body.contents.text.data = msgtext;
3563 
3564 	    /*---- The corresponding things we're attaching ----*/
3565 	    body->nested.part->next  = mail_newbody_part();
3566 	    body->nested.part->next->body.id = generate_message_id(role);
3567 	    copy_body(&body->nested.part->next->body, a->body);
3568 
3569 	    if(fetch_contents(stream, msgno, a->number,
3570 			      &body->nested.part->next->body)){
3571 		pine_send(outgoing, &body, "FORWARD MESSAGE",
3572 			  role, NULL, NULL, redraft_pos, NULL, NULL, 0);
3573 
3574 		ps_global->mangled_screen = 1;
3575 		pine_free_body(&body);
3576 		free_redraft_pos(&redraft_pos);
3577 	    }
3578 	    else{
3579 		mail_free_body(&body);
3580 		so_give((STORE_S **) &msgtext);
3581 		free_redraft_pos(&redraft_pos);
3582 		q_status_message(SM_ORDER | SM_DING, 4, 5,
3583 		   _("Error fetching message contents.  Can't forward message."));
3584 	    }
3585 	}
3586 	else
3587 	  q_status_message(SM_ORDER | SM_DING, 3, 4,
3588 			   _("Error allocating message text"));
3589 
3590 	mail_free_envelope(&outgoing);
3591 	free_action(&role);
3592     }
3593 }
3594 
3595 
3596 void
forward_msg_att(MAILSTREAM * stream,long int msgno,ATTACH_S * a)3597 forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3598 {
3599     char          *p, *sig = NULL;
3600     int            ret;
3601     void          *msgtext;
3602     ENVELOPE      *outgoing;
3603     BODY          *body;
3604     ACTION_S      *role = NULL;
3605     REPLY_S        reply;
3606     REDRAFT_POS_S *redraft_pos = NULL;
3607 
3608     outgoing             = mail_newenvelope();
3609     memset((void *)&reply, 0, sizeof(reply));
3610 
3611     if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
3612 	/*
3613 	 * as with all text bound for the composer, build it in
3614 	 * a storage object of the type it understands...
3615 	 */
3616 	if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3617 	    int impl, template_len = 0;
3618 	    long rflags = ROLE_FORWARD;
3619 	    PAT_STATE dummy;
3620 
3621 	    ret = 'n';
3622 	    if(ps_global->full_header == 2)
3623 	       ret = want_to(_("Forward message as an attachment"), 'n', 0,
3624 			     NO_HELP, WT_SEQ_SENSITIVE);
3625 	    /* Setup possible role */
3626 	    if(nonempty_patterns(rflags, &dummy)){
3627 		role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3628 		if(confirm_role(rflags, &role))
3629 		  role = combine_inherited_role(role);
3630 		else{				/* cancel reply */
3631 		    role = NULL;
3632 		    cmd_cancelled("Forward");
3633 		    mail_free_envelope(&outgoing);
3634 		    so_give((STORE_S **) &msgtext);
3635 		    return;
3636 		}
3637 	    }
3638 
3639 	    if(role)
3640 	      q_status_message1(SM_ORDER, 3, 4,
3641 				_("Forwarding using role \"%s\""), role->nick);
3642 
3643 	    outgoing->message_id = generate_message_id(role);
3644 
3645 	    if(role && role->template){
3646 		char *filtered;
3647 
3648 		impl = 1;
3649 		filtered = detoken(role, a->body->nested.msg->env,
3650 				   0, 0, 0, &redraft_pos, &impl);
3651 		if(filtered){
3652 		    if(*filtered){
3653 			so_puts((STORE_S *)msgtext, filtered);
3654 			if(impl == 1)
3655 			  template_len = strlen(filtered);
3656 		    }
3657 
3658 		    fs_give((void **)&filtered);
3659 		}
3660 	    }
3661 	    else
3662 	      impl = 1;
3663 
3664 	    if((sig = detoken(role, a->body->nested.msg->env,
3665 			     2, 0, 1, &redraft_pos, &impl)) != NULL){
3666 		if(impl == 2)
3667 		  redraft_pos->offset += template_len;
3668 
3669 		so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
3670 
3671 		fs_give((void **)&sig);
3672 	    }
3673 	    else
3674 	      so_puts((STORE_S *)msgtext, NEWLINE);
3675 
3676 	    if(ret == 'y'){
3677 		/*---- New Body to start with ----*/
3678 		body	   = mail_newbody();
3679 		body->type = TYPEMULTIPART;
3680 
3681 		/*---- The TEXT part/body ----*/
3682 		body->nested.part = mail_newbody_part();
3683 		body->nested.part->body.type = TYPETEXT;
3684 		body->nested.part->body.contents.text.data = msgtext;
3685 
3686 		if(!forward_mime_msg(stream, msgno,
3687 				     p = body_partno(stream, msgno, a->body),
3688 				     a->body->nested.msg->env,
3689 				     &body->nested.part->next, msgtext))
3690 		  mail_free_body(&body);
3691 	    }
3692 	    else{
3693 		reply.forw = 1;
3694 		if(a->body->nested.msg->body){
3695 		    char *charset;
3696 
3697 		    charset
3698 		      = parameter_val(a->body->nested.msg->body->parameter,
3699 				          "charset");
3700 
3701 		    if(charset && strucmp(charset, "us-ascii") != 0){
3702 			CONV_TABLE *ct;
3703 
3704 			/*
3705 			 * There is a non-ascii charset,
3706 			 * is there conversion happening?
3707 			 */
3708 			if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3709 			   || !ct->table){
3710 			    reply.orig_charset = charset;
3711 			    charset = NULL;
3712 			}
3713 		    }
3714 
3715 		    if(charset)
3716 		      fs_give((void **) &charset);
3717 		}
3718 
3719 		body = forward_body(stream, a->body->nested.msg->env,
3720 				    a->body->nested.msg->body, msgno,
3721 				    p = body_partno(stream, msgno, a->body),
3722 				    msgtext, FWD_NONE);
3723 	    }
3724 
3725 	    fs_give((void **) &p);
3726 
3727 	    if(body){
3728 		pine_send(outgoing, &body,
3729 			  "FORWARD MESSAGE",
3730 			  role, NULL,
3731 			  reply.forw ? &reply : NULL,
3732 			  redraft_pos, NULL, NULL, 0);
3733 
3734 		ps_global->mangled_screen = 1;
3735 		pine_free_body(&body);
3736 		free_redraft_pos(&redraft_pos);
3737 		free_action(&role);
3738 	    }
3739 	    else{
3740 		so_give((STORE_S **) &msgtext);
3741 		q_status_message(SM_ORDER | SM_DING, 4, 5,
3742 		   _("Error fetching message contents.  Can't forward message."));
3743 	    }
3744 	}
3745 	else
3746 	  q_status_message(SM_ORDER | SM_DING, 3, 4,
3747 			   _("Error allocating message text"));
3748     }
3749     else
3750       q_status_message1(SM_ORDER,3,4,
3751 			_("Error fetching message %s. Can't forward it."),
3752 			long2string(msgno));
3753 
3754     if(reply.orig_charset)
3755       fs_give((void **)&reply.orig_charset);
3756 
3757     mail_free_envelope(&outgoing);
3758 }
3759 
3760 
3761 void
reply_msg_att(MAILSTREAM * stream,long int msgno,ATTACH_S * a)3762 reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
3763 {
3764     ADDRESS       *saved_from, *saved_to, *saved_cc, *saved_resent;
3765     ENVELOPE      *outgoing;
3766     BODY          *body;
3767     void          *msgtext;
3768     char          *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
3769     int            include_text = 0, flags = RSF_QUERY_REPLY_ALL;
3770     int            rolemsg = 0, copytomsg = 0;
3771     long           rflags;
3772     PAT_STATE      dummy;
3773     REDRAFT_POS_S *redraft_pos = NULL;
3774     ACTION_S      *role = NULL;
3775 
3776     outgoing = mail_newenvelope();
3777 
3778     dprint((4,"\n - attachment reply \n"));
3779 
3780     saved_from		  = (ADDRESS *) NULL;
3781     saved_to		  = (ADDRESS *) NULL;
3782     saved_cc		  = (ADDRESS *) NULL;
3783     saved_resent	  = (ADDRESS *) NULL;
3784     outgoing->subject	  = NULL;
3785 
3786     prefix = reply_quote_str(a->body->nested.msg->env);
3787     /*
3788      * For consistency, the first question is always "include text?"
3789      */
3790     if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0
3791        && reply_news_test(a->body->nested.msg->env, outgoing) > 0
3792        && reply_harvest(ps_global, msgno, a->number,
3793 			a->body->nested.msg->env, &saved_from,
3794 			&saved_to, &saved_cc, &saved_resent, &flags)){
3795 	outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
3796 					  NULL, 0);
3797 	clear_cursor_pos();
3798 	reply_seed(ps_global, outgoing, a->body->nested.msg->env,
3799 		   saved_from, saved_to, saved_cc, saved_resent,
3800 		   &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
3801 	if(errmsg){
3802 	    if(*errmsg){
3803 		q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
3804 		display_message(NO_OP_COMMAND);
3805 	    }
3806 
3807 	    fs_give((void **)&errmsg);
3808 	}
3809 
3810 	if(sp_expunge_count(stream))	/* current msg was expunged */
3811 	  goto seeyalater;
3812 
3813 	/* Setup possible role */
3814 	rflags = ROLE_REPLY;
3815 	if(nonempty_patterns(rflags, &dummy)){
3816 	    role = set_role_from_msg(ps_global, rflags, msgno, a->number);
3817 	    if(confirm_role(rflags, &role))
3818 	      role = combine_inherited_role(role);
3819 	    else{				/* cancel reply */
3820 		role = NULL;
3821 		cmd_cancelled("Reply");
3822 		goto seeyalater;
3823 	    }
3824 	}
3825 
3826 	if(role){
3827 	    rolemsg++;
3828 
3829 	    /* override fcc gotten in reply_seed */
3830 	    if(role->fcc && fcc)
3831 	      fs_give((void **) &fcc);
3832 	}
3833 
3834 	if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
3835 	    ADDRESS *us_in_to_and_cc, *ap;
3836 
3837 	    us_in_to_and_cc = (ADDRESS *) NULL;
3838 	    if(a->body->nested.msg->env && a->body->nested.msg->env->to)
3839 	      if((ap=reply_cp_addr(ps_global, 0L, NULL,
3840 				   NULL, us_in_to_and_cc, NULL,
3841 				   a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
3842 		reply_append_addr(&us_in_to_and_cc, ap);
3843 
3844 	    if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
3845 	      if((ap=reply_cp_addr(ps_global, 0L, NULL,
3846 				   NULL, us_in_to_and_cc, NULL,
3847 				   a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
3848 		reply_append_addr(&us_in_to_and_cc, ap);
3849 
3850 	    /*
3851 	     * A list of all of our addresses that appear in the To
3852 	     * and cc fields is in us_in_to_and_cc.
3853 	     * If there is exactly one address in that list then
3854 	     * use it for the outgoing From.
3855 	     */
3856 	    if(us_in_to_and_cc && !us_in_to_and_cc->next){
3857 		PINEFIELD *custom, *pf;
3858 		ADDRESS *a = NULL;
3859 		char *addr = NULL;
3860 
3861 		/*
3862 		 * Check to see if this address is different from what
3863 		 * we would have used anyway. If it is, notify the user
3864 		 * with a status message. This is pretty hokey because we're
3865 		 * mimicking how pine_send would set the From address and
3866 		 * there is no coordination between the two.
3867 		 */
3868 
3869 		/* in case user has a custom From value */
3870 		custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
3871 
3872 		pf = (PINEFIELD *) fs_get(sizeof(*pf));
3873 		memset((void *) pf, 0, sizeof(*pf));
3874 		pf->name = cpystr("From");
3875 		pf->addr = &a;
3876 		if(set_default_hdrval(pf, custom) >= UseAsDef
3877 		   && pf->textbuf && pf->textbuf[0]){
3878 		    removing_trailing_white_space(pf->textbuf);
3879 		    (void)removing_double_quotes(pf->textbuf);
3880 		    build_address(pf->textbuf, &addr, NULL, NULL, NULL);
3881 		    rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
3882 		    if(addr)
3883 		      fs_give((void **) &addr);
3884 		}
3885 
3886 		if(!*pf->addr)
3887 		  *pf->addr = generate_from();
3888 
3889 		if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
3890 		    copytomsg++;
3891 		    if(!role){
3892 			role = (ACTION_S *) fs_get(sizeof(*role));
3893 			memset((void *) role, 0, sizeof(*role));
3894 			role->is_a_role = 1;
3895 		    }
3896 
3897 		    role->from = us_in_to_and_cc;
3898 		    us_in_to_and_cc = NULL;
3899 		}
3900 
3901 		free_customs(custom);
3902 		free_customs(pf);
3903 	    }
3904 
3905 	    if(us_in_to_and_cc)
3906 	      mail_free_address(&us_in_to_and_cc);
3907 
3908 	}
3909 
3910 	if(role){
3911 	    if(rolemsg && copytomsg)
3912 	      q_status_message1(SM_ORDER, 3, 4,
3913 				_("Replying using role \"%s\" and To as From"), role->nick);
3914 	    else if(rolemsg)
3915 	      q_status_message1(SM_ORDER, 3, 4,
3916 				_("Replying using role \"%s\""), role->nick);
3917 	    else if(copytomsg)
3918 	      q_status_message(SM_ORDER, 3, 4,
3919 			       _("Replying using incoming To as outgoing From"));
3920 	}
3921 
3922 	outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
3923 	outgoing->references = reply_build_refs(a->body->nested.msg->env);
3924 	outgoing->message_id = generate_message_id(role);
3925 
3926 	if(!outgoing->to && !outgoing->cc
3927 	   && !outgoing->bcc && !outgoing->newsgroups)
3928 	  q_status_message(SM_ORDER | SM_DING, 3, 6,
3929 			   _("Warning: no valid addresses to reply to!"));
3930 
3931 	/*
3932 	 * Now fix up the body...
3933 	 */
3934 	if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
3935 	    REPLY_S reply;
3936 
3937 	    memset((void *)&reply, 0, sizeof(reply));
3938 	    reply.forw = 1;
3939 	    if(a->body->nested.msg->body){
3940 		char *charset;
3941 
3942 		charset
3943 		  = parameter_val(a->body->nested.msg->body->parameter,
3944 				      "charset");
3945 
3946 		if(charset && strucmp(charset, "us-ascii") != 0){
3947 		    CONV_TABLE *ct;
3948 
3949 		    /*
3950 		     * There is a non-ascii charset,
3951 		     * is there conversion happening?
3952 		     */
3953 		    if(!(ct=conversion_table(charset, ps_global->posting_charmap))
3954 		       || !ct->table){
3955 			reply.orig_charset = charset;
3956 			charset = NULL;
3957 		    }
3958 		}
3959 
3960 		if(charset)
3961 		  fs_give((void **) &charset);
3962 	    }
3963 
3964 	    if((body = reply_body(stream, a->body->nested.msg->env,
3965 				 a->body->nested.msg->body, msgno,
3966 				 tp = body_partno(stream, msgno, a->body),
3967 				 msgtext, prefix, include_text, role,
3968 				 1, &redraft_pos)) != NULL){
3969 		/* partially formatted outgoing message */
3970 		pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
3971 			  role, fcc, &reply, redraft_pos, NULL, NULL, 0);
3972 
3973 		pine_free_body(&body);
3974 		ps_global->mangled_screen = 1;
3975 	    }
3976 	    else
3977 	      q_status_message(SM_ORDER | SM_DING, 3, 4,
3978 			       _("Error building message body"));
3979 
3980 	    fs_give((void **) &tp);
3981 	    if(reply.orig_charset)
3982 	      fs_give((void **)&reply.orig_charset);
3983 	}
3984 	else
3985 	  q_status_message(SM_ORDER | SM_DING, 3, 4,
3986 			   _("Error allocating message text"));
3987     }
3988 
3989 seeyalater:
3990     mail_free_envelope(&outgoing);
3991     mail_free_address(&saved_from);
3992     mail_free_address(&saved_to);
3993     mail_free_address(&saved_cc);
3994     mail_free_address(&saved_resent);
3995 
3996     if(prefix)
3997       fs_give((void **) &prefix);
3998 
3999     if(fcc)
4000       fs_give((void **) &fcc);
4001 
4002     free_redraft_pos(&redraft_pos);
4003     free_action(&role);
4004 }
4005 
4006 
4007 void
bounce_msg_att(MAILSTREAM * stream,long int msgno,char * part,char * subject)4008 bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
4009 {
4010     char *errstr;
4011 
4012     if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
4013       q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
4014 }
4015 
4016 
4017 void
pipe_attachment(long int msgno,ATTACH_S * a)4018 pipe_attachment(long int msgno, ATTACH_S *a)
4019 {
4020     char    *err, *resultfilename = NULL, prompt[80], *p;
4021     int      rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
4022     long     ku;
4023     PIPE_S  *syspipe;
4024     HelpType help;
4025     char     pipe_command[MAXPATH+1];
4026     unsigned          flagsforhist = 1;	/* raw=2 /capture=1 */
4027     static HISTORY_S *history = NULL;
4028     ESCKEY_S pipe_opt[6];
4029 
4030     if(ps_global->restricted){
4031 	q_status_message(SM_ORDER | SM_DING, 0, 4,
4032 			 "Alpine demo can't pipe attachments");
4033 	return;
4034     }
4035 
4036     help = NO_HELP;
4037     pipe_command[0] = '\0';
4038 
4039     init_hist(&history, HISTSIZE);
4040     flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4041     if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
4042 	strncpy(pipe_command, p, sizeof(pipe_command));
4043 	pipe_command[sizeof(pipe_command)-1] = '\0';
4044 	if(history->hist[history->curindex]){
4045 	    flagsforhist = history->hist[history->curindex]->flags;
4046 	    raw     = (flagsforhist & 0x2) ? 1 : 0;
4047 	    capture = (flagsforhist & 0x1) ? 1 : 0;
4048 	}
4049     }
4050 
4051     pipe_opt[j].ch    = 0;
4052     pipe_opt[j].rval  = 0;
4053     pipe_opt[j].name  = "";
4054     pipe_opt[j++].label = "";
4055 
4056     pipe_opt[j].ch    = ctrl('W');
4057     pipe_opt[j].rval  = 10;
4058     pipe_opt[j].name  = "^W";
4059     pipe_opt[j++].label = NULL;
4060 
4061     pipe_opt[j].ch    = ctrl('Y');
4062     pipe_opt[j].rval  = 11;
4063     pipe_opt[j].name  = "^Y";
4064     pipe_opt[j++].label = NULL;
4065 
4066     pipe_opt[j].ch      = KEY_UP;
4067     pipe_opt[j].rval    = 30;
4068     pipe_opt[j].name    = "";
4069     ku = j;
4070     pipe_opt[j++].label = "";
4071 
4072     pipe_opt[j].ch      = KEY_DOWN;
4073     pipe_opt[j].rval    = 31;
4074     pipe_opt[j].name    = "";
4075     pipe_opt[j++].label = "";
4076 
4077     pipe_opt[j].ch = -1;
4078 
4079     while(1){
4080 	int flags;
4081 
4082 	snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
4083 		a->number, capture ? "" : "(Free Output) ");
4084 	prompt[sizeof(prompt)-1] = '\0';
4085 	pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
4086 	pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
4087 
4088 	/*
4089 	 * 2 is really 1 because there will be one real entry and
4090 	 * one entry of "" because of the get_prev_hist above.
4091 	 */
4092 	if(items_in_hist(history) > 2){
4093 	    pipe_opt[ku].name  = HISTORY_UP_KEYNAME;
4094 	    pipe_opt[ku].label = HISTORY_KEYLABEL;
4095 	    pipe_opt[ku+1].name  = HISTORY_DOWN_KEYNAME;
4096 	    pipe_opt[ku+1].label = HISTORY_KEYLABEL;
4097 	}
4098 	else{
4099 	    pipe_opt[ku].name  = "";
4100 	    pipe_opt[ku].label = "";
4101 	    pipe_opt[ku+1].name  = "";
4102 	    pipe_opt[ku+1].label = "";
4103 	}
4104 
4105 	flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
4106 	rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
4107 			      sizeof(pipe_command), prompt,
4108 			      pipe_opt, help, &flags);
4109 	if(rc == -1){
4110 	    q_status_message(SM_ORDER | SM_DING, 3, 4,
4111 			     "Internal problem encountered");
4112 	    break;
4113 	}
4114 	else if(rc == 10){
4115 	    raw = !raw;			/* flip raw text */
4116 	}
4117 	else if(rc == 11){
4118 	    capture = !capture;		/* flip capture output */
4119 	}
4120 	else if(rc == 30){
4121 	    flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4122 	    if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
4123 		strncpy(pipe_command, p, sizeof(pipe_command));
4124 		pipe_command[sizeof(pipe_command)-1] = '\0';
4125 		if(history->hist[history->curindex]){
4126 		    flagsforhist = history->hist[history->curindex]->flags;
4127 		    raw     = (flagsforhist & 0x2) ? 1 : 0;
4128 		    capture = (flagsforhist & 0x1) ? 1 : 0;
4129 		}
4130 	    }
4131 	    else
4132 	      Writechar(BELL, 0);
4133 	}
4134 	else if(rc == 31){
4135 	    flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4136 	    if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
4137 		strncpy(pipe_command, p, sizeof(pipe_command));
4138 		pipe_command[sizeof(pipe_command)-1] = '\0';
4139 		if(history->hist[history->curindex]){
4140 		    flagsforhist = history->hist[history->curindex]->flags;
4141 		    raw     = (flagsforhist & 0x2) ? 1 : 0;
4142 		    capture = (flagsforhist & 0x1) ? 1 : 0;
4143 		}
4144 	    }
4145 	    else
4146 	      Writechar(BELL, 0);
4147 	}
4148 	else if(rc == 0){
4149 	    if(pipe_command[0] == '\0'){
4150 		cmd_cancelled("Pipe command");
4151 		break;
4152 	    }
4153 
4154 	    flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
4155 	    save_hist(history, pipe_command, flagsforhist, NULL);
4156 
4157 	    flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
4158 	    flags |= (raw ? PIPE_RAW : 0);
4159 	    if(!capture){
4160 #ifndef	_WINDOWS
4161 		ClearScreen();
4162 		fflush(stdout);
4163 		clear_cursor_pos();
4164 		ps_global->mangled_screen = 1;
4165 #endif
4166 		flags |= PIPE_RESET;
4167 	    }
4168 
4169 	    if((syspipe = open_system_pipe(pipe_command,
4170 				   (flags&PIPE_RESET) ? NULL : &resultfilename,
4171 				   NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
4172 		int is_text = 0;
4173 		gf_io_t  pc;		/* wire up a generic putchar */
4174 
4175 		is_text = (a && a->body && a->body->type == TYPETEXT);
4176 
4177 		gf_set_writec(&pc, syspipe, 0L, PipeStar,
4178 			      (is_text && !raw) ? WRITE_TO_LOCALE : 0);
4179 
4180 		/*------ Write the image to a temporary file ------*/
4181 		if(raw){		/* pipe raw text */
4182 		    FETCH_READC_S fetch_part;
4183 
4184 		    err = NULL;
4185 
4186 		    if(capture)
4187 		      we_cancel = busy_cue(NULL, NULL, 1);
4188 		    else
4189 		      suspend_busy_cue();
4190 
4191 		    gf_filter_init();
4192 		    fetch_readc_init(&fetch_part, ps_global->mail_stream,
4193 				     msgno, a->number, a->body->size.bytes, 0, 0);
4194 		    gf_link_filter(gf_nvtnl_local, NULL);
4195 		    err = gf_pipe(FETCH_READC, pc);
4196 
4197 		    if(capture){
4198 			if(we_cancel)
4199 			  cancel_busy_cue(0);
4200 		    }
4201 		    else
4202 		      resume_busy_cue(0);
4203 		}
4204 		else{
4205 		    /* BUG: there's got to be a better way */
4206 		    if(!capture)
4207 		      ps_global->print = (PRINT_S *) 1;
4208 
4209 		    suspend_busy_cue();
4210 		    err = detach(ps_global->mail_stream, msgno,
4211 				 a->number, 0L, (long *)NULL, pc, NULL, 0);
4212 		    ps_global->print = (PRINT_S *) NULL;
4213 		    resume_busy_cue(0);
4214 		}
4215 
4216 		(void) close_system_pipe(&syspipe, NULL, pipe_callback);
4217 
4218 		if(err)
4219 		  q_status_message1(SM_ORDER | SM_DING, 3, 4,
4220 				    _("Error detaching for pipe: %s"), err);
4221 
4222 		display_output_file(resultfilename,
4223 				    (err)
4224 				      ? _("PIPE ATTACHMENT (ERROR)")
4225 				      : _("PIPE ATTACHMENT"),
4226 				    NULL, DOF_EMPTY);
4227 
4228 		fs_give((void **) &resultfilename);
4229 	    }
4230 	    else
4231 	      q_status_message(SM_ORDER | SM_DING, 3, 4,
4232 			       _("Error opening pipe"));
4233 
4234 	    break;
4235 	}
4236 	else if(rc == 1){
4237 	    cmd_cancelled("Pipe");
4238 	    break;
4239 	}
4240 	else if(rc == 3)
4241 	  help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
4242     }
4243 }
4244 
4245 
4246 int
delete_attachment(long int msgno,ATTACH_S * a)4247 delete_attachment(long int msgno, ATTACH_S *a)
4248 {
4249     int expbits, rv = 0;
4250 
4251     if(!msgno_exceptions(ps_global->mail_stream, msgno,
4252 			 a->number, &expbits, FALSE)
4253        || !(expbits & MSG_EX_DELETE)){
4254 	expbits |= MSG_EX_DELETE;
4255 	msgno_exceptions(ps_global->mail_stream, msgno,
4256 			 a->number, &expbits, TRUE);
4257 
4258 	q_status_message1(SM_ORDER, 0, 3,
4259 			_("Part %s will be omitted only if message is Saved"),
4260 			  a->number);
4261 	rv = 1;
4262     }
4263     else
4264       q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
4265 			a->number);
4266 
4267     return(rv);
4268 }
4269 
4270 
4271 int
undelete_attachment(long int msgno,ATTACH_S * a,int * expbitsp)4272 undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
4273 {
4274     int rv = 0;
4275 
4276     if(msgno_exceptions(ps_global->mail_stream, msgno,
4277 			a->number, expbitsp, FALSE)
4278        && ((*expbitsp) & MSG_EX_DELETE)){
4279 	(*expbitsp) ^= MSG_EX_DELETE;
4280 	msgno_exceptions(ps_global->mail_stream, msgno,
4281 			 a->number, expbitsp, TRUE);
4282 	rv = 1;
4283     }
4284     else
4285       q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
4286 			a->number);
4287 
4288     return(rv);
4289 }
4290 
4291 
4292 /*----------------------------------------------------------------------
4293   Resolve any deferred tests for attachment displayability
4294 
4295   Args: attachment structure
4296 
4297   Returns: undefer's attachment's displayability test
4298   ----*/
4299 int
dispatch_attachment(ATTACH_S * a)4300 dispatch_attachment(ATTACH_S *a)
4301 {
4302     if(a->test_deferred){
4303 	a->test_deferred = 0;
4304 	a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
4305     }
4306 
4307     return(a->can_display);
4308 }
4309 
4310 
4311 #ifdef	_WINDOWS
4312 int
scroll_att_popup(sparms,in_handle)4313 scroll_att_popup(sparms, in_handle)
4314     SCROLL_S *sparms;
4315     int	      in_handle;
4316 {
4317     MPopup scrat_popup[20];
4318     int	   i = -1, n;
4319 
4320     if(in_handle){
4321 	scrat_popup[++i].type	    = tIndex;
4322 	scrat_popup[i].label.style  = lNormal;
4323 	scrat_popup[i].label.string = "View Selectable Item";
4324 	scrat_popup[i].data.val	    = ctrl('L');
4325     }
4326 
4327     scrat_popup[++i].type	= tQueue;
4328     scrat_popup[i].label.style	= lNormal;
4329     scrat_popup[i].label.string = "&Save";
4330     scrat_popup[i].data.val	= 'S';
4331 
4332     scrat_popup[++i].type	= tQueue;
4333     scrat_popup[i].label.style	= lNormal;
4334     if(msgno_exceptions(ps_global->mail_stream,
4335 			mn_m2raw(ps_global->msgmap,
4336 				 mn_get_cur(ps_global->msgmap)),
4337 			scrat_attachp->number, &n, FALSE)
4338        && (n & MSG_EX_DELETE)){
4339 	scrat_popup[i].label.string = "&Undelete";
4340 	scrat_popup[i].data.val	    = 'U';
4341     }
4342     else{
4343 	scrat_popup[i].label.string = "&Delete";
4344 	scrat_popup[i].data.val	    = 'D';
4345     }
4346 
4347     if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
4348 	scrat_popup[++i].type	    = tQueue;
4349 	scrat_popup[i].label.style  = lNormal;
4350 	scrat_popup[i].label.string = "&Reply";
4351 	scrat_popup[i].data.val	    = 'R';
4352 
4353 	scrat_popup[++i].type	    = tQueue;
4354 	scrat_popup[i].label.style  = lNormal;
4355 	scrat_popup[i].label.string = "&Forward";
4356 	scrat_popup[i].data.val	    = 'f';
4357     }
4358 
4359     scrat_popup[++i].type = tSeparator;
4360 
4361     scrat_popup[++i].type	    = tQueue;
4362     scrat_popup[i].label.style  = lNormal;
4363     scrat_popup[i].label.string = "Attachment Index";
4364     scrat_popup[i].data.val	    = '<';
4365 
4366     scrat_popup[++i].type = tTail;
4367 
4368     return(mswin_popup(scrat_popup) == 0 && in_handle);
4369 }
4370 
4371 
4372 void
display_att_window(a)4373 display_att_window(a)
4374      ATTACH_S *a;
4375 {
4376 #if !defined(DOS) && !defined(OS2)
4377     char     prefix[8];
4378 #endif
4379 
4380     if(a->body->type == TYPEMULTIPART){
4381 	if(a->body->subtype){
4382 /*	    if(!strucmp(a->body->subtype, "digest"))
4383 	      display_digest_att(msgno, a, flags);
4384 	    else */
4385 	      q_status_message1(SM_ORDER, 3, 5,
4386 				"Can't display Multipart/%s",
4387 				a->body->subtype);
4388 	}
4389 	else
4390 	  q_status_message(SM_ORDER, 3, 5,
4391 			   "Can't display unknown Multipart Subtype");
4392     }
4393 /*    else if(MIME_VCARD_A(a))
4394       display_vcard_att_window(msgno, a, flags);*/
4395     else if(a->body->type == TYPETEXT)
4396       display_text_att_window(a);
4397     else if(a->body->type == TYPEMESSAGE)
4398       display_msg_att_window(a);
4399 }
4400 
4401 
4402 void
display_text_att_window(a)4403 display_text_att_window(a)
4404     ATTACH_S *a;
4405 {
4406     STORE_S  *store;
4407     long      msgno;
4408 
4409     msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4410 
4411     if(store = format_text_att(msgno, a, NULL)){
4412 	if (mswin_displaytext("ATTACHED TEXT",
4413 			      so_text(store),
4414 			      strlen((char *) so_text(store)),
4415 			      NULL, NULL, 0) >= 0)
4416 	  store->txt = (void *) NULL;	/* free'd in mswin_displaytext */
4417 
4418 	so_give(&store);	/* free resources associated with store */
4419     }
4420     else
4421       q_status_message(SM_ORDER | SM_DING, 3, 3,
4422 		       "Error allocating space for attachment.");
4423 }
4424 
4425 
4426 void
display_msg_att_window(a)4427 display_msg_att_window(a)
4428     ATTACH_S *a;
4429 {
4430     STORE_S  *store;
4431     gf_io_t   pc;
4432     ATTACH_S *ap = a;
4433     long      msgno;
4434 
4435     msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
4436 
4437     /* BUG, should check this return code */
4438     (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
4439 
4440     /* initialize a storage object */
4441     if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
4442 
4443 	gf_set_so_writec(&pc, store);
4444 
4445 	if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
4446 	   && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
4447 				strlen((char *) so_text(store)),
4448 				NULL, NULL, 0) >= 0)
4449 	  /* free'd in mswin_displaytext */
4450 	  store->txt = (void *) NULL;
4451 
4452 	gf_clear_so_writec(store);
4453 
4454 	so_give(&store);
4455     }
4456     else
4457       q_status_message(SM_ORDER | SM_DING, 3, 3,
4458 		       "Error allocating space for message.");
4459 }
4460 #endif
4461