Added preliminary Symbian support.
[silc.git] / lib / silcutil / silcmime.c
index d7d2ddc24b727d632abea52f0d150e665446f4b0..14fde956f14e7b2379ca973d535294ca5e1acc24 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2005 Pekka Riikonen
+  Copyright (C) 2005 - 2006 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 "silcincludes.h"
-#include "silcmime.h"
+#include "silc.h"
 
-struct SilcMimeStruct {
-  SilcHashTable fields;
-  unsigned char *data;
-  SilcUInt32 data_len;
-  SilcDList multiparts;
-  char *boundary;
-  char *multitype;
-};
+/************************ Static utility functions **************************/
 
-struct SilcMimeAssemblerStruct {
-  SilcHashTable fragments;
-};
+/* MIME fields destructor */
 
 static void silc_mime_field_dest(void *key, void *context, void *user_context)
 {
@@ -39,6 +29,28 @@ static void silc_mime_field_dest(void *key, void *context, void *user_context)
   silc_free(context);
 }
 
+/* Assembler fragment destructor */
+
+static void silc_mime_assembler_dest(void *key, void *context,
+                                    void *user_context)
+{
+  silc_free(key);
+  silc_hash_table_free(context);
+}
+
+/* Assembler partial MIME destructor */
+
+static void silc_mime_assemble_dest(void *key, void *context,
+                                   void *user_context)
+{
+  silc_mime_free(context);
+}
+
+
+/******************************* Public API *********************************/
+
+/* Allocate MIME context */
+
 SilcMime silc_mime_alloc(void)
 {
   SilcMime mime;
@@ -58,6 +70,8 @@ SilcMime silc_mime_alloc(void)
   return mime;
 }
 
+/* Free MIME context */
+
 void silc_mime_free(SilcMime mime)
 {
   SilcMime m;
@@ -77,12 +91,7 @@ void silc_mime_free(SilcMime mime)
   silc_free(mime);
 }
 
-static void silc_mime_assembler_dest(void *key, void *context,
-                                                         void *user_context)
-{
-  silc_free(key);
-  silc_hash_table_free(context);
-}
+/* Allocate MIME assembler */
 
 SilcMimeAssembler silc_mime_assembler_alloc(void)
 {
@@ -104,15 +113,20 @@ SilcMimeAssembler silc_mime_assembler_alloc(void)
   return assembler;
 }
 
+/* Free MIME assembler */
+
 void silc_mime_assembler_free(SilcMimeAssembler assembler)
 {
   silc_hash_table_free(assembler->fragments);
   silc_free(assembler);
 }
 
-SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len)
+/* Decode MIME message */
+
+SilcMime silc_mime_decode(SilcMime mime, const unsigned char *data,
+                         SilcUInt32 data_len)
 {
-  SilcMime mime;
+  SilcMime m = NULL;
   int i, k;
   char *tmp, *field, *value, *line;
 
@@ -121,9 +135,12 @@ SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len)
   if (!data)
     return NULL;
 
-  mime = silc_mime_alloc();
-  if (!mime)
-    return NULL;
+  if (!mime) {
+    mime = silc_mime_alloc();
+    if (!mime)
+      return NULL;
+    m = mime;
+  }
 
   /* Parse the fields */
   line = tmp = (char *)data;
@@ -212,10 +229,10 @@ SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len)
       line = strdup(value);
       if (strrchr(line, '"')) {
        *strrchr(line, '"') = '\0';
-       snprintf(b, sizeof(b) - 1, "--%s", line + 1);
+       silc_silc_snprintf(b, sizeof(b) - 1, "--%s", line + 1);
        mime->boundary = strdup(line + 1);
       } else {
-       snprintf(b, sizeof(b) - 1, "--%s", line);
+       silc_silc_snprintf(b, sizeof(b) - 1, "--%s", line);
        mime->boundary = strdup(line);
       }
       silc_free(line);
@@ -255,7 +272,7 @@ SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len)
          k -= 2;
 
          /* Parse the part */
-         p = silc_mime_decode(line, k - i);
+         p = silc_mime_decode(NULL, line, k - i);
          if (!p)
            goto err;
 
@@ -265,20 +282,26 @@ SilcMime silc_mime_decode(const unsigned char *data, SilcUInt32 data_len)
       }
     }
   } else {
-    /* Get data area */
-    if (i >= data_len)
+    /* Get data area.  If we are at the end and we have fields present
+       there is no data area present, but, if fields are not present we
+       only have data area. */
+    if (i >= data_len && !silc_hash_table_count(mime->fields))
       i = 0;
     SILC_LOG_DEBUG(("Data len %d", data_len - i));
-    silc_mime_add_data(mime, tmp + i, data_len - i);
+    if (data_len - i)
+      silc_mime_add_data(mime, tmp + i, data_len - i);
   }
 
   return mime;
 
  err:
-  silc_mime_free(mime);
+  if (m)
+    silc_mime_free(m);
   return NULL;
 }
 
+/* Encode MIME message */
+
 unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
 {
   SilcMime part;
@@ -302,7 +325,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
   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_silc_snprintf(tmp, sizeof(tmp) - 1, "%s: %s\r\n", field, value);
     silc_buffer_strformat(&buf, tmp, SILC_STRFMT_END);
     i++;
   }
@@ -347,8 +370,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_silc_snprintf(tmp2, sizeof(tmp2) - 1, "\r\n");
+      silc_silc_snprintf(tmp, sizeof(tmp) - 1, "%s--%s\r\n%s",
               i != 0 ? "\r\n" : "", mime->boundary, tmp2);
       i = 1;
 
@@ -364,7 +387,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_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)
@@ -379,11 +402,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len)
   return ret;
 }
 
-static void silc_mime_assemble_dest(void *key, void *context,
-                                                        void *user_context)
-{
-  silc_mime_free(context);
-}
+/* Assembles MIME message from partial MIME messages */
 
 SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
 {
@@ -512,7 +531,7 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
   }
 
   /* Now parse the complete MIME message and deliver it */
-  complete = silc_mime_decode((const unsigned char *)compbuf->head,
+  complete = silc_mime_decode(NULL, (const unsigned char *)compbuf->head,
                              silc_buffer_truelen(compbuf));
   if (!complete)
     goto err;
@@ -532,6 +551,8 @@ SilcMime silc_mime_assemble(SilcMimeAssembler assembler, SilcMime partial)
   return NULL;
 }
 
+/* Encodes partial MIME messages */
+
 SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
 {
   unsigned char *buf, *tmp;
@@ -557,7 +578,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_silc_snprintf(id, sizeof(id) - 1, "%X%X%X%s",
             (unsigned int)rand(), (unsigned int)time(NULL),
             (unsigned int)buf_len, type);
 
@@ -569,7 +590,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_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);
@@ -599,14 +620,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_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_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);
@@ -642,6 +663,8 @@ SilcDList silc_mime_encode_partial(SilcMime mime, int max_size)
   return list;
 }
 
+/* Free partial MIME list */
+
 void silc_mime_partial_free(SilcDList partials)
 {
   SilcBuffer buf;
@@ -655,6 +678,8 @@ void silc_mime_partial_free(SilcDList partials)
   silc_dlist_uninit(partials);
 }
 
+/* Add field */
+
 void silc_mime_add_field(SilcMime mime, const char *field, const char *value)
 {
   if (!mime || !field || !value)
@@ -663,6 +688,8 @@ void silc_mime_add_field(SilcMime mime, const char *field, const char *value)
   silc_hash_table_add(mime->fields, strdup(field), strdup(value));
 }
 
+/* Get field */
+
 const char *silc_mime_get_field(SilcMime mime, const char *field)
 {
   char *value;
@@ -677,6 +704,8 @@ const char *silc_mime_get_field(SilcMime mime, const char *field)
   return (const char *)value;
 }
 
+/* Add data */
+
 void silc_mime_add_data(SilcMime mime, const unsigned char *data,
                        SilcUInt32 data_len)
 {
@@ -690,6 +719,8 @@ void silc_mime_add_data(SilcMime mime, const unsigned char *data,
   mime->data_len = data_len;
 }
 
+/* Get data */
+
 const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len)
 {
   if (!mime)
@@ -701,6 +732,28 @@ const unsigned char *silc_mime_get_data(SilcMime mime, SilcUInt32 *data_len)
   return mime->data;
 }
 
+/* Steal data */
+
+unsigned char *silc_mime_steal_data(SilcMime mime, SilcUInt32 *data_len)
+{
+  unsigned char *data;
+
+  if (!mime)
+    return NULL;
+
+  if (data_len)
+    *data_len = mime->data_len;
+
+  data = mime->data;
+
+  mime->data = NULL;
+  mime->data_len = 0;
+
+  return data;
+}
+
+/* Returns TRUE if partial message */
+
 SilcBool silc_mime_is_partial(SilcMime mime)
 {
   const char *type = silc_mime_get_field(mime, "Content-Type");
@@ -713,6 +766,8 @@ SilcBool silc_mime_is_partial(SilcMime mime)
   return TRUE;
 }
 
+/* Set as multipart message */
+
 void silc_mime_set_multipart(SilcMime mime, const char *type,
                             const char *boundary)
 {
@@ -722,7 +777,7 @@ 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_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);
@@ -732,6 +787,8 @@ void silc_mime_set_multipart(SilcMime mime, const char *type,
   mime->multiparts = silc_dlist_init();
 }
 
+/* Add multipart */
+
 SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part)
 {
   if (!mime || !mime->multiparts || !part)
@@ -741,6 +798,8 @@ SilcBool silc_mime_add_multipart(SilcMime mime, SilcMime part)
   return TRUE;
 }
 
+/* Return TRUE if has multiparts */
+
 SilcBool silc_mime_is_multipart(SilcMime mime)
 {
   if (!mime)
@@ -749,6 +808,8 @@ SilcBool silc_mime_is_multipart(SilcMime mime)
   return mime->multiparts != NULL;
 }
 
+/* Returns multiparts */
+
 SilcDList silc_mime_get_multiparts(SilcMime mime, const char **type)
 {
   if (!mime)