/* $Id: mailcap.c,v 1.9 2001/11/30 10:00:06 ukai Exp $ */ #include "fm.h" #include "myctype.h" #include #include #include "parsetag.h" #include "local.h" static struct mailcap DefaultMailcap[] = { {"image/*", "xv %s", 0, NULL, NULL, NULL}, /* */ {"audio/basic", "showaudio %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(expandName(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); }