summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2021-10-03 21:12:56 -0600
committerEduardo Chappa <chappa@washington.edu>2021-10-03 21:12:56 -0600
commit76da583d4aa4600148048c4cb025d9248d41326c (patch)
treeb8e2661db937ac7424a02644bbf6c95ffc477345
parentda6cdd6b8083201c66349133693a173704f5d4df (diff)
downloadalpine-76da583d4aa4600148048c4cb025d9248d41326c.tar.xz
* Alpine can display (with the help of an image viewer) images embedded in the
html part of a message and will pass these images to a browser when using the external command.
-rw-r--r--alpine/mailview.c180
-rw-r--r--alpine/mailview.h1
-rw-r--r--pith/filter.c119
-rw-r--r--pith/handle.c11
-rw-r--r--pith/handle.h7
-rw-r--r--pith/pine.hlp13
6 files changed, 285 insertions, 46 deletions
diff --git a/alpine/mailview.c b/alpine/mailview.c
index 926f58b2..770a122e 100644
--- a/alpine/mailview.c
+++ b/alpine/mailview.c
@@ -167,6 +167,8 @@ int scroll_handle_column(int, int);
int scroll_handle_index(int, int);
void scroll_handle_set_loc(POSLIST_S **, int, int);
int dot_on_handle(long, int);
+int imgdata_open(HANDLE_S *);
+char *img_handler(HANDLE_S *);
int url_launch(HANDLE_S *);
int url_launch_too_long(int);
char *url_external_handler(HANDLE_S *, int);
@@ -833,6 +835,53 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
else
launch_opts[6].ch = -1;
+ if(handle->type == imgData){
+ if(!handle->h.img.tool
+ && !(handle->h.img.tool = img_handler(handle))){
+ /* TRANSLATORS: a question */
+ if(want_to(_("No Image-Viewer application defined. Define now"),
+ 'y', 0, NO_HELP, WT_SEQ_SENSITIVE) == 'y'){
+ /* Prompt for the displayer? */
+ tmp[0] = '\0';
+ while(1){
+ flags = OE_APPEND_CURRENT |
+ OE_SEQ_SENSITIVE |
+ OE_KEEP_TRAILING_SPACE;
+
+ rc = optionally_enter(tmp, -FOOTER_ROWS(ps_global), 0,
+ sizeof(tmp),
+ _("Image Viewer: "),
+ NULL, NO_HELP, &flags);
+ if(rc == 0){
+ if((flags & OE_USER_MODIFIED) && *tmp){
+ if(can_access(tmp, EXECUTE_ACCESS) == 0){
+ set_variable(V_IMAGE_VIEWER, tmp, TRUE, TRUE, Main);
+ handle->h.img.tool = cpystr(tmp);
+ mailcap_free(); /* redo the mailcap list */
+ break;
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 2, 2,
+ _("Image Viewer Not Found: %s"),
+ error_description(errno));
+ continue;
+ }
+ }
+ else
+ return(0);
+ }
+ else if(rc == 1 || rc == -1){
+ return(0);
+ }
+ else if(rc == 4){
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+ }
+ }
+ }
+ }
+ }
+
if(handle->type == Attach
&& handle->h.attach
&& handle->h.attach->body
@@ -889,7 +938,7 @@ scroll_handle_prompt(HANDLE_S *handle, int force)
}
else
snprintf(prompt, sizeof(prompt), "View selected %s %s%s%s%.*s%s ? ",
- (handle->type == URL) ? "URL" : "Attachment",
+ (handle->type == URL) ? "URL" : ((handle->type == imgData) ? "Image" : "Attachment"),
external > 0 ? "using external viewer " : "",
external > 0 ? (images > 0 ? "including all images" : "including inline images only") : "",
(handle->type == URL) ? "\"" : "",
@@ -1061,6 +1110,18 @@ scroll_handle_launch(HANDLE_S *handle, int force)
DA_FROM_VIEW | DA_DIDPROMPT, handle->h.ical.depth);
break;
+ case imgData:
+ if(handle->h.img.src){
+ if(scroll_handle_prompt(handle, force)){
+ if(imgdata_open(handle)
+ || ps_global->next_screen != SCREEN_FUN_NULL)
+ return(1); /* done with this screen */
+ }
+ else
+ return(-1);
+ }
+ break;
+
case Function :
(*handle->h.func.f)(handle->h.func.args.stream,
handle->h.func.args.msgmap,
@@ -1552,6 +1613,92 @@ do_url_launch(char *toolp, char *url)
return(rv);
}
+int
+imgdata_open(HANDLE_S *handle)
+{
+ return do_imgdata_open(handle->h.img.tool, handle->h.img.src);
+}
+
+int
+do_imgdata_open(char *toolp, char *data)
+{
+ gf_io_t pc, writec, readc;
+ STORE_S *so, *img;
+ char *tmpfile = NULL, *err = NULL, *imgdata, *encoding;
+
+ if(!toolp) return 1;
+
+ encoding = strchr(data, ';');
+ if(encoding) encoding++;
+ imgdata = strchr(data, ',');
+ if(imgdata) imgdata++;
+ tmpfile = temp_nam(NULL, "img-data-"); /* create temporary file */
+
+ img = so_get(CharStar, NULL, EDIT_ACCESS); /* allocate a pointer to save data */
+ so_seek(img, 0L, 0); /* rewind img to start */
+ gf_set_so_writec(&writec, img); /* set method to write to img in writec */
+ gf_set_so_readc(&readc, img); /* set method to read from img in readc */
+
+ if(imgdata
+ && encoding
+ && imgdata
+ && img
+ && gf_puts(imgdata, writec) /* write imgdata to img using writec */
+ && (so = so_get(FileStar, tmpfile, WRITE_ACCESS|OWNER_ONLY)) != NULL){ /* open temporary file */
+
+ so_seek(img, 0L, 0); /* rewind img to start */
+ so_seek(so, 0L, 0); /* rewind so to start */
+ gf_set_so_writec(&pc, so); /* set method to write to so in pc */
+ gf_filter_init(); /* start a filter */
+ if(!struncmp(encoding, "BASE64", 6)) /* link base64 filter */
+ gf_link_filter(gf_b64_binary, NULL);
+
+ err = gf_pipe(readc, pc); /* pass data from imgdata to so, reading from readc and writing with pc */
+
+ gf_clear_so_writec(so); /* disassociate so and pc */
+
+ if(so_give(&so)) /* write tmp to disk and free so */
+ err = "Error writing image to file";
+
+ gf_clear_so_writec(img); /* disassociate img and writec */
+ gf_clear_so_readc(img); /* disassociate img and readc */
+ so_give(&img); /* free img */
+ }
+ else err = "Error creating space for temporary image";
+
+ /* toolp tells us that there is a program to execute, which is
+ * either the image viewer or the mailcap. No matter which, the
+ * information is now in the mailcap structure, so let's use it
+ */
+ if(!err){
+ char *subtype = NULL;
+ if(!struncmp(data, "IMAGE/", 6)){
+ data += 6;
+ for(subtype = data; data && *data != ';'; data++);
+ if(data){
+ *data = '\0';
+ if(mailcap_can_display(TYPEIMAGE, subtype, NULL, 0)){
+ MCAP_CMD_S *mc_cmd;
+
+ mc_cmd = mailcap_build_command(TYPEIMAGE, subtype,
+ NULL, tmpfile, NULL, 0);
+ exec_mailcap_cmd(mc_cmd, tmpfile, 0);
+ }
+ *data = ';';
+ data = subtype - 6;
+ }
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 2, 2,
+ _("\"Image-Viewer\" not defined: Can't open image"));
+
+ if(tmpfile) /* file was deleted by exec_mailcap_cmd */
+ fs_give((void **) &tmpfile);
+
+ return 0;
+}
+
int
url_launch_too_long(int return_value)
@@ -1676,6 +1823,37 @@ get_url_external_handler(char *url, int specific)
return(cmd);
}
+char *
+img_handler(HANDLE_S *handle)
+{
+ char *src = handle && handle->h.img.src ? handle->h.img.src : NULL;
+ char *cmd, *subtype;
+
+ if(!src) return NULL;
+
+ if(ps_global->VAR_IMAGE_VIEWER)
+ cmd = cpystr(ps_global->VAR_IMAGE_VIEWER);
+ else if(!struncmp(src, "IMAGE/", 6)){
+ src += 6;
+ for(subtype = src; src && *src != ';'; src++);
+ if(src){
+ *src = '\0';
+ if(mailcap_can_display(TYPEIMAGE, subtype, NULL, 0)){
+ MCAP_CMD_S *mc_cmd;
+
+ mc_cmd = mailcap_build_command(TYPEIMAGE, subtype,
+ NULL, "_IMG_", NULL, 0);
+ if(mc_cmd){
+ cmd = mc_cmd->command;
+ fs_give((void **)&mc_cmd);
+ }
+ }
+ *src = ';';
+ }
+ }
+
+ return cmd;
+}
url_tool_t
url_local_handler(char *s)
diff --git a/alpine/mailview.h b/alpine/mailview.h
index f981a88d..e31667b3 100644
--- a/alpine/mailview.h
+++ b/alpine/mailview.h
@@ -117,6 +117,7 @@ typedef struct scrolltool_s {
/* exported prototypes */
char *get_url_external_handler(char *, int);
int do_url_launch(char *, char *);
+int do_imgdata_open(char *, char *);
void mail_view_screen(struct pine *);
url_tool_t url_local_handler(char *);
int url_local_mailto(char *);
diff --git a/pith/filter.c b/pith/filter.c
index 09f3be3c..8900b51d 100644
--- a/pith/filter.c
+++ b/pith/filter.c
@@ -2844,7 +2844,8 @@ typedef struct _center_s {
* Collector data and state information
*/
typedef struct collector_s {
- char buf[HTML_BUF_LEN]; /* buffer to collect data */
+ char *buf; /* buffer to collect data */
+ unsigned long bufsize; /* size of buffer to collect data */
int len; /* length of that buffer */
unsigned unquoted_data:1; /* parameter is not quoted... */
unsigned end_tag:1; /* collecting a closing tag */
@@ -2952,6 +2953,7 @@ typedef struct _html_opts {
#define NEW_CLCTR(X) { \
ED(X) = (CLCTR_S *)fs_get(sizeof(CLCTR_S)); \
memset(ED(X), 0, sizeof(CLCTR_S)); \
+ ED(X)->buf = memset((void *) fs_get(ED(X)->bufsize = HTML_BUF_LEN), 0, HTML_BUF_LEN); \
HD(X)->token = html_element_collector; \
}
@@ -3224,7 +3226,7 @@ int html_code(HANDLER_S *, int, int);
int html_ins(HANDLER_S *, int, int);
int html_del(HANDLER_S *, int, int);
int html_abbr(HANDLER_S *, int, int);
-char *cid_tempfile_name(char *, long, int *);
+char *img_tempfile_name(char *, long, int *);
/*
* Protos for RSS 2.0 Tag handlers
@@ -4472,30 +4474,50 @@ html_img(HANDLER_S *hd, int ch, int cmd)
* if we ever decide web bugs aren't a problem
* anymore then we might expand the scope
*/
- if(src
- && DO_HANDLES(hd->html_data)
- && RELATED_OK(hd->html_data)
- && struncmp(src, "cid:", 4) == 0){
- char buf[32];
- int i, n;
- HANDLE_S *h = new_handle(HANDLESP(hd->html_data));
-
- h->type = IMG;
- h->h.img.src = cpystr(src + 4);
- h->h.img.alt = cpystr((alt) ? alt : "Attached Image");
-
- HTML_TEXT(hd->html_data, TAG_EMBED);
- HTML_TEXT(hd->html_data, TAG_HANDLE);
-
- sprintf(buf, "%d", h->key);
- n = strlen(buf);
- HTML_TEXT(hd->html_data, n);
- for(i = 0; i < n; i++){
+ if(src && DO_HANDLES(hd->html_data)){
+ if(RELATED_OK(hd->html_data)
+ && struncmp(src, "cid:", 4) == 0){
+ char buf[32];
+ int i, n;
+ HANDLE_S *h = new_handle(HANDLESP(hd->html_data));
+
+ h->type = IMG;
+ h->h.img.src = cpystr(src + 4);
+ h->h.img.alt = cpystr((alt) ? alt : "Attached Image");
+
+ HTML_TEXT(hd->html_data, TAG_EMBED);
+ HTML_TEXT(hd->html_data, TAG_HANDLE);
+
+ sprintf(buf, "%d", h->key);
+ n = strlen(buf);
+ HTML_TEXT(hd->html_data, n);
+ for(i = 0; i < n; i++){
unsigned int uic = buf[i];
HTML_TEXT(hd->html_data, uic);
- }
+ }
- return(0);
+ return(0);
+ }
+ else if(struncmp(src, "data:", 5) == 0){
+ char buf[32];
+ int i, n;
+ HANDLE_S *h = new_handle(HANDLESP(hd->html_data));
+
+ h->type = imgData;
+ h->h.img.src = cpystr(src + 5);
+ h->h.img.alt = cpystr((alt) ? alt : "Embedded Image");
+
+ HTML_TEXT(hd->html_data, TAG_EMBED);
+ HTML_TEXT(hd->html_data, TAG_HANDLE);
+
+ sprintf(buf, "%d", h->key);
+ n = strlen(buf);
+ HTML_TEXT(hd->html_data, n);
+ for(i = 0; i < n; i++){
+ unsigned int uic = buf[i];
+ HTML_TEXT(hd->html_data, uic);
+ }
+ }
}
else if(alt && strlen(alt) < 256){ /* arbitrary "reasonable" limit */
HTML_DUMP_LIT(hd->html_data, alt, strlen(alt));
@@ -7471,8 +7493,15 @@ html_element_collector(FILTER_S *fd, int ch)
|| ED(fd)->unquoted_data
|| isalnum(ch)
|| strchr("#-.!", ch)){
+ if(ED(fd)->len >= ((ED(fd)->element || !ED(fd)->hit_equal)
+ ? ED(fd)->bufsize:MAX_ELEMENT)){
+ unsigned long i, bufsize = ED(fd)->bufsize;
+ ED(fd)->bufsize = ED(fd)->len + HTML_BUF_LEN;
+ fs_resize((void **) &ED(fd)->buf, ED(fd)->bufsize);
+ memset(&ED(fd)->buf[bufsize], '\0', ED(fd)->bufsize - bufsize);
+ }
if(ED(fd)->len < ((ED(fd)->element || !ED(fd)->hit_equal)
- ? HTML_BUF_LEN:MAX_ELEMENT)){
+ ? ED(fd)->bufsize:MAX_ELEMENT)){
ED(fd)->buf[(ED(fd)->len)++] = ch;
}
else
@@ -7494,8 +7523,15 @@ html_element_collector(FILTER_S *fd, int ch)
if(!ep->alternate)
ED(fd)->badform = 1;
else{
+ if(ED(fd)->len >= ((ED(fd)->element || !ED(fd)->hit_equal)
+ ? ED(fd)->bufsize:MAX_ELEMENT)){
+ unsigned long bufsize = ED(fd)->bufsize;
+ ED(fd)->bufsize = ED(fd)->len + HTML_BUF_LEN;
+ fs_resize((void **) &ED(fd)->buf, ED(fd)->bufsize);
+ memset(&ED(fd)->buf[bufsize], '\0', ED(fd)->bufsize - bufsize);
+ }
if(ED(fd)->len < ((ED(fd)->element || !ED(fd)->hit_equal)
- ? HTML_BUF_LEN:MAX_ELEMENT)){
+ ? ED(fd)->bufsize:MAX_ELEMENT)){
ED(fd)->buf[(ED(fd)->len)++] = ch; /* add this exception */
}
else
@@ -7563,7 +7599,8 @@ html_element_flush(CLCTR_S *el_data)
el_data->was_quoted = 0; /* reset collector buf and state */
el_data->len = 0;
- memset(el_data->buf, 0, HTML_BUF_LEN);
+ fs_give((void **) &el_data->buf);
+ el_data->bufsize = 0;
return(rv); /* report whatever happened above */
}
@@ -9050,8 +9087,12 @@ gf_html2plain_rss_free_items(RSS_ITEM_S **itemp)
}
}
+#define CID_NONE 0x00
+#define CID_DATA 0x01
+#define IMG_DATA 0x10
+
char *
-cid_tempfile_name(char *line, long n, int *is_cidp)
+img_tempfile_name(char *line, long n, int *flagp)
{
int f2 = 0;
int i, found;
@@ -9062,13 +9103,15 @@ cid_tempfile_name(char *line, long n, int *is_cidp)
c = line[n];
line[n] = '\0';
s = NULL;
- *is_cidp = 0;
+ *flagp = CID_NONE;
if(n > 0){
if (line[0] == '\"')
f2 = 1;
- if (n - f2 > 3){
+ if (n - f2 > 4 && !struncmp(line+f2, "data:", 5))
+ *flagp = IMG_DATA;
+ else if (n - f2 > 3){
if (!struncmp(line+f2, "cid:", 4)){
- *is_cidp = 1;
+ *flagp = CID_DATA;
f2 += 4;
s = fs_get((n - f2 + 4)*sizeof(char));
sprintf(s, "<%s", line+f2);
@@ -9167,18 +9210,18 @@ gf_html_cid2file(FILTER_S *f, int cmd)
}
else if (state == 2){ /* collect all data */
if(ASCII_ISSPACE(c) || c == '>'){
- int is_cid;
+ int flag;
if(f->n > 0){
- char *s = cid_tempfile_name(f->line, f->n, &is_cid);
- if(is_cid){
+ char *s = img_tempfile_name(f->line, f->n, &flag);
+ if(flag & CID_DATA){
RESET_FILTER(f);
if(s != NULL)
for(; *s != '\0'; s++)
COLLECT(f, *s);
- }
- }
- GF_PUTC(f->next, '\"');
- if(is_cid || f->t){
+ }
+ }
+ GF_PUTC(f->next, '\"');
+ if((flag & (CID_DATA | IMG_DATA)) || f->t){
for(p = f->line; f->n; f->n--, p++){
if(*p == '\"') continue;
GF_PUTC(f->next, *p);
@@ -9203,7 +9246,7 @@ gf_html_cid2file(FILTER_S *f, int cmd)
}
else if(cmd == GF_EOD){
if(f->f1 == 2){
- char *s = cid_tempfile_name(f->line, f->n, &f->f2);
+ char *s = img_tempfile_name(f->line, f->n, &f->f2);
GF_PUTC(f->next, '\"');
if (f->f2 || f->t){
for(p = s; *p; p++){
diff --git a/pith/handle.c b/pith/handle.c
index 01b9b4d8..9dc8cd59 100644
--- a/pith/handle.c
+++ b/pith/handle.c
@@ -129,6 +129,17 @@ free_handle(HANDLE_S **h)
fs_give((void **) &(*h)->h.url.name);
}
+ if((*h)->type == imgData){ /* destroy malloc'd data */
+ if((*h)->h.img.src)
+ fs_give((void **) &(*h)->h.img.src);
+
+ if((*h)->h.img.alt)
+ fs_give((void **) &(*h)->h.img.alt);
+
+ if((*h)->h.url.tool)
+ fs_give((void **) &(*h)->h.url.tool);
+ }
+
free_handle_locations(&(*h)->loc);
fs_give((void **) h);
diff --git a/pith/handle.h b/pith/handle.h
index eae39da2..b36b41ec 100644
--- a/pith/handle.h
+++ b/pith/handle.h
@@ -35,7 +35,7 @@ typedef struct screen_position_list {
*/
typedef struct handle_s {
int key; /* tag number embedded in text */
- enum {URL, iCal, Attach, Folder, Function, IMG} type;
+ enum {URL, iCal, Attach, Folder, Function, IMG, imgData} type;
unsigned force_display:1; /* Don't ask before launching */
unsigned using_is_used:1; /* bit below is being used */
unsigned is_used:1; /* if not, remove it from list */
@@ -49,8 +49,9 @@ typedef struct handle_s {
*name; /* URL's NAME attribute */
} url; /* stuff to describe URL handle */
struct {
- char *src, /* src of image (CID: only?) */
- *alt; /* image alternate text */
+ char *src, /* src of image [cid:, data:] */
+ *alt, /* image alternate text */
+ *tool; /* program to display data: */
} img; /* stuff to describe img */
ATTACH_S *attach; /* Attachment struct for this handle */
struct {
diff --git a/pith/pine.hlp b/pith/pine.hlp
index a671ea7a..126a776a 100644
--- a/pith/pine.hlp
+++ b/pith/pine.hlp
@@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any
reasonable place to be called from.
Dummy change to get revision in pine.hlp
============= h_revision =================
-Alpine Commit 595 2021-09-29 19:05:06
+Alpine Commit 597 2021-10-03 21:12:53
============= h_news =================
<HTML>
<HEAD>
@@ -185,16 +185,21 @@ new additions to Alpine, please check it periodically.
<P> Changes since the release of version <!--#echo var="ALPINE_VERSION"--> include:
+<P> New features include:
+<UL>
+<LI> In the past Alpine did not recognize images embedded in an HTML file, so now it does and
+a link to open them is given. Additionally, Alpine did not pass these images to an external
+browser for display using the external command, and now it will.
+</UL>
+
<P>
-Bug fixes
+Bug fixes:
<UL>
<LI> Alpine crashes when it cannot retrieve privacy policy due to
failure to connect to external server.
</UL>
-
-
<P>Version 2.25 adds new features and addresses bugs found in previous
releases.