bb3accb8239a224fbf90558b71b7c36b71884fba
[oweals/gnunet.git] / src / fs / fs_unindex.c
1 /*
2      This file is part of GNUnet.
3      (C) 2003--2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file fs/fs_unindex.c
23  * @author Krista Grothoff
24  * @author Christian Grothoff
25  * @brief Unindex file.
26  */
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_fs_service.h"
30 #include "gnunet_protocols.h"
31 #include "fs_api.h"
32 #include "fs_tree.h"
33 #include "block_fs.h"
34 #include "fs_publish_ublock.h"
35
36
37 /**
38  * Function called by the tree encoder to obtain
39  * a block of plaintext data (for the lowest level
40  * of the tree).
41  *
42  * @param cls our publishing context
43  * @param offset identifies which block to get
44  * @param max (maximum) number of bytes to get; returning
45  *        fewer will also cause errors
46  * @param buf where to copy the plaintext buffer
47  * @param emsg location to store an error message (on error)
48  * @return number of bytes copied to buf, 0 on error
49  */
50 static size_t
51 unindex_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg)
52 {
53   struct GNUNET_FS_UnindexContext *uc = cls;
54   size_t pt_size;
55
56   pt_size = GNUNET_MIN (max, uc->file_size - offset);
57   if (offset != GNUNET_DISK_file_seek (uc->fh, offset, GNUNET_DISK_SEEK_SET))
58   {
59     *emsg = GNUNET_strdup (_("Failed to find given position in file"));
60     return 0;
61   }
62   if (pt_size != GNUNET_DISK_file_read (uc->fh, buf, pt_size))
63   {
64     *emsg = GNUNET_strdup (_("Failed to read file"));
65     return 0;
66   }
67   return pt_size;
68 }
69
70
71 /**
72  * Fill in all of the generic fields for
73  * an unindex event and call the callback.
74  *
75  * @param pi structure to fill in
76  * @param uc overall unindex context
77  * @param offset where we are in the file (for progress)
78  */
79 void
80 GNUNET_FS_unindex_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
81                                 struct GNUNET_FS_UnindexContext *uc,
82                                 uint64_t offset)
83 {
84   pi->value.unindex.uc = uc;
85   pi->value.unindex.cctx = uc->client_info;
86   pi->value.unindex.filename = uc->filename;
87   pi->value.unindex.size = uc->file_size;
88   pi->value.unindex.eta =
89       GNUNET_TIME_calculate_eta (uc->start_time, offset, uc->file_size);
90   pi->value.unindex.duration =
91       GNUNET_TIME_absolute_get_duration (uc->start_time);
92   pi->value.unindex.completed = offset;
93   pi->fsh = uc->h;
94   uc->client_info = uc->h->upcb (uc->h->upcb_cls, pi);
95 }
96
97
98 /**
99  * Function called with information about our
100  * progress in computing the tree encoding.
101  *
102  * @param cls closure
103  * @param offset where are we in the file
104  * @param pt_block plaintext of the currently processed block
105  * @param pt_size size of pt_block
106  * @param depth depth of the block in the tree, 0 for DBLOCK
107  */
108 static void
109 unindex_progress (void *cls, uint64_t offset, const void *pt_block,
110                   size_t pt_size, unsigned int depth)
111 {
112   struct GNUNET_FS_UnindexContext *uc = cls;
113   struct GNUNET_FS_ProgressInfo pi;
114
115   pi.status = GNUNET_FS_STATUS_UNINDEX_PROGRESS;
116   pi.value.unindex.specifics.progress.data = pt_block;
117   pi.value.unindex.specifics.progress.offset = offset;
118   pi.value.unindex.specifics.progress.data_len = pt_size;
119   pi.value.unindex.specifics.progress.depth = depth;
120   GNUNET_FS_unindex_make_status_ (&pi, uc, offset);
121 }
122
123
124 /**
125  * We've encountered an error during
126  * unindexing.  Signal the client.
127  *
128  * @param uc context for the failed unindexing operation
129  */
130 static void
131 signal_unindex_error (struct GNUNET_FS_UnindexContext *uc)
132 {
133   struct GNUNET_FS_ProgressInfo pi;
134
135   pi.status = GNUNET_FS_STATUS_UNINDEX_ERROR;
136   pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
137   pi.value.unindex.specifics.error.message = uc->emsg;
138   GNUNET_FS_unindex_make_status_ (&pi, uc, 0);
139 }
140
141
142 /**
143  * Continuation called to notify client about result of the
144  * datastore removal operation.
145  *
146  * @param cls closure
147  * @param success GNUNET_SYSERR on failure
148  * @param min_expiration minimum expiration time required for content to be stored
149  * @param msg NULL on success, otherwise an error message
150  */
151 static void
152 process_cont (void *cls, int success, struct GNUNET_TIME_Absolute min_expiration, const char *msg)
153 {
154   struct GNUNET_FS_UnindexContext *uc = cls;
155
156   if (success == GNUNET_SYSERR)
157   {
158     uc->emsg = GNUNET_strdup (msg);
159     signal_unindex_error (uc);
160     return;
161   }
162   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163               "Datastore REMOVE operation succeeded\n");
164   GNUNET_FS_tree_encoder_next (uc->tc);
165 }
166
167
168 /**
169  * Function called asking for the current (encoded)
170  * block to be processed.  After processing the
171  * client should either call "GNUNET_FS_tree_encode_next"
172  * or (on error) "GNUNET_FS_tree_encode_finish".
173  *
174  * @param cls closure
175  * @param chk content hash key for the block (key for lookup in the datastore)
176  * @param offset offset of the block
177  * @param depth depth of the block, 0 for DBLOCK
178  * @param type type of the block (IBLOCK or DBLOCK)
179  * @param block the (encrypted) block
180  * @param block_size size of block (in bytes)
181  */
182 static void
183 unindex_process (void *cls, const struct ContentHashKey *chk, uint64_t offset,
184                  unsigned int depth, enum GNUNET_BLOCK_Type type,
185                  const void *block, uint16_t block_size)
186 {
187   struct GNUNET_FS_UnindexContext *uc = cls;
188   uint32_t size;
189   const void *data;
190   struct OnDemandBlock odb;
191
192   if (type != GNUNET_BLOCK_TYPE_FS_DBLOCK)
193   {
194     size = block_size;
195     data = block;
196   }
197   else                          /* on-demand encoded DBLOCK */
198   {
199     size = sizeof (struct OnDemandBlock);
200     odb.offset = GNUNET_htonll (offset);
201     odb.file_id = uc->file_id;
202     data = &odb;
203   }
204   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205               "Sending REMOVE request to DATASTORE service\n");
206   GNUNET_DATASTORE_remove (uc->dsh, &chk->query, size, data, -2, 1,
207                            GNUNET_CONSTANTS_SERVICE_TIMEOUT, &process_cont, uc);
208   uc->chk = *chk;
209 }
210
211
212 /**
213  * Function called with the response from the
214  * FS service to our unindexing request.
215  *
216  * @param cls closure, unindex context
217  * @param msg NULL on timeout, otherwise the response
218  */
219 static void
220 process_fs_response (void *cls, const struct GNUNET_MessageHeader *msg)
221 {
222   struct GNUNET_FS_UnindexContext *uc = cls;
223   struct GNUNET_FS_ProgressInfo pi;
224
225   if (uc->client != NULL)
226   {
227     GNUNET_CLIENT_disconnect (uc->client);
228     uc->client = NULL;
229   }
230   if (uc->state != UNINDEX_STATE_FS_NOTIFY)
231   {
232     uc->state = UNINDEX_STATE_ERROR;
233     uc->emsg =
234         GNUNET_strdup (_("Unexpected time for a response from `fs' service."));
235     GNUNET_FS_unindex_sync_ (uc);
236     signal_unindex_error (uc);
237     return;
238   }
239   if (NULL == msg)
240   {
241     uc->state = UNINDEX_STATE_ERROR;
242     uc->emsg = GNUNET_strdup (_("Timeout waiting for `fs' service."));
243     GNUNET_FS_unindex_sync_ (uc);
244     signal_unindex_error (uc);
245     return;
246   }
247   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK)
248   {
249     uc->state = UNINDEX_STATE_ERROR;
250     uc->emsg = GNUNET_strdup (_("Invalid response from `fs' service."));
251     GNUNET_FS_unindex_sync_ (uc);
252     signal_unindex_error (uc);
253     return;
254   }
255   uc->state = UNINDEX_STATE_COMPLETE;
256   pi.status = GNUNET_FS_STATUS_UNINDEX_COMPLETED;
257   pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
258   GNUNET_FS_unindex_sync_ (uc);
259   GNUNET_FS_unindex_make_status_ (&pi, uc, uc->file_size);
260 }
261
262
263 /**
264  * Function called when we are done with removing UBlocks.
265  * Disconnect from datastore and notify FS service about
266  * the unindex event.
267  *
268  * @param uc our unindexing context
269  */
270 static void
271 unindex_finish (struct GNUNET_FS_UnindexContext *uc)
272 {
273   char *emsg;
274   struct GNUNET_FS_Uri *uri;
275   struct UnindexMessage req;
276
277   /* generate final progress message */
278   unindex_progress (uc, uc->file_size, NULL, 0, 0);
279   GNUNET_FS_tree_encoder_finish (uc->tc, &uri, &emsg);
280   uc->tc = NULL;
281   if (uri != NULL)
282     GNUNET_FS_uri_destroy (uri);
283   GNUNET_DISK_file_close (uc->fh);
284   uc->fh = NULL;
285   GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
286   uc->dsh = NULL;
287   uc->state = UNINDEX_STATE_FS_NOTIFY;
288   GNUNET_FS_unindex_sync_ (uc);
289   uc->client = GNUNET_CLIENT_connect ("fs", uc->h->cfg);
290   if (uc->client == NULL)
291   {
292     uc->state = UNINDEX_STATE_ERROR;
293     uc->emsg =
294         GNUNET_strdup (_("Failed to connect to FS service for unindexing."));
295     GNUNET_FS_unindex_sync_ (uc);
296     signal_unindex_error (uc);
297     return;
298   }
299   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
300               "Sending UNINDEX message to FS service\n");
301   req.header.size = htons (sizeof (struct UnindexMessage));
302   req.header.type = htons (GNUNET_MESSAGE_TYPE_FS_UNINDEX);
303   req.reserved = 0;
304   req.file_id = uc->file_id;
305   GNUNET_break (GNUNET_OK ==
306                 GNUNET_CLIENT_transmit_and_get_response (uc->client,
307                                                          &req.header,
308                                                          GNUNET_CONSTANTS_SERVICE_TIMEOUT,
309                                                          GNUNET_YES,
310                                                          &process_fs_response,
311                                                          uc));
312 }
313
314
315
316 /**
317  * Function called by the directory scanner as we extract keywords
318  * that we will need to remove UBlocks.
319  *
320  * @param cls the 'struct GNUNET_FS_UnindexContext *'
321  * @param filename which file we are making progress on
322  * @param is_directory GNUNET_YES if this is a directory,
323  *                     GNUNET_NO if this is a file
324  *                     GNUNET_SYSERR if it is neither (or unknown)
325  * @param reason kind of progress we are making
326  */
327 static void
328 unindex_directory_scan_cb (void *cls, 
329                            const char *filename, 
330                            int is_directory,
331                            enum GNUNET_FS_DirScannerProgressUpdateReason reason)
332 {
333   struct GNUNET_FS_UnindexContext *uc = cls;
334   static struct GNUNET_FS_ShareTreeItem * directory_scan_result;
335
336   switch (reason)
337   {
338   case GNUNET_FS_DIRSCANNER_FINISHED:
339     directory_scan_result = GNUNET_FS_directory_scan_get_result (uc->dscan);
340     uc->dscan = NULL;
341     if (NULL != directory_scan_result->ksk_uri)
342     {
343       uc->ksk_uri = GNUNET_FS_uri_dup (directory_scan_result->ksk_uri);
344       uc->state = UNINDEX_STATE_DS_REMOVE_KBLOCKS;
345       GNUNET_FS_unindex_sync_ (uc);
346       GNUNET_FS_unindex_do_remove_kblocks_ (uc);
347     }
348     else
349     {
350       uc->emsg = GNUNET_strdup (_("Failed to get KSKs from directory scan."));
351       GNUNET_FS_unindex_sync_ (uc);
352       unindex_finish (uc);
353     }
354     GNUNET_FS_share_tree_free (directory_scan_result);
355     break;
356   case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
357     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
358                 _("Internal error scanning `%s'.\n"),
359                 uc->filename);
360     GNUNET_FS_directory_scan_abort (uc->dscan);
361     uc->dscan = NULL;
362     uc->emsg = GNUNET_strdup (_("Failed to get KSKs from directory scan."));
363     GNUNET_FS_unindex_sync_ (uc);
364     unindex_finish (uc);
365     break;
366   default:
367     break;
368   }
369 }
370
371
372 /**
373  * If necessary, connect to the datastore and remove the UBlocks.
374  *
375  * @param uc context for the unindex operation.
376  */
377 void
378 GNUNET_FS_unindex_do_extract_keywords_ (struct GNUNET_FS_UnindexContext *uc)
379 {
380   char *ex;
381
382   if (GNUNET_OK !=
383       GNUNET_CONFIGURATION_get_value_string (uc->h->cfg, "FS", "EXTRACTORS", &ex))
384     ex = NULL;
385   uc->dscan = GNUNET_FS_directory_scan_start (uc->filename,
386                                               GNUNET_NO, ex,
387                                               &unindex_directory_scan_cb, 
388                                               uc);
389   GNUNET_free_non_null (ex);
390 }
391
392
393 /**
394  * Continuation called to notify client about result of the remove
395  * operation for the UBlock.
396  *
397  * @param cls the 'struct GNUNET_FS_UnindexContext *' 
398  * @param success GNUNET_SYSERR on failure (including timeout/queue drop)
399  *                GNUNET_NO if content was already there
400  *                GNUNET_YES (or other positive value) on success
401  * @param min_expiration minimum expiration time required for 0-priority content to be stored
402  *                by the datacache at this time, zero for unknown, forever if we have no
403  *                space for 0-priority content
404  * @param msg NULL on success, otherwise an error message
405  */
406 static void
407 continue_after_remove (void *cls,
408                        int32_t success,
409                        struct GNUNET_TIME_Absolute min_expiration,
410                        const char *msg)
411 {
412   struct GNUNET_FS_UnindexContext *uc = cls;
413
414   uc->dqe = NULL;
415   if (success != GNUNET_YES)  
416     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
417                 _("Failed to remove UBlock: %s\n"),
418                 msg);  
419   uc->ksk_offset++;
420   GNUNET_FS_unindex_do_remove_kblocks_ (uc);
421 }
422
423
424 /**
425  * Function called from datastore with result from us looking for
426  * a UBlock.  There are four cases:
427  * 1) no result, means we move on to the next keyword
428  * 2) UID is the same as the first UID, means we move on to next keyword
429  * 3) UBlock for a different CHK, means we keep looking for more
430  * 4) UBlock is for our CHK, means we remove the block and then move
431  *           on to the next keyword
432  *
433  * @param cls the 'struct GNUNET_FS_UnindexContext *'
434  * @param key key for the content
435  * @param size number of bytes in data
436  * @param data content stored
437  * @param type type of the content
438  * @param priority priority of the content
439  * @param anonymity anonymity-level for the content
440  * @param expiration expiration time for the content
441  * @param uid unique identifier for the datum;
442  *        maybe 0 if no unique identifier is available
443  */
444 static void
445 process_kblock_for_unindex (void *cls,
446                             const struct GNUNET_HashCode *key,
447                             size_t size, const void *data,
448                             enum GNUNET_BLOCK_Type type,
449                             uint32_t priority,
450                             uint32_t anonymity,
451                             struct GNUNET_TIME_Absolute
452                             expiration, uint64_t uid)
453 {
454   struct GNUNET_FS_UnindexContext *uc = cls;
455   const struct UBlock *ub;
456   struct GNUNET_FS_Uri *chk_uri;
457   struct GNUNET_HashCode query;
458
459   uc->dqe = NULL;
460   if (NULL == data)
461   {
462     /* no result */
463     uc->ksk_offset++;
464     GNUNET_FS_unindex_do_remove_kblocks_ (uc);
465     return;
466   }
467   if (0 == uc->first_uid)
468   {
469     /* remember UID of first result to detect cycles */
470     uc->first_uid = uid;    
471   }
472   else if (uid == uc->first_uid)
473   {
474     /* no more additional results */
475     uc->ksk_offset++;
476     GNUNET_FS_unindex_do_remove_kblocks_ (uc);
477     return;  
478   }
479   GNUNET_assert (GNUNET_BLOCK_TYPE_FS_UBLOCK == type);
480   if (size < sizeof (struct UBlock))
481   {
482     GNUNET_break (0);
483     goto get_next;
484   }
485   ub = data;
486   GNUNET_CRYPTO_hash (&ub->verification_key,
487                       sizeof (ub->verification_key),
488                       &query);
489   if (0 != memcmp (&query, key, sizeof (struct GNUNET_HashCode)))
490   {
491     /* result does not match our keyword, skip */
492     goto get_next;
493   }
494   {
495     char pt[size - sizeof (struct UBlock)];  
496     struct GNUNET_CRYPTO_EccPublicSignKey anon_pub;
497     const char *keyword;
498      
499     GNUNET_CRYPTO_ecc_key_get_public_for_signature (GNUNET_CRYPTO_ecc_key_get_anonymous (),
500                                                     &anon_pub);
501     keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
502     GNUNET_FS_ublock_decrypt_ (&ub[1], size - sizeof (struct UBlock),
503                                &anon_pub,
504                                keyword,
505                                pt);
506     if (NULL == memchr (&pt[1], 0, sizeof (pt) - 1))
507     {
508       GNUNET_break_op (0); /* malformed UBlock */
509       goto get_next;
510     }
511     chk_uri = GNUNET_FS_uri_parse (&pt[1], NULL);
512     if (NULL == chk_uri)
513     {
514       GNUNET_break_op (0); /* malformed UBlock */
515       goto get_next;
516     }
517   }
518   if (0 != memcmp (&uc->chk,
519                    &chk_uri->data.chk.chk,
520                    sizeof (struct ContentHashKey)))
521   {
522     /* different CHK, ignore */
523     GNUNET_FS_uri_destroy (chk_uri);
524     goto get_next;
525   }
526   GNUNET_FS_uri_destroy (chk_uri);
527   /* matches! */
528   uc->dqe = GNUNET_DATASTORE_remove (uc->dsh,
529                                      key, size, data,
530                                      0 /* priority */, 1 /* queue size */,
531                                      GNUNET_TIME_UNIT_FOREVER_REL,
532                                      &continue_after_remove,
533                                      uc);
534   return;
535  get_next:
536   uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
537                                       uc->roff++,
538                                       &uc->uquery,
539                                       GNUNET_BLOCK_TYPE_FS_UBLOCK,
540                                       0 /* priority */, 1 /* queue size */,
541                                       GNUNET_TIME_UNIT_FOREVER_REL,
542                                       &process_kblock_for_unindex,
543                                       uc);
544 }
545
546
547 /**
548  * If necessary, connect to the datastore and remove the KBlocks.
549  *
550  * @param uc context for the unindex operation.
551  */
552 void
553 GNUNET_FS_unindex_do_remove_kblocks_ (struct GNUNET_FS_UnindexContext *uc)
554 {
555   const char *keyword;
556   const struct GNUNET_CRYPTO_EccPrivateKey *anon;
557   struct GNUNET_CRYPTO_EccPublicSignKey anon_pub;
558   struct GNUNET_CRYPTO_EccPublicSignKey dpub;
559
560   if (NULL == uc->dsh)
561     uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
562   if (NULL == uc->dsh)
563   {
564     uc->state = UNINDEX_STATE_ERROR;
565     uc->emsg = GNUNET_strdup (_("Failed to connect to `datastore' service."));
566     GNUNET_FS_unindex_sync_ (uc);
567     signal_unindex_error (uc);
568     return;
569   }
570   if ( (NULL == uc->ksk_uri) ||
571        (uc->ksk_offset >= uc->ksk_uri->data.ksk.keywordCount) )
572   {
573     unindex_finish (uc);
574     return;
575   }
576   anon = GNUNET_CRYPTO_ecc_key_get_anonymous ();
577   GNUNET_CRYPTO_ecc_key_get_public_for_signature (anon, &anon_pub);
578   keyword = &uc->ksk_uri->data.ksk.keywords[uc->ksk_offset][1];
579   GNUNET_CRYPTO_ecc_public_key_derive (&anon_pub,
580                                        keyword,
581                                        "fs-ublock",
582                                        &dpub);
583   GNUNET_CRYPTO_hash (&dpub,
584                       sizeof (dpub),
585                       &uc->uquery);
586   uc->first_uid = 0;
587   uc->dqe = GNUNET_DATASTORE_get_key (uc->dsh,
588                                       uc->roff++,
589                                       &uc->uquery,
590                                       GNUNET_BLOCK_TYPE_FS_UBLOCK,
591                                       0 /* priority */, 1 /* queue size */,
592                                       GNUNET_TIME_UNIT_FOREVER_REL,
593                                       &process_kblock_for_unindex,
594                                       uc);
595 }
596
597
598 /**
599  * Function called when the tree encoder has
600  * processed all blocks.  Clean up.
601  *
602  * @param cls our unindexing context
603  * @param tc not used
604  */
605 static void
606 unindex_extract_keywords (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
607 {
608   struct GNUNET_FS_UnindexContext *uc = cls;
609
610   uc->state = UNINDEX_STATE_EXTRACT_KEYWORDS;
611   GNUNET_FS_unindex_sync_ (uc);
612   GNUNET_FS_unindex_do_extract_keywords_ (uc);
613 }
614
615
616 /**
617  * Connect to the datastore and remove the blocks.
618  *
619  * @param uc context for the unindex operation.
620  */
621 void
622 GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc)
623 {
624   if (NULL == uc->dsh)
625     uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
626   if (NULL == uc->dsh)
627   {
628     uc->state = UNINDEX_STATE_ERROR;
629     uc->emsg = GNUNET_strdup (_("Failed to connect to `datastore' service."));
630     GNUNET_FS_unindex_sync_ (uc);
631     signal_unindex_error (uc);
632     return;
633   }
634   uc->fh =
635       GNUNET_DISK_file_open (uc->filename, GNUNET_DISK_OPEN_READ,
636                              GNUNET_DISK_PERM_NONE);
637   if (NULL == uc->fh)
638   {
639     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
640     uc->dsh = NULL;
641     uc->state = UNINDEX_STATE_ERROR;
642     uc->emsg = GNUNET_strdup (_("Failed to open file for unindexing."));
643     GNUNET_FS_unindex_sync_ (uc);
644     signal_unindex_error (uc);
645     return;
646   }
647   uc->tc =
648       GNUNET_FS_tree_encoder_create (uc->h, uc->file_size, uc, &unindex_reader,
649                                      &unindex_process, &unindex_progress,
650                                      &unindex_extract_keywords);
651   GNUNET_FS_tree_encoder_next (uc->tc);
652 }
653
654
655 /**
656  * Function called once the hash of the file
657  * that is being unindexed has been computed.
658  *
659  * @param cls closure, unindex context
660  * @param file_id computed hash, NULL on error
661  */
662 void
663 GNUNET_FS_unindex_process_hash_ (void *cls, const struct GNUNET_HashCode * file_id)
664 {
665   struct GNUNET_FS_UnindexContext *uc = cls;
666
667   uc->fhc = NULL;
668   if (uc->state != UNINDEX_STATE_HASHING)
669   {
670     GNUNET_FS_unindex_stop (uc);
671     return;
672   }
673   if (file_id == NULL)
674   {
675     uc->state = UNINDEX_STATE_ERROR;
676     uc->emsg = GNUNET_strdup (_("Failed to compute hash of file."));
677     GNUNET_FS_unindex_sync_ (uc);
678     signal_unindex_error (uc);
679     return;
680   }
681   uc->file_id = *file_id;
682   uc->state = UNINDEX_STATE_DS_REMOVE;
683   GNUNET_FS_unindex_sync_ (uc);
684   GNUNET_FS_unindex_do_remove_ (uc);
685 }
686
687
688 /**
689  * Create SUSPEND event for the given unindex operation
690  * and then clean up our state (without stop signal).
691  *
692  * @param cls the `struct GNUNET_FS_UnindexContext` to signal for
693  */
694 void
695 GNUNET_FS_unindex_signal_suspend_ (void *cls)
696 {
697   struct GNUNET_FS_UnindexContext *uc = cls;
698   struct GNUNET_FS_ProgressInfo pi;
699
700   /* FIXME: lots of duplication with unindex_stop here! */
701   if (uc->dscan != NULL)
702   {
703     GNUNET_FS_directory_scan_abort (uc->dscan);
704     uc->dscan = NULL;
705   }
706   if (NULL != uc->dqe)
707   {
708     GNUNET_DATASTORE_cancel (uc->dqe);
709     uc->dqe = NULL;
710   }
711   if (uc->fhc != NULL)
712   {
713     GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
714     uc->fhc = NULL;
715   }
716   if (NULL != uc->ksk_uri)
717   {
718     GNUNET_FS_uri_destroy (uc->ksk_uri);
719     uc->ksk_uri = NULL;
720   }
721   if (uc->client != NULL)
722   {
723     GNUNET_CLIENT_disconnect (uc->client);
724     uc->client = NULL;
725   }
726   if (NULL != uc->dsh)
727   {
728     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
729     uc->dsh = NULL;
730   }
731   if (NULL != uc->tc)
732   {
733     GNUNET_FS_tree_encoder_finish (uc->tc, NULL, NULL);
734     uc->tc = NULL;
735   }
736   if (uc->fh != NULL)
737   {
738     GNUNET_DISK_file_close (uc->fh);
739     uc->fh = NULL;
740   }
741   GNUNET_FS_end_top (uc->h, uc->top);
742   pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND;
743   GNUNET_FS_unindex_make_status_ (&pi, uc,
744                                   (uc->state ==
745                                    UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
746   GNUNET_break (NULL == uc->client_info);
747   GNUNET_free (uc->filename);
748   GNUNET_free_non_null (uc->serialization);
749   GNUNET_free_non_null (uc->emsg);
750   GNUNET_free (uc);
751 }
752
753
754 /**
755  * Unindex a file.
756  *
757  * @param h handle to the file sharing subsystem
758  * @param filename file to unindex
759  * @param cctx initial value for the client context
760  * @return NULL on error, otherwise handle
761  */
762 struct GNUNET_FS_UnindexContext *
763 GNUNET_FS_unindex_start (struct GNUNET_FS_Handle *h, 
764                          const char *filename,
765                          void *cctx)
766 {
767   struct GNUNET_FS_UnindexContext *ret;
768   struct GNUNET_FS_ProgressInfo pi;
769   uint64_t size;
770
771   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &size, GNUNET_YES, GNUNET_YES))
772     return NULL;
773   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_UnindexContext));
774   ret->h = h;
775   ret->filename = GNUNET_strdup (filename);
776   ret->start_time = GNUNET_TIME_absolute_get ();
777   ret->file_size = size;
778   ret->client_info = cctx;
779   GNUNET_FS_unindex_sync_ (ret);
780   pi.status = GNUNET_FS_STATUS_UNINDEX_START;
781   pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
782   GNUNET_FS_unindex_make_status_ (&pi, ret, 0);
783   ret->fhc =
784       GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, filename,
785                                HASHING_BLOCKSIZE,
786                                &GNUNET_FS_unindex_process_hash_, ret);
787   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_unindex_signal_suspend_, ret);
788   return ret;
789 }
790
791
792 /**
793  * Clean up after completion of an unindex operation.
794  *
795  * @param uc handle
796  */
797 void
798 GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
799 {
800   struct GNUNET_FS_ProgressInfo pi;
801
802   if (uc->dscan != NULL)
803   {
804     GNUNET_FS_directory_scan_abort (uc->dscan);
805     uc->dscan = NULL;
806   }
807   if (NULL != uc->dqe)
808   {
809     GNUNET_DATASTORE_cancel (uc->dqe);
810     uc->dqe = NULL;
811   }
812   if (uc->fhc != NULL)
813   {
814     GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
815     uc->fhc = NULL;
816   }
817   if (uc->client != NULL)
818   {
819     GNUNET_CLIENT_disconnect (uc->client);
820     uc->client = NULL;
821   }
822   if (NULL != uc->dsh)
823   {
824     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
825     uc->dsh = NULL;
826   }
827   if (NULL != uc->ksk_uri)
828   {
829     GNUNET_FS_uri_destroy (uc->ksk_uri);
830     uc->ksk_uri = NULL;
831   }
832   if (NULL != uc->tc)
833   {
834     GNUNET_FS_tree_encoder_finish (uc->tc, NULL, NULL);
835     uc->tc = NULL;
836   }
837   if (uc->fh != NULL)
838   {
839     GNUNET_DISK_file_close (uc->fh);
840     uc->fh = NULL;
841   }
842   GNUNET_FS_end_top (uc->h, uc->top);
843   if (uc->serialization != NULL)
844   {
845     GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
846                                  uc->serialization);
847     GNUNET_free (uc->serialization);
848     uc->serialization = NULL;
849   }
850   pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED;
851   pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
852   GNUNET_FS_unindex_make_status_ (&pi, uc,
853                                   (uc->state ==
854                                    UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
855   GNUNET_break (NULL == uc->client_info);
856   GNUNET_free_non_null (uc->emsg);
857   GNUNET_free (uc->filename);
858   GNUNET_free (uc);
859 }
860
861 /* end of fs_unindex.c */