diff options
author | Eduardo Chappa <chappa@washington.edu> | 2021-10-03 21:12:56 -0600 |
---|---|---|
committer | Eduardo Chappa <chappa@washington.edu> | 2021-10-03 21:12:56 -0600 |
commit | 76da583d4aa4600148048c4cb025d9248d41326c (patch) | |
tree | b8e2661db937ac7424a02644bbf6c95ffc477345 | |
parent | da6cdd6b8083201c66349133693a173704f5d4df (diff) | |
download | alpine-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.c | 180 | ||||
-rw-r--r-- | alpine/mailview.h | 1 | ||||
-rw-r--r-- | pith/filter.c | 119 | ||||
-rw-r--r-- | pith/handle.c | 11 | ||||
-rw-r--r-- | pith/handle.h | 7 | ||||
-rw-r--r-- | pith/pine.hlp | 13 |
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. |