static char rcsid[] = "@(#)$Id: metapager.c,v 1.53 2006/07/02 10:43:08 hurtta Exp $";

/*****************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.53 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 *
 *  Initially written by: Michael Elkins <elkins@aero.org>, 1995
 ****************************************************************************/

#include "def_elm.h"
#include "s_elm.h"

DEBUG_VAR(Debug,__FILE__,"ui");

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif       

static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str) 
     char *str;
{
    return (unsigned char *)str;
}

void PressAnyKeyToContinue(void)
{
    struct menu_context *cpage;
     
    int LINES, COLUMNS;

    cpage = Raw(ON | NO_TITE);
    menu_get_sizes(cpage,&LINES, &COLUMNS);

 redraw:
    menu_PutLineX(cpage,
		  LINES-1, 0, 
		  CATGETS(elm_msg_cat, ElmSet, ElmMetaPagePressAnyKey,
			  "Press any key to continue..."));
    if (menu_ReadCh(cpage, REDRAW_MARK) == REDRAW_MARK)
	goto redraw;

    Raw(OFF | NO_TITE);
    printf("\n\r");
}

static void this_message P_((out_state_t *buffer, int *inf));

static void this_message(buffer,inf)
     out_state_t *buffer; 
     int *inf;
{

    if (!*inf)
	state_printf(buffer,CATGETS(elm_msg_cat, ElmSet, 
				     ElmThisMessage,
				     "(** This message "));
    else
	state_printf(buffer,CATGETS(elm_msg_cat, ElmSet, 
				     ElmCommaAnd,
				     ", and "));

    (*inf)++;
}

struct string * title_text(hdr,current,message_count,width,cs)
     struct header_rec *hdr;
     int current, message_count;
     int width;
     charset_t cs;
{
    struct string * res = new_string(cs);
    char buf2[STRING];
    struct string *t;
    int l1,len;

    if (hdr->status & DELETED)
	t = format_string(CATGETS(elm_msg_cat, ElmSet, 
				  ElmPagerStatDeleted,
				  "[Deleted] %d/%d "),
			  current,message_count);
    else
	t = format_string(CATGETS(elm_msg_cat, ElmSet, 
				  ElmPagerStatMessage,
				  "Message %d/%d "),
			  current,message_count);
    append_string(&res,t);
    free_string(&t);
    l1 = string_len(res);

    elm_date_str(buf2, hdr->time_sent + hdr->tz_offset, sizeof buf2);
    strfcat(buf2, " ", sizeof buf2);
    strfcat(buf2, hdr->time_zone, sizeof buf2);

    len = width - 2 - l1 - strlen(buf2);

    if (hdr->from) {
	struct string * buf4 = new_string(cs);
	int buf4_visible_len = 0;
	int filllen;
	struct addr_item *p;

#define ADDRLEN   15

	for (p = hdr->from; p->addr && p->fullname; p++) {
	    int left = len- buf4_visible_len -4;
	    int total = 0;
	    int reserve = strlen(p->addr);
	    
	    if (reserve > ADDRLEN)
		reserve = ADDRLEN;
	    
	    if (left < 4) {
		break;
	    }
	    
	    if (string_len(buf4) > 0) {
		add_ascii_to_string(buf4,s2us(", "));
		buf4_visible_len += 2;
		
		left = len - buf4_visible_len-4;
	    }

	    if (reserve > left) {
		add_ascii_to_string(buf4,s2us("..."));
		buf4_visible_len += 3;
		break;
	    }


	    if (left > reserve+8 &&
		(0 < (total = string_len(p->fullname)))) {
		struct string * Sbase;
		struct string * s1 = NULL;
		int X = 0;
		int visible_len;		    
		int Old;
		
		/* Use convert_string instead of dup_string so 
		 * that text with unknown charset is printed
		 * as [?charset?]
		 */
		Sbase = convert_string(cs,p->fullname,1);
		
		Old = string_len(Sbase);
		
		s1 = curses_printable_clip(Sbase,&X,len+total,&visible_len,
					   left-reserve-8);
			
		if (s1) {
		    append_string(&buf4,s1);
		    
		    buf4_visible_len += visible_len;   /* Assumed */
		    free_string(&s1);
		}		    

		if (X < Old) {
		    /* Look if rest of name fits on place of '...' */

		    left = len - buf4_visible_len-4;

		    s1 = curses_printable_clip(Sbase,&X,len+total,&visible_len,
					       left-reserve-4);
			    
		    if (X == Old && s1) {

			append_string(&buf4,s1);
				
			buf4_visible_len += visible_len;   /* Assumed */
			
		    } else {
			
			add_ascii_to_string(buf4,s2us("..."));
			buf4_visible_len += 3;
		    }
		    if (s1)
			free_string(&s1);
		}
			
		free_string(&Sbase);

		
	    } else if (total > 0) {
		add_ascii_to_string(buf4,s2us("..."));
		buf4_visible_len += 3;
	    }
	    
	    /* And include address */
	    left = len - buf4_visible_len -4;
	    if (left > 4) {
		char *T;
		
		struct string * s0 = NULL;
		struct string * s2 = NULL;
		int X = 0;
		int visible_len;		    
		
		T = p->addr;
		if ((total=strlen(p->addr)) > left-3) {
		    int clip = total-left +6;
		    
		    if (clip > total)
			clip=total;
		    
		    T = p->addr + clip;
		}			
		
		s0 = format_string(FRM(" <%s%s>"),
				   T > p->addr ? "..." : "",
				   T);
		
		s2 = curses_printable_clip(s0,&X,len+total,&visible_len,
					   left);
		
		free_string(&s0);
		if (s2) {
		    append_string(&buf4,s2);
			    
		    buf4_visible_len += visible_len;   /* Assumed */
			    
		    free_string(&s2);
		}		    
	    }
	}

	filllen  = len - buf4_visible_len;
	if (filllen < 0)
	    filllen = 0;
	
	t = format_string(FRM("%S%*s %s"),			    
			  buf4,
			  filllen,"",
			  buf2);
	free_string(&buf4);
    } else if (len > 10) 
	t = format_string(FRM("(env) %-*.*s %s"),
			  len-7,
			  len-7,
			  hdr->env_from,
			  buf2);
    
    append_string(&res,t);
    free_string(&t);
    
    return res;
}


struct pager_page * init_pager_page(mptr) 
     struct menu_common *mptr;
{
    struct pager_page *p = safe_malloc(sizeof (*p));

    /* bzero is defined on hdrs/defs.h */
    bzero((void *)p, sizeof (*p));

    p->root   = new_menu_context();

    p->border_line  = NULL;
    p->prompt_area  = NULL;

    p->PARAM[0].t = mp_menu_common;
    p->PARAM[1].t = mp_END;

    mp_list_set_mcommon(p->PARAM,elm_mp_menu,mptr);

    return p;
}

void exit_pager_page(pager_page,other)
     struct pager_page  ** pager_page;
     struct menu_context * other;
{ 
    if ((*pager_page)->border_line)
	erase_menu_context( &((*pager_page)->border_line));

    if ((*pager_page)->prompt_area)
	erase_menu_context( &((*pager_page)->prompt_area));

    if ((*pager_page)->root)
	erase_menu_context( &((*pager_page)->root));

    menu_trigger_redraw(other);

    free(*pager_page);
    (*pager_page) = NULL;
}


#if ANSI_C
type_mismatch_prompt mime_signature_mismatch;
#endif

/* Return 1 to decode */
int mime_signature_mismatch(ptr, displaying)
     mime_t *ptr;
     int displaying;
{
    int ret = 0;
    struct menu_context  * page = new_menu_context();
    int update = 1;

    int LINES, COLUMNS;

    struct string * filename = NULL;
    
    int mp = give_dt_enumerate_as_int(&mime_parameters);
    
    CONST struct string *pv;
    CONST char * pva;
    
    if ( mp &&
	 (pv = get_mime_param(ptr->DISPOSITION_opts,"filename"))) {
	
	filename = dup_string(pv);
	
    } else if ( (pva = get_mime_param_compat(ptr->DISPOSITION_opts,
					     "filename"))) {
	
	filename = format_string(FRM("./%s"),pva);
	
    }

    menu_get_sizes(page, &LINES, &COLUMNS);   
    
    while (1) {
	int res;

	if (menu_resized(page)) {
	    menu_get_sizes(page, &LINES, &COLUMNS);  
	    
	    update = 1;
	}
	
	if (menu_need_redraw(page)) {
	    update = 1;
	}
	
	if (update) {
	    menu_ClearScreen(page);

	    menu_print_format_center(page,
				     1, CATGETS(elm_msg_cat, ElmSet, 
						ElmMiscMatchTitle,
						"Mime type check"));
					    
	    menu_PutLineX(page,4,0,
			  CATGETS(elm_msg_cat, ElmSet, 
				  ElmMiscMatchType,"Content-Type: %s/%s"),
			  get_major_type_name(ptr->TYPE), 
			  get_subtype_name(ptr->TYPE));

	    if (filename)
		menu_PutLineX(page,6,0,
			      CATGETS(elm_msg_cat, ElmSet, 
				      ElmMiscMatchFilename,
				      "Filename: %S"),
			      filename);

	    menu_PutLineX(page,9,0,
			  CATGETS(elm_msg_cat, ElmSet, 
				  ElmMiscMatchLine1,
				  "Content of mail part do not match to signature (magic number) of"));

	    menu_PutLineX(page,10,0,
			  CATGETS(elm_msg_cat, ElmSet, 
				  ElmMiscMatchLine2,
				  "Mime content-type. Content-type may be misleading and incorrect."));
	    menu_PutLineX(page,11,0,
			  CATGETS(elm_msg_cat, ElmSet, 
				  ElmMiscMatchLine3,
				  "Viewing it according of content-type may result to incorrect result."));


	    update = 0;
	}


	if (displaying)
	    res = prompt_letter(LINES-3,"",*def_ans_no,
				PROMPT_yesno|PROMPT_cancel|
				PROMPT_redraw_mark|PROMPT_ctrlL,
				page,
				CATGETS(elm_msg_cat, ElmSet, ElmMiscMatchAsk,
					"View %s/%s anyway? (%c/%c) "), 
				get_major_type_name(ptr->TYPE), 
				get_subtype_name(ptr->TYPE),
				*def_ans_yes, *def_ans_no);
	else
	    res = prompt_letter(LINES-3,"",*def_ans_no,
				PROMPT_yesno|PROMPT_cancel|
				PROMPT_redraw_mark|PROMPT_ctrlL,
				page,
				CATGETS(elm_msg_cat, ElmSet, ElmMiscMatchAsk2,
					"Process %s/%s anyway? (%c/%c) "), 
				get_major_type_name(ptr->TYPE), 
				get_subtype_name(ptr->TYPE),
				*def_ans_yes, *def_ans_no);

	    
	if (TERMCH_interrupt_char == res ||
	    EOF == res) {

	    ret = 0;
	    break;
	}

	if (res == ('L'&31) || res == REDRAW_MARK) {
	    menu_ClearScreen(page);   /* Clear possible redraw mark */
	    update = 1;
	}

	if (res == *def_ans_yes) {
	    ret = 1;
	    break;
	}

	if (res == *def_ans_no) {
	    ret = 0;
	    break;
	}
    }

    if (filename)
	free_string(&filename);
    
    erase_menu_context(&page);

    return ret;
}


int metapager (fp, hdr, do_headers, current, message_count, pager_page)
     FILE *fp;
     struct header_rec *hdr;
     int do_headers;
     int current;
     int message_count;
     struct pager_page *pager_page;
{

    int wait_ret, fork_ret, builtin = 0, status, len;
    int ch = 0;
    char buf[VERY_LONG_STRING];
    FILE                * fpout = NULL;   /* Buffer for external pager */
    struct stringbuffer * bout  = NULL;   /* Buffer for internal pager */
    out_state_t           buffer;         /* General handle for both   */
    
    int err;				/* place holder for errno */
    char tempfile[STRING];
    charset_t  charset_vector[255];
    int x1;
    int LINES, COLUMNS;
    char * exp_pager;
    char *tmp;

    menu_get_sizes(pager_page->root,&LINES, &COLUMNS);

    /* check to see if we want the internal pager */

    exp_pager = give_dt_estr_as_str(&pager_e,"pager");
    if (!exp_pager) {
	sleep_message(); 
	builtin++;

    } else if (strincmp(exp_pager, "builtin", 7) == 0 ||
	strincmp(exp_pager, "internal", 8) == 0 ||

	(builtin_lines < 0 
	 ? hdr->lines < LINES-1 + builtin_lines 
	 : hdr->lines < builtin_lines)) {
	
	builtin++;
    }
    
    tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
    if (!tmp)
	return 0;

    if (!builtin) {
	

	elm_sfprintf (tempfile, sizeof tempfile,
		      FRM("%selm.%d"), tmp, getpid());
	
	unlink(tempfile);
	fpout = safeopen_rdwr(tempfile);
	unlink(tempfile); /* We can now unlink tempfile ... So nobody
			   * sees data easily (and O_EXCL should prevent
			   * evil playings with symlinks) ...
			   */
	if (!fpout) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmPagerFailedTmpFile,
			      "Failed to create temporary file!"));
	    return 0;
	}
	
	out_state_clear(&buffer,STATE_out_file);
	set_out_state_file(fpout,&buffer);


	/* For external pager we use system charset for display */
	charset_vector[0] = system_charset; 
	charset_vector[1] = NULL; 
	buffer.display_charset = charset_vector; 
						  	
	DPRINT(Debug,9, (&Debug, 
			 "metapager(): using tempfile %s\n", tempfile));
    } else {
	if (hdr -> content_length > 8000) {
	    bout = create_filebuffer();
	    DPRINT(Debug,1,(&Debug,   
			    "metapager(): using stringbuffer (possibly file based)\n"));
	} else {
	    bout = create_membuffer();
	    DPRINT(Debug,1,(&Debug,   
			    "metapager(): using stringbuffer\n"));
	}
	out_state_clear(&buffer,STATE_out_buffer);

	/* Internal pager uses display_charset ... */
	buffer.display_charset = 
	    give_display_charsets(charset_vector,
				  sizeof charset_vector / 
				  sizeof (charset_vector[0]));

	set_out_state_sbuffer (bout,&buffer);	
    }
        
    if (fseek (fp, hdr->offset, SEEK_SET) == -1) {
	err = errno;
	DPRINT(Debug,1,(&Debug,   
			"Error: seek %d bytes into file, errno %s (show_message)\n",
			hdr->offset, error_description(err)));
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSeekFailedFile,
			  "ELM [seek] couldn't read %d bytes into file (%s)."),
		  hdr->offset, error_description(err));	
	
	out_state_destroy(&buffer);
	if (!builtin)
	    fclose(fpout);
	else
	    free_stringbuffer(&bout);
	
	return (0);
    }
    
    /* this is only true if metapager() is not called from ViewAttachment() */
    if (do_headers) {
	header_list_ptr all_hdrs = NULL, last_hdr = NULL;
	int in_envelope = 1;
	    
	if (title_messages && current > 0) {
	    int addempty = 0;

	    struct string *T = title_text(hdr,current,message_count,
					  COLUMNS,
					  buffer.display_charset[0]);

	    /* first print a title line */
	    int  inf = 0;


	    state_printf(&buffer,FRM("%S"),T);

	    free_string(&T);

	    /** Print the subject line centered if there is enough room **/
	    if (hdr->subject &&
		(len = string_len(hdr->subject)) > 0 && 
		matches_weedlist("subject:")) {
		len = (COLUMNS-len)/2;
		if (len < 0)
		    len = 0;

		state_printf(&buffer,
			     FRM("%*.*s%S"),
			     len,len,"",hdr->subject);
		addempty++;
	    } else
		len = 0;
      
	    state_putc('\n',&buffer);
		
      
	    /* now some information about this message */
      
	    if (hdr->status & EXPIRED) {
		this_message(&buffer,&inf);
		state_printf(&buffer,CATGETS(elm_msg_cat, ElmSet, 
					     ElmHasExpired,
					     "has EXPIRED"));
	    }
	    if (hdr->status & CONFIDENTIAL) {
		this_message(&buffer,&inf);
		state_printf(&buffer,CATGETS(elm_msg_cat, ElmSet, 
					     ElmTaggedConfidential,
					     "is tagged CONFIDENTIAL"));
	    }      
	    if (hdr->status & URGENT) {
		this_message(&buffer,&inf);
		state_printf(&buffer,CATGETS(elm_msg_cat, ElmSet, 
					     ElmTaggedUrgent,
					     "is tagged URGENT"));
	    }
	    if (hdr->status & PRIVATE_MAIL) {
		this_message(&buffer,&inf);
		state_printf(&buffer,CATGETS(elm_msg_cat, ElmSet, 
					     ElmTaggedPrivate,
					     "is tagged PRIVATE"));
	    }
	    if (inf) {
		state_puts(" **)\n", &buffer);
		addempty++;
	    }
	    
	    if (addempty)
		state_putc('\n',&buffer);
	}

	/** Now do the headers. **/



#if 0
	/* HACK:   Required by state_write_headers / state_write_header so that 8bit headers are
	           printed correctly
	*/

	for (x1 = 0; buffer.display_charset[x1]; x1++) {
	    if (hdr->header_charset == buffer.display_charset[x1])
		break;
	}
	if (buffer.display_charset[x1]) {
	    DPRINT(Debug,9, (&Debug, 
			     "metapager(): Using charset %s from headers as header filter charset\n",
			     buffer.display_charset[x1]->MIME_name ? 
			     buffer.display_charset[x1]->MIME_name :
			     "<no MIME name>"));
	    buffer.filter = buffer.display_charset[x1];    
	} else 
	    buffer.filter = buffer.display_charset[0];    
#endif


	/* Now state_write_header() changes itself buffer.filter according
	 *  of current header charset and buffer.display_charset
	 */


	/* read_header_line with flag = 1 (RHL_MARK_FOLDING)
	 * terminates header with \n and marks folding with \n
	 */
	while (0 < read_header_line (fp, buf, VERY_LONG_STRING,
				     RHL_MARK_FOLDING)) {

	    /* read_header_line with flag = 1 (RHL_MARK_FOLDING)
	     * returns \n on EOF
	     */
	    if (0 == strcmp(buf,"\n"))
		break;

	    if (in_envelope) {
#ifdef MMDF
		if (strcmp (buf, MSG_SEPARATOR) == 0)
		    continue;
#endif

		if (0 == strncmp(buf,"From ",5)) {
		    in_envelope = 2;
		} else if (2 == in_envelope && 
			   first_word_nc(buf, ">From")) {
		    /* OK */
		} else {
		    DPRINT(Debug,12,(&Debug,   
				     " .... Leaving envelope part: %s\n",
				     buf));
		    in_envelope = 0;
		    goto not_in_envelope;
		}
		
		if (elm_filter && matches_weedlist(buf))
		    continue;
		
		state_puts(buf, &buffer);

	    }
	    else { 
		char *ptr; 

	    not_in_envelope:
		ptr  = strchr(buf,':');
		if (ptr) {		
		    *ptr = '\0';
		    ptr++;
		    
		    /* Only skip first whitespace for display purposes ... */
		    if (whitespace(*ptr))
			ptr++;		    
		    update_header_list(&all_hdrs, &last_hdr, buf, ptr);
		} else {
		    DPRINT(Debug,12,(&Debug,   
				     " .... not a header: '%s' (update_header_list not called\n",
			    buf));
		}
	    }
	}

	state_write_headers(&buffer,all_hdrs,
			    rfc822_header_filter,
			    elm_filter,			    
			    !(hdr -> status & NOHDRENCODING),
			    hdr->header_charset
			    );
	
	buffer.filter = NULL;   /* End HACK */
	state_putc('\n',&buffer);
	
	delete_headers(&all_hdrs);


	if (hdr->list_info)
	    metapager_list_note(&buffer,hdr->list_info);
    }

    

    /* Now this does decoding of MIME and PGP --
       copy_body assumes that headers are already read
    */
    copy_body(fp,hdr,
	      "",&buffer,CM_REMOVE_HEADER|CM_DECODE|CM_DISPLAYING);
    
    out_state_destroy(&buffer);
    if (!builtin)
	rewind(fpout);
    clear_error();
    
    /** Now run the pager! **/
    
    if (builtin) {
	ch = builtinplusplus (bout, pager_page);
	free_stringbuffer(&bout);
	return (ch);
    }
    
    /** The rest of the code is for an external pager. **/

    { 
	struct menu_context *cpage;

	cpage = Raw(OFF); /* Raw(OFF) must do in parent.... 
			   * Or otherwise in Raw(ON) does not
			   * have effect (because Raw (ON /OFF) 
			   * does nothing if it thinks that mode is
			   * already correct)
			   */


	menu_ClearScreen(cpage);   /* Clear screen before calling external pager 
				      this needs to be come AFTER Raw(OFF) so
				      correct screen is cleared (after ti/te)
				   */
    
	if ((fork_ret = fork()) == -1) {
	    err = errno;
	    DPRINT(Debug,1,(&Debug,    
			    "Error: fork failed, errno %s (metapager)\n",
			    error_description(err)));
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmPreparePagerFork,
			      "Could not prepare for external pager(fork()-%s)."),
		      error_description(err));	
	    PressAnyKeyToContinue();
	    Raw (ON);
	
	    fclose(fpout);
	    return (0);

	} else if (fork_ret == 0) {
	    /* child fork */
	    
	    /* Direct our temporary file to standard input of child.
	     * Because we immediately unlinked it (for security reasons) 
	     * after creating we don't have name for it and
	     * we can't use < in system_call
	     */
	    
	    if (dup2 (fileno(fpout),0) == -1) {
		err = errno;
		DPRINT(Debug,1,(&Debug,   
				"Error: dup failed, errno %s (metapager)\n",
				error_description(err)));	
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmPreparePagerDup,
				  "Could not prepare for external pager(dup()-%s)."),
			  error_description(err));	
		_exit(err);	
	    }
	    
	    fclose(fpout);
	    clear_error();
	    menu_ClearScreen(cpage);
	
	    /* now execute pager and exit */
	    
	    /* system_call() will return user to user's normal permissions. */
	    _exit(system_call(exp_pager, SY_ENAB_SIGINT, NULL));
	}
	
	fclose (fpout);
	
	while (((wait_ret = wait (&status)) != fork_ret && wait_ret != -1)
	       /* Handle possible signals ... */	 
	       || (wait_ret == -1 && errno == EINTR))
	    ;
	
	/* turn raw on **after** child terminates in case child
	 * doesn't put us back to cooked mode after we return ourselves to
	 * raw. In that point we don't want output ti yet...
	 */
	cpage = Raw(ON | NO_TITE);
    
	if (prompt_after_pager) {
	    menu_StartXX(cpage,pg_BOLD);
	    menu_PutLineX(cpage,
			  LINES-1, 0, 
			  CATGETS(elm_msg_cat, ElmSet, 
				  ElmCommandIToReturn,
				  " Command ('i' to return to index): "));
	    menu_EndXX(cpage,pg_BOLD);
	    FlushBuffer();
	    ch = menu_ReadCh(cpage, 'i' | READCH_CURSOR);
	}
	else
	    ch = 0;
	
	/* This is necessary so that ti is outputted
	 * after te that was caused on Raw(OFF) before pager.
	 */
	Raw(OFF | NO_TITE);
	Raw(ON);
    }

    return (ch == 'i' || ch == 'q' ? 0 : ch);
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1