summaryrefslogtreecommitdiff
path: root/alpine
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2020-06-07 21:14:19 -0600
committerEduardo Chappa <chappa@washington.edu>2020-06-07 21:14:19 -0600
commitd1300c95499f3b422c2299432eb4acc93cf4618a (patch)
treef516d0630fbb085ac689c2010e07cbbe6c281ca5 /alpine
parent6c702a26f10f04bf225aa914b2eae5b89e3d0b4a (diff)
downloadalpine-d1300c95499f3b422c2299432eb4acc93cf4618a.tar.xz
* Experimental: Alpine can pass an HTML message to an external web browser, by using
the "External" command in the ATTACHMENT INDEX screen. * Experimental: New configuration variable "External Command Loads Inline Images Only" that controls if Alpine will pass to an external browser a link to all the images in the HTML message, or will only pass a link to inline images included in the message. For your privacy and security this feature is enabled by default.
Diffstat (limited to 'alpine')
-rw-r--r--alpine/alpine.c1
-rw-r--r--alpine/confscroll.c1
-rw-r--r--alpine/keymenu.c2
-rw-r--r--alpine/keymenu.h1
-rw-r--r--alpine/mailpart.c162
-rw-r--r--alpine/mailpart.h2
-rw-r--r--alpine/mailview.c65
7 files changed, 219 insertions, 15 deletions
diff --git a/alpine/alpine.c b/alpine/alpine.c
index 98814787..d6e5e44e 100644
--- a/alpine/alpine.c
+++ b/alpine/alpine.c
@@ -3393,6 +3393,7 @@ goodnight_gracey(struct pine *pine_state, int exit_val)
free_saved_query_parameters();
#endif
+ html_dir_clean(1); /* force remove of remaining files */
free_pine_struct(&pine_state);
free_histlist();
diff --git a/alpine/confscroll.c b/alpine/confscroll.c
index 44c02ca5..ee182b21 100644
--- a/alpine/confscroll.c
+++ b/alpine/confscroll.c
@@ -330,6 +330,7 @@ exclude_config_var(struct pine *ps, struct variable *var, int allow_hard_to_conf
return(!(var->is_user && var->is_used && !var->is_obsolete));
switch(var - ps->vars){
+ case V_HTML_DIRECTORY :
case V_MAIL_DIRECTORY :
case V_INCOMING_FOLDERS :
case V_FOLDER_SPEC :
diff --git a/alpine/keymenu.c b/alpine/keymenu.c
index 529d79f6..65ba6f88 100644
--- a/alpine/keymenu.c
+++ b/alpine/keymenu.c
@@ -787,7 +787,7 @@ struct key att_index_keys[] =
HELP_MENU,
OTHER_MENU,
HDRMODE_MENU,
- NULL_MENU,
+ {"X",N_("eXternal"),{MC_EXTERNAL,1,{'x'}},KS_NONE},
NULL_MENU,
NULL_MENU,
NULL_MENU,
diff --git a/alpine/keymenu.h b/alpine/keymenu.h
index 21315e6a..e29ec201 100644
--- a/alpine/keymenu.h
+++ b/alpine/keymenu.h
@@ -216,6 +216,7 @@ struct key_menu {
#define MC_QUOTA 803
#define MC_ADDHEADER 804
#define MC_XOAUTH2 805
+#define MC_EXTERNAL 806
/* Commands for S/MIME screens */
diff --git a/alpine/mailpart.c b/alpine/mailpart.c
index df1de5d6..0e742fc7 100644
--- a/alpine/mailpart.c
+++ b/alpine/mailpart.c
@@ -69,6 +69,7 @@ static char rcsid[] = "$Id: mailpart.c 1074 2008-06-04 00:08:43Z hubert@u.washin
#include "../pith/smime.h"
#include "../pith/ical.h"
#include "../pith/body.h"
+#include "../pith/init.h"
/*
* Information used to paint and maintain a line on the attachment
@@ -146,6 +147,7 @@ int format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
void display_vcard_att(long, ATTACH_S *, int);
void display_vcalendar_att(long, ATTACH_S *, int);
void display_attach_info(long, ATTACH_S *);
+int display_html_external_attachment(long int, ATTACH_S *, int);
void forward_attachment(MAILSTREAM *, long, ATTACH_S *);
void forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
void reply_msg_att(MAILSTREAM *, long, ATTACH_S *);
@@ -540,6 +542,11 @@ attachment_screen(struct pine *ps)
break;
+ case MC_EXTERNAL:
+ display_html_external_attachment(msgno, current->attp,
+ DA_EXTERNAL | DA_SAVE | (F_OFF(F_EXTERNAL_INLINE_IMAGES, ps_global) ? DA_ALLIMAGES : 0));
+ break;
+
case MC_EXIT : /* exit attachment screen */
ps->next_screen = mail_view_screen;
break;
@@ -1989,6 +1996,158 @@ print_digest_att(long int msgno, ATTACH_S *a)
}
}
+int
+display_html_external_attachment(long int msgno, ATTACH_S *a, int flags)
+{
+ char dir_path[MAXPATH+1];
+ char *filename = NULL;
+ char *file_path; /* file:///some/path/ */
+ STORE_S *store;
+ gf_io_t pc;
+ char *err;
+ int we_cancel = 0, saved, errs;
+ char *tool;
+ ATTACH_S *att;
+ unsigned long rawno;
+
+ if(a->body == NULL){
+ q_status_message(SM_ORDER | SM_DING, 3, 5, _("Attachment has no body!"));
+ return 1;
+ } else if (a->body->type != TYPETEXT
+ || a->body->subtype == NULL
+ || strucmp(a->body->subtype, "HTML")){
+ q_status_message(SM_ORDER | SM_DING, 3, 5, _("Not a TEXT/HTML attachment"));
+ return 1;
+ }
+
+ /* zero these variables, just in case. Do not try freeing them. They have short lives */
+ for(att = ps_global->atmts; att->description != NULL; att++){
+ att->cid_tmpfile = NULL;
+ att->tmpdir = NULL;
+ }
+
+ /* setup the environment first */
+ if(!ps_global->html_dir){
+ if(!html_directory_path(ps_global->VAR_HTML_DIRECTORY, dir_path, MAXPATH)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error creating full path for %s"), ps_global->VAR_HTML_DIRECTORY);
+ return 1;
+ } else if (init_html_directory(dir_path) < 0){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error initializing %s"), dir_path);
+ return 1;
+ }
+ ps_global->html_dir = cpystr(dir_path);
+ }
+ else{
+ strncpy(dir_path, ps_global->html_dir, sizeof(dir_path));
+ dir_path[sizeof(dir_path)-1] = '\0';
+ }
+
+ if(create_random_dir(dir_path, sizeof(dir_path)) < 0){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error creating temp dir in %s"), dir_path);
+ return 1;
+ }
+
+ a->tmpdir = cpystr(dir_path);
+ add_html_log(&ps_global->html_dir_list, a->tmpdir);
+
+ /* Process the text/html part */
+ filename = temp_nam_ext(a->tmpdir, "tmp-html-", HTML_EXT);
+
+ if(!filename){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error \"%s\", Can't create temporary file"),
+ error_description(errno));
+ return(1);
+ }
+
+ if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error \"%s\", Can't write file %s"),
+ error_description(errno), filename);
+ if(filename){
+ our_unlink(filename);
+ fs_give((void **)&filename);
+ }
+ return(1);
+ }
+
+ if(a->body->size.bytes){
+ char msg_buf[128];
+
+ snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
+ a->description ? "\"" : "",
+ a->description ? a->description : "attachment number ",
+ a->description ? "" : a->number,
+ a->description ? "\"" : "");
+ msg_buf[sizeof(msg_buf)-1] = '\0';
+ we_cancel = init_att_progress(msg_buf, ps_global->mail_stream, a->body);
+ }
+
+ gf_set_so_writec(&pc, store);
+
+ err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL,
+ DT_EXTERNAL | ((flags & DA_ALLIMAGES) ? DT_ALLIMAGES : 0));
+
+ gf_clear_so_writec(store);
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ so_give(&store);
+
+ /*----- Download all needed inline attachments ------*/
+ saved = errs = 0;
+ rawno = mn_m2raw(ps_global->msgmap, msgno);
+ for (att = ps_global->atmts; rawno > 0 && att->description != NULL; att++){
+ if(att->cid_tmpfile){
+ if(write_attachment_to_file(ps_global->mail_stream, rawno,
+ att, GER_NONE, att->cid_tmpfile) == 1)
+ saved++;
+ else
+ errs++;
+ fs_give((void **) &att->cid_tmpfile);
+ }
+ if(att->tmpdir)
+ fs_give((void **) &att->tmpdir);
+ }
+
+ if(err){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ "%s: Error saving image to temp file %s",
+ err, filename);
+ if(filename){
+ our_unlink(filename);
+ fs_give((void **)&filename);
+ }
+ return(1);
+ }
+
+ tool = get_url_external_handler("http://", 1);
+ if(tool == NULL) tool = get_url_external_handler("http://", 0);
+ if(tool == NULL) tool = get_url_external_handler("https://", 1);
+ if(tool == NULL) tool = get_url_external_handler("https://", 0);
+
+ file_path = fs_get((strlen(filename) + strlen("file://") + 1)*sizeof(char));
+ sprintf(file_path, "file://%s", filename);
+
+ /*----- Run the viewer process ----*/
+ if(do_url_launch(tool, file_path) == 0)
+ q_status_message(SM_ORDER, 3, 3, "Opened message in external browser");
+ else
+ q_status_message(SM_ORDER|SM_DING, 3, 5, "Failed to open message in external browser");
+
+ if(filename)
+ fs_give((void **)&filename);
+
+ if(file_path)
+ fs_give((void **)&file_path);
+
+ ps_global->mangled_screen = 1;
+
+ return(0);
+}
+
/*----------------------------------------------------------------------
Unpack and display the given attachment associated with given message no.
@@ -2012,6 +2171,9 @@ display_attachment(long int msgno, ATTACH_S *a, int flags)
char ext[32];
char mtype[128];
+ if(flags & DA_EXTERNAL)
+ return display_html_external_attachment(msgno, a, flags);
+
/*------- Display the attachment -------*/
if(dispatch_attachment(a) == MCD_NONE){
/*----- Can't display this type ------*/
diff --git a/alpine/mailpart.h b/alpine/mailpart.h
index 6f1c0023..ecc1c59e 100644
--- a/alpine/mailpart.h
+++ b/alpine/mailpart.h
@@ -27,6 +27,8 @@
#define DA_FROM_VIEW 0x02 /* see mailpart.c */
#define DA_RESIZE 0x04
#define DA_DIDPROMPT 0x08 /* Already prompted to view att */
+#define DA_EXTERNAL 0x10 /* use external viewer operations */
+#define DA_ALLIMAGES 0x20 /* external browser displays all images */
/* exported prototypes */
diff --git a/alpine/mailview.c b/alpine/mailview.c
index eec5805e..dbc009ed 100644
--- a/alpine/mailview.c
+++ b/alpine/mailview.c
@@ -720,15 +720,15 @@ int
scroll_handle_prompt(HANDLE_S *handle, int force)
{
char prompt[256], tmp[MAILTMPLEN];
- int rc, flags, local_h;
+ int rc, flags, local_h, external, images;
static ESCKEY_S launch_opts[] = {
/* TRANSLATORS: command names, editURL means user gets to edit a URL if they
want, editApp is edit application where they edit the application used to
view a URL */
{'y', 'y', "Y", N_("Yes")},
{'n', 'n', "N", N_("No")},
- {-2, 0, NULL, NULL},
- {-2, 0, NULL, NULL},
+ {0, 'x', "X", ""},
+ {0, 'i', "I", ""},
{0, 'u', "U", N_("editURL")},
{0, 'a', "A", N_("editApp")},
{-1, 0, NULL, NULL}};
@@ -833,6 +833,25 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
else
launch_opts[4].ch = -1;
+ if(handle->type == Attach
+ && handle->h.attach
+ && handle->h.attach->body
+ && handle->h.attach->body->type == TYPETEXT
+ && !strucmp(handle->h.attach->body->subtype, "HTML")){
+ images = F_OFF(F_EXTERNAL_INLINE_IMAGES, ps_global) ? 1 : 0;
+ external = 0; /* default to not using external viewer, set to 1 to make it default */
+ force = 0; /* do not open automatically */
+ launch_opts[2].ch = 'x';
+ launch_opts[2].label = external > 0 ? N_("No eXternal") : N_("External");
+ launch_opts[3].ch = external > 0 ? 'i' : -2;
+ launch_opts[3].label = images ? N_("Inline imgs") : N_("All images");
+ }
+ else {
+ launch_opts[2].ch = -2; /* skip */
+ launch_opts[3].ch = -2; /* skip */
+ external = images = -1;
+ }
+
if(force
|| (handle->type == URL
&& (!struncmp(handle->h.url.path, "x-alpine-", 9)
@@ -852,13 +871,15 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
(int) MIN(MAX(0,sc - 25), sizeof(prompt)-50), handle->h.url.path+7,
(strlen(handle->h.url.path+7) > MAX(0,sc-25)) ? "..." : "");
else
- snprintf(prompt, sizeof(prompt), "View selected %s %s%.*s%s ? ",
+ snprintf(prompt, sizeof(prompt), "View selected %s %s%s%s%.*s%s ? ",
(handle->type == URL) ? "URL" : "Attachment",
+ external > 0 ? "using external viewer " : "",
+ external > 0 ? (images > 0 ? "including all images" : "including inline images only") : "",
(handle->type == URL) ? "\"" : "",
- (int) MIN(MAX(0,sc-27), sizeof(prompt)-50),
+ (int) MIN(MAX(0,sc-27-(external ? (images ? 41 : 50) : 0)), sizeof(prompt)-50),
(handle->type == URL) ? handle->h.url.path : "",
(handle->type == URL)
- ? ((strlen(handle->h.url.path) > MAX(0,sc-27))
+ ? ((strlen(handle->h.url.path) > MAX(0,sc-27 - (external ? (images > 0 ? 41 : 50) : 0)))
? "...\"" : "\"") : "");
prompt[sizeof(prompt)-1] = '\0';
@@ -866,7 +887,20 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
switch(radio_buttons(prompt, -FOOTER_ROWS(ps_global),
launch_opts, 'y', 'n', NO_HELP, RB_SEQ_SENSITIVE)){
case 'y' :
- return(1);
+ return(external > 0 ? (images > 0 ? -2 : -1) : 1);
+
+ case 'x' :
+ external = 1 - external;
+ images = F_OFF(F_EXTERNAL_INLINE_IMAGES, ps_global) ? 1 : 0;
+ launch_opts[2].label = external > 0 ? N_("No eXternal") : N_("External");
+ launch_opts[3].ch = external > 0 ? 'i' : -2;
+ launch_opts[3].label = images ? N_("Inline imgs") : N_("All images");
+ break;
+
+ case 'i' :
+ images = 1 - images;
+ launch_opts[3].label = images ? N_("Inline imgs") : N_("All images");
+ break;
case 'u' :
strncpy(tmp, handle->h.url.path, sizeof(tmp)-1);
@@ -950,6 +984,7 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
int
scroll_handle_launch(HANDLE_S *handle, int force)
{
+ int flags;
switch(handle->type){
case URL :
if(handle->h.url.path){
@@ -965,13 +1000,15 @@ scroll_handle_launch(HANDLE_S *handle, int force)
break;
case Attach :
- if(scroll_handle_prompt(handle, force))
- display_attachment(mn_m2raw(ps_global->msgmap,
- mn_get_cur(ps_global->msgmap)),
- handle->h.attach, DA_FROM_VIEW | DA_DIDPROMPT);
- else
- return(-1);
-
+ flags = DA_FROM_VIEW | DA_DIDPROMPT;
+ switch(scroll_handle_prompt(handle, force)){
+ case 1 : break;
+ case -2 : flags |= DA_ALLIMAGES;
+ case -1 : flags |= DA_EXTERNAL; break;
+ default : return -1;
+ }
+ display_attachment(mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)), handle->h.attach, flags);
break;
case Folder :