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