aboutsummaryrefslogtreecommitdiffstats
path: root/mailcap.c
diff options
context:
space:
mode:
Diffstat (limited to 'mailcap.c')
-rw-r--r--mailcap.c472
1 files changed, 472 insertions, 0 deletions
diff --git a/mailcap.c b/mailcap.c
new file mode 100644
index 0000000..e26da72
--- /dev/null
+++ b/mailcap.c
@@ -0,0 +1,472 @@
+/* $Id: mailcap.c,v 1.12 2003/01/17 17:06:04 ukai Exp $ */
+#include "fm.h"
+#include "myctype.h"
+#include <stdio.h>
+#include <errno.h>
+#include "parsetag.h"
+#include "local.h"
+
+static struct mailcap DefaultMailcap[] = {
+ {"image/*", DEF_IMAGE_VIEWER " %s", 0, NULL, NULL, NULL}, /* */
+ {"audio/basic", DEF_AUDIO_PLAYER " %s", 0, NULL, NULL, NULL},
+ {NULL, NULL, 0, NULL, NULL, NULL}
+};
+
+static TextList *mailcap_list;
+static struct mailcap **UserMailcap;
+
+int
+mailcapMatch(struct mailcap *mcap, char *type)
+{
+ char *cap = mcap->type, *p;
+ int level;
+ for (p = cap; *p != '/'; p++) {
+ if (TOLOWER(*p) != TOLOWER(*type))
+ return 0;
+ type++;
+ }
+ if (*type != '/')
+ return 0;
+ p++;
+ type++;
+ if (mcap->flags & MAILCAP_HTMLOUTPUT)
+ level = 1;
+ else
+ level = 0;
+ if (*p == '*')
+ return 10 + level;
+ while (*p) {
+ if (TOLOWER(*p) != TOLOWER(*type))
+ return 0;
+ p++;
+ type++;
+ }
+ if (*type != '\0')
+ return 0;
+ return 20 + level;
+}
+
+struct mailcap *
+searchMailcap(struct mailcap *table, char *type)
+{
+ int level = 0;
+ struct mailcap *mcap = NULL;
+ int i;
+
+ if (table == NULL)
+ return NULL;
+ for (; table->type; table++) {
+ i = mailcapMatch(table, type);
+ if (i > level) {
+ if (table->test) {
+ Str command =
+ unquote_mailcap(table->test, type, NULL, NULL, NULL);
+ if (system(command->ptr) != 0)
+ continue;
+ }
+ level = i;
+ mcap = table;
+ }
+ }
+ return mcap;
+}
+
+static int
+matchMailcapAttr(char *p, char *attr, int len, Str *value)
+{
+ int quoted;
+ char *q = NULL;
+
+ if (strncasecmp(p, attr, len) == 0) {
+ p += len;
+ SKIP_BLANKS(p);
+ if (value) {
+ *value = Strnew();
+ if (*p == '=') {
+ p++;
+ SKIP_BLANKS(p);
+ quoted = 0;
+ while (*p && (quoted || *p != ';')) {
+ if (quoted || !IS_SPACE(*p))
+ q = p;
+ if (quoted)
+ quoted = 0;
+ else if (*p == '\\')
+ quoted = 1;
+ Strcat_char(*value, *p);
+ p++;
+ }
+ if (q)
+ Strshrink(*value, p - q - 1);
+ }
+ return 1;
+ }
+ else {
+ if (*p == '\0' || *p == ';') {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+extractMailcapEntry(char *mcap_entry, struct mailcap *mcap)
+{
+ int j, k;
+ char *p;
+ int quoted;
+ Str tmp;
+
+ bzero(mcap, sizeof(struct mailcap));
+ p = mcap_entry;
+ SKIP_BLANKS(p);
+ k = -1;
+ for (j = 0; p[j] && p[j] != ';'; j++) {
+ if (!IS_SPACE(p[j]))
+ k = j;
+ }
+ mcap->type = allocStr(p, (k >= 0) ? k + 1 : j);
+ if (!p[j])
+ return 0;
+ p += j + 1;
+
+ SKIP_BLANKS(p);
+ k = -1;
+ quoted = 0;
+ for (j = 0; p[j] && (quoted || p[j] != ';'); j++) {
+ if (quoted || !IS_SPACE(p[j]))
+ k = j;
+ if (quoted)
+ quoted = 0;
+ else if (p[j] == '\\')
+ quoted = 1;
+ }
+ mcap->viewer = allocStr(p, (k >= 0) ? k + 1 : j);
+ p += j;
+
+ while (*p == ';') {
+ p++;
+ SKIP_BLANKS(p);
+ if (matchMailcapAttr(p, "needsterminal", 13, NULL)) {
+ mcap->flags |= MAILCAP_NEEDSTERMINAL;
+ }
+ else if (matchMailcapAttr(p, "copiousoutput", 13, NULL)) {
+ mcap->flags |= MAILCAP_COPIOUSOUTPUT;
+ }
+ else if (matchMailcapAttr(p, "x-htmloutput", 12, NULL) ||
+ matchMailcapAttr(p, "htmloutput", 10, NULL)) {
+ mcap->flags |= MAILCAP_HTMLOUTPUT;
+ }
+ else if (matchMailcapAttr(p, "test", 4, &tmp)) {
+ mcap->test = allocStr(tmp->ptr, tmp->length);
+ }
+ else if (matchMailcapAttr(p, "nametemplate", 12, &tmp)) {
+ mcap->nametemplate = allocStr(tmp->ptr, tmp->length);
+ }
+ else if (matchMailcapAttr(p, "edit", 4, &tmp)) {
+ mcap->edit = allocStr(tmp->ptr, tmp->length);
+ }
+ quoted = 0;
+ while (*p && (quoted || *p != ';')) {
+ if (quoted)
+ quoted = 0;
+ else if (*p == '\\')
+ quoted = 1;
+ p++;
+ }
+ }
+ return 1;
+}
+
+static struct mailcap *
+loadMailcap(char *filename)
+{
+ FILE *f;
+ int i, n;
+ Str tmp;
+ struct mailcap *mcap;
+
+ f = fopen(expandPath(filename), "r");
+ if (f == NULL)
+ return NULL;
+ i = 0;
+ while (tmp = Strfgets(f), tmp->length > 0) {
+ if (tmp->ptr[0] != '#')
+ i++;
+ }
+ fseek(f, 0, 0);
+ n = i;
+ mcap = New_N(struct mailcap, n + 1);
+ i = 0;
+ while (tmp = Strfgets(f), tmp->length > 0) {
+ if (tmp->ptr[0] == '#')
+ continue;
+ redo:
+ while (IS_SPACE(Strlastchar(tmp)))
+ Strshrink(tmp, 1);
+ if (Strlastchar(tmp) == '\\') {
+ /* continuation */
+ Strshrink(tmp, 1);
+ Strcat(tmp, Strfgets(f));
+ goto redo;
+ }
+ if (extractMailcapEntry(tmp->ptr, &mcap[i]))
+ i++;
+ }
+ bzero(&mcap[i], sizeof(struct mailcap));
+ fclose(f);
+ return mcap;
+}
+
+void
+initMailcap()
+{
+ TextListItem *tl;
+ int i;
+
+ if (non_null(mailcap_files))
+ mailcap_list = make_domain_list(mailcap_files);
+ else
+ mailcap_list = NULL;
+ if (mailcap_list == NULL)
+ return;
+ UserMailcap = New_N(struct mailcap *, mailcap_list->nitem);
+ for (i = 0, tl = mailcap_list->first; tl; i++, tl = tl->next)
+ UserMailcap[i] = loadMailcap(tl->ptr);
+
+}
+
+char *
+acceptableMimeTypes()
+{
+ static Str types = NULL;
+ TextList *l;
+ Hash_si *mhash;
+ char *p;
+ int i;
+
+ if (types != NULL)
+ return types->ptr;
+
+ /* generate acceptable media types */
+ l = newTextList();
+ mhash = newHash_si(16); /* XXX */
+ pushText(l, "text");
+ putHash_si(mhash, "text", 1);
+ pushText(l, "image");
+ putHash_si(mhash, "image", 1);
+ for (i = 0; i < mailcap_list->nitem; i++) {
+ struct mailcap *mp = UserMailcap[i];
+ char *mt;
+ if (mp == NULL)
+ continue;
+ for (; mp->type; mp++) {
+ p = strchr(mp->type, '/');
+ if (p == NULL)
+ continue;
+ mt = allocStr(mp->type, p - mp->type);
+ if (getHash_si(mhash, mt, 0) == 0) {
+ pushText(l, mt);
+ putHash_si(mhash, mt, 1);
+ }
+ }
+ }
+ while ((p = popText(l)) != NULL) {
+ if (types == NULL)
+ types = Strnew();
+ else
+ Strcat_charp(types, ", ");
+ Strcat_charp(types, p);
+ Strcat_charp(types, "/*");
+ }
+ return types->ptr;
+}
+
+struct mailcap *
+searchExtViewer(char *type)
+{
+ struct mailcap *p;
+ int i;
+
+ if (mailcap_list == NULL)
+ goto no_user_mailcap;
+
+ for (i = 0; i < mailcap_list->nitem; i++) {
+ if ((p = searchMailcap(UserMailcap[i], type)) != NULL)
+ return p;
+ }
+
+ no_user_mailcap:
+ return searchMailcap(DefaultMailcap, type);
+}
+
+#define MC_NORMAL 0
+#define MC_PREC 1
+#define MC_PREC2 2
+#define MC_QUOTED 3
+
+#define MCF_SQUOTED (1 << 0)
+#define MCF_DQUOTED (1 << 1)
+
+Str
+quote_mailcap(char *s, int flag)
+{
+ Str d;
+
+ d = Strnew();
+
+ for (;; ++s)
+ switch (*s) {
+ case '\0':
+ goto end;
+ case '$':
+ case '`':
+ case '"':
+ case '\\':
+ if (!(flag & MCF_SQUOTED))
+ Strcat_char(d, '\\');
+
+ Strcat_char(d, *s);
+ break;
+ case '\'':
+ if (flag & MCF_SQUOTED) {
+ Strcat_charp(d, "'\\''");
+ break;
+ }
+ default:
+ if (!flag && !IS_ALNUM(*s))
+ Strcat_char(d, '\\');
+ case '_':
+ case '.':
+ case ':':
+ case '/':
+ Strcat_char(d, *s);
+ break;
+ }
+ end:
+ return d;
+}
+
+
+static Str
+unquote_mailcap_loop(char *qstr, char *type, char *name, char *attr,
+ int *mc_stat, int flag0)
+{
+ Str str, tmp, test, then;
+ char *p;
+ int status = MC_NORMAL, prev_status = MC_NORMAL, sp = 0, flag;
+
+ if (mc_stat)
+ *mc_stat = 0;
+
+ if (qstr == NULL)
+ return NULL;
+
+ str = Strnew();
+ tmp = test = then = NULL;
+
+ for (flag = flag0, p = qstr; *p; p++) {
+ if (status == MC_QUOTED) {
+ if (prev_status == MC_PREC2)
+ Strcat_char(tmp, *p);
+ else
+ Strcat_char(str, *p);
+ status = prev_status;
+ continue;
+ }
+ else if (*p == '\\') {
+ prev_status = status;
+ status = MC_QUOTED;
+ continue;
+ }
+ switch (status) {
+ case MC_NORMAL:
+ if (*p == '%') {
+ status = MC_PREC;
+ }
+ else {
+ if (*p == '\'') {
+ if (!flag0 && flag & MCF_SQUOTED)
+ flag &= ~MCF_SQUOTED;
+ else if (!flag)
+ flag |= MCF_SQUOTED;
+ }
+ else if (*p == '"') {
+ if (!flag0 && flag & MCF_DQUOTED)
+ flag &= ~MCF_DQUOTED;
+ else if (!flag)
+ flag |= MCF_DQUOTED;
+ }
+ Strcat_char(str, *p);
+ }
+ break;
+ case MC_PREC:
+ if (IS_ALPHA(*p)) {
+ switch (*p) {
+ case 's':
+ if (name) {
+ Strcat_charp(str, quote_mailcap(name, flag)->ptr);
+ if (mc_stat)
+ *mc_stat |= MCSTAT_REPNAME;
+ }
+ break;
+ case 't':
+ if (type) {
+ Strcat_charp(str, quote_mailcap(type, flag)->ptr);
+ if (mc_stat)
+ *mc_stat |= MCSTAT_REPTYPE;
+ }
+ break;
+ }
+ status = MC_NORMAL;
+ }
+ else if (*p == '{') {
+ status = MC_PREC2;
+ test = then = NULL;
+ tmp = Strnew();
+ }
+ else if (*p == '%') {
+ Strcat_char(str, *p);
+ }
+ break;
+ case MC_PREC2:
+ if (sp > 0 || *p == '{') {
+ Strcat_char(tmp, *p);
+
+ switch (*p) {
+ case '{':
+ ++sp;
+ break;
+ case '}':
+ --sp;
+ break;
+ default:
+ break;
+ }
+ }
+ else if (*p == '}') {
+ char *q;
+ if (attr && (q = strcasestr(attr, tmp->ptr)) != NULL &&
+ (q == attr || IS_SPACE(*(q - 1)) || *(q - 1) == ';') &&
+ matchattr(q, tmp->ptr, tmp->length, &tmp)) {
+ Strcat_charp(str, quote_mailcap(tmp->ptr, flag)->ptr);
+ if (mc_stat)
+ *mc_stat |= MCSTAT_REPPARAM;
+ }
+ status = MC_NORMAL;
+ }
+ else {
+ Strcat_char(tmp, *p);
+ }
+ break;
+ }
+ }
+ return str;
+}
+
+Str
+unquote_mailcap(char *qstr, char *type, char *name, char *attr, int *mc_stat)
+{
+ return unquote_mailcap_loop(qstr, type, name, attr, mc_stat, 0);
+}