Added SILC Thread Queue API
[silc.git] / lib / silcutil / silcmime.c
index aa138a5100188e0216e4b02f7eac1bc8c45138f9..839eb354b9ea6841c129d104e765f184588a6b69 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 - 2006 Pekka Riikonen
+  Copyright (C) 2005 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 #include "silc.h"
 
+/************************** Types and definitions ***************************/
+
+/* MIME fragment ID context */
+typedef struct {
+  char *id;
+  SilcInt64 starttime;
+} SilcMimeFragmentIdStruct, *SilcMimeFragmentId;
+
 /************************ Static utility functions **************************/
 
 /* MIME fields destructor */
@@ -34,11 +42,16 @@ static void silc_mime_field_dest(void *key, void *context, void *user_context)
 static void silc_mime_assembler_dest(void *key, void *context,
                                     void *user_context)
 {
-  silc_free(key);
+  SilcMimeFragmentId id = key;
+
+  silc_free(id->id);
+  silc_free(id);
+
+  /* Free all fragments */
   silc_hash_table_free(context);
 }
 
-/* Assembler partial MIME destructor */
+/* Assembler partial MIME fragmentn destructor */
 
 static void silc_mime_assemble_dest(void *key, void *context,
                                    void *user_context)
@@ -46,6 +59,23 @@ static void silc_mime_assemble_dest(void *key, void *context,
   silc_mime_free(context);
 }
 
+/* MIME fragment ID hashing */
+
+static SilcUInt32 silc_mime_hash_id(void *key, void *user_context)
+{
+  SilcMimeFragmentId id = key;
+  return silc_hash_string_case(id->id, user_context);
+}
+
+/* MIME fragment ID comparing */
+
+static SilcBool silc_mime_id_compare(void *key1, void *key2,
+                                    void *user_context)
+{
+  SilcMimeFragmentId id1 = key1, id2 = key2;
+  return silc_hash_string_case_compare(id1->id, id2->id, user_context);
+}
+
 
 /******************************* Public API *********************************/
 
@@ -59,8 +89,8 @@ SilcMime silc_mime_alloc(void)
   if (!mime)
     return NULL;
 
-  mime->fields = silc_hash_table_alloc(0, silc_hash_string, mime,
-                                      silc_hash_string_compare, mime,
+  mime->fields = silc_hash_table_alloc(NULL, 0, silc_hash_string_case, mime,
+                                      silc_hash_string_case_compare, mime,
                                       silc_mime_field_dest, mime, TRUE);
   if (!mime->fields) {
     silc_mime_free(mime);
@@ -102,8 +132,8 @@ SilcMimeAssembler silc_mime_assembler_alloc(void)
     return NULL;
 
   assembler->fragments =
-    silc_hash_table_alloc(0, silc_hash_string, NULL,
-                         silc_hash_string_compare, NULL,
+    silc_hash_table_alloc(NULL, 0, silc_mime_hash_id, NULL,
+                         silc_mime_id_compare, NULL,
                          silc_mime_assembler_dest, assembler, TRUE);
   if (!assembler->fragments) {
     silc_mime_assembler_free(assembler);
@@ -121,6 +151,31 @@ void silc_mime_assembler_free(SilcMimeAssembler assembler)
   silc_free(assembler);
 }
 
+/* Purge assembler from old unfinished fragments */
+
+void silc_mime_assembler_purge(SilcMimeAssembler assembler,
+                              SilcUInt32 purge_minutes)
+{
+  SilcMimeFragmentId id;
+  SilcHashTableList htl;
+  SilcInt64 curtime = silc_time();
+  SilcUInt32 timeout = purge_minutes ? purge_minutes * 60 : 5 * 60;
+
+  SILC_LOG_DEBUG(("Purge MIME assembler"));
+
+  silc_hash_table_list(assembler->fragments, &htl);
+  while (silc_hash_table_get(&htl, (void *)&id, NULL)) {
+    if (curtime - id->starttime <= timeout)
+      continue;
+
+    SILC_LOG_DEBUG(("Purge partial MIME id %s", id->id));
+
+    /* Purge */
+    silc_hash_table_del(assembler->fragments, id);
+  }
+  silc_hash_table_list_reset(&htl);
+}
+
 /* Decode MIME message */
 
 SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
@@ -132,8 +187,10 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
 
   SILC_LOG_DEBUG(("Parsing MIME message"));
 
-  if (!data)
+  if (!data) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     return NULL;
+  }
 
   if (!mime) {
     mime = silc_mime_alloc();
@@ -149,26 +206,34 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
     if (data_len - i >= 2 && tmp[i] == '\r' && tmp[i + 1] == '\n') {
       /* Get field */
       field = strchr(line, ':');
-      if (!field)
+      if (!field) {
+       silc_set_errno(SILC_ERR_BAD_ENCODING);
        goto err;
+      }
       field = silc_memdup(line, field - line);
       if (!field)
        goto err;
 
       /* Get value. Remove whitespaces too. */
       value = strchr(line, ':');
-      if ((tmp + i) - value < 2)
+      if ((tmp + i) - value < 2) {
+       silc_set_errno(SILC_ERR_OVERFLOW);
        goto err;
+      }
       value++;
       for (k = 0; k < (tmp + i) - value; k++) {
-       if (value[k] == '\r')
+       if (value[k] == '\r') {
+         silc_set_errno(SILC_ERR_BAD_ENCODING);
          goto err;
+        }
        if (value[k] != ' ' && value[k] != '\t')
          break;
       }
       value += k;
-      if ((tmp + i) - value < 1)
+      if ((tmp + i) - value < 1) {
+       silc_set_errno(SILC_ERR_OVERFLOW);
        goto err;
+      }
       value = silc_memdup(value, (tmp + i) - value);
       if (!value)
        goto err;
@@ -198,6 +263,7 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
   if (field && strstr(field, "multipart")) {
     char b[1024];
     SilcMime p;
+    unsigned int len;
 
     mime->multiparts = silc_dlist_init();
     if (!mime->multiparts)
@@ -205,15 +271,24 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
 
     /* Get multipart type */
     value = strchr(field, '/');
-    if (!value)
+    if (!value) {
+      silc_set_errno(SILC_ERR_BAD_ENCODING);
       goto err;
+    }
     value++;
     if (strchr(field, '"'))
       value++;
-    if (!strchr(field, ';'))
+    if (!strchr(field, ';')) {
+      silc_set_errno(SILC_ERR_BAD_ENCODING);
       goto err;
+    }
     memset(b, 0, sizeof(b));
-    strncat(b, value, strchr(field, ';') - value);
+    len = (unsigned int)(strchr(field, ';') - value);
+    if (len > sizeof(b) - 1) {
+      silc_set_errno(SILC_ERR_OVERFLOW);
+      goto err;
+    }
+    strncpy(b, value, len);
     if (strchr(b, '"'))
       *strchr(b, '"') = '\0';
     mime->multitype = silc_memdup(b, strlen(b));
@@ -226,14 +301,14 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
       SILC_LOG_DEBUG(("Boundary '%s'", value));
 
       memset(b, 0, sizeof(b));
-      line = strdup(value);
+      line = silc_strdup(value);
       if (strrchr(line, '"')) {
        *strrchr(line, '"') = '\0';
-       snprintf(b, sizeof(b) - 1, "--%s", line + 1);
-       mime->boundary = strdup(line + 1);
+       silc_snprintf(b, sizeof(b) - 1, "--%s", line + 1);
+       mime->boundary = silc_strdup(line + 1);
       } else {
-       snprintf(b, sizeof(b) - 1, "--%s", line);
-       mime->boundary = strdup(line);
+       silc_snprintf(b, sizeof(b) - 1, "--%s", line);
+       mime->boundary = silc_strdup(line);
       }
       silc_free(line);
 
@@ -265,8 +340,10 @@ SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
                tmp[k] == '-' && tmp[k + 1] == '-')
              if (!memcmp(tmp + k, b, strlen(b)))
                break;
-         if (k >= data_len)
+         if (k >= data_len) {
+           silc_set_errno(SILC_ERR_OVERFLOW);
            goto err;
+         }
 
          /* Remove preceding CRLF */
          k -= 2;
@@ -322,10 +399,10 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
   /* Encode the headers. Order doesn't matter */
   i = 0;
   silc_hash_table_list(mime->fields, &htl);
-  while (silc_hash_table_get(&htl, (void **)&field, (void **)&value)) {
+  while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
     memset(tmp, 0, sizeof(tmp));
     SILC_LOG_DEBUG(("Header %s: %s", field, value));
-    snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
+    silc_snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
     silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END);
     i++;
   }
@@ -342,6 +419,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
   if (silc_buffer_len(&buf)) {
     silc_buffer_put(buffer, buf.head, silc_buffer_len(&buf));
     silc_buffer_pull(buffer, silc_buffer_len(&buf));
+    silc_buffer_purge(&buf);
   }
 
   /* Add data */
@@ -370,8 +448,8 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
 
       /* If fields are not present, add extra CRLF */
       if (!silc_hash_table_count(part->fields))
-       snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
-      snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s",
+       silc_snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
+      silc_snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s",
               i != 0 ? "\r\n" : "", mime->boundary, tmp2);
       i = 1;
 
@@ -387,7 +465,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
     }
 
     memset(tmp, 0, sizeof(tmp));
-    snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary);
+    silc_snprintf(tmp, sizeof(tmp) - 1, "\r\n--%s--\r\n", mime->boundary);
     buffer = silc_buffer_realloc(buffer, silc_buffer_truelen(buffer) +
                                 strlen(tmp));
     if (!buffer)
@@ -407,6 +485,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
 SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
 {
   char *type, *id = NULL, *tmp;
+  SilcMimeFragmentIdStruct *fragid, query;
   SilcHashTable f;
   SilcMime p, complete;
   int i, number, total = -1;
@@ -416,23 +495,31 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
 
   SILC_LOG_DEBUG(("Assembling MIME fragments"));
 
-  if (!assembler || !partial)
+  if (!assembler || !partial) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     goto err;
+  }
 
   type = (char *)silc_mime_get_field(partial, "Content-Type");
-  if (!type)
+  if (!type) {
+    silc_set_errno(SILC_ERR_BAD_ENCODING);
     goto err;
+  }
 
   /* Get ID */
   tmp = strstr(type, "id=");
-  if (!tmp)
+  if (!tmp) {
+    silc_set_errno(SILC_ERR_BAD_ENCODING);
     goto err;
-  if (strlen(tmp) <= 4)
+  }
+  if (strlen(tmp) <= 4) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     goto err;
+  }
   tmp += 3;
   if (*tmp == '"')
     tmp++;
-  id = strdup(tmp);
+  id = silc_strdup(tmp);
   if (strchr(id, ';'))
     *strchr(id, ';') = '\0';
   if (strrchr(id, '"'))
@@ -442,14 +529,18 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
 
   /* Get fragment number */
   tmp = strstr(type, "number=");
-  if (!tmp)
+  if (!tmp) {
+    silc_set_errno(SILC_ERR_BAD_ENCODING);
     goto err;
+  }
   tmp = strchr(tmp, '=');
-  if (strlen(tmp) < 2)
+  if (strlen(tmp) < 2) {
+    silc_set_errno(SILC_ERR_OVERFLOW);
     goto err;
+  }
   tmp++;
   if (strchr(tmp, ';')) {
-    tmp = strdup(tmp);
+    tmp = silc_strdup(tmp);
     *strchr(tmp, ';') = '\0';
     number = atoi(tmp);
     silc_free(tmp);
@@ -460,15 +551,23 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
   SILC_LOG_DEBUG(("Fragment number %d", number));
 
   /* Find fragments with this ID. */
-  if (!silc_hash_table_find(assembler->fragments, (void *)id,
-                           NULL, (void **)&f)) {
+  query.id = id;
+  if (!silc_hash_table_find(assembler->fragments, (void *)&query,
+                           NULL, (void *)&f)) {
     /* This is new fragment to new message.  Add to hash table and return. */
-    f = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
+    f = silc_hash_table_alloc(NULL, 0, silc_hash_uint, NULL, NULL, NULL,
                              silc_mime_assemble_dest, NULL, TRUE);
     if (!f)
-        goto err;
+      goto err;
+
+    fragid = silc_calloc(1, sizeof(*fragid));
+    if (!fragid)
+      goto err;
+    fragid->id = id;
+    fragid->starttime = silc_time();
+
     silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
-    silc_hash_table_add(assembler->fragments, id, f);
+    silc_hash_table_add(assembler->fragments, fragid, f);
     return NULL;
   }
 
@@ -476,11 +575,13 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
   tmp = strstr(type, "total=");
   if (tmp) {
     tmp = strchr(tmp, '=');
-    if (strlen(tmp) < 2)
+    if (strlen(tmp) < 2) {
+      silc_set_errno(SILC_ERR_OVERFLOW);
       goto err;
+    }
     tmp++;
     if (strchr(tmp, ';')) {
-      tmp = strdup(tmp);
+      tmp = silc_strdup(tmp);
       *strchr(tmp, ';') = '\0';
       total = atoi(tmp);
       silc_free(tmp);
@@ -494,25 +595,30 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
   /* If more fragments to come, add to hash table */
   if (number != total) {
     silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
+    silc_free(id);
     return NULL;
   }
 
   silc_hash_table_add(f, SILC_32_TO_PTR(number), partial);
 
   /* Verify that we really have all the fragments */
-  if (silc_hash_table_count(f) < total)
+  if (silc_hash_table_count(f) < total) {
+    silc_free(id);
     return NULL;
+  }
 
   /* Assemble the complete MIME message now. We get them in order from
      the hash table. */
   for (i = 1; i <= total; i++) {
-    if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void **)&p))
+    if (!silc_hash_table_find(f, SILC_32_TO_PTR(i), NULL, (void *)&p))
       goto err;
 
     /* The fragment is in the data portion of the partial message */
     data = silc_mime_get_data(p, &data_len);
-    if (!data)
+    if (!data) {
+      silc_set_errno(SILC_ERR_BAD_ENCODING);
       goto err;
+    }
 
     /* Assemble */
     if (!compbuf) {
@@ -537,7 +643,7 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
     goto err;
 
   /* Delete the hash table entry. Destructors will free memory */
-  silc_hash_table_del(assembler->fragments, (void *)id);
+  silc_hash_table_del(assembler->fragments, (void *)&query);
   silc_free(id);
   silc_buffer_free(compbuf);
 
@@ -578,7 +684,7 @@ SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
     memset(type, 0, sizeof(type));
     gethostname(type, sizeof(type) - 1);
     srand((time(NULL) + buf_len) ^ rand());
-    snprintf(id, sizeof(id) - 1, "%X%X%X%s",
+    silc_snprintf(id, sizeof(id) - 1, "%X%X%X%s",
             (unsigned int)rand(), (unsigned int)time(NULL),
             (unsigned int)buf_len, type);
 
@@ -590,7 +696,7 @@ SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
 
     silc_mime_add_field(partial, "MIME-Version", "1.0");
     memset(type, 0, sizeof(type));
-    snprintf(type, sizeof(type) - 1,
+    silc_snprintf(type, sizeof(type) - 1,
             "message/partial; id=\"%s\"; number=1", id);
     silc_mime_add_field(partial, "Content-Type", type);
     silc_mime_add_data(partial, buf, max_size);
@@ -620,14 +726,14 @@ SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
       silc_mime_add_field(partial, "MIME-Version", "1.0");
 
       if (len > max_size) {
-       snprintf(type, sizeof(type) - 1,
+       silc_snprintf(type, sizeof(type) - 1,
                 "message/partial; id=\"%s\"; number=%d",
                 id, num++);
        silc_mime_add_data(partial, buf + off, max_size);
        off += max_size;
        len -= max_size;
       } else {
-       snprintf(type, sizeof(type) - 1,
+       silc_snprintf(type, sizeof(type) - 1,
                 "message/partial; id=\"%s\"; number=%d; total=%d",
                 id, num, num);
        silc_mime_add_data(partial, buf + off, len);
@@ -685,7 +791,7 @@ void silc_mime_add_field(SilcMime mime, const char *field, const char *value)
   if (!mime || !field || !value)
     return;
 
-  silc_hash_table_add(mime->fields, strdup(field), strdup(value));
+  silc_hash_table_add(mime->fields, silc_strdup(field), silc_strdup(value));
 }
 
 /* Get field */
@@ -698,7 +804,7 @@ const char *silc_mime_get_field(SilcMime mime, const char *field)
     return NULL;
 
   if (!silc_hash_table_find(mime->fields, (void *)field,
-                           NULL, (void **)&value))
+                           NULL, (void *)&value))
     return NULL;
 
   return (const char *)value;
@@ -777,10 +883,10 @@ void silc_mime_set_multipart(SilcMime mime, const char *type,
     return;
 
   memset(tmp, 0, sizeof(tmp));
-  snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary);
+  silc_snprintf(tmp, sizeof(tmp) - 1, "multipart/%s; boundary=%s", type, boundary);
   silc_mime_add_field(mime, "Content-Type", tmp);
   silc_free(mime->boundary);
-  mime->boundary = strdup(boundary);
+  mime->boundary = silc_strdup(boundary);
 
   if (mime->multiparts)
     return;
@@ -791,8 +897,10 @@ void silc_mime_set_multipart(SilcMime mime, const char *type,
 
 SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part)
 {
-  if (!mime || !mime->multiparts || !part)
+  if (!mime || !mime->multiparts || !part) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     return FALSE;
+  }
 
   silc_dlist_add(mime->multiparts, part);
   return TRUE;
@@ -802,8 +910,10 @@ SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part)
 
 SilcBool silc_mime_is_multipart(SilcMime mime)
 {
-  if (!mime)
+  if (!mime) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     return FALSE;
+  }
 
   return mime->multiparts != NULL;
 }
@@ -812,8 +922,10 @@ SilcBool silc_mime_is_multipart(SilcMime mime)
 
 SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type)
 {
-  if (!mime)
+  if (!mime) {
+    silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
     return NULL;
+  }
 
   if (type)
     *type = (const char *)mime->multitype;