diff options
Diffstat (limited to 'pith')
-rw-r--r-- | pith/atttype.h | 2 | ||||
-rw-r--r-- | pith/charconv/filesys.c | 19 | ||||
-rw-r--r-- | pith/charconv/filesys.h | 1 | ||||
-rw-r--r-- | pith/conf.c | 8 | ||||
-rw-r--r-- | pith/conf.h | 2 | ||||
-rw-r--r-- | pith/conftype.h | 2 | ||||
-rw-r--r-- | pith/detach.c | 7 | ||||
-rw-r--r-- | pith/detach.h | 8 | ||||
-rw-r--r-- | pith/filter.c | 177 | ||||
-rw-r--r-- | pith/filter.h | 1 | ||||
-rw-r--r-- | pith/init.c | 144 | ||||
-rw-r--r-- | pith/init.h | 7 | ||||
-rw-r--r-- | pith/mailpart.h | 3 | ||||
-rw-r--r-- | pith/mimedesc.c | 2 | ||||
-rw-r--r-- | pith/newmail.c | 6 | ||||
-rw-r--r-- | pith/osdep/creatdir.c | 42 | ||||
-rw-r--r-- | pith/osdep/creatdir.h | 2 | ||||
-rw-r--r-- | pith/pine.hlp | 213 | ||||
-rw-r--r-- | pith/state.c | 6 | ||||
-rw-r--r-- | pith/state.h | 9 |
20 files changed, 653 insertions, 8 deletions
diff --git a/pith/atttype.h b/pith/atttype.h index df471d4f..a43a16bb 100644 --- a/pith/atttype.h +++ b/pith/atttype.h @@ -25,6 +25,8 @@ typedef struct attachment { unsigned can_display:4; unsigned shown:1; unsigned suppress_editorial:1; + char *cid_tmpfile; + char *tmpdir; char *number; char size[25]; } ATTACH_S; diff --git a/pith/charconv/filesys.c b/pith/charconv/filesys.c index be8e323f..c8b1c8d9 100644 --- a/pith/charconv/filesys.c +++ b/pith/charconv/filesys.c @@ -500,6 +500,25 @@ our_rename(char *oldpath, char *newpath) #endif /* UNIX */ } +int +our_rmdir(char *path) +{ +#ifdef _WINDOWS + LPTSTR p = NULL; + int ret = -1; + + p = utf8_to_lptstr((LPSTR) path); + + if(p){ + ret = _trmdir(p); + fs_give((void **) &p); + } + + return ret; +#else /* UNIX */ + return(rmdir(fname_to_locale(path))); +#endif /* UNIX */ +} int our_unlink(char *path) diff --git a/pith/charconv/filesys.h b/pith/charconv/filesys.h index 8b18ffa9..a30b18e2 100644 --- a/pith/charconv/filesys.h +++ b/pith/charconv/filesys.h @@ -37,6 +37,7 @@ int our_creat(char *, mode_t); int our_mkdir(char *, mode_t); int our_rename(char *, char *); int our_unlink(char *); +int our_rmdir(char *); int our_link(char *, char *); int our_lstat(char *, struct stat *); int our_chmod(char *, mode_t); diff --git a/pith/conf.c b/pith/conf.c index a3f50b39..1835c404 100644 --- a/pith/conf.c +++ b/pith/conf.c @@ -105,6 +105,8 @@ CONF_TXT_T cf_text_smtp_server[] = "List of SMTP servers for sending mail. If b CONF_TXT_T cf_text_nntp_server[] = "NNTP server for posting news. Also sets news-collections for news reading."; +CONF_TXT_T cf_html_directory[] = "Directory used by Alpine to save copies of html messages before they are\n#opened by an external web browser. The default directory is .alpine-html\n# in unix systems and alpine-html in a windows system."; + #ifdef SMIME CONF_TXT_T cf_text_publiccertdir[] = "Public certificates are kept in files in this directory. The files should\n# contain certificates in PEM format. The name of each file should look\n# like <emailaddress>.crt. The default directory is .alpine-smime/public."; @@ -907,6 +909,8 @@ static struct variable variables[] = { NULL, cf_text_window_position}, {"cursor-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL}, #endif /* _WINDOWS */ +{"html-messages-directory", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, + NULL, cf_html_directory}, #ifdef SMIME {"smime-public-cert-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, "S/MIME - Public Cert Directory", cf_text_publiccertdir}, @@ -1743,6 +1747,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) #ifdef DF_VAR_SPELLER GLO_SPELLER = cpystr(DF_VAR_SPELLER); #endif + GLO_HTML_DIRECTORY = cpystr(DF_HTML_DIRECTORY); #ifdef SMIME if(ps->smimedir){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/public", ps->smimedir); @@ -2094,6 +2099,7 @@ init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **)) fs_give((void **)&ps_global->vars[V_OPER_DIR].main_user_val.p); } + set_current_val(&vars[V_HTML_DIRECTORY], TRUE, TRUE); set_current_val(&vars[V_PERSONAL_PRINT_CATEGORY], TRUE, TRUE); ps->printer_category = -1; if(VAR_PERSONAL_PRINT_CATEGORY != NULL) @@ -3026,6 +3032,8 @@ feature_list(int index) F_VIEW_SEL_URL_HOST, h_config_enable_view_web_host, PREF_VIEW, 1}, {"enable-msg-view-forced-arrows", "Enable Message View Forced Arrows", F_FORCE_ARROWS, h_config_enable_view_arrows, PREF_VIEW, 0}, + {"external-command-loads-inline-images-only", NULL, + F_EXTERNAL_INLINE_IMAGES, h_external_loads_inline_images_only, PREF_VIEW, 1}, /* set to TRUE for windows */ {"pass-c1-control-characters-as-is", NULL, F_PASS_C1_CONTROL_CHARS, h_config_pass_c1_control, PREF_VIEW, 0}, diff --git a/pith/conf.h b/pith/conf.h index 949e87f6..e9023cc4 100644 --- a/pith/conf.h +++ b/pith/conf.h @@ -476,6 +476,8 @@ #define VAR_PROMPT_BACK_COLOR vars[V_PROMPT_BACK_COLOR].current_val.p #define VAR_VIEW_HDR_COLORS vars[V_VIEW_HDR_COLORS].current_val.l #define VAR_INDEX_TOKEN_COLORS vars[V_INDEX_TOKEN_COLORS].current_val.l +#define VAR_HTML_DIRECTORY vars[V_HTML_DIRECTORY].current_val.p +#define GLO_HTML_DIRECTORY vars[V_HTML_DIRECTORY].global_val.p #ifdef SMIME #define VAR_PUBLICCERT_DIR vars[V_PUBLICCERT_DIR].current_val.p #define GLO_PUBLICCERT_DIR vars[V_PUBLICCERT_DIR].global_val.p diff --git a/pith/conftype.h b/pith/conftype.h index 88f479c1..38f62416 100644 --- a/pith/conftype.h +++ b/pith/conftype.h @@ -292,6 +292,7 @@ typedef enum { V_PERSONAL_NAME = 0 , V_CURSOR_STYLE #endif #endif + , V_HTML_DIRECTORY #ifdef SMIME , V_PUBLICCERT_DIR , V_PUBLICCERT_CONTAINER @@ -462,6 +463,7 @@ typedef enum { F_VIEW_SEL_URL_HOST, F_SCAN_ADDR, F_FORCE_ARROWS, + F_EXTERNAL_INLINE_IMAGES, F_PREFER_PLAIN_TEXT, F_QUELL_CHARSET_WARNING, F_COPY_TO_TO_FROM, diff --git a/pith/detach.c b/pith/detach.c index 9774e1c9..4a2318e7 100644 --- a/pith/detach.c +++ b/pith/detach.c @@ -202,7 +202,7 @@ detach(MAILSTREAM *stream, /* c-client stream to use */ } /* convert all text to UTF-8 */ - if(is_text & !(flags & DT_BINARY)){ + if(is_text & !(flags & (DT_BINARY | DT_EXTERNAL))){ charset = parameter_val(body->parameter, "charset"); /* @@ -277,6 +277,11 @@ detach(MAILSTREAM *stream, /* c-client stream to use */ || body->type == TYPEMULTIPART) gf_link_filter(gf_nvtnl_local, NULL); + if(flags & DT_EXTERNAL){ + char i = flags & DT_ALLIMAGES ? 'i' : '\0'; + gf_link_filter(gf_html_cid2file, (void *) &i); + } + /* * If we're detaching a text segment and a user-defined filter may * need to be invoked later (see below), decode the segment into diff --git a/pith/detach.h b/pith/detach.h index 79ed9e5a..1bfa34ef 100644 --- a/pith/detach.h +++ b/pith/detach.h @@ -55,9 +55,11 @@ extern FETCH_READC_S *g_fr_desc; * This lazily gets combined with FT_ flags from c-client so make * it different from all those possible values. */ -#define DT_NODFILTER (long) 0x10000 -#define DT_NOINTR (long) 0x20000 -#define DT_BINARY (long) 0x40000 +#define DT_NODFILTER (long) 0x010000 +#define DT_NOINTR (long) 0x020000 +#define DT_BINARY (long) 0x040000 +#define DT_EXTERNAL (long) 0x080000 +#define DT_ALLIMAGES (long) 0x100000 /* exported prototypes */ char *detach_raw(MAILSTREAM *, long, char *, gf_io_t, int); diff --git a/pith/filter.c b/pith/filter.c index 5ac4992f..1e2db4d1 100644 --- a/pith/filter.c +++ b/pith/filter.c @@ -3228,6 +3228,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(unsigned char *, long, int *); /* * Protos for RSS 2.0 Tag handlers @@ -9055,6 +9056,182 @@ gf_html2plain_rss_free_items(RSS_ITEM_S **itemp) } } +char * +cid_tempfile_name(unsigned char *line, long n, int *is_cidp) +{ + int f2 = 0; + int i, found; + char *s, *t = NULL, *u; + char imgfile[1024]; + char *extp = NULL; + + + s = NULL; + *is_cidp = 0; + if(n > 0){ + if (line[0] == '\"') f2 = 1; + if (n - f2 > 3){ + if (!struncmp(line+f2, "cid:", 4)){ + *is_cidp = 1; + f2 += 4; + s = fs_get((n - f2 + 4)*sizeof(char)); + snprintf(s, n - f2 + 2, "<%s", line+f2); + if (s[strlen(s)-1] == '\"') + s[strlen(s)-1] = '>'; + else{ + i = strlen(s); + s[i] = '>'; + s[i + 1] = '\0'; + } + /* find the tmpdir where all these files will be saved to */ + if(t == NULL){ + for(i = 0; ps_global->atmts[i].tmpdir == NULL && ps_global->atmts[i].description != NULL; i++); + t = ps_global->atmts[i].description ? ps_global->atmts[i].tmpdir : NULL; + } + + /* now we need to look for s in the list of attachments */ + for (i = 0, found = 0; found == 0 && ps_global->atmts[i].description != NULL; i++) + if (ps_global->atmts[i].body + && ps_global->atmts[i].body->type == TYPEIMAGE + && strcmp(ps_global->atmts[i].body->id, s) == 0){ + found++; + break; + } + + fs_give((void **) &s); + if(found && ps_global->atmts[i].cid_tmpfile == NULL){ + PARAMETER *param; + if (ps_global->atmts[i].cid_tmpfile == NULL){ + for(param = ps_global->atmts[i].body->parameter; param ; param = param->next){ + if (!strucmp(param->attribute, "NAME")){ + strncpy(imgfile, param->value, sizeof(imgfile)); + imgfile[sizeof(imgfile)-1] = '\0'; + extp = strrchr(imgfile, '.'); + if(extp) extp++; + } + } + ps_global->atmts[i].cid_tmpfile = temp_nam_ext(t, "tmp-img-", extp); + } + } + if(found && ps_global->atmts[i].cid_tmpfile != NULL) + s = strstr(ps_global->atmts[i].cid_tmpfile, "tmp-img-"); + } + } + } + return s; +} + +#define COLLECT(X, C) { \ + if((X)->n == buflen){ \ + fs_resize((void **) &((X)->line), buflen + 1024); \ + (X)->linep = (X)->line + buflen; \ + buflen += 1024; \ + } \ + *((X)->linep)++ = (C); \ + (X)->n = (X)->linep - (X)->line; \ +} + +#define RESET_FILTER(X) { \ + (X)->linep = (X)->line; \ + (X)->n = 0L; \ +} + +void +gf_html_cid2file(FILTER_S *f, int cmd) +{ + register char *p; + register unsigned char c; + static long buflen = 0L; + + GF_INIT(f, f->next); + + if(cmd == GF_DATA){ + register int state = f->f1; + + while(GF_GETC(f, c)){ + + if(state == 0){ /* look for "<img " */ + if (c == '<') f->f2 = 1; + else if(f->f2 > 0){ + if (f->f2 == 1 && (c == 'i' || c == 'I')) f->f2 = 2; + else if (f->f2 == 2 && (c == 'm' || c == 'M')) f->f2 = 3; + else if (f->f2 == 3 && (c == 'g' || c == 'G')) f->f2 = 4; + else if (f->f2 == 4 && HTML_ISSPACE(c)){ f->f2 = 0; state = 1; } + else f->f2 = 0; + } + } + else if(state == 1){ /* look for "src=" */ + if (c == 's' || c == 'S') f->f2 = 1; + else if (f->f2 == 1 && (c == 'r' || c == 'R')) f->f2 = 2; + else if (f->f2 == 2 && (c == 'c' || c == 'C')) f->f2 = 3; + else if (f->f2 == 3 && c == '='){ GF_PUTC(f->next, c); state = 2; } + else if (f->f2 == 3 && !HTML_ISSPACE(c)) f->f2 = 0; + else f->f2 = 0; + } + else if (state == 2){ /* collect all data */ + if(HTML_ISSPACE(c) || c == '>'){ + long n; + int is_cid; + if(f->n > 0){ + char *s = cid_tempfile_name(f->line, f->n, &is_cid); + if(is_cid){ + RESET_FILTER(f); + if(s != NULL) + for(; *s != '\0'; s++) + COLLECT(f, *s); + } + } + GF_PUTC(f->next, '\"'); + if(is_cid || f->t){ + for(p = f->line; f->n; f->n--, p++){ + if(*p == '\"') continue; + GF_PUTC(f->next, *p); + } + } + else f->n = 0; + GF_PUTC(f->next, '\"'); + GF_PUTC(f->next, c); + state = HTML_ISSPACE(c) ? 1 : 0; + RESET_FILTER(f); + } + else COLLECT(f, c); /* collect this data */ + } + + p = f->line; + if(state < 2) + GF_PUTC(f->next, c); + } + + f->f1 = state; + GF_END(f, f->next); + } + else if(cmd == GF_EOD){ + if(f->f1 == 2){ + char *s = cid_tempfile_name(f->line, f->n, &f->f2); + GF_PUTC(f->next, '\"'); + if (f->f2 || f->t){ + for(p = s; *p; p++){ + if(*p == '\"') continue; + GF_PUTC(f->next, *p); + } + } + GF_PUTC(f->next, '\"'); + GF_PUTC(f->next, '>'); + } + + buflen = 0; + fs_give((void **)&(f->line)); /* free temp line buffer */ + (void) GF_FLUSH(f->next); + (*f->next->f)(f->next, GF_EOD); + } + else if(cmd == GF_RESET){ + dprint((9, "-- gf_reset cid2file\n")); + f->n = 0L; /* number of bytes in buffer */ + f->f1 = 0; /* state */ + f->f2 = 0; /* total number of bytes read that match pattern */ + f->t = *(char *)f->opt; + } +} /* END OF HTML-TO-PLAIN text filter */ diff --git a/pith/filter.h b/pith/filter.h index bccd61c9..18494622 100644 --- a/pith/filter.h +++ b/pith/filter.h @@ -200,6 +200,7 @@ void *gf_html2plain_rss_opt(RSS_FEED_S **, int); void gf_html2plain_rss_free(RSS_FEED_S **); void gf_html2plain_rss_free_items(RSS_ITEM_S **); void gf_escape_filter(FILTER_S *, int); +void gf_html_cid2file(FILTER_S *, int); void gf_control_filter(FILTER_S *, int); void *gf_control_filter_opt(int *); void gf_tag_filter(FILTER_S *, int); diff --git a/pith/init.c b/pith/init.c index e0c60182..5528943a 100644 --- a/pith/init.c +++ b/pith/init.c @@ -544,3 +544,147 @@ prune_move_folder(char *oldpath, char *newpath, CONTEXT_S *prune_cntxt) return 0; } + +char * +html_directory_path(char *sname, char *sbuf, size_t len) +{ + *sbuf = '\0'; + if(sname && *sname){ + size_t snl = strlen(sname); + if(is_absolute_path(sname)){ + strncpy(sbuf, sname, len-1); + sbuf[len-1] = '\0'; + fnexpand(sbuf, len); + } + else if(ps_global->VAR_OPER_DIR){ + if(strlen(ps_global->VAR_OPER_DIR) + snl < len - 1) + build_path(sbuf, ps_global->VAR_OPER_DIR, sname, len); + } + else if(strlen(ps_global->home_dir) + snl < len - 1) + build_path(sbuf, ps_global->home_dir, sname, len); + } + return(*sbuf ? sbuf : NULL); +} + +int +init_html_directory(char *path) +{ + int rv; + switch(is_writable_dir(path)){ + case 0: rv = 0; break; + case 3: rv = create_mail_dir(path) < 0 ? -1 : 0; break; + default: rv = -1; break; + } + return rv; +} + +HTML_LOG_S * +create_html_log(void) +{ + HTML_LOG_S *rv; + rv = fs_get(sizeof(HTML_LOG_S)); + memset((void *) rv, 0, sizeof(HTML_LOG_S)); + + return rv; +} + +void +add_html_log(HTML_LOG_S **htmlp, char *d) +{ + HTML_LOG_S *h; + + for(h = *htmlp; h && h->next; h = h->next); + + if(!h){ + h = create_html_log(); + *htmlp = h; + } + else { + h->next = create_html_log(); + h = h->next; + } + h->dir = cpystr(d); + h->to_delete = time(0) + 10*60; /* 10 minutes life */ +} + +void +free_html_log(HTML_LOG_S **htmlp) +{ + if (htmlp == NULL || *htmlp == NULL) + return; + + if((*htmlp)->dir) fs_give((void **) &(*htmlp)->dir); + if((*htmlp)->next) free_html_log(&(*htmlp)->next); + fs_give((void **)htmlp); +} + +void +html_dir_clean(int force) +{ + HTML_LOG_S *h, prev, *j; + time_t now = time(0); + DIR *dirp; + struct dirent *d; + struct stat sbuf; + char fpath[MAXPATH]; + int delete_happened = 0; + + /* these are barriers to try to not to enter here */ + if(ps_global->html_dir == NULL + || ps_global->html_dir_list == NULL + || our_stat(ps_global->html_dir, &sbuf) < 0) return; + + for(h = ps_global->html_dir_list; h; h = h->next){ + if(force || now >= h->to_delete){ /* the time to delete has come */ + if(strlen(h->dir) + strlen(ps_global->html_dir) + 3 < MAXPATH){ + if(our_stat(h->dir, &sbuf) < 0) + continue; + if((sbuf.st_mode & S_IFMT) == S_IFDIR){ + if((dirp = opendir(h->dir)) != NULL){ + while ((d = readdir(dirp)) != NULL){ + if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + if(strlen(h->dir) + strlen(d->d_name) + 3 < MAXPATH){ + snprintf(fpath, sizeof(fpath) - 1, "%s%s%s", h->dir, S_FILESEP, d->d_name); + fpath[sizeof(fpath)-1] = '\0'; + our_unlink(fpath); + } + } + closedir(dirp); + } + if(!our_rmdir(h->dir)){ + h->to_delete = 0; /* mark it deleted */ + delete_happened++; + } + } + } + } + } + + /* remove all deleted directories from the list */ + if(delete_happened){ + prev.to_delete = 1; /* do not free this entry */ + (&prev)->next = ps_global->html_dir_list; + for (j = &prev; j && j->next ; ){ + if(j->next->to_delete == 0){ + HTML_LOG_S *k; + k = j->next; + j->next = k->next; + k->next = NULL; + free_html_log(&k); + } + else j = j->next; + } + ps_global->html_dir_list = (&prev)->next; + } + + /* this is wrong, but we do not care if this fails. What this + * is trying to do is to the delete the .alpine-html directory + * if it is empty. We could test if it is empty by reading the + * directory. Instead we will use a weaker test checking if we + * have anything else to erase. If we don't, then we try to + * erase ps_global->html_dir, and we do not worry if we fail. + */ + if(ps_global->html_dir_list == NULL) + our_rmdir(ps_global->html_dir); +} diff --git a/pith/init.h b/pith/init.h index 6513a575..206cd63e 100644 --- a/pith/init.h +++ b/pith/init.h @@ -37,6 +37,11 @@ int prune_move_folder(char *, char *, CONTEXT_S *); int first_run_of_month(void); int first_run_of_year(void); struct sm_folder *get_mail_list(CONTEXT_S *, char *); - +char *html_directory_path(char *, char *, size_t); +int init_html_directory(char *); +HTML_LOG_S *create_html_log(void); +void add_html_log(HTML_LOG_S **, char *); +void free_html_log(HTML_LOG_S **); +void html_dir_clean(int); #endif /* PITH_INIT_INCLUDED */ diff --git a/pith/mailpart.h b/pith/mailpart.h index 9e5a52f9..24b8d050 100644 --- a/pith/mailpart.h +++ b/pith/mailpart.h @@ -35,6 +35,9 @@ /* Is this a VCalendar type? */ #define MIME_VCALENDAR(t,s) ((t) == TYPETEXT && (s) && !strucmp((s),"CALENDAR")) +/* Is this a text/html attachment? */ +#define TEXT_HTML(t,s) ((t) == TYPETEXT && (s) && !strucmp((s),"HTML")) + /* Is this a multipart signed? */ #define MIME_MULT_SIGNED(t,s) ((t) == TYPEMULTIPART && (s) && !strucmp((s),"signed")) diff --git a/pith/mimedesc.c b/pith/mimedesc.c index 665ce253..e25f5d4e 100644 --- a/pith/mimedesc.c +++ b/pith/mimedesc.c @@ -434,6 +434,8 @@ zero_atmts(ATTACH_S *atmts) ATTACH_S *a; for(a = atmts; a->description != NULL; a++){ + a->tmpdir = NULL; /* short lived variabled, cleared right after use */ + a->cid_tmpfile = NULL; /* resetting pointer is enough */ fs_give((void **)&(a->description)); fs_give((void **)&(a->number)); } diff --git a/pith/newmail.c b/pith/newmail.c index db8d36bb..1f026eeb 100644 --- a/pith/newmail.c +++ b/pith/newmail.c @@ -31,6 +31,7 @@ static char rcsid[] = "$Id: newmail.c 1266 2009-07-14 18:39:12Z hubert@u.washing #include "../pith/options.h" #include "../pith/folder.h" #include "../pith/ablookup.h" +#include "../pith/init.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" @@ -102,12 +103,15 @@ new_mail(int force_arg, CheckPointTime time_for_check_point, int flags) timeo = get_input_timeout(); - if(time_for_check_point == GoodTime) + if(time_for_check_point == GoodTime){ adrbk_maintenance(); + html_dir_clean(0); + } if(time_for_check_point == GoodTime || force_arg) folder_unseen_count_updater(UFU_ANNOUNCE | (force_arg ? UFU_FORCE : 0)); + if(sp_need_to_rethread(ps_global->mail_stream)) force = 1; diff --git a/pith/osdep/creatdir.c b/pith/osdep/creatdir.c index 76a854a6..56ee4a75 100644 --- a/pith/osdep/creatdir.c +++ b/pith/osdep/creatdir.c @@ -53,3 +53,45 @@ create_mail_dir(char *dir) return(0); } + + +/*---------------------------------------------------------------------- + Create random directory + + Args: dir -- Name of the directory that contains the random directory + len -- size of dir. + + Result: Directory is created. Returns 0 on success, else -1 on error + and errno is valid. + ----*/ +int +create_random_dir(char *dir, size_t len) +{ + size_t olen, dlen = strlen(dir); + + olen = dlen; /* save original length */ + + if(dir[dlen-1] != C_FILESEP){ + dir[dlen++] = C_FILESEP; + dir[dlen] = '\0'; + } + + if(dlen + 6 < len) + strcat(dir, "XXXXXX"); + else{ + dir[olen] = '\0'; + return -1; + } + +#ifndef _WINDOWS + dir = mkdtemp(dir); + our_chmod(dir, MAILDIR_MODE); + + /* Some systems need this, on others we don't care if it fails */ + our_chown(dir, getuid(), getgid()); +#else + dir = _mktemp(dir); +#endif /* !_WINDOWS */ + + return(0); +} diff --git a/pith/osdep/creatdir.h b/pith/osdep/creatdir.h index e9d844e7..8ad6eeb5 100644 --- a/pith/osdep/creatdir.h +++ b/pith/osdep/creatdir.h @@ -22,6 +22,6 @@ * Exported Prototypes */ int create_mail_dir(char *); - +int create_random_dir(char *, size_t); #endif /* PITH_OSDEP_CREATDIR_INCLUDED */ diff --git a/pith/pine.hlp b/pith/pine.hlp index 1bfaf279..2a65f1e0 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 432 2020-05-21 21:53:25 +Alpine Commit 433 2020-06-07 20:59:33 ============= h_news ================= <HTML> <HEAD> @@ -188,6 +188,16 @@ problems you find with this release. protocols. Thanks to Geoffrey Bodwin for a report that lead to this implementation. +<LI> Alpine can pass an HTML message to an external web browser, by using the + "External" command in the <a href="h_attachment_screen">ATTACHMENT INDEX</a> + screen. <A href="h_command_external_browser">Learn more</A>. + +<LI> New configuration variable + <a href="h_external_loads_inline_images_only"><!--#echo var="FEAT_external-command-loads-inline-images-only"--></a> + 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. + <LI> New variable system-certs-path that allows users to indicate the location of the directory where certificates are located. In PC-Alpine this must be C:\\libressl\\ssl\\certs. The C: drive can be replaced @@ -4340,6 +4350,7 @@ There are also additional details on <li><a href="h_config_enable_mouse">FEATURE: <!--#echo var="FEAT_enable-mouse-in-xterm"--></a> <li><a href="h_config_enable_view_addresses">FEATURE: <!--#echo var="FEAT_enable-msg-view-addresses"--></a> <li><a href="h_config_enable_view_attach">FEATURE: <!--#echo var="FEAT_enable-msg-view-attachments"--></a> +<li><a href="h_external_loads_inline_images_only">FEATURE: <!--#echo var="FEAT_external-command-loads-inline-images-only"--></a> <li><a href="h_config_enable_view_arrows">FEATURE: <!--#echo var="FEAT_enable-msg-view-forced-arrows"--></a> <li><a href="h_config_enable_view_url">FEATURE: <!--#echo var="FEAT_enable-msg-view-urls"--></a> <li><a href="h_config_enable_view_web_host">FEATURE: <!--#echo var="FEAT_enable-msg-view-web-hostnames"--></a> @@ -13542,6 +13553,9 @@ Available commands include: <DT>View</DT> <DD>View the currently selected attachment. +<DT>External</DT> +<DD>Passes a TEXT/HTML message to an external browser for its display. + <DT>Prev Attach</DT> <DD>Move to previous attachment. @@ -30891,6 +30905,48 @@ Ctrl-B key can be used to select the previous web hostnames in the same way. <End of help on this topic> </BODY> </HTML> +====== h_external_loads_inline_images_only ===== +<HTML> +<HEAD> +<TITLE>FEATURE: <!--#echo var="FEAT_external-command-loads-inline-images-only"--></TITLE> +</HEAD> +<BODY> +<H1>FEATURE: <!--#echo var="FEAT_external-command-loads-inline-images-only"--></H1> + +Alpine allows you to pass a HTML message to the browser that you have configured in your +<A HREF="h_config_browser"><!--#echo var="VAR_url-viewers"--></A> variable. This allows +you to read a message outside of Alpine. This is desirable when Alpine does not display +html correctly, or when you wish to read the message and see the inline images in the message. + +<P> +An inline image is one that comes with the message and is necessary for the correct display of +the message. However, there are instances in which the source of an image will come from +external servers. If this feature is enabled (the default) then Alpine will only pass inline +images to the browser and will remove the link to external images, so only inline images will +be used to display the message, and no external image will be loaded. Alpine does this to protect +your privacy and security. + +<P> +Please note that messages are usually formatted by the sender so that they display correctly once +all images have been loaded. Enabling this feature might cause the message not to be correctly +displayed by your browser. + +<P> +Also consider security and privacy implications of opening an HTML message in a browser. You are +always protected when you do not use this feature, but you might not have the same level of +protection if you try to open a spam or scam using the html view. Also commercial email is +normally embedded with links to external images that let them, their partners, and your internet +service provider (which could be your employer or school) know that you opened their message or +connected to a web site, violating your privacy. + +<P> +If you are worried about your privacy and security, do not use an external viewer to open +html files, and keep using the internal mechanisms that Alpine provides to read messages. + +<P> +<End of help on this topic> +</BODY> +<HTML> ====== h_config_enable_view_arrows ===== <HTML> <HEAD> @@ -35597,6 +35653,161 @@ message to you and on how the list works.) <End of help on this topic> </BODY> </HTML> +====== h_command_external_browser ===== +<HTML> +<HEAD> +<TITLE>The External Browser Command</TITLE> +</HEAD> +<BODY> +<H1>The External Browser Command</H1> + +<P> +A new command was added to Alpine that allows users to send HTML messages to be displayed +by a web browser. This is helpful when a user wants to see images in the context of the +message, or get a better display of the message than Alpine provides. + +<P> +The simplest way to use this command is to do as follows. While reading a message, +press the "V" to go to the <A href="h_attachment_screen">ATTACHMENT SCREEN</A>. +In that screen move the cursor until it is on top of a TEXT/HTML attachment and press the +"X" key. This will make Alpine launch the browser you have configured for +in the <a href="h_config_browser"><!--#echo var="VAR_url-viewers"--></a> variable, and +you should be able to read the message in your browser, as well as in Alpine. + +<P> If the message you sent to your browser has inline images, then the images attached to +the message that are necessary for the display of the message are also sent to the browser +for its display. + +<P> The text that follows will explain more details about this command, and is only +recommended for more advanced users. + +<h1><CENTER>Displaying Images</CENTER></h1> + +<P> First, we will talk about displaying images in an HTML file. Typically, HTML images +are displayed as the result of some specific code of the form +<center> +<PRE> +<img src="...">, +</PRE> +</center> +where the text between the quotes tells Alpine how to find the image. +If the source of the image is internal to the message, Alpine passes that image to the +browser. Otherwise Alpine erases the link to the image. This is done so that +you can be protected from a bad use of external images. Images can be used to track +that you read the message, or your location, devices you own, etc.. Since Alpine does +not open images in any messages, your +privacy is always protected this way. Therefore, when you do not pass the links to +external images to a browser, your privacy is being protected. However, doing this +might make the message not be displayed correctly, since when the message was +created the images were part of the formatted message, and not having images might make +this formatting look awkward. + +<P> If you would like that Alpine display all images, regardless of their source, and +regardless of the sender, then you need to disable the feature <a +href="h_external_loads_inline_images_only"><!--#echo var="FEAT_external-command-loads-inline-images-only"--></a>. +The message will display as intended, but you will leak information to the sender of the +message, as well as to your internet service provider, which could be your employer, or +school, etc. + +<P> Alpine provides an alternative mechanism to either send all links to the images to the +external browser or to send only those that are attached to the message you are trying to +display. In order to use this mechanism you must first enable +<a href="h_config_enable_view_attach"><!--#echo var="FEAT_enable-msg-view-attachments"--></a>. +This allows Alpine to add direct links to each attachments. If you want to send an HTML +attachment to an external browser, you would place the cursor over the attachment and press +the "Return" or "Enter" key to open the attachment. When you do that +you will see a prompt and menu which says + +<pre> +View selected Attachment ? + Y [Yes] X External +^C Cancel N No +</pre> + +<P>In order to send this message to an external browser, you would press the "X" +key. This will change the prompt and menu to + +<pre> +View selected Attachment using external viewer including inline images only ? + Y [Yes] X No eXternal +^C Cancel N No I All images +</pre> + +<P>This is telling you that if you answer "Yes" to this question, and external +browser will be used to send this messages, and only inline images, that is, those attached +in the message will be sent to the browser. If you would like to send all images in +this case, the menu tells you that you must press the "I" key. Pressing +that key changes the prompt and menu to + +<pre> +View selected Attachment using external viewer including all images ? + Y [Yes] X No eXternal +^C Cancel N No I Inline imgs +</pre> + +<P>and as you can see the prompt says that if you press "Yes" then the message +will be sent to the browser including the source of all images, including those in +external servers. Notice that the "X" command now is a toggle. If you +were to press it now, you would return to the original prompt, + +<pre> +View selected Attachment ? + Y [Yes] X External +^C Cancel N No +</pre> + +<P>which means that if you answer "Yes" at this time your message would not +be sent to your external browser for display, unless you have configured a mailcap +entry to display HTML files. + +<P> One of the lessons of this discussion is that if you never press the "X" +command in the ATTACHMENT SCREEN, and you never press the "X" command when +launching a viewer for an attachment in the MESSAGE TEXT screen, you will never use this +mechanism, and Alpine will resort to your already configured mechanisms to open HTML +text.This means you can live your life without worrying that Alpine will do anything +different because of this new feature. You do not have to use it, but if you do, you should +know the risks and advantages and decide when and how to use it. + +<h1><CENTER>Saving HTML Messages to Disk</CENTER></h1> + +<P> +No matter what your reason to send a message to an external web browser is, Alpine must +write your message to a file (and also all related inline images), and point your browser +to open that file. Alpine saves all your messages and auxiliary images in a subdirectory +of the ~/.alpine-html directory in unix-like Alpine, or the alpine-html folder in your home +directory in PC-Alpine. If Alpine cannot access these directories, or create folders +in them, then the full mechanism described above will fail, and you will not be able +to send messages to an external browser for display. + +<P> +Unfortunately browsers do not remove the file that Alpine created, nor the images +that Alpine saved in order to display this message, so if you use this mechanism often +you will create many directories and files which the browser will not remove. Alpine +will remove these files when you exit Alpine. Any temporary directory that Alpine +created that has existed for longer than 10 minutes will be automatically erased. +Alpine also erases these directories upon exiting. + +<P> +When Alpine creates a directory to house the files associated to a message that will +pass to an external browser, it tries to delete that directory later on, according to +the discussion above. What this means is that you should consider the contents of the +~/.alpine-html directory in unix-like Alpine and the alpine-html folder in PC-Alpine +as temporary, and not rely on their existence. If you attempt to save files in one of +these directories, chances are your data will be deleted by Alpine. Since deleting is +a destructive action, +every session of Alpine that you have open will only attempt to remove the directories +it created, with their content in them. If this operation fails, Alpine will not try +to investigate, nor will report to you, why the opeation failed. Therefore, users +should periodically check their html directory to see if there is content there that +they wish to delete. + +<P> +<UL> +<LI><A HREF="h_finding_help">Finding more information and requesting help</A> +</UL><P> +<End of help on this topic> +</BODY> +</HTML> ===== h_mainhelp_smime ====== <HTML> <HEAD> diff --git a/pith/state.c b/pith/state.c index f0212270..a7728d67 100644 --- a/pith/state.c +++ b/pith/state.c @@ -138,6 +138,12 @@ free_pine_struct(struct pine **pps) if((*pps)->folders_dir != NULL) fs_give((void **)&(*pps)->folders_dir); + if((*pps)->html_dir != NULL) + fs_give((void **)&(*pps)->html_dir); + + if((*pps)->html_dir_list != NULL) + free_html_log(&(*pps)->html_dir_list); + if((*pps)->ui.homedir) fs_give((void **)&(*pps)->ui.homedir); diff --git a/pith/state.h b/pith/state.h index 41ef1b43..3fec3691 100644 --- a/pith/state.h +++ b/pith/state.h @@ -61,6 +61,13 @@ struct ttyo { footer_rows; /* number of rows for status and keymenu */ }; +/* struct to keep track of external html files generated by Alpine */ +typedef struct html_log_s { + char *dir; /* directory path */ + time_t to_delete; /* time to delete dir */ + struct html_log_s *next; /* pointer to next entry */ +} HTML_LOG_S; + /* * HEADER_ROWS is always 2. 1 for the titlebar and 1 for the * blank line after the titlebar. We should probably make it go down @@ -120,7 +127,9 @@ struct pine { INDEX_COL_S *index_disp_format; char *folders_dir; + char *html_dir; + HTML_LOG_S *html_dir_list; unsigned signal_in_progress:1; /* we are handling a signal */ unsigned mangled_footer:1; /* footer needs repainting */ unsigned mangled_header:1; /* header needs repainting */ |