summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlain Magloire <alainm@gnu.org>2003-02-22 18:09:21 +0000
committerAlain Magloire <alainm@gnu.org>2003-02-22 18:09:21 +0000
commitadaadb24ddb3e78682ddbe2ef767326fb0c50ccc (patch)
tree3c4f09aa9a6bc0b717b93d6525e89317c9e84024
parentaee0148e16092f11e6ba09deee638e1a68b30103 (diff)
downloadmailutils-adaadb24ddb3e78682ddbe2ef767326fb0c50ccc.tar.gz
mailutils-adaadb24ddb3e78682ddbe2ef767326fb0c50ccc.tar.bz2
Added Files:
mailcap.c First implementation of rfc1524.
-rw-r--r--mailbox/mailcap.c733
1 files changed, 733 insertions, 0 deletions
diff --git a/mailbox/mailcap.c b/mailbox/mailcap.c
new file mode 100644
index 000000000..9645c2d85
--- /dev/null
+++ b/mailbox/mailcap.c
@@ -0,0 +1,733 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2003 Free Software Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Mailutils is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with GNU Mailutils; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+
+#include <mailcap.h>
+
+/* Definition of the structure, this should be in mailutils/sys/mailcap.h. */
+struct _mu_mailcap_entry
+{
+ char * typefield;
+ char * viewcommand;
+ char ** fields;
+ size_t fields_count;
+};
+
+/* Definition of the structure, this should be in mailutils/sys/mailcap.h. */
+struct _mu_mailcap
+{
+ mu_mailcap_entry_t *entries;
+ size_t entries_count;
+};
+
+
+static int mu_mailcap_parse (mu_mailcap_t mailcap, stream_t stream);
+static int mu_mailcap_parse_entry (mu_mailcap_entry_t entry, char *buffer);
+static char * stripwhite (char *string);
+static char * tokenize (char *s, char **save_ptr);
+
+int
+mu_mailcap_create (mu_mailcap_t * pmailcap, stream_t stream)
+{
+ mu_mailcap_t mailcap;
+ int status = 0;
+
+ if (stream == NULL || pmailcap == NULL)
+ {
+ return EINVAL;
+ }
+
+ mailcap = calloc (1, sizeof (*mailcap));
+ if (mailcap != NULL)
+ {
+ status = mu_mailcap_parse (mailcap, stream);
+ if (status != 0)
+ {
+ mu_mailcap_destroy (&mailcap);
+ }
+ else
+ {
+ *pmailcap = mailcap;
+ }
+ }
+ else
+ {
+ status = ENOMEM;
+ }
+ return status;
+}
+
+void
+mu_mailcap_destroy (mu_mailcap_t * pmailcap)
+{
+ if (pmailcap != NULL && *pmailcap != NULL)
+ {
+ int i;
+ mu_mailcap_t mailcap = *pmailcap;
+
+ for (i = 0; i < mailcap->entries_count; i++)
+ {
+ int j;
+ mu_mailcap_entry_t entry = mailcap->entries[i];
+ free (entry->typefield);
+ free (entry->viewcommand);
+ for (j = 0; j < entry->fields_count; j++)
+ {
+ free (entry->fields[j]);
+ }
+ }
+ }
+}
+
+int
+mu_mailcap_entries_count (mu_mailcap_t mailcap, size_t *pcount)
+{
+ int status = 0;
+ if (mailcap == NULL)
+ {
+ status = EINVAL;
+ }
+ if (pcount != NULL)
+ {
+ *pcount = mailcap->entries_count;
+ }
+ return status;
+}
+
+int
+mu_mailcap_get_entry (mu_mailcap_t mailcap, size_t no, mu_mailcap_entry_t *pentry)
+{
+ int status = 0;
+ if (mailcap == NULL || pentry == NULL)
+ {
+ status = EINVAL;
+ }
+ else if (no == 0 || no > mailcap->entries_count)
+ {
+ status = ENOENT;
+ }
+ else
+ {
+ *pentry = mailcap->entries[no - 1];
+ }
+ return status;
+}
+
+int
+mu_mailcap_entry_get_typefield(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
+{
+ int status = 0;
+ int len = 0;
+
+ if (entry == NULL)
+ {
+ status = EINVAL;
+ }
+ else
+ {
+ len = strlen(entry->typefield);
+ if (buffer != NULL && buflen > 0)
+ {
+ buflen--;
+ len = (len < buflen) ? len : buflen;
+ memcpy (buffer, entry->typefield, len);
+ buffer[len] = '\0';
+ }
+ }
+ if (pn)
+ {
+ *pn = len;
+ }
+ return status;
+}
+
+int
+mu_mailcap_entry_get_viewcommand(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
+{
+ int status = 0;
+ int len = 0;
+
+ if (entry == NULL)
+ {
+ status = EINVAL;
+ }
+ else
+ {
+ len = strlen(entry->viewcommand);
+ if (buffer != NULL && buflen > 0)
+ {
+ buflen--;
+ len = (len < buflen) ? len : buflen;
+ memcpy (buffer, entry->viewcommand, len);
+ buffer[len] = '\0';
+ }
+ }
+ if (pn)
+ {
+ *pn = len;
+ }
+ return status;
+}
+
+int
+mu_mailcap_entry_fields_count (mu_mailcap_entry_t entry, size_t *pcount)
+{
+ int status = 0;
+ if (entry == NULL)
+ {
+ status = EINVAL;
+ }
+ if (pcount != NULL)
+ {
+ *pcount = entry->fields_count;
+ }
+ return status;
+}
+
+int
+mu_mailcap_entry_get_field (mu_mailcap_entry_t entry, size_t no, char *buffer, size_t buflen, size_t *pn)
+{
+ int status = 0;
+ int len = 0;
+
+ if (entry == NULL)
+ {
+ status = EINVAL;
+ }
+ else if ( no == 0 || no > entry->fields_count)
+ {
+ status = ENOENT;
+ }
+ else
+ {
+ len = strlen(entry->fields[no - 1]);
+ if (buffer != NULL && buflen > 0)
+ {
+ buflen--;
+ len = (len < buflen) ? len : buflen;
+ memcpy (buffer, entry->fields[no - 1], len);
+ buffer[len] = '\0';
+ }
+ }
+ if (pn)
+ {
+ *pn = len;
+ }
+ return status;
+}
+
+int
+mu_mailcap_entry_get_compose(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
+{
+ return mu_mailcap_entry_get_value (entry, "compose", buffer, buflen, pn);
+}
+
+int
+mu_mailcap_entry_get_composetyped(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
+{
+ return mu_mailcap_entry_get_value (entry, "composetyped", buffer, buflen, pn);
+}
+
+int
+mu_mailcap_entry_get_edit(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
+{
+ return mu_mailcap_entry_get_value (entry, "edit", buffer, buflen, pn);
+}
+
+int
+mu_mailcap_entry_get_test(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
+{
+ return mu_mailcap_entry_get_value (entry, "test", buffer, buflen, pn);
+}
+
+int
+mu_mailcap_entry_get_x11bitmap(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
+{
+ return mu_mailcap_entry_get_value (entry, "x11-bitmap", buffer, buflen, pn);
+}
+
+int
+mu_mailcap_entry_get_description(mu_mailcap_entry_t entry, char *buffer, size_t buflen, size_t *pn)
+{
+ return mu_mailcap_entry_get_value (entry, "description", buffer, buflen, pn);
+}
+
+int
+mu_mailcap_entry_needsterminal(mu_mailcap_entry_t entry, int *on)
+{
+ int status = 0;
+ int found = 0;
+ if (entry == NULL)
+ {
+ status = EINVAL;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i < entry->fields_count; i++)
+ {
+ int n = strcasecmp (entry->fields[i], "needsterminal");
+ if (n == 0)
+ {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (on)
+ *on = found;
+ return status;
+}
+
+int
+mu_mailcap_entry_coupiousoutput(mu_mailcap_entry_t entry, int *on)
+{
+ int status = 0;
+ int found = 0;
+ if (entry == NULL)
+ {
+ status = EINVAL;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i < entry->fields_count; i++)
+ {
+ int n = strcasecmp (entry->fields[i], "coupiousoutput");
+ if (n == 0)
+ {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (on)
+ *on = found;
+ return status;
+}
+
+int
+mu_mailcap_entry_get_value (mu_mailcap_entry_t entry, const char *key, char *buffer, size_t buflen, size_t *pn)
+{
+ int len = 0;
+ int status = 0;
+ if (entry == NULL)
+ {
+ status = EINVAL;
+ }
+ else
+ {
+ int i;
+ int key_len = strlen (key);
+ for (i = 0; i < entry->fields_count; i++)
+ {
+ int n = strncasecmp (entry->fields[i], key, key_len);
+ if (n == 0)
+ {
+ int field_len = strlen(entry->fields[i]);
+ if (field_len > key_len)
+ {
+ int c = entry->fields[i][key_len];
+ if (isspace(c) || c == '=')
+ {
+ char *value = strchr (entry->fields[i], '=');
+ if (value != NULL)
+ {
+ value++; /* Pass the equal. */
+ /* Remove prepend space. */
+ for (; isspace ((unsigned char)*value); value++)
+ ;
+ len = strlen (value);
+ /* Strip surrounding double quotes */
+ if (len > 1 && value[0] == '"' && value[len - 1] == '"')
+ {
+ value++;
+ len -= 2;
+ }
+ if (buffer != NULL && buflen > 0)
+ {
+ buflen--;
+ len = (len < buflen) ? len : buflen;
+ memcpy (buffer, value, len);
+ buffer[len] = '\0';
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (pn)
+ *pn = len;
+ return status;
+}
+
+/* Strip whitespace from the start and end of STRING. Return a pointer
+ into STRING. */
+static char *
+stripwhite (char *string)
+{
+ register char *s, *t;
+
+ for (s = string; isspace ((unsigned char)*s); s++)
+ ;
+
+ if (*s == 0)
+ return (s);
+
+ t = s + strlen (s) - 1;
+ while (t > s && isspace (*t))
+ t--;
+ *++t = '\0';
+
+ return s;
+}
+
+/*
+ * break the line on ';'. Same as strtok() but
+ * check for escaped "\;"
+ */
+static char *
+tokenize (char *s, char **save_ptr)
+{
+ int c;
+ char *token;
+
+ if (s == NULL)
+ {
+ s = *save_ptr;
+ }
+
+ if (*s == '\0')
+ {
+ *save_ptr = s;
+ return NULL;
+ }
+
+ for (token = s, c = 0; *s; s++)
+ {
+ if (*s == ';' && c != '\\')
+ {
+ break;
+ }
+ c = *s;
+ }
+
+ if (*s == '\0')
+ {
+ *save_ptr = s;
+ }
+ else
+ {
+ *s = '\0';
+ *save_ptr = s + 1;
+ }
+ return token;
+}
+
+/**
+ * parse the mailcap line, fields are separated by ';'
+ */
+static int
+mu_mailcap_parse_entry (mu_mailcap_entry_t entry, char *buffer)
+{
+ char *token = NULL;
+ char *s = NULL;
+ int i;
+ for (i = 0, token = tokenize (buffer, &s); token != NULL; token = tokenize (NULL, &s), i++)
+ {
+ switch (i)
+ {
+ /* The first entry in a mailcap line is the typefield. */
+ case 0:
+ entry->typefield = strdup (stripwhite (token));
+ break;
+
+ /* The second entry in a mailcap line is the view-command. */
+ case 1:
+ entry->viewcommand = strdup (stripwhite(token));
+ break;
+
+ /* The rest are the optionnal fields. */
+ default:
+ {
+ char **fields = realloc (entry->fields, (entry->fields_count + 1) * sizeof (*fields));
+ if (fields != NULL)
+ {
+ entry->fields = fields;
+ entry->fields[entry->fields_count] = strdup (stripwhite (token));
+ entry->fields_count++;
+ }
+ }
+ }
+ }
+ /* Make sure typefield and viewcommand are not null. */
+ if (entry->typefield == NULL)
+ {
+ entry->typefield = strdup ("");
+ }
+ if (entry->viewcommand == NULL)
+ {
+ entry->viewcommand = strdup ("");
+ }
+ return 0;
+}
+
+/*
+ * parse a mailcap file or stream,
+ * - ignore empty line.
+ * - ignore line starting with '#'
+ * - multiline is done with the '\' as continuation
+ * example:
+# comment
+application/pgp; gpg < %s | metamail; needsterminal; \
+ test=test %{encapsulation}=entity ; copiousoutput
+ */
+static int
+mu_mailcap_parse (mu_mailcap_t mailcap, stream_t stream)
+{
+ off_t off;
+ int status;
+ size_t n;
+ char *previous;
+ char *buffer;
+ int buflen = 512;
+
+ buffer = malloc (buflen * sizeof (*buffer));
+ if (buffer == NULL)
+ {
+ return ENOMEM;
+ }
+
+ /*
+ * We are doing this a little more complexe then expected, because we do not want
+ * to seek() back in the stream:
+ * - we have to take care of continuation line i.e. line ending with '\'
+ * - we have to take to account that the line may be bigger then the buffer and reallocate
+ * - check the return of malloc/realloc
+ * The old continuation line is save in the "previous" pointer and prepend to the buffer.
+ */
+ for (previous = NULL, off = n = 0; (status = stream_readline (stream, buffer, buflen, off, &n)) == 0 && n > 0; off += n)
+ {
+ int len;
+
+ /* If there is no trailing newline, that means the buffer was too small,
+ * make room for the buffer and continue reading */
+ if (buffer[n - 1] != '\n')
+ {
+ char *b = realloc (buffer, buflen * sizeof (*buffer));
+ buflen *= 2;
+ if (b == NULL)
+ {
+ status = ENOMEM;
+ break;
+ }
+ buffer = b;
+ /*
+ * Fake this as a continuation line, for simplicity.
+ */
+ strcat (buffer, "\\");
+ }
+ else
+ {
+ /* Nuke the trailing newline. */
+ buffer[n - 1] = '\0';
+ }
+
+ /* recalculate the len. */
+ len = strlen (buffer);
+
+ /* Ending with a '\' means continuation line. */
+ if (len && buffer[len - 1] == '\\')
+ {
+ buffer[len - 1] = '\0';
+ /*
+ * Check for any previous line:
+ * - if yes append the buffer to the previous line.
+ * - if not set the buffer as the previous line and continue.
+ */
+ if (previous == NULL)
+ {
+ previous = strdup (buffer);
+ if (previous == NULL)
+ {
+ status = ENOMEM;
+ break;
+ }
+ }
+ else
+ {
+ char *b = realloc (previous, strlen (previous) + len + 1);
+ if (b == NULL)
+ {
+ status = ENOMEM;
+ break;
+ }
+ previous = b;
+ strcat(previous, buffer);
+ }
+ }
+ else
+ {
+ /* Did we have a previous incomplete line?
+ * if yes make one line from the previous and the buffer.
+ */
+ if (previous != NULL)
+ {
+ int prev_len = strlen (previous);
+ int total = prev_len + len + 1;
+ if (total > buflen)
+ {
+ char *b = realloc (buffer, total * sizeof (*buffer));
+ if (b == NULL)
+ {
+ status = ENOMEM;
+ break;
+ }
+ buffer = b;
+ buflen = total;
+ }
+ memmove (buffer + prev_len, buffer, len + 1);
+ memcpy (buffer, previous, prev_len);
+ free (previous);
+ previous = NULL;
+ }
+ }
+
+ /* Parse the well-form mailcap line entry. */
+ if (previous == NULL) {
+ /* Nuke the trailing/prepend spaces. */
+ char *line = stripwhite(buffer);
+ /* Ignore comments or empty lines */
+ if (*line != '#' && *line != '\0')
+ {
+ mu_mailcap_entry_t *entries;
+ entries = realloc (mailcap->entries, (mailcap->entries_count + 1) * sizeof (*entries));
+ if (entries != NULL)
+ {
+ mailcap->entries = entries;
+ mailcap->entries[mailcap->entries_count] = calloc (1, sizeof(**entries));
+ if (mailcap->entries[mailcap->entries_count] != NULL)
+ {
+ mu_mailcap_parse_entry (mailcap->entries[mailcap->entries_count], line);
+ }
+ mailcap->entries_count++;
+ }
+ else
+ {
+ status = ENOMEM;
+ break;
+ }
+ }
+ }
+ }
+
+ if (buffer != NULL)
+ {
+ free (buffer);
+ }
+ if (previous != NULL)
+ {
+ free (previous);
+ }
+ return status;
+}
+
+#ifdef STANDALONE_TEST
+int main()
+{
+ stream_t stream = NULL;
+ int status = 0;
+
+ status = file_stream_create (&stream, "/home/alain/mailcap", MU_STREAM_READ);
+ if (status == 0)
+ {
+ status = stream_open(stream);
+ if (status == 0)
+ {
+ mu_mailcap_t mailcap;
+ status = mu_mailcap_create (&mailcap, stream);
+ if (status == 0)
+ {
+ int i, n;
+ size_t count = 0;
+ char buffer[256];
+
+ mu_mailcap_entries_count (mailcap, &count);
+ for (i = 1; i <= count; i++)
+ {
+ int j;
+ mu_mailcap_entry_t entry = NULL;
+ int fields_count = 0;
+
+ printf ("entry[%d]\n", i);
+#if 1
+
+ mu_mailcap_get_entry (mailcap, i, &entry);
+ /* Print typefield. */
+ mu_mailcap_entry_get_typefield (entry, buffer, sizeof (buffer), NULL);
+ printf ("\ttypefield: %s\n", buffer);
+
+ /* Print view-command. */
+ mu_mailcap_entry_get_viewcommand (entry, buffer, sizeof (buffer), NULL);
+ printf ("\tview-command: %s\n", buffer);
+
+ /* Print fields. */
+ mu_mailcap_entry_fields_count (entry, &fields_count);
+ for (j = 1; j <= fields_count; j++)
+ {
+ mu_mailcap_entry_get_field (entry, j, buffer, sizeof (buffer), NULL);
+ printf("\tfields[%d]: %s\n", j, buffer);
+ }
+ n = 0;
+ mu_mailcap_entry_get_compose (entry, buffer, sizeof (buffer), &n);
+ if (n > 0)
+ {
+ printf("\tcompose[%s]\n", buffer);
+ }
+ printf("\n");
+ }
+#else
+ for (i = 0; i < mailcap->entries_count; i++)
+ {
+ int j;
+ mu_mailcap_entry_t entry = mailcap->entries[i];
+ printf("[%s];[%s]", entry->typefield, entry->viewcommand);
+ for (j = 0; j < entry->fields_count; j++)
+ {
+ printf(";[%s]", entry->fields[j]);
+ }
+ printf("\n");
+ }
+#endif
+ mu_mailcap_destroy (&mailcap);
+ }
+ }
+ }
+ return 0;
+}
+
+#endif

Return to:

Send suggestions and report system problems to the System administrator.