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