1 /* $Id: mailcap.c,v 1.13 2006/08/07 03:10:26 ukai Exp $ */
2 #include "fm.h"
3 #include "myctype.h"
4 #include <stdio.h>
5 #include <errno.h>
6 #include "parsetag.h"
7 #include "local.h"
8 
9 static struct mailcap DefaultMailcap[] = {
10     {"image/*", DEF_IMAGE_VIEWER " %s", 0, NULL, NULL, NULL},	/* */
11     {"audio/basic", DEF_AUDIO_PLAYER " %s", 0, NULL, NULL, NULL},
12     {NULL, NULL, 0, NULL, NULL, NULL}
13 };
14 
15 static TextList *mailcap_list;
16 static struct mailcap **UserMailcap;
17 
18 int
mailcapMatch(struct mailcap * mcap,char * type)19 mailcapMatch(struct mailcap *mcap, char *type)
20 {
21     char *cap = mcap->type, *p;
22     int level;
23     for (p = cap; *p != '/'; p++) {
24 	if (TOLOWER(*p) != TOLOWER(*type))
25 	    return 0;
26 	type++;
27     }
28     if (*type != '/')
29 	return 0;
30     p++;
31     type++;
32     if (mcap->flags & MAILCAP_HTMLOUTPUT)
33 	level = 1;
34     else
35 	level = 0;
36     if (*p == '*')
37 	return 10 + level;
38     while (*p) {
39 	if (TOLOWER(*p) != TOLOWER(*type))
40 	    return 0;
41 	p++;
42 	type++;
43     }
44     if (*type != '\0')
45 	return 0;
46     return 20 + level;
47 }
48 
49 struct mailcap *
searchMailcap(struct mailcap * table,char * type)50 searchMailcap(struct mailcap *table, char *type)
51 {
52     int level = 0;
53     struct mailcap *mcap = NULL;
54     int i;
55 
56     if (table == NULL)
57 	return NULL;
58     for (; table->type; table++) {
59 	i = mailcapMatch(table, type);
60 	if (i > level) {
61 	    if (table->test) {
62 		Str command =
63 		    unquote_mailcap(table->test, type, NULL, NULL, NULL);
64 		if (system(command->ptr) != 0)
65 		    continue;
66 	    }
67 	    level = i;
68 	    mcap = table;
69 	}
70     }
71     return mcap;
72 }
73 
74 static int
matchMailcapAttr(char * p,char * attr,size_t len,Str * value)75 matchMailcapAttr(char *p, char *attr, size_t len, Str *value)
76 {
77     int quoted;
78     char *q = NULL;
79 
80     if (strncasecmp(p, attr, len) == 0) {
81 	p += len;
82 	SKIP_BLANKS(p);
83 	if (value) {
84 	    *value = Strnew();
85 	    if (*p == '=') {
86 		p++;
87 		SKIP_BLANKS(p);
88 		quoted = 0;
89 		while (*p && (quoted || *p != ';')) {
90 		    if (quoted || !IS_SPACE(*p))
91 			q = p;
92 		    if (quoted)
93 			quoted = 0;
94 		    else if (*p == '\\')
95 			quoted = 1;
96 		    Strcat_char(*value, *p);
97 		    p++;
98 		}
99 		if (q)
100 		    Strshrink(*value, p - q - 1);
101 	    }
102 	    return 1;
103 	}
104 	else {
105 	    if (*p == '\0' || *p == ';') {
106 		return 1;
107 	    }
108 	}
109     }
110     return 0;
111 }
112 
113 static int
extractMailcapEntry(char * mcap_entry,struct mailcap * mcap)114 extractMailcapEntry(char *mcap_entry, struct mailcap *mcap)
115 {
116     int j, k;
117     char *p;
118     int quoted;
119     Str tmp;
120 
121     bzero(mcap, sizeof(struct mailcap));
122     p = mcap_entry;
123     SKIP_BLANKS(p);
124     k = -1;
125     for (j = 0; p[j] && p[j] != ';'; j++) {
126 	if (!IS_SPACE(p[j]))
127 	    k = j;
128     }
129     mcap->type = allocStr(p, (k >= 0) ? k + 1 : j);
130     if (!p[j])
131 	return 0;
132     p += j + 1;
133 
134     SKIP_BLANKS(p);
135     k = -1;
136     quoted = 0;
137     for (j = 0; p[j] && (quoted || p[j] != ';'); j++) {
138 	if (quoted || !IS_SPACE(p[j]))
139 	    k = j;
140 	if (quoted)
141 	    quoted = 0;
142 	else if (p[j] == '\\')
143 	    quoted = 1;
144     }
145     mcap->viewer = allocStr(p, (k >= 0) ? k + 1 : j);
146     p += j;
147 
148     while (*p == ';') {
149 	p++;
150 	SKIP_BLANKS(p);
151 	if (matchMailcapAttr(p, "needsterminal", 13, NULL)) {
152 	    mcap->flags |= MAILCAP_NEEDSTERMINAL;
153 	}
154 	else if (matchMailcapAttr(p, "copiousoutput", 13, NULL)) {
155 	    mcap->flags |= MAILCAP_COPIOUSOUTPUT;
156 	}
157 	else if (matchMailcapAttr(p, "x-htmloutput", 12, NULL) ||
158 		 matchMailcapAttr(p, "htmloutput", 10, NULL)) {
159 	    mcap->flags |= MAILCAP_HTMLOUTPUT;
160 	}
161 	else if (matchMailcapAttr(p, "test", 4, &tmp)) {
162 	    mcap->test = allocStr(tmp->ptr, tmp->length);
163 	}
164 	else if (matchMailcapAttr(p, "nametemplate", 12, &tmp)) {
165 	    mcap->nametemplate = allocStr(tmp->ptr, tmp->length);
166 	}
167 	else if (matchMailcapAttr(p, "edit", 4, &tmp)) {
168 	    mcap->edit = allocStr(tmp->ptr, tmp->length);
169 	}
170 	quoted = 0;
171 	while (*p && (quoted || *p != ';')) {
172 	    if (quoted)
173 		quoted = 0;
174 	    else if (*p == '\\')
175 		quoted = 1;
176 	    p++;
177 	}
178     }
179     return 1;
180 }
181 
182 static struct mailcap *
loadMailcap(char * filename)183 loadMailcap(char *filename)
184 {
185     FILE *f;
186     int i, n;
187     Str tmp;
188     struct mailcap *mcap;
189 
190     f = fopen(expandPath(filename), "r");
191     if (f == NULL)
192 	return NULL;
193     i = 0;
194     while (tmp = Strfgets(f), tmp->length > 0) {
195 	if (tmp->ptr[0] != '#')
196 	    i++;
197     }
198     fseek(f, 0, 0);
199     n = i;
200     mcap = New_N(struct mailcap, n + 1);
201     i = 0;
202     while (tmp = Strfgets(f), tmp->length > 0) {
203 	if (tmp->ptr[0] == '#')
204 	    continue;
205       redo:
206 	while (IS_SPACE(Strlastchar(tmp)))
207 	    Strshrink(tmp, 1);
208 	if (Strlastchar(tmp) == '\\') {
209 	    /* continuation */
210 	    Strshrink(tmp, 1);
211 	    Strcat(tmp, Strfgets(f));
212 	    goto redo;
213 	}
214 	if (extractMailcapEntry(tmp->ptr, &mcap[i]))
215 	    i++;
216     }
217     bzero(&mcap[i], sizeof(struct mailcap));
218     fclose(f);
219     return mcap;
220 }
221 
222 void
initMailcap()223 initMailcap()
224 {
225     TextListItem *tl;
226     int i;
227 
228     if (non_null(mailcap_files))
229 	mailcap_list = make_domain_list(mailcap_files);
230     else
231 	mailcap_list = NULL;
232     if (mailcap_list == NULL)
233 	return;
234     UserMailcap = New_N(struct mailcap *, mailcap_list->nitem);
235     for (i = 0, tl = mailcap_list->first; tl; i++, tl = tl->next)
236 	UserMailcap[i] = loadMailcap(tl->ptr);
237 
238 }
239 
240 char *
acceptableMimeTypes()241 acceptableMimeTypes()
242 {
243     static Str types = NULL;
244     TextList *l;
245     Hash_si *mhash;
246     char *p;
247     int i;
248 
249     if (types != NULL)
250 	return types->ptr;
251 
252     /* generate acceptable media types */
253     l = newTextList();
254     mhash = newHash_si(16);	/* XXX */
255     /* pushText(l, "text"); */
256     putHash_si(mhash, "text", 1);
257     pushText(l, "image");
258     putHash_si(mhash, "image", 1);
259     for (i = 0; i < mailcap_list->nitem; i++) {
260 	struct mailcap *mp = UserMailcap[i];
261 	char *mt;
262 	if (mp == NULL)
263 	    continue;
264 	for (; mp->type; mp++) {
265 	    p = strchr(mp->type, '/');
266 	    if (p == NULL)
267 		continue;
268 	    mt = allocStr(mp->type, p - mp->type);
269 	    if (getHash_si(mhash, mt, 0) == 0) {
270 		pushText(l, mt);
271 		putHash_si(mhash, mt, 1);
272 	    }
273 	}
274     }
275     types = Strnew();
276     Strcat_charp(types, "text/html, text/*;q=0.5");
277     while ((p = popText(l)) != NULL) {
278 	Strcat_charp(types, ", ");
279 	Strcat_charp(types, p);
280 	Strcat_charp(types, "/*");
281     }
282     return types->ptr;
283 }
284 
285 struct mailcap *
searchExtViewer(char * type)286 searchExtViewer(char *type)
287 {
288     struct mailcap *p;
289     int i;
290 
291     if (mailcap_list == NULL)
292 	goto no_user_mailcap;
293 
294     for (i = 0; i < mailcap_list->nitem; i++) {
295 	if ((p = searchMailcap(UserMailcap[i], type)) != NULL)
296 	    return p;
297     }
298 
299   no_user_mailcap:
300     return searchMailcap(DefaultMailcap, type);
301 }
302 
303 #define MC_NORMAL 0
304 #define MC_PREC   1
305 #define MC_PREC2  2
306 #define MC_QUOTED 3
307 
308 #define MCF_SQUOTED (1 << 0)
309 #define MCF_DQUOTED (1 << 1)
310 
311 Str
quote_mailcap(char * s,int flag)312 quote_mailcap(char *s, int flag)
313 {
314     Str d;
315 
316     d = Strnew();
317 
318     for (;; ++s)
319 	switch (*s) {
320 	case '\0':
321 	    goto end;
322 	case '$':
323 	case '`':
324 	case '"':
325 	case '\\':
326 	    if (!(flag & MCF_SQUOTED))
327 		Strcat_char(d, '\\');
328 
329 	    Strcat_char(d, *s);
330 	    break;
331 	case '\'':
332 	    if (flag & MCF_SQUOTED) {
333 		Strcat_charp(d, "'\\''");
334 		break;
335 	    }
336 	default:
337 	    if (!flag && !IS_ALNUM(*s))
338 		Strcat_char(d, '\\');
339 	case '_':
340 	case '.':
341 	case ':':
342 	case '/':
343 	    Strcat_char(d, *s);
344 	    break;
345 	}
346   end:
347     return d;
348 }
349 
350 
351 static Str
unquote_mailcap_loop(char * qstr,char * type,char * name,char * attr,int * mc_stat,int flag0)352 unquote_mailcap_loop(char *qstr, char *type, char *name, char *attr,
353 		     int *mc_stat, int flag0)
354 {
355     Str str, tmp, test, then;
356     char *p;
357     int status = MC_NORMAL, prev_status = MC_NORMAL, sp = 0, flag;
358 
359     if (mc_stat)
360 	*mc_stat = 0;
361 
362     if (qstr == NULL)
363 	return NULL;
364 
365     str = Strnew();
366     tmp = test = then = NULL;
367 
368     for (flag = flag0, p = qstr; *p; p++) {
369 	if (status == MC_QUOTED) {
370 	    if (prev_status == MC_PREC2)
371 		Strcat_char(tmp, *p);
372 	    else
373 		Strcat_char(str, *p);
374 	    status = prev_status;
375 	    continue;
376 	}
377 	else if (*p == '\\') {
378 	    prev_status = status;
379 	    status = MC_QUOTED;
380 	    continue;
381 	}
382 	switch (status) {
383 	case MC_NORMAL:
384 	    if (*p == '%') {
385 		status = MC_PREC;
386 	    }
387 	    else {
388 		if (*p == '\'') {
389 		    if (!flag0 && flag & MCF_SQUOTED)
390 			flag &= ~MCF_SQUOTED;
391 		    else if (!flag)
392 			flag |= MCF_SQUOTED;
393 		}
394 		else if (*p == '"') {
395 		    if (!flag0 && flag & MCF_DQUOTED)
396 			flag &= ~MCF_DQUOTED;
397 		    else if (!flag)
398 			flag |= MCF_DQUOTED;
399 		}
400 		Strcat_char(str, *p);
401 	    }
402 	    break;
403 	case MC_PREC:
404 	    if (IS_ALPHA(*p)) {
405 		switch (*p) {
406 		case 's':
407 		    if (name) {
408 			Strcat_charp(str, quote_mailcap(name, flag)->ptr);
409 			if (mc_stat)
410 			    *mc_stat |= MCSTAT_REPNAME;
411 		    }
412 		    break;
413 		case 't':
414 		    if (type) {
415 			Strcat_charp(str, quote_mailcap(type, flag)->ptr);
416 			if (mc_stat)
417 			    *mc_stat |= MCSTAT_REPTYPE;
418 		    }
419 		    break;
420 		}
421 		status = MC_NORMAL;
422 	    }
423 	    else if (*p == '{') {
424 		status = MC_PREC2;
425 		test = then = NULL;
426 		tmp = Strnew();
427 	    }
428 	    else if (*p == '%') {
429 		Strcat_char(str, *p);
430 	    }
431 	    break;
432 	case MC_PREC2:
433 	    if (sp > 0 || *p == '{') {
434 		Strcat_char(tmp, *p);
435 
436 		switch (*p) {
437 		case '{':
438 		    ++sp;
439 		    break;
440 		case '}':
441 		    --sp;
442 		    break;
443 		default:
444 		    break;
445 		}
446 	    }
447 	    else if (*p == '}') {
448 		char *q;
449 		if (attr && (q = strcasestr(attr, tmp->ptr)) != NULL &&
450 		    (q == attr || IS_SPACE(*(q - 1)) || *(q - 1) == ';') &&
451 		    matchattr(q, tmp->ptr, tmp->length, &tmp)) {
452 		    Strcat_charp(str, quote_mailcap(tmp->ptr, flag)->ptr);
453 		    if (mc_stat)
454 			*mc_stat |= MCSTAT_REPPARAM;
455 		}
456 		status = MC_NORMAL;
457 	    }
458 	    else {
459 		Strcat_char(tmp, *p);
460 	    }
461 	    break;
462 	}
463     }
464     return str;
465 }
466 
467 Str
unquote_mailcap(char * qstr,char * type,char * name,char * attr,int * mc_stat)468 unquote_mailcap(char *qstr, char *type, char *name, char *attr, int *mc_stat)
469 {
470     return unquote_mailcap_loop(qstr, type, name, attr, mc_stat, 0);
471 }
472