+/**
+ * Head of the doubly-linked list (for cleanup).
+ */
+static struct TransmitCallbackContext *tcc_head;
+
+/**
+ * Tail of the doubly-linked list (for cleanup).
+ */
+static struct TransmitCallbackContext *tcc_tail;
+
+/**
+ * Have we already cleaned up the TCCs and are hence no longer
+ * willing (or able) to transmit anything to anyone?
+ */
+static int cleaning_done;
+
+/**
+ * Handle for pending get request.
+ */
+static struct GNUNET_STATISTICS_GetHandle *stat_get;
+
+
+/**
+ * Task that is used to remove expired entries from
+ * the datastore. This task will schedule itself
+ * again automatically to always delete all expired
+ * content quickly.
+ *
+ * @param cls not used
+ * @param tc task context
+ */
+static void
+delete_expired (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Iterate over the expired items stored in the datastore.
+ * Delete all expired items; once we have processed all
+ * expired items, re-schedule the "delete_expired" task.
+ *
+ * @param cls not used
+ * @param key key for the content
+ * @param size number of bytes in data
+ * @param data content stored
+ * @param type type of the content
+ * @param priority priority of the content
+ * @param anonymity anonymity-level for the content
+ * @param expiration expiration time for the content
+ * @param uid unique identifier for the datum;
+ * maybe 0 if no unique identifier is available
+ *
+ * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue
+ * (continue on call to "next", of course),
+ * GNUNET_NO to delete the item and continue (if supported)
+ */
+static int
+expired_processor (void *cls, const GNUNET_HashCode * key, uint32_t size,
+ const void *data, enum GNUNET_BLOCK_Type type,
+ uint32_t priority, uint32_t anonymity,
+ struct GNUNET_TIME_Absolute expiration, uint64_t uid)
+{
+ struct GNUNET_TIME_Absolute now;
+
+ if (key == NULL)
+ {
+ expired_kill_task =
+ GNUNET_SCHEDULER_add_delayed_with_priority (MAX_EXPIRE_DELAY,
+ GNUNET_SCHEDULER_PRIORITY_IDLE,
+ &delete_expired, NULL);
+ return GNUNET_SYSERR;
+ }
+ now = GNUNET_TIME_absolute_get ();
+ if (expiration.abs_value > now.abs_value)
+ {
+ /* finished processing */
+ expired_kill_task =
+ GNUNET_SCHEDULER_add_delayed_with_priority (MAX_EXPIRE_DELAY,
+ GNUNET_SCHEDULER_PRIORITY_IDLE,
+ &delete_expired, NULL);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Deleting content `%s' of type %u that expired %llu ms ago\n",
+ GNUNET_h2s (key), type,
+ (unsigned long long) (now.abs_value - expiration.abs_value));
+ min_expiration = now;
+ GNUNET_STATISTICS_update (stats, gettext_noop ("# bytes expired"), size,
+ GNUNET_YES);
+ GNUNET_CONTAINER_bloomfilter_remove (filter, key);
+ expired_kill_task =
+ GNUNET_SCHEDULER_add_delayed_with_priority (MIN_EXPIRE_DELAY,
+ GNUNET_SCHEDULER_PRIORITY_IDLE,
+ &delete_expired, NULL);
+ return GNUNET_NO;
+}
+
+
+/**
+ * Task that is used to remove expired entries from
+ * the datastore. This task will schedule itself
+ * again automatically to always delete all expired
+ * content quickly.
+ *
+ * @param cls not used
+ * @param tc task context
+ */
+static void
+delete_expired (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ expired_kill_task = GNUNET_SCHEDULER_NO_TASK;
+ plugin->api->get_expiration (plugin->api->cls, &expired_processor, NULL);
+}
+
+
+/**
+ * An iterator over a set of items stored in the datastore
+ * that deletes until we're happy with respect to our quota.
+ *
+ * @param cls closure
+ * @param key key for the content
+ * @param size number of bytes in data
+ * @param data content stored
+ * @param type type of the content
+ * @param priority priority of the content
+ * @param anonymity anonymity-level for the content
+ * @param expiration expiration time for the content
+ * @param uid unique identifier for the datum;
+ * maybe 0 if no unique identifier is available
+ *
+ * @return GNUNET_SYSERR to abort the iteration, GNUNET_OK to continue
+ * (continue on call to "next", of course),
+ * GNUNET_NO to delete the item and continue (if supported)
+ */
+static int
+quota_processor (void *cls, const GNUNET_HashCode * key, uint32_t size,
+ const void *data, enum GNUNET_BLOCK_Type type,
+ uint32_t priority, uint32_t anonymity,
+ struct GNUNET_TIME_Absolute expiration, uint64_t uid)
+{
+ unsigned long long *need = cls;
+
+ if (NULL == key)
+ return GNUNET_SYSERR;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Deleting %llu bytes of low-priority (%u) content `%s' of type %u at %llu ms prior to expiration (still trying to free another %llu bytes)\n",
+ (unsigned long long) (size + GNUNET_DATASTORE_ENTRY_OVERHEAD),
+ (unsigned int) priority,
+ GNUNET_h2s (key), type,
+ (unsigned long long) GNUNET_TIME_absolute_get_remaining (expiration).rel_value,
+ *need);
+ if (size + GNUNET_DATASTORE_ENTRY_OVERHEAD > *need)
+ *need = 0;
+ else
+ *need -= size + GNUNET_DATASTORE_ENTRY_OVERHEAD;
+ if (priority > 0)
+ min_expiration = GNUNET_TIME_UNIT_FOREVER_ABS;
+ else
+ min_expiration = expiration;
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# bytes purged (low-priority)"),
+ size, GNUNET_YES);
+ GNUNET_CONTAINER_bloomfilter_remove (filter, key);
+ return GNUNET_NO;
+}
+
+
+/**
+ * Manage available disk space by running tasks
+ * that will discard content if necessary. This
+ * function will be run whenever a request for
+ * "need" bytes of storage could only be satisfied
+ * by eating into the "cache" (and we want our cache
+ * space back).
+ *
+ * @param need number of bytes of content that were
+ * placed into the "cache" (and hence the
+ * number of bytes that should be removed).
+ */
+static void
+manage_space (unsigned long long need)
+{
+ unsigned long long last;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Asked to free up %llu bytes of cache space\n", need);
+ last = 0;
+ while ((need > 0) && (last != need))
+ {
+ last = need;
+ plugin->api->get_expiration (plugin->api->cls, "a_processor, &need);
+ }
+}
+
+