mailcap.c (9246B)
1 /* $Id$ */ 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 }