Commit adaadb24 adaadb24ddb3e78682ddbe2ef767326fb0c50ccc by Alain Magloire

Added Files:

 	mailcap.c
First implementation of rfc1524.
1 parent aee0148e
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2003 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with GNU Mailutils; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <stdlib.h>
23 #include <ctype.h>
24 #include <string.h>
25 #include <errno.h>
26
27 #include <mailcap.h>
28
29 /* Definition of the structure, this should be in mailutils/sys/mailcap.h. */
30 struct _mu_mailcap_entry
31 {
32 char * typefield;
33 char * viewcommand;
34 char ** fields;
35 size_t fields_count;
36 };
37
38 /* Definition of the structure, this should be in mailutils/sys/mailcap.h. */
39 struct _mu_mailcap
40 {
41 mu_mailcap_entry_t *entries;
42 size_t entries_count;
43 };
44
45
46 static int mu_mailcap_parse (mu_mailcap_t mailcap, stream_t stream);
47 static int mu_mailcap_parse_entry (mu_mailcap_entry_t entry, char *buffer);
48 static char * stripwhite (char *string);
49 static char * tokenize (char *s, char **save_ptr);
50
51 int
52 mu_mailcap_create (mu_mailcap_t * pmailcap, stream_t stream)
53 {
54 mu_mailcap_t mailcap;
55 int status = 0;
56
57 if (stream == NULL || pmailcap == NULL)
58 {
59 return EINVAL;
60 }
61
62 mailcap = calloc (1, sizeof (*mailcap));
63 if (mailcap != NULL)
64 {
65 status = mu_mailcap_parse (mailcap, stream);
66 if (status != 0)
67 {
68 mu_mailcap_destroy (&mailcap);
69 }
70 else
71 {
72 *pmailcap = mailcap;
73 }
74 }
75 else
76 {
77 status = ENOMEM;
78 }
79 return status;
80 }
81
82 void
83 mu_mailcap_destroy (mu_mailcap_t * pmailcap)
84 {
85 if (pmailcap != NULL && *pmailcap != NULL)
86 {
87 int i;
88 mu_mailcap_t mailcap = *pmailcap;
89
90 for (i = 0; i < mailcap->entries_count; i++)
91 {
92 int j;
93 mu_mailcap_entry_t entry = mailcap->entries[i];
94 free (entry->typefield);
95 free (entry->viewcommand);
96 for (j = 0; j < entry->fields_count; j++)
97 {
98 free (entry->fields[j]);
99 }
100 }
101 }
102 }
103
104 int
105 mu_mailcap_entries_count (mu_mailcap_t mailcap, size_t *pcount)
106 {
107 int status = 0;
108 if (mailcap == NULL)
109 {
110 status = EINVAL;
111 }
112 if (pcount != NULL)
113 {
114 *pcount = mailcap->entries_count;
115 }
116 return status;
117 }
118
119 int
120 mu_mailcap_get_entry (mu_mailcap_t mailcap, size_t no, mu_mailcap_entry_t *pentry)
121 {
122 int status = 0;
123 if (mailcap == NULL || pentry == NULL)
124 {
125 status = EINVAL;
126 }
127 else if (no == 0 || no > mailcap->entries_count)
128 {
129 status = ENOENT;
130 }
131 else
132 {
133 *pentry = mailcap->entries[no - 1];
134 }
135 return status;
136 }
137
138 int
139 mu_mailcap_entry_get_typefield(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
140 {
141 int status = 0;
142 int len = 0;
143
144 if (entry == NULL)
145 {
146 status = EINVAL;
147 }
148 else
149 {
150 len = strlen(entry->typefield);
151 if (buffer != NULL && buflen > 0)
152 {
153 buflen--;
154 len = (len < buflen) ? len : buflen;
155 memcpy (buffer, entry->typefield, len);
156 buffer[len] = '\0';
157 }
158 }
159 if (pn)
160 {
161 *pn = len;
162 }
163 return status;
164 }
165
166 int
167 mu_mailcap_entry_get_viewcommand(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
168 {
169 int status = 0;
170 int len = 0;
171
172 if (entry == NULL)
173 {
174 status = EINVAL;
175 }
176 else
177 {
178 len = strlen(entry->viewcommand);
179 if (buffer != NULL && buflen > 0)
180 {
181 buflen--;
182 len = (len < buflen) ? len : buflen;
183 memcpy (buffer, entry->viewcommand, len);
184 buffer[len] = '\0';
185 }
186 }
187 if (pn)
188 {
189 *pn = len;
190 }
191 return status;
192 }
193
194 int
195 mu_mailcap_entry_fields_count (mu_mailcap_entry_t entry, size_t *pcount)
196 {
197 int status = 0;
198 if (entry == NULL)
199 {
200 status = EINVAL;
201 }
202 if (pcount != NULL)
203 {
204 *pcount = entry->fields_count;
205 }
206 return status;
207 }
208
209 int
210 mu_mailcap_entry_get_field (mu_mailcap_entry_t entry, size_t no, char *buffer, size_t buflen, size_t *pn)
211 {
212 int status = 0;
213 int len = 0;
214
215 if (entry == NULL)
216 {
217 status = EINVAL;
218 }
219 else if ( no == 0 || no > entry->fields_count)
220 {
221 status = ENOENT;
222 }
223 else
224 {
225 len = strlen(entry->fields[no - 1]);
226 if (buffer != NULL && buflen > 0)
227 {
228 buflen--;
229 len = (len < buflen) ? len : buflen;
230 memcpy (buffer, entry->fields[no - 1], len);
231 buffer[len] = '\0';
232 }
233 }
234 if (pn)
235 {
236 *pn = len;
237 }
238 return status;
239 }
240
241 int
242 mu_mailcap_entry_get_compose(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
243 {
244 return mu_mailcap_entry_get_value (entry, "compose", buffer, buflen, pn);
245 }
246
247 int
248 mu_mailcap_entry_get_composetyped(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
249 {
250 return mu_mailcap_entry_get_value (entry, "composetyped", buffer, buflen, pn);
251 }
252
253 int
254 mu_mailcap_entry_get_edit(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
255 {
256 return mu_mailcap_entry_get_value (entry, "edit", buffer, buflen, pn);
257 }
258
259 int
260 mu_mailcap_entry_get_test(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
261 {
262 return mu_mailcap_entry_get_value (entry, "test", buffer, buflen, pn);
263 }
264
265 int
266 mu_mailcap_entry_get_x11bitmap(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
267 {
268 return mu_mailcap_entry_get_value (entry, "x11-bitmap", buffer, buflen, pn);
269 }
270
271 int
272 mu_mailcap_entry_get_description(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
273 {
274 return mu_mailcap_entry_get_value (entry, "description", buffer, buflen, pn);
275 }
276
277 int
278 mu_mailcap_entry_needsterminal(mu_mailcap_entry_t entry, int *on)
279 {
280 int status = 0;
281 int found = 0;
282 if (entry == NULL)
283 {
284 status = EINVAL;
285 }
286 else
287 {
288 int i;
289 for (i = 0; i < entry->fields_count; i++)
290 {
291 int n = strcasecmp (entry->fields[i], "needsterminal");
292 if (n == 0)
293 {
294 found = 1;
295 break;
296 }
297 }
298 }
299 if (on)
300 *on = found;
301 return status;
302 }
303
304 int
305 mu_mailcap_entry_coupiousoutput(mu_mailcap_entry_t entry, int *on)
306 {
307 int status = 0;
308 int found = 0;
309 if (entry == NULL)
310 {
311 status = EINVAL;
312 }
313 else
314 {
315 int i;
316 for (i = 0; i < entry->fields_count; i++)
317 {
318 int n = strcasecmp (entry->fields[i], "coupiousoutput");
319 if (n == 0)
320 {
321 found = 1;
322 break;
323 }
324 }
325 }
326 if (on)
327 *on = found;
328 return status;
329 }
330
331 int
332 mu_mailcap_entry_get_value (mu_mailcap_entry_t entry, const char *key, char *buffer, size_t buflen, size_t *pn)
333 {
334 int len = 0;
335 int status = 0;
336 if (entry == NULL)
337 {
338 status = EINVAL;
339 }
340 else
341 {
342 int i;
343 int key_len = strlen (key);
344 for (i = 0; i < entry->fields_count; i++)
345 {
346 int n = strncasecmp (entry->fields[i], key, key_len);
347 if (n == 0)
348 {
349 int field_len = strlen(entry->fields[i]);
350 if (field_len > key_len)
351 {
352 int c = entry->fields[i][key_len];
353 if (isspace(c) || c == '=')
354 {
355 char *value = strchr (entry->fields[i], '=');
356 if (value != NULL)
357 {
358 value++; /* Pass the equal. */
359 /* Remove prepend space. */
360 for (; isspace ((unsigned char)*value); value++)
361 ;
362 len = strlen (value);
363 /* Strip surrounding double quotes */
364 if (len > 1 && value[0] == '"' && value[len - 1] == '"')
365 {
366 value++;
367 len -= 2;
368 }
369 if (buffer != NULL && buflen > 0)
370 {
371 buflen--;
372 len = (len < buflen) ? len : buflen;
373 memcpy (buffer, value, len);
374 buffer[len] = '\0';
375 }
376 break;
377 }
378 }
379 }
380 }
381 }
382 }
383 if (pn)
384 *pn = len;
385 return status;
386 }
387
388 /* Strip whitespace from the start and end of STRING. Return a pointer
389 into STRING. */
390 static char *
391 stripwhite (char *string)
392 {
393 register char *s, *t;
394
395 for (s = string; isspace ((unsigned char)*s); s++)
396 ;
397
398 if (*s == 0)
399 return (s);
400
401 t = s + strlen (s) - 1;
402 while (t > s && isspace (*t))
403 t--;
404 *++t = '\0';
405
406 return s;
407 }
408
409 /*
410 * break the line on ';'. Same as strtok() but
411 * check for escaped "\;"
412 */
413 static char *
414 tokenize (char *s, char **save_ptr)
415 {
416 int c;
417 char *token;
418
419 if (s == NULL)
420 {
421 s = *save_ptr;
422 }
423
424 if (*s == '\0')
425 {
426 *save_ptr = s;
427 return NULL;
428 }
429
430 for (token = s, c = 0; *s; s++)
431 {
432 if (*s == ';' && c != '\\')
433 {
434 break;
435 }
436 c = *s;
437 }
438
439 if (*s == '\0')
440 {
441 *save_ptr = s;
442 }
443 else
444 {
445 *s = '\0';
446 *save_ptr = s + 1;
447 }
448 return token;
449 }
450
451 /**
452 * parse the mailcap line, fields are separated by ';'
453 */
454 static int
455 mu_mailcap_parse_entry (mu_mailcap_entry_t entry, char *buffer)
456 {
457 char *token = NULL;
458 char *s = NULL;
459 int i;
460 for (i = 0, token = tokenize (buffer, &s); token != NULL; token = tokenize (NULL, &s), i++)
461 {
462 switch (i)
463 {
464 /* The first entry in a mailcap line is the typefield. */
465 case 0:
466 entry->typefield = strdup (stripwhite (token));
467 break;
468
469 /* The second entry in a mailcap line is the view-command. */
470 case 1:
471 entry->viewcommand = strdup (stripwhite(token));
472 break;
473
474 /* The rest are the optionnal fields. */
475 default:
476 {
477 char **fields = realloc (entry->fields, (entry->fields_count + 1) * sizeof (*fields));
478 if (fields != NULL)
479 {
480 entry->fields = fields;
481 entry->fields[entry->fields_count] = strdup (stripwhite (token));
482 entry->fields_count++;
483 }
484 }
485 }
486 }
487 /* Make sure typefield and viewcommand are not null. */
488 if (entry->typefield == NULL)
489 {
490 entry->typefield = strdup ("");
491 }
492 if (entry->viewcommand == NULL)
493 {
494 entry->viewcommand = strdup ("");
495 }
496 return 0;
497 }
498
499 /*
500 * parse a mailcap file or stream,
501 * - ignore empty line.
502 * - ignore line starting with '#'
503 * - multiline is done with the '\' as continuation
504 * example:
505 # comment
506 application/pgp; gpg < %s | metamail; needsterminal; \
507 test=test %{encapsulation}=entity ; copiousoutput
508 */
509 static int
510 mu_mailcap_parse (mu_mailcap_t mailcap, stream_t stream)
511 {
512 off_t off;
513 int status;
514 size_t n;
515 char *previous;
516 char *buffer;
517 int buflen = 512;
518
519 buffer = malloc (buflen * sizeof (*buffer));
520 if (buffer == NULL)
521 {
522 return ENOMEM;
523 }
524
525 /*
526 * We are doing this a little more complexe then expected, because we do not want
527 * to seek() back in the stream:
528 * - we have to take care of continuation line i.e. line ending with '\'
529 * - we have to take to account that the line may be bigger then the buffer and reallocate
530 * - check the return of malloc/realloc
531 * The old continuation line is save in the "previous" pointer and prepend to the buffer.
532 */
533 for (previous = NULL, off = n = 0; (status = stream_readline (stream, buffer, buflen, off, &n)) == 0 && n > 0; off += n)
534 {
535 int len;
536
537 /* If there is no trailing newline, that means the buffer was too small,
538 * make room for the buffer and continue reading */
539 if (buffer[n - 1] != '\n')
540 {
541 char *b = realloc (buffer, buflen * sizeof (*buffer));
542 buflen *= 2;
543 if (b == NULL)
544 {
545 status = ENOMEM;
546 break;
547 }
548 buffer = b;
549 /*
550 * Fake this as a continuation line, for simplicity.
551 */
552 strcat (buffer, "\\");
553 }
554 else
555 {
556 /* Nuke the trailing newline. */
557 buffer[n - 1] = '\0';
558 }
559
560 /* recalculate the len. */
561 len = strlen (buffer);
562
563 /* Ending with a '\' means continuation line. */
564 if (len && buffer[len - 1] == '\\')
565 {
566 buffer[len - 1] = '\0';
567 /*
568 * Check for any previous line:
569 * - if yes append the buffer to the previous line.
570 * - if not set the buffer as the previous line and continue.
571 */
572 if (previous == NULL)
573 {
574 previous = strdup (buffer);
575 if (previous == NULL)
576 {
577 status = ENOMEM;
578 break;
579 }
580 }
581 else
582 {
583 char *b = realloc (previous, strlen (previous) + len + 1);
584 if (b == NULL)
585 {
586 status = ENOMEM;
587 break;
588 }
589 previous = b;
590 strcat(previous, buffer);
591 }
592 }
593 else
594 {
595 /* Did we have a previous incomplete line?
596 * if yes make one line from the previous and the buffer.
597 */
598 if (previous != NULL)
599 {
600 int prev_len = strlen (previous);
601 int total = prev_len + len + 1;
602 if (total > buflen)
603 {
604 char *b = realloc (buffer, total * sizeof (*buffer));
605 if (b == NULL)
606 {
607 status = ENOMEM;
608 break;
609 }
610 buffer = b;
611 buflen = total;
612 }
613 memmove (buffer + prev_len, buffer, len + 1);
614 memcpy (buffer, previous, prev_len);
615 free (previous);
616 previous = NULL;
617 }
618 }
619
620 /* Parse the well-form mailcap line entry. */
621 if (previous == NULL) {
622 /* Nuke the trailing/prepend spaces. */
623 char *line = stripwhite(buffer);
624 /* Ignore comments or empty lines */
625 if (*line != '#' && *line != '\0')
626 {
627 mu_mailcap_entry_t *entries;
628 entries = realloc (mailcap->entries, (mailcap->entries_count + 1) * sizeof (*entries));
629 if (entries != NULL)
630 {
631 mailcap->entries = entries;
632 mailcap->entries[mailcap->entries_count] = calloc (1, sizeof(**entries));
633 if (mailcap->entries[mailcap->entries_count] != NULL)
634 {
635 mu_mailcap_parse_entry (mailcap->entries[mailcap->entries_count], line);
636 }
637 mailcap->entries_count++;
638 }
639 else
640 {
641 status = ENOMEM;
642 break;
643 }
644 }
645 }
646 }
647
648 if (buffer != NULL)
649 {
650 free (buffer);
651 }
652 if (previous != NULL)
653 {
654 free (previous);
655 }
656 return status;
657 }
658
659 #ifdef STANDALONE_TEST
660 int main()
661 {
662 stream_t stream = NULL;
663 int status = 0;
664
665 status = file_stream_create (&stream, "/home/alain/mailcap", MU_STREAM_READ);
666 if (status == 0)
667 {
668 status = stream_open(stream);
669 if (status == 0)
670 {
671 mu_mailcap_t mailcap;
672 status = mu_mailcap_create (&mailcap, stream);
673 if (status == 0)
674 {
675 int i, n;
676 size_t count = 0;
677 char buffer[256];
678
679 mu_mailcap_entries_count (mailcap, &count);
680 for (i = 1; i <= count; i++)
681 {
682 int j;
683 mu_mailcap_entry_t entry = NULL;
684 int fields_count = 0;
685
686 printf ("entry[%d]\n", i);
687 #if 1
688
689 mu_mailcap_get_entry (mailcap, i, &entry);
690 /* Print typefield. */
691 mu_mailcap_entry_get_typefield (entry, buffer, sizeof (buffer), NULL);
692 printf ("\ttypefield: %s\n", buffer);
693
694 /* Print view-command. */
695 mu_mailcap_entry_get_viewcommand (entry, buffer, sizeof (buffer), NULL);
696 printf ("\tview-command: %s\n", buffer);
697
698 /* Print fields. */
699 mu_mailcap_entry_fields_count (entry, &fields_count);
700 for (j = 1; j <= fields_count; j++)
701 {
702 mu_mailcap_entry_get_field (entry, j, buffer, sizeof (buffer), NULL);
703 printf("\tfields[%d]: %s\n", j, buffer);
704 }
705 n = 0;
706 mu_mailcap_entry_get_compose (entry, buffer, sizeof (buffer), &n);
707 if (n > 0)
708 {
709 printf("\tcompose[%s]\n", buffer);
710 }
711 printf("\n");
712 }
713 #else
714 for (i = 0; i < mailcap->entries_count; i++)
715 {
716 int j;
717 mu_mailcap_entry_t entry = mailcap->entries[i];
718 printf("[%s];[%s]", entry->typefield, entry->viewcommand);
719 for (j = 0; j < entry->fields_count; j++)
720 {
721 printf(";[%s]", entry->fields[j]);
722 }
723 printf("\n");
724 }
725 #endif
726 mu_mailcap_destroy (&mailcap);
727 }
728 }
729 }
730 return 0;
731 }
732
733 #endif