+
+/**
+ * If necessary, connect to the datastore and remove the KBlocks.
+ *
+ * @param uc context for the unindex operation.
+ */
+void
+GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc)
+{
+ const char *keyword;
+ const struct GNUNET_CRYPTO_EcdsaPrivateKey *anon;
+ struct GNUNET_CRYPTO_EcdsaPublicKey anon_pub;
+ struct GNUNET_CRYPTO_EcdsaPublicKey dpub;
+
+ if (NULL == uc->dsh)
+ uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
+ if (NULL == uc->dsh)
+ {
+ uc->state = UNINDEX_STATE_ERROR;
+ uc->emsg = GNUNET_strdup (_("Failed to connect to `datastore' service."));
+ GNUNET_FS_unindex_sync_ (uc);
+ signal_unindex_error (uc);
+ return;
+ }
+ if ( (NULL == uc->ksk_uri) ||
+ (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount) )
+ {
+ unindex_finish (uc);
+ return;
+ }
+ anon = GNUNET_CRYPTO_ecdsa_key_get_anonymous ();
+ GNUNET_CRYPTO_ecdsa_key_get_public (anon, &anon_pub);
+ keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
+ GNUNET_CRYPTO_ecdsa_public_key_derive (&anon_pub,
+ keyword,
+ "fs-ublock",
+ &dpub);
+ GNUNET_CRYPTO_hash (&dpub,
+ sizeof (dpub),
+ &uc->uquery);
+ uc->first_uid = 0;
+ uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
+ uc->roff++,
+ &uc->uquery,
+ GNUNET_BLOCK_TYPE_FS_UBLOCK,
+ 0 /* priority */, 1 /* queue size */,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &process_kblock_for_unindex,
+ uc);
+}
+
+
+/**
+ * Function called when the tree encoder has
+ * processed all blocks. Clean up.
+ *
+ * @param cls our unindexing context
+ * @param tc not used
+ */
+static void
+unindex_extract_keywords (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_FS_UnindexContext *uc = cls;
+
+ uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS;
+ GNUNET_FS_unindex_sync_ (uc);
+ GNUNET_FS_unindex_do_extract_keywords_ (uc);
+}
+
+
+/**
+ * Connect to the datastore and remove the blocks.
+ *
+ * @param uc context for the unindex operation.
+ */
+void
+GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc)
+{
+ if (NULL == uc->dsh)
+ uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
+ if (NULL == uc->dsh)
+ {
+ uc->state = UNINDEX_STATE_ERROR;
+ uc->emsg = GNUNET_strdup (_("Failed to connect to `datastore' service."));
+ GNUNET_FS_unindex_sync_ (uc);
+ signal_unindex_error (uc);
+ return;
+ }
+ uc->fh =
+ GNUNET_DISK_file_open (uc->filename, GNUNET_DISK_OPEN_READ,
+ GNUNET_DISK_PERM_NONE);
+ if (NULL == uc->fh)
+ {
+ GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
+ uc->dsh = NULL;
+ uc->state = UNINDEX_STATE_ERROR;
+ uc->emsg = GNUNET_strdup (_("Failed to open file for unindexing."));
+ GNUNET_FS_unindex_sync_ (uc);
+ signal_unindex_error (uc);
+ return;
+ }
+ uc->tc =
+ GNUNET_FS_tree_encoder_create (uc->h, uc->file_size, uc, &unindex_reader,
+ &unindex_process, &unindex_progress,
+ &unindex_extract_keywords);
+ GNUNET_FS_tree_encoder_next (uc->tc);
+}
+
+
+/**
+ * Function called once the hash of the file
+ * that is being unindexed has been computed.
+ *
+ * @param cls closure, unindex context
+ * @param file_id computed hash, NULL on error
+ */
+void
+GNUNET_FS_unindex_process_hash_ (void *cls, const struct GNUNET_HashCode * file_id)
+{
+ struct GNUNET_FS_UnindexContext *uc = cls;
+
+ uc->fhc = NULL;
+ if (uc->state != UNINDEX_STATE_HASHING)
+ {
+ GNUNET_FS_unindex_stop (uc);
+ return;
+ }
+ if (file_id == NULL)
+ {
+ uc->state = UNINDEX_STATE_ERROR;
+ uc->emsg = GNUNET_strdup (_("Failed to compute hash of file."));
+ GNUNET_FS_unindex_sync_ (uc);
+ signal_unindex_error (uc);
+ return;
+ }
+ uc->file_id = *file_id;
+ uc->state = UNINDEX_STATE_DS_REMOVE;
+ GNUNET_FS_unindex_sync_ (uc);
+ GNUNET_FS_unindex_do_remove_ (uc);
+}
+
+
+/**
+ * Create SUSPEND event for the given unindex operation
+ * and then clean up our state (without stop signal).
+ *
+ * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
+ */
+void
+GNUNET_FS_unindex_signal_suspend_ (void *cls)
+{
+ struct GNUNET_FS_UnindexContext *uc = cls;
+ struct GNUNET_FS_ProgressInfo pi;
+
+ /* FIXME: lots of duplication with unindex_stop here! */
+ if (uc->dscan != NULL)
+ {
+ GNUNET_FS_directory_scan_abort (uc->dscan);
+ uc->dscan = NULL;
+ }
+ if (NULL != uc->dqe)
+ {
+ GNUNET_DATASTORE_cancel (uc->dqe);
+ uc->dqe = NULL;
+ }
+ if (uc->fhc != NULL)
+ {
+ GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
+ uc->fhc = NULL;
+ }
+ if (NULL != uc->ksk_uri)
+ {
+ GNUNET_FS_uri_destroy (uc->ksk_uri);
+ uc->ksk_uri = NULL;
+ }
+ if (uc->client != NULL)
+ {
+ GNUNET_CLIENT_disconnect (uc->client);
+ uc->client = NULL;
+ }
+ if (NULL != uc->dsh)
+ {
+ GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
+ uc->dsh = NULL;
+ }
+ if (NULL != uc->tc)
+ {
+ GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
+ uc->tc = NULL;
+ }
+ if (uc->fh != NULL)
+ {
+ GNUNET_DISK_file_close (uc->fh);
+ uc->fh = NULL;
+ }
+ GNUNET_FS_end_top (uc->h, uc->top);
+ pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND;
+ GNUNET_FS_unindex_make_status_ (&pi, uc,
+ (uc->state ==
+ UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
+ GNUNET_break (NULL == uc->client_info);
+ GNUNET_free (uc->filename);
+ GNUNET_free_non_null (uc->serialization);
+ GNUNET_free_non_null (uc->emsg);
+ GNUNET_free (uc);
+}
+
+
+/**
+ * Unindex a file.
+ *
+ * @param h handle to the file sharing subsystem
+ * @param filename file to unindex
+ * @param cctx initial value for the client context
+ * @return NULL on error, otherwise handle
+ */
+struct GNUNET_FS_UnindexContext *
+GNUNET_FS_unindex_start (struct GNUNET_FS_Handle *h,
+ const char *filename,
+ void *cctx)
+{
+ struct GNUNET_FS_UnindexContext *ret;
+ struct GNUNET_FS_ProgressInfo pi;
+ uint64_t size;
+
+ if (GNUNET_OK != GNUNET_DISK_file_size (filename, &size, GNUNET_YES, GNUNET_YES))
+ return NULL;
+ ret = GNUNET_new (struct GNUNET_FS_UnindexContext);
+ ret->h = h;
+ ret->filename = GNUNET_strdup (filename);
+ ret->start_time = GNUNET_TIME_absolute_get ();
+ ret->file_size = size;
+ ret->client_info = cctx;
+ GNUNET_FS_unindex_sync_ (ret);
+ pi.status = GNUNET_FS_STATUS_UNINDEX_START;
+ pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
+ GNUNET_FS_unindex_make_status_ (&pi, ret, 0);
+ ret->fhc =
+ GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, filename,
+ HASHING_BLOCKSIZE,
+ &GNUNET_FS_unindex_process_hash_, ret);
+ ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_unindex_signal_suspend_, ret);
+ return ret;
+}
+
+
+/**
+ * Clean up after completion of an unindex operation.
+ *
+ * @param uc handle
+ */
+void
+GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
+{
+ struct GNUNET_FS_ProgressInfo pi;
+
+ if (uc->dscan != NULL)
+ {
+ GNUNET_FS_directory_scan_abort (uc->dscan);
+ uc->dscan = NULL;
+ }
+ if (NULL != uc->dqe)
+ {
+ GNUNET_DATASTORE_cancel (uc->dqe);
+ uc->dqe = NULL;
+ }
+ if (uc->fhc != NULL)
+ {
+ GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
+ uc->fhc = NULL;
+ }
+ if (uc->client != NULL)
+ {
+ GNUNET_CLIENT_disconnect (uc->client);
+ uc->client = NULL;
+ }
+ if (NULL != uc->dsh)
+ {
+ GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
+ uc->dsh = NULL;
+ }
+ if (NULL != uc->ksk_uri)
+ {
+ GNUNET_FS_uri_destroy (uc->ksk_uri);
+ uc->ksk_uri = NULL;
+ }
+ if (NULL != uc->tc)
+ {
+ GNUNET_FS_tree_encoder_finish (uc->tc, NULL);
+ uc->tc = NULL;
+ }
+ if (uc->fh != NULL)
+ {
+ GNUNET_DISK_file_close (uc->fh);
+ uc->fh = NULL;
+ }
+ GNUNET_FS_end_top (uc->h, uc->top);
+ if (uc->serialization != NULL)
+ {
+ GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
+ uc->serialization);
+ GNUNET_free (uc->serialization);
+ uc->serialization = NULL;
+ }
+ pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED;
+ pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
+ GNUNET_FS_unindex_make_status_ (&pi, uc,
+ (uc->state ==
+ UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
+ GNUNET_break (NULL == uc->client_info);
+ GNUNET_free_non_null (uc->emsg);
+ GNUNET_free (uc->filename);
+ GNUNET_free (uc);