Intial commit
[profile/ivi/w3m.git] / mailcap.c
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
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 *
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
75 matchMailcapAttr(char *p, char *attr, int 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
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 *
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
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 *
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 *
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
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
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
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 }