Added silc_mime_assembler_purge.
authorPekka Riikonen <priikone@silcnet.org>
Thu, 26 Jul 2007 15:24:41 +0000 (15:24 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 26 Jul 2007 15:24:41 +0000 (15:24 +0000)
lib/silcutil/silcmime.c
lib/silcutil/silcmime.h
lib/silcutil/tests/test_silcmime.c

index 6afc48f9be17d658cff6bf8a3c4be1276b93d9b0..c3851ea26f7ec99b3d9de714a3779a3a33975a0d 100644 (file)
 
 #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(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_compare(id1->id, id2->id, user_context);
+}
+
 
 /******************************* Public API *********************************/
 
@@ -102,8 +132,8 @@ SilcMimeAssembler silc_mime_assembler_alloc(void)
     return NULL;
 
   assembler->fragments =
-    silc_hash_table_alloc(NULL, 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,
@@ -412,6 +467,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;
@@ -465,15 +521,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,
+  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(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;
   }
 
@@ -499,14 +563,17 @@ 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. */
@@ -542,7 +609,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);
 
index 8bc221cd07ec2d968228c3e60b42d296ec94a410..82aa8eeada800b5687992bc14c96ed05e4e08fad 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
@@ -115,6 +115,29 @@ SilcMimeAssembler silc_mime_assembler_alloc(void);
  ***/
 void silc_mime_assembler_free(SilcMimeAssembler assembler);
 
+/****f* silcutil/SILCMIMEAPI/silc_mime_assembler_purge
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mime_assembler_purge(SilcMimeAssembler assembler,
+ *                                   SilcUInt32 purge_minutes);
+ *
+ * DESCRIPTION
+ *
+ *    Purges the MIME fragment assembler from old fragments that have never
+ *    completed into a full MIME message.  This function may be called
+ *    periodically to purge MIME fragments.  The `purge_minutes' specify
+ *    how old fragments are purged.  If it is 0, fragments older than 5 minutes
+ *    are purged, by default.  The value is in minutes.
+ *
+ *    It is usefull to call this periodically to assure that memory is not
+ *    consumed needlessly by keeping old unfinished fragments in a long
+ *    running assembler.
+ *
+ ***/
+void silc_mime_assembler_purge(SilcMimeAssembler assembler,
+                              SilcUInt32 purge_minutes);
+
 /****f* silcutil/SILCMIMEAPI/silc_mime_decode
  *
  * SYNOPSIS
@@ -176,7 +199,7 @@ unsigned char *silc_mime_encode(SilcMime mime, SilcUInt32 *encoded_len);
  *
  *    Processes and attempts to assemble the received MIME fragment `partial'.
  *    To check if a received MIME message is a fragment use the
- *    silc_mime_is_partial function.  Returns NULL if all fragments has not
+ *    silc_mime_is_partial function.  Returns NULL if all fragments have not
  *    yet been received, or the newly allocated completed MIME message if
  *    all fragments were received.  The caller must free the returned
  *    SilcMime context.  The caller must not free the `partial'.
index 6ebcecd617c4b5769ee7fa5dcc276204f533af78..e27f614f50a3407902826c5326b315dfd4538985 100644 (file)
@@ -267,6 +267,7 @@ int main(int argc, char **argv)
         SILC_LOG_DEBUG(("Error encoding"));
       SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
       silc_free(enc);
+      silc_mime_free(part);
     }
   }
   silc_mime_partial_free(frag);