summaryrefslogtreecommitdiff
path: root/pith
diff options
context:
space:
mode:
Diffstat (limited to 'pith')
-rw-r--r--pith/atttype.h2
-rw-r--r--pith/charconv/filesys.c19
-rw-r--r--pith/charconv/filesys.h1
-rw-r--r--pith/conf.c8
-rw-r--r--pith/conf.h2
-rw-r--r--pith/conftype.h2
-rw-r--r--pith/detach.c7
-rw-r--r--pith/detach.h8
-rw-r--r--pith/filter.c177
-rw-r--r--pith/filter.h1
-rw-r--r--pith/init.c144
-rw-r--r--pith/init.h7
-rw-r--r--pith/mailpart.h3
-rw-r--r--pith/mimedesc.c2
-rw-r--r--pith/newmail.c6
-rw-r--r--pith/osdep/creatdir.c42
-rw-r--r--pith/osdep/creatdir.h2
-rw-r--r--pith/pine.hlp213
-rw-r--r--pith/state.c6
-rw-r--r--pith/state.h9
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
+ &quot;External&quot; 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.
&lt;End of help on this topic&gt;
</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>
+&lt;End of help on this topic&gt;
+</BODY>
+<HTML>
====== h_config_enable_view_arrows =====
<HTML>
<HEAD>
@@ -35597,6 +35653,161 @@ message to you and on how the list works.)
&lt;End of help on this topic&gt;
</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 &quot;V&quot; 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
+&quot;X&quot; 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>
+&lt;img src=&quot;...&quot;&gt;,
+</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 &quot;Return&quot; or &quot;Enter&quot; key to open the attachment. When you do that
+you will see a prompt and menu which says
+
+<pre>
+View selected Attachment ?
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Y [Yes]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X External
+^C Cancel&nbsp;&nbsp;&nbsp;&nbsp;N No
+</pre>
+
+<P>In order to send this message to an external browser, you would press the &quot;X&quot;
+key. This will change the prompt and menu to
+
+<pre>
+View selected Attachment using external viewer including inline images only ?
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Y [Yes]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X No eXternal
+^C Cancel&nbsp;&nbsp;&nbsp;&nbsp;N No&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;I All images
+</pre>
+
+<P>This is telling you that if you answer &quot;Yes&quot; 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 &quot;I&quot; key. Pressing
+that key changes the prompt and menu to
+
+<pre>
+View selected Attachment using external viewer including all images ?
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Y [Yes]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X No eXternal
+^C Cancel&nbsp;&nbsp;&nbsp;&nbsp;N No&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;I Inline imgs
+</pre>
+
+<P>and as you can see the prompt says that if you press &quot;Yes&quot; then the message
+will be sent to the browser including the source of all images, including those in
+external servers. Notice that the &quot;X&quot; command now is a toggle. If you
+were to press it now, you would return to the original prompt,
+
+<pre>
+View selected Attachment ?
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Y [Yes]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X External
+^C Cancel&nbsp;&nbsp;&nbsp;&nbsp;N No
+</pre>
+
+<P>which means that if you answer &quot;Yes&quot; 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 &quot;X&quot;
+command in the ATTACHMENT SCREEN, and you never press the &quot;X&quot; 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>
+&lt;End of help on this topic&gt;
+</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 */