9a834ee86b4af02a81f269b0802ebda687818163
[oweals/gnunet.git] / src / fs / fs_publish.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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_publish.c
23  * @brief publish a file or directory in GNUnet
24  * @see https://gnunet.org/encoding
25  * @author Krista Bennett
26  * @author Christian Grothoff
27  */
28
29 #include "platform.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_signatures.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_fs_service.h"
34 #include "fs_api.h"
35 #include "fs_tree.h"
36
37 #define DEBUG_PUBLISH GNUNET_EXTRA_LOGGING
38
39 /**
40  * Maximum legal size for a kblock.
41  */
42 #define MAX_KBLOCK_SIZE (60 * 1024)
43
44
45 /**
46  * Fill in all of the generic fields for
47  * a publish event and call the callback.
48  *
49  * @param pi structure to fill in
50  * @param pc overall publishing context
51  * @param p file information for the file being published
52  * @param offset where in the file are we so far
53  * @return value returned from callback
54  */
55 void *
56 GNUNET_FS_publish_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
57                                 struct GNUNET_FS_PublishContext *pc,
58                                 const struct GNUNET_FS_FileInformation *p,
59                                 uint64_t offset)
60 {
61   pi->value.publish.pc = pc;
62   pi->value.publish.fi = p;
63   pi->value.publish.cctx = p->client_info;
64   pi->value.publish.pctx = (NULL == p->dir) ? NULL : p->dir->client_info;
65   pi->value.publish.filename = p->filename;
66   pi->value.publish.size =
67       (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
68   pi->value.publish.eta =
69       GNUNET_TIME_calculate_eta (p->start_time, offset, pi->value.publish.size);
70   pi->value.publish.completed = offset;
71   pi->value.publish.duration =
72       GNUNET_TIME_absolute_get_duration (p->start_time);
73   pi->value.publish.anonymity = p->bo.anonymity_level;
74   return pc->h->upcb (pc->h->upcb_cls, pi);
75 }
76
77
78 /**
79  * Cleanup the publish context, we're done with it.
80  *
81  * @param cls struct to clean up after
82  * @param tc scheduler context
83  */
84 static void
85 publish_cleanup (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
86 {
87   struct GNUNET_FS_PublishContext *pc = cls;
88
89 #if DEBUG_PUBLISH
90   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up publish context (done!)\n");
91 #endif
92   if (pc->fhc != NULL)
93   {
94     GNUNET_CRYPTO_hash_file_cancel (pc->fhc);
95     pc->fhc = NULL;
96   }
97   GNUNET_FS_file_information_destroy (pc->fi, NULL, NULL);
98   if (pc->namespace != NULL)
99   {
100     GNUNET_FS_namespace_delete (pc->namespace, GNUNET_NO);
101     pc->namespace = NULL;
102   }
103   GNUNET_free_non_null (pc->nid);
104   GNUNET_free_non_null (pc->nuid);
105   GNUNET_free_non_null (pc->serialization);
106   if (pc->dsh != NULL)
107   {
108     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
109     pc->dsh = NULL;
110   }
111   if (pc->client != NULL)
112   {
113     GNUNET_CLIENT_disconnect (pc->client, GNUNET_NO);
114     pc->client = NULL;
115   }
116   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == pc->upload_task);
117   GNUNET_free (pc);
118 }
119
120
121 /**
122  * Function called by the datastore API with
123  * the result from the PUT request.
124  *
125  * @param cls the 'struct GNUNET_FS_PublishContext'
126  * @param success GNUNET_OK on success
127  * @param msg error message (or NULL)
128  */
129 static void
130 ds_put_cont (void *cls, int success, const char *msg)
131 {
132   struct GNUNET_FS_PublishContext *pc = cls;
133   struct GNUNET_FS_ProgressInfo pi;
134
135   pc->qre = NULL;
136   if (GNUNET_SYSERR == pc->in_network_wait)
137   {
138     /* we were aborted in the meantime, finish shutdown! */
139     GNUNET_SCHEDULER_add_continuation (&publish_cleanup, pc,
140                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
141     return;
142   }
143   GNUNET_assert (GNUNET_YES == pc->in_network_wait);
144   pc->in_network_wait = GNUNET_NO;
145   if (GNUNET_SYSERR == success)
146   {
147     GNUNET_asprintf (&pc->fi_pos->emsg, _("Publishing failed: %s"), msg);
148     pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
149     pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
150     pi.value.publish.specifics.error.message = pc->fi_pos->emsg;
151     pc->fi_pos->client_info =
152         GNUNET_FS_publish_make_status_ (&pi, pc, pc->fi_pos, 0);
153     if ((pc->fi_pos->is_directory == GNUNET_NO) &&
154         (pc->fi_pos->filename != NULL) &&
155         (pc->fi_pos->data.file.do_index == GNUNET_YES))
156     {
157       /* run unindex to clean up */
158       GNUNET_FS_unindex_start (pc->h, pc->fi_pos->filename, NULL);
159     }
160   }
161   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == pc->upload_task);
162   pc->upload_task =
163       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
164                                           &GNUNET_FS_publish_main_, pc);
165 }
166
167
168 /**
169  * Generate the callback that signals clients
170  * that a file (or directory) has been completely
171  * published.
172  *
173  * @param p the completed upload
174  * @param pc context of the publication
175  */
176 static void
177 signal_publish_completion (struct GNUNET_FS_FileInformation *p,
178                            struct GNUNET_FS_PublishContext *pc)
179 {
180   struct GNUNET_FS_ProgressInfo pi;
181
182   pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
183   pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
184   pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
185   p->client_info =
186       GNUNET_FS_publish_make_status_ (&pi, pc, p,
187                                       GNUNET_ntohll (p->chk_uri->data.
188                                                      chk.file_length));
189 }
190
191
192 /**
193  * Generate the callback that signals clients
194  * that a file (or directory) has encountered
195  * a problem during publication.
196  *
197  * @param p the upload that had trouble
198  * @param pc context of the publication
199  * @param emsg error message
200  */
201 static void
202 signal_publish_error (struct GNUNET_FS_FileInformation *p,
203                       struct GNUNET_FS_PublishContext *pc, const char *emsg)
204 {
205   struct GNUNET_FS_ProgressInfo pi;
206
207   p->emsg = GNUNET_strdup (emsg);
208   pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
209   pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
210   pi.value.publish.specifics.error.message = emsg;
211   p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
212   if ((p->is_directory == GNUNET_NO) && (p->filename != NULL) &&
213       (p->data.file.do_index == GNUNET_YES))
214   {
215     /* run unindex to clean up */
216     GNUNET_FS_unindex_start (pc->h, p->filename, NULL);
217   }
218
219 }
220
221
222 /**
223  * Datastore returns from reservation cancel request.
224  *
225  * @param cls the 'struct GNUNET_FS_PublishContext'
226  * @param success success code (not used)
227  * @param msg error message (typically NULL, not used)
228  */
229 static void
230 finish_release_reserve (void *cls, int success, const char *msg)
231 {
232   struct GNUNET_FS_PublishContext *pc = cls;
233
234   pc->qre = NULL;
235 #if DEBUG_PUBLISH
236   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Releasing reserve done!\n");
237 #endif
238   signal_publish_completion (pc->fi, pc);
239   pc->all_done = GNUNET_YES;
240   GNUNET_FS_publish_sync_ (pc);
241 }
242
243
244 /**
245  * We've finished publishing the SBlock as part of a larger upload.
246  * Check the result and complete the larger upload.
247  *
248  * @param cls the "struct GNUNET_FS_PublishContext*" of the larger upload
249  * @param uri URI of the published SBlock
250  * @param emsg NULL on success, otherwise error message
251  */
252 static void
253 publish_sblocks_cont (void *cls, const struct GNUNET_FS_Uri *uri,
254                       const char *emsg)
255 {
256   struct GNUNET_FS_PublishContext *pc = cls;
257
258   if (NULL != emsg)
259   {
260     signal_publish_error (pc->fi, pc, emsg);
261     GNUNET_FS_publish_sync_ (pc);
262     return;
263   }
264   GNUNET_assert (pc->qre == NULL);
265   if ((pc->dsh != NULL) && (pc->rid != 0))
266   {
267     pc->qre =
268         GNUNET_DATASTORE_release_reserve (pc->dsh, pc->rid, UINT_MAX, UINT_MAX,
269                                           GNUNET_TIME_UNIT_FOREVER_REL,
270                                           &finish_release_reserve, pc);
271   }
272   else
273   {
274     finish_release_reserve (pc, GNUNET_OK, NULL);
275   }
276 }
277
278
279 /**
280  * We are almost done publishing the structure,
281  * add SBlocks (if needed).
282  *
283  * @param pc overall upload data
284  */
285 static void
286 publish_sblock (struct GNUNET_FS_PublishContext *pc)
287 {
288   if (NULL != pc->namespace)
289     GNUNET_FS_publish_sks (pc->h, pc->namespace, pc->nid, pc->nuid,
290                            pc->fi->meta, pc->fi->chk_uri, &pc->fi->bo,
291                            pc->options, &publish_sblocks_cont, pc);
292   else
293     publish_sblocks_cont (pc, NULL, NULL);
294 }
295
296
297 /**
298  * We've finished publishing a KBlock as part of a larger upload.
299  * Check the result and continue the larger upload.
300  *
301  * @param cls the "struct GNUNET_FS_PublishContext*"
302  *        of the larger upload
303  * @param uri URI of the published blocks
304  * @param emsg NULL on success, otherwise error message
305  */
306 static void
307 publish_kblocks_cont (void *cls, const struct GNUNET_FS_Uri *uri,
308                       const char *emsg)
309 {
310   struct GNUNET_FS_PublishContext *pc = cls;
311   struct GNUNET_FS_FileInformation *p = pc->fi_pos;
312
313   if (NULL != emsg)
314   {
315 #if DEBUG_PUBLISH
316     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error uploading KSK blocks: %s\n",
317                 emsg);
318 #endif
319     signal_publish_error (p, pc, emsg);
320     GNUNET_FS_file_information_sync_ (p);
321     GNUNET_FS_publish_sync_ (pc);
322     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == pc->upload_task);
323     pc->upload_task =
324         GNUNET_SCHEDULER_add_with_priority
325         (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, pc);
326     return;
327   }
328 #if DEBUG_PUBLISH
329   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330               "KSK blocks published, moving on to next file\n");
331 #endif
332   if (NULL != p->dir)
333     signal_publish_completion (p, pc);
334   /* move on to next file */
335   if (NULL != p->next)
336     pc->fi_pos = p->next;
337   else
338     pc->fi_pos = p->dir;
339   GNUNET_FS_publish_sync_ (pc);
340   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == pc->upload_task);
341   pc->upload_task =
342       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
343                                           &GNUNET_FS_publish_main_, pc);
344 }
345
346
347 /**
348  * Function called by the tree encoder to obtain
349  * a block of plaintext data (for the lowest level
350  * of the tree).
351  *
352  * @param cls our publishing context
353  * @param offset identifies which block to get
354  * @param max (maximum) number of bytes to get; returning
355  *        fewer will also cause errors
356  * @param buf where to copy the plaintext buffer
357  * @param emsg location to store an error message (on error)
358  * @return number of bytes copied to buf, 0 on error
359  */
360 static size_t
361 block_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg)
362 {
363   struct GNUNET_FS_PublishContext *pc = cls;
364   struct GNUNET_FS_FileInformation *p;
365   size_t pt_size;
366   const char *dd;
367
368   p = pc->fi_pos;
369   if (p->is_directory)
370   {
371     pt_size = GNUNET_MIN (max, p->data.dir.dir_size - offset);
372     dd = p->data.dir.dir_data;
373     memcpy (buf, &dd[offset], pt_size);
374   }
375   else
376   {
377     pt_size = GNUNET_MIN (max, p->data.file.file_size - offset);
378     if (pt_size == 0)
379       return 0;                 /* calling reader with pt_size==0
380                                  * might free buf, so don't! */
381     if (pt_size !=
382         p->data.file.reader (p->data.file.reader_cls, offset, pt_size, buf,
383                              emsg))
384       return 0;
385   }
386   return pt_size;
387 }
388
389
390 /**
391  * The tree encoder has finished processing a
392  * file.   Call it's finish method and deal with
393  * the final result.
394  *
395  * @param cls our publishing context
396  * @param tc scheduler's task context (not used)
397  */
398 static void
399 encode_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
400 {
401   struct GNUNET_FS_PublishContext *pc = cls;
402   struct GNUNET_FS_FileInformation *p;
403   struct GNUNET_FS_ProgressInfo pi;
404   char *emsg;
405   uint64_t flen;
406
407   p = pc->fi_pos;
408   GNUNET_FS_tree_encoder_finish (p->te, &p->chk_uri, &emsg);
409   p->te = NULL;
410   if (NULL != emsg)
411   {
412 #if DEBUG_PUBLISH
413     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error during tree walk: %s\n", emsg);
414 #endif
415     GNUNET_asprintf (&p->emsg, _("Publishing failed: %s"), emsg);
416     GNUNET_free (emsg);
417     pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
418     pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
419     pi.value.publish.specifics.error.message = p->emsg;
420     p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
421   }
422 #if DEBUG_PUBLISH
423   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished with tree encoder\n");
424 #endif
425   /* final progress event */
426   flen = GNUNET_FS_uri_chk_get_file_size (p->chk_uri);
427   pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
428   pi.value.publish.specifics.progress.data = NULL;
429   pi.value.publish.specifics.progress.offset = flen;
430   pi.value.publish.specifics.progress.data_len = 0;
431   pi.value.publish.specifics.progress.depth = GNUNET_FS_compute_depth (flen);
432   p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, flen);
433
434   /* continue with main */
435   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == pc->upload_task);
436   pc->upload_task =
437       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
438                                           &GNUNET_FS_publish_main_, pc);
439 }
440
441
442 /**
443  * Function called asking for the current (encoded)
444  * block to be processed.  After processing the
445  * client should either call "GNUNET_FS_tree_encode_next"
446  * or (on error) "GNUNET_FS_tree_encode_finish".
447  *
448  * @param cls closure
449  * @param chk content hash key for the block
450  * @param offset offset of the block in the file
451  * @param depth depth of the block in the file, 0 for DBLOCK
452  * @param type type of the block (IBLOCK or DBLOCK)
453  * @param block the (encrypted) block
454  * @param block_size size of block (in bytes)
455  */
456 static void
457 block_proc (void *cls, const struct ContentHashKey *chk, uint64_t offset,
458             unsigned int depth, enum GNUNET_BLOCK_Type type, const void *block,
459             uint16_t block_size)
460 {
461   struct GNUNET_FS_PublishContext *pc = cls;
462   struct GNUNET_FS_FileInformation *p;
463   struct OnDemandBlock odb;
464
465   p = pc->fi_pos;
466   if (NULL == pc->dsh)
467   {
468 #if DEBUG_PUBLISH
469     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Waiting for datastore connection\n");
470 #endif
471     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == pc->upload_task);
472     pc->upload_task =
473         GNUNET_SCHEDULER_add_with_priority
474         (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, pc);
475     return;
476   }
477
478   GNUNET_assert (GNUNET_NO == pc->in_network_wait);
479   pc->in_network_wait = GNUNET_YES;
480   if ((!p->is_directory) && (GNUNET_YES == p->data.file.do_index) &&
481       (type == GNUNET_BLOCK_TYPE_FS_DBLOCK))
482   {
483 #if DEBUG_PUBLISH
484     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485                 "Indexing block `%s' for offset %llu with index size %u\n",
486                 GNUNET_h2s (&chk->query), (unsigned long long) offset,
487                 sizeof (struct OnDemandBlock));
488 #endif
489     odb.offset = GNUNET_htonll (offset);
490     odb.file_id = p->data.file.file_id;
491     GNUNET_assert (pc->qre == NULL);
492     pc->qre =
493         GNUNET_DATASTORE_put (pc->dsh, (p->is_directory) ? 0 : pc->rid,
494                               &chk->query, sizeof (struct OnDemandBlock), &odb,
495                               GNUNET_BLOCK_TYPE_FS_ONDEMAND,
496                               p->bo.content_priority, p->bo.anonymity_level,
497                               p->bo.replication_level, p->bo.expiration_time,
498                               -2, 1, GNUNET_CONSTANTS_SERVICE_TIMEOUT,
499                               &ds_put_cont, pc);
500     return;
501   }
502 #if DEBUG_PUBLISH
503   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
504               "Publishing block `%s' for offset %llu with size %u\n",
505               GNUNET_h2s (&chk->query), (unsigned long long) offset,
506               (unsigned int) block_size);
507 #endif
508   GNUNET_assert (pc->qre == NULL);
509   pc->qre =
510       GNUNET_DATASTORE_put (pc->dsh, (p->is_directory) ? 0 : pc->rid,
511                             &chk->query, block_size, block, type,
512                             p->bo.content_priority, p->bo.anonymity_level,
513                             p->bo.replication_level, p->bo.expiration_time, -2,
514                             1, GNUNET_CONSTANTS_SERVICE_TIMEOUT, &ds_put_cont,
515                             pc);
516 }
517
518
519 /**
520  * Function called with information about our
521  * progress in computing the tree encoding.
522  *
523  * @param cls closure
524  * @param offset where are we in the file
525  * @param pt_block plaintext of the currently processed block
526  * @param pt_size size of pt_block
527  * @param depth depth of the block in the tree, 0 for DBLOCK
528  */
529 static void
530 progress_proc (void *cls, uint64_t offset, const void *pt_block, size_t pt_size,
531                unsigned int depth)
532 {
533   struct GNUNET_FS_PublishContext *pc = cls;
534   struct GNUNET_FS_FileInformation *p;
535   struct GNUNET_FS_ProgressInfo pi;
536
537   p = pc->fi_pos;
538   pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
539   pi.value.publish.specifics.progress.data = pt_block;
540   pi.value.publish.specifics.progress.offset = offset;
541   pi.value.publish.specifics.progress.data_len = pt_size;
542   pi.value.publish.specifics.progress.depth = depth;
543   p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, offset);
544 }
545
546
547 /**
548  * We are uploading a file or directory; load (if necessary) the next
549  * block into memory, encrypt it and send it to the FS service.  Then
550  * continue with the main task.
551  *
552  * @param pc overall upload data
553  */
554 static void
555 publish_content (struct GNUNET_FS_PublishContext *pc)
556 {
557   struct GNUNET_FS_FileInformation *p;
558   char *emsg;
559   struct GNUNET_FS_DirectoryBuilder *db;
560   struct GNUNET_FS_FileInformation *dirpos;
561   void *raw_data;
562   uint64_t size;
563
564   p = pc->fi_pos;
565   GNUNET_assert (p != NULL);
566   if (NULL == p->te)
567   {
568     if (p->is_directory)
569     {
570 #if DEBUG_PUBLISH
571       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating directory\n");
572 #endif
573       db = GNUNET_FS_directory_builder_create (p->meta);
574       dirpos = p->data.dir.entries;
575       while (NULL != dirpos)
576       {
577         if (dirpos->is_directory)
578         {
579           raw_data = dirpos->data.dir.dir_data;
580           dirpos->data.dir.dir_data = NULL;
581         }
582         else
583         {
584           raw_data = NULL;
585           if ((dirpos->data.file.file_size < MAX_INLINE_SIZE) &&
586               (dirpos->data.file.file_size > 0))
587           {
588             raw_data = GNUNET_malloc (dirpos->data.file.file_size);
589             emsg = NULL;
590             if (dirpos->data.file.file_size !=
591                 dirpos->data.file.reader (dirpos->data.file.reader_cls, 0,
592                                           dirpos->data.file.file_size, raw_data,
593                                           &emsg))
594             {
595               GNUNET_free_non_null (emsg);
596               GNUNET_free (raw_data);
597               raw_data = NULL;
598             }
599           }
600         }
601         GNUNET_FS_directory_builder_add (db, dirpos->chk_uri, dirpos->meta,
602                                          raw_data);
603         GNUNET_free_non_null (raw_data);
604         dirpos = dirpos->next;
605       }
606       GNUNET_free_non_null (p->data.dir.dir_data);
607       p->data.dir.dir_data = NULL;
608       p->data.dir.dir_size = 0;
609       GNUNET_FS_directory_builder_finish (db, &p->data.dir.dir_size,
610                                           &p->data.dir.dir_data);
611       GNUNET_FS_file_information_sync_ (p);
612     }
613     size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
614 #if DEBUG_PUBLISH
615     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating tree encoder\n");
616 #endif
617     p->te =
618         GNUNET_FS_tree_encoder_create (pc->h, size, pc, &block_reader,
619                                        &block_proc, &progress_proc,
620                                        &encode_cont);
621
622   }
623 #if DEBUG_PUBLISH
624   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing next block from tree\n");
625 #endif
626   GNUNET_FS_tree_encoder_next (p->te);
627 }
628
629
630 /**
631  * Process the response (or lack thereof) from
632  * the "fs" service to our 'start index' request.
633  *
634  * @param cls closure (of type "struct GNUNET_FS_PublishContext*"_)
635  * @param msg the response we got
636  */
637 static void
638 process_index_start_response (void *cls, const struct GNUNET_MessageHeader *msg)
639 {
640   struct GNUNET_FS_PublishContext *pc = cls;
641   struct GNUNET_FS_FileInformation *p;
642   const char *emsg;
643   uint16_t msize;
644
645   GNUNET_CLIENT_disconnect (pc->client, GNUNET_NO);
646   pc->client = NULL;
647   p = pc->fi_pos;
648   if (msg == NULL)
649   {
650     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
651                 _
652                 ("Can not index file `%s': %s.  Will try to insert instead.\n"),
653                 p->filename,
654                 _("timeout on index-start request to `fs' service"));
655     p->data.file.do_index = GNUNET_NO;
656     GNUNET_FS_file_information_sync_ (p);
657     publish_content (pc);
658     return;
659   }
660   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK)
661   {
662     msize = ntohs (msg->size);
663     emsg = (const char *) &msg[1];
664     if ((msize <= sizeof (struct GNUNET_MessageHeader)) ||
665         (emsg[msize - sizeof (struct GNUNET_MessageHeader) - 1] != '\0'))
666       emsg = gettext_noop ("unknown error");
667     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
668                 _
669                 ("Can not index file `%s': %s.  Will try to insert instead.\n"),
670                 p->filename, gettext (emsg));
671     p->data.file.do_index = GNUNET_NO;
672     GNUNET_FS_file_information_sync_ (p);
673     publish_content (pc);
674     return;
675   }
676   p->data.file.index_start_confirmed = GNUNET_YES;
677   /* success! continue with indexing */
678   GNUNET_FS_file_information_sync_ (p);
679   publish_content (pc);
680 }
681
682
683 /**
684  * Function called once the hash computation over an
685  * indexed file has completed.
686  *
687  * @param cls closure, our publishing context
688  * @param res resulting hash, NULL on error
689  */
690 static void
691 hash_for_index_cb (void *cls, const GNUNET_HashCode * res)
692 {
693   struct GNUNET_FS_PublishContext *pc = cls;
694   struct GNUNET_FS_FileInformation *p;
695   struct IndexStartMessage *ism;
696   size_t slen;
697   struct GNUNET_CLIENT_Connection *client;
698   uint64_t dev;
699   uint64_t ino;
700   char *fn;
701
702   pc->fhc = NULL;
703   p = pc->fi_pos;
704   if (NULL == res)
705   {
706     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
707                 _
708                 ("Can not index file `%s': %s.  Will try to insert instead.\n"),
709                 p->filename, _("failed to compute hash"));
710     p->data.file.do_index = GNUNET_NO;
711     GNUNET_FS_file_information_sync_ (p);
712     publish_content (pc);
713     return;
714   }
715   if (GNUNET_YES == p->data.file.index_start_confirmed)
716   {
717     publish_content (pc);
718     return;
719   }
720   fn = GNUNET_STRINGS_filename_expand (p->filename);
721   GNUNET_assert (fn != NULL);
722   slen = strlen (fn) + 1;
723   if (slen >=
724       GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct IndexStartMessage))
725   {
726     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
727                 _
728                 ("Can not index file `%s': %s.  Will try to insert instead.\n"),
729                 fn, _("filename too long"));
730     GNUNET_free (fn);
731     p->data.file.do_index = GNUNET_NO;
732     GNUNET_FS_file_information_sync_ (p);
733     publish_content (pc);
734     return;
735   }
736 #if DEBUG_PUBLISH
737   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hash of indexed file `%s' is `%s'\n",
738               p->filename, GNUNET_h2s (res));
739 #endif
740   if (0 != (pc->options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
741   {
742     p->data.file.file_id = *res;
743     p->data.file.have_hash = GNUNET_YES;
744     p->data.file.index_start_confirmed = GNUNET_YES;
745     GNUNET_FS_file_information_sync_ (p);
746     publish_content (pc);
747     GNUNET_free (fn);
748     return;
749   }
750   client = GNUNET_CLIENT_connect ("fs", pc->h->cfg);
751   if (NULL == client)
752   {
753     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
754                 _
755                 ("Can not index file `%s': %s.  Will try to insert instead.\n"),
756                 p->filename, _("could not connect to `fs' service"));
757     p->data.file.do_index = GNUNET_NO;
758     publish_content (pc);
759     GNUNET_free (fn);
760     return;
761   }
762   if (p->data.file.have_hash != GNUNET_YES)
763   {
764     p->data.file.file_id = *res;
765     p->data.file.have_hash = GNUNET_YES;
766     GNUNET_FS_file_information_sync_ (p);
767   }
768   ism = GNUNET_malloc (sizeof (struct IndexStartMessage) + slen);
769   ism->header.size = htons (sizeof (struct IndexStartMessage) + slen);
770   ism->header.type = htons (GNUNET_MESSAGE_TYPE_FS_INDEX_START);
771   if (GNUNET_OK == GNUNET_DISK_file_get_identifiers (p->filename, &dev, &ino))
772   {
773     ism->device = GNUNET_htonll (dev);
774     ism->inode = GNUNET_htonll (ino);
775   }
776 #if DEBUG_PUBLISH
777   else
778   {
779     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
780                 _("Failed to get file identifiers for `%s'\n"), p->filename);
781   }
782 #endif
783   ism->file_id = *res;
784   memcpy (&ism[1], fn, slen);
785   GNUNET_free (fn);
786   pc->client = client;
787   GNUNET_break (GNUNET_YES ==
788                 GNUNET_CLIENT_transmit_and_get_response (client, &ism->header,
789                                                          GNUNET_TIME_UNIT_FOREVER_REL,
790                                                          GNUNET_YES,
791                                                          &process_index_start_response,
792                                                          pc));
793   GNUNET_free (ism);
794 }
795
796
797 /**
798  * Main function that performs the upload.
799  *
800  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
801  * @param tc task context
802  */
803 void
804 GNUNET_FS_publish_main_ (void *cls,
805                          const struct GNUNET_SCHEDULER_TaskContext *tc)
806 {
807   struct GNUNET_FS_PublishContext *pc = cls;
808   struct GNUNET_FS_ProgressInfo pi;
809   struct GNUNET_FS_FileInformation *p;
810   struct GNUNET_FS_Uri *loc;
811   char *fn;
812
813   pc->upload_task = GNUNET_SCHEDULER_NO_TASK;
814   p = pc->fi_pos;
815   if (NULL == p)
816   {
817 #if DEBUG_PUBLISH
818     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819                 "Publishing complete, now publishing SKS and KSK blocks.\n");
820 #endif
821     /* upload of entire hierarchy complete,
822      * publish namespace entries */
823     GNUNET_FS_publish_sync_ (pc);
824     publish_sblock (pc);
825     return;
826   }
827   /* find starting position */
828   while ((p->is_directory) && (NULL != p->data.dir.entries) && (NULL == p->emsg)
829          && (NULL == p->data.dir.entries->chk_uri))
830   {
831     p = p->data.dir.entries;
832     pc->fi_pos = p;
833     GNUNET_FS_publish_sync_ (pc);
834   }
835   /* abort on error */
836   if (NULL != p->emsg)
837   {
838 #if DEBUG_PUBLISH
839     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Error uploading: %s\n", p->emsg);
840 #endif
841     /* error with current file, abort all
842      * related files as well! */
843     while (NULL != p->dir)
844     {
845       fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
846                                                    EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
847       p = p->dir;
848       if (fn != NULL)
849       {
850         GNUNET_asprintf (&p->emsg, _("Recursive upload failed at `%s': %s"), fn,
851                          p->emsg);
852         GNUNET_free (fn);
853       }
854       else
855       {
856         GNUNET_asprintf (&p->emsg, _("Recursive upload failed: %s"), p->emsg);
857       }
858       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
859       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
860       pi.value.publish.specifics.error.message = p->emsg;
861       p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
862     }
863     pc->all_done = GNUNET_YES;
864     GNUNET_FS_publish_sync_ (pc);
865     return;
866   }
867   /* handle completion */
868   if (NULL != p->chk_uri)
869   {
870 #if DEBUG_PUBLISH
871     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
872                 "File upload complete, now publishing KSK blocks.\n");
873 #endif
874     if (0 == p->bo.anonymity_level)
875     {
876       /* zero anonymity, box CHK URI in LOC URI */
877       loc =
878           GNUNET_FS_uri_loc_create (p->chk_uri, pc->h->cfg,
879                                     p->bo.expiration_time);
880       GNUNET_FS_uri_destroy (p->chk_uri);
881       p->chk_uri = loc;
882     }
883     GNUNET_FS_publish_sync_ (pc);
884     /* upload of "p" complete, publish KBlocks! */
885     if (p->keywords != NULL)
886     {
887       GNUNET_FS_publish_ksk (pc->h, p->keywords, p->meta, p->chk_uri, &p->bo,
888                              pc->options, &publish_kblocks_cont, pc);
889     }
890     else
891     {
892       publish_kblocks_cont (pc, p->chk_uri, NULL);
893     }
894     return;
895   }
896   if ((!p->is_directory) && (p->data.file.do_index))
897   {
898     if (NULL == p->filename)
899     {
900       p->data.file.do_index = GNUNET_NO;
901       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
902                   _
903                   ("Can not index file `%s': %s.  Will try to insert instead.\n"),
904                   "<no-name>", _("needs to be an actual file"));
905       GNUNET_FS_file_information_sync_ (p);
906       publish_content (pc);
907       return;
908     }
909     if (p->data.file.have_hash)
910     {
911       hash_for_index_cb (pc, &p->data.file.file_id);
912     }
913     else
914     {
915       p->start_time = GNUNET_TIME_absolute_get ();
916       pc->fhc =
917           GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, p->filename,
918                                    HASHING_BLOCKSIZE, &hash_for_index_cb, pc);
919     }
920     return;
921   }
922   publish_content (pc);
923 }
924
925
926 /**
927  * Signal the FS's progress function that we are starting
928  * an upload.
929  *
930  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
931  * @param fi the entry in the publish-structure
932  * @param length length of the file or directory
933  * @param meta metadata for the file or directory (can be modified)
934  * @param uri pointer to the keywords that will be used for this entry (can be modified)
935  * @param bo block options
936  * @param do_index should we index?
937  * @param client_info pointer to client context set upon creation (can be modified)
938  * @return GNUNET_OK to continue (always)
939  */
940 static int
941 fip_signal_start (void *cls, struct GNUNET_FS_FileInformation *fi,
942                   uint64_t length, struct GNUNET_CONTAINER_MetaData *meta,
943                   struct GNUNET_FS_Uri **uri, struct GNUNET_FS_BlockOptions *bo,
944                   int *do_index, void **client_info)
945 {
946   struct GNUNET_FS_PublishContext *pc = cls;
947   struct GNUNET_FS_ProgressInfo pi;
948   unsigned int kc;
949   uint64_t left;
950
951 #if DEBUG_PUBLISH
952   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting publish operation\n");
953 #endif
954   if (*do_index)
955   {
956     /* space for on-demand blocks */
957     pc->reserve_space +=
958         ((length + DBLOCK_SIZE -
959           1) / DBLOCK_SIZE) * sizeof (struct OnDemandBlock);
960   }
961   else
962   {
963     /* space for DBlocks */
964     pc->reserve_space += length;
965   }
966   /* entries for IBlocks and DBlocks, space for IBlocks */
967   left = length;
968   while (1)
969   {
970     left = (left + DBLOCK_SIZE - 1) / DBLOCK_SIZE;
971     pc->reserve_entries += left;
972     if (left <= 1)
973       break;
974     left = left * sizeof (struct ContentHashKey);
975     pc->reserve_space += left;
976   }
977   pc->reserve_entries++;
978   /* entries and space for keywords */
979   if (NULL != *uri)
980   {
981     kc = GNUNET_FS_uri_ksk_get_keyword_count (*uri);
982     pc->reserve_entries += kc;
983     pc->reserve_space += GNUNET_SERVER_MAX_MESSAGE_SIZE * kc;
984   }
985   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
986   *client_info = GNUNET_FS_publish_make_status_ (&pi, pc, fi, 0);
987   GNUNET_FS_file_information_sync_ (fi);
988   return GNUNET_OK;
989 }
990
991
992 /**
993  * Signal the FS's progress function that we are suspending
994  * an upload.
995  *
996  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
997  * @param fi the entry in the publish-structure
998  * @param length length of the file or directory
999  * @param meta metadata for the file or directory (can be modified)
1000  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1001  * @param bo block options
1002  * @param do_index should we index?
1003  * @param client_info pointer to client context set upon creation (can be modified)
1004  * @return GNUNET_OK to continue (always)
1005  */
1006 static int
1007 fip_signal_suspend (void *cls, struct GNUNET_FS_FileInformation *fi,
1008                     uint64_t length, struct GNUNET_CONTAINER_MetaData *meta,
1009                     struct GNUNET_FS_Uri **uri,
1010                     struct GNUNET_FS_BlockOptions *bo, int *do_index,
1011                     void **client_info)
1012 {
1013   struct GNUNET_FS_PublishContext *pc = cls;
1014   struct GNUNET_FS_ProgressInfo pi;
1015   uint64_t off;
1016
1017 #if DEBUG_PUBLISH
1018   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Suspending publish operation\n");
1019 #endif
1020   GNUNET_free_non_null (fi->serialization);
1021   fi->serialization = NULL;
1022   off = (fi->chk_uri == NULL) ? 0 : length;
1023   pi.status = GNUNET_FS_STATUS_PUBLISH_SUSPEND;
1024   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1025   *client_info = NULL;
1026   if (NULL != pc->qre)
1027   {
1028     GNUNET_DATASTORE_cancel (pc->qre);
1029     pc->qre = NULL;
1030   }
1031   if (NULL != pc->dsh)
1032   {
1033     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1034     pc->dsh = NULL;
1035   }
1036   pc->rid = 0;
1037   return GNUNET_OK;
1038 }
1039
1040
1041 /**
1042  * Create SUSPEND event for the given publish operation
1043  * and then clean up our state (without stop signal).
1044  *
1045  * @param cls the 'struct GNUNET_FS_PublishContext' to signal for
1046  */
1047 void
1048 GNUNET_FS_publish_signal_suspend_ (void *cls)
1049 {
1050   struct GNUNET_FS_PublishContext *pc = cls;
1051
1052   if (GNUNET_SCHEDULER_NO_TASK != pc->upload_task)
1053   {
1054     GNUNET_SCHEDULER_cancel (pc->upload_task);
1055     pc->upload_task = GNUNET_SCHEDULER_NO_TASK;
1056   }
1057   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_suspend, pc);
1058   GNUNET_FS_end_top (pc->h, pc->top);
1059   pc->top = NULL;
1060   publish_cleanup (pc, NULL);
1061 }
1062
1063
1064 /**
1065  * We have gotten a reply for our space reservation request.
1066  * Either fail (insufficient space) or start publishing for good.
1067  *
1068  * @param cls the 'struct GNUNET_FS_PublishContext*'
1069  * @param success positive reservation ID on success
1070  * @param msg error message on error, otherwise NULL
1071  */
1072 static void
1073 finish_reserve (void *cls, int success, const char *msg)
1074 {
1075   struct GNUNET_FS_PublishContext *pc = cls;
1076
1077   pc->qre = NULL;
1078 #if DEBUG_PUBLISH
1079   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Reservation complete (%d)!\n", success);
1080 #endif
1081   if ((msg != NULL) || (success <= 0))
1082   {
1083     GNUNET_asprintf (&pc->fi->emsg, _("Insufficient space for publishing: %s"),
1084                      msg);
1085     signal_publish_error (pc->fi, pc, pc->fi->emsg);
1086     return;
1087   }
1088   pc->rid = success;
1089   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == pc->upload_task);
1090   pc->upload_task =
1091       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1092                                           &GNUNET_FS_publish_main_, pc);
1093 }
1094
1095
1096 /**
1097  * Publish a file or directory.
1098  *
1099  * @param h handle to the file sharing subsystem
1100  * @param fi information about the file or directory structure to publish
1101  * @param namespace namespace to publish the file in, NULL for no namespace
1102  * @param nid identifier to use for the publishd content in the namespace
1103  *        (can be NULL, must be NULL if namespace is NULL)
1104  * @param nuid update-identifier that will be used for future updates
1105  *        (can be NULL, must be NULL if namespace or nid is NULL)
1106  * @param options options for the publication
1107  * @return context that can be used to control the publish operation
1108  */
1109 struct GNUNET_FS_PublishContext *
1110 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
1111                          struct GNUNET_FS_FileInformation *fi,
1112                          struct GNUNET_FS_Namespace *namespace, const char *nid,
1113                          const char *nuid,
1114                          enum GNUNET_FS_PublishOptions options)
1115 {
1116   struct GNUNET_FS_PublishContext *ret;
1117   struct GNUNET_DATASTORE_Handle *dsh;
1118
1119   GNUNET_assert (NULL != h);
1120   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1121   {
1122     dsh = GNUNET_DATASTORE_connect (h->cfg);
1123     if (NULL == dsh)
1124       return NULL;
1125   }
1126   else
1127   {
1128     dsh = NULL;
1129   }
1130   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
1131   ret->dsh = dsh;
1132   ret->h = h;
1133   ret->fi = fi;
1134   ret->namespace = namespace;
1135   ret->options = options;
1136   if (namespace != NULL)
1137   {
1138     namespace->rc++;
1139     GNUNET_assert (NULL != nid);
1140     ret->nid = GNUNET_strdup (nid);
1141     if (NULL != nuid)
1142       ret->nuid = GNUNET_strdup (nuid);
1143   }
1144   /* signal start */
1145   GNUNET_FS_file_information_inspect (ret->fi, &fip_signal_start, ret);
1146   ret->fi_pos = ret->fi;
1147   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret);
1148   GNUNET_FS_publish_sync_ (ret);
1149   if (NULL != ret->dsh)
1150   {
1151     GNUNET_assert (NULL == ret->qre);
1152     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1153                 _
1154                 ("Reserving space for %u entries and %llu bytes for publication\n"),
1155                 (unsigned int) ret->reserve_entries,
1156                 (unsigned long long) ret->reserve_space);
1157     ret->qre =
1158         GNUNET_DATASTORE_reserve (ret->dsh, ret->reserve_space,
1159                                   ret->reserve_entries, UINT_MAX, UINT_MAX,
1160                                   GNUNET_TIME_UNIT_FOREVER_REL, &finish_reserve,
1161                                   ret);
1162   }
1163   else
1164   {
1165     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ret->upload_task);
1166     ret->upload_task =
1167         GNUNET_SCHEDULER_add_with_priority
1168         (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, ret);
1169   }
1170   return ret;
1171 }
1172
1173
1174 /**
1175  * Signal the FS's progress function that we are stopping
1176  * an upload.
1177  *
1178  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1179  * @param fi the entry in the publish-structure
1180  * @param length length of the file or directory
1181  * @param meta metadata for the file or directory (can be modified)
1182  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1183  * @param bo block options (can be modified)
1184  * @param do_index should we index?
1185  * @param client_info pointer to client context set upon creation (can be modified)
1186  * @return GNUNET_OK to continue (always)
1187  */
1188 static int
1189 fip_signal_stop (void *cls, struct GNUNET_FS_FileInformation *fi,
1190                  uint64_t length, struct GNUNET_CONTAINER_MetaData *meta,
1191                  struct GNUNET_FS_Uri **uri, struct GNUNET_FS_BlockOptions *bo,
1192                  int *do_index, void **client_info)
1193 {
1194   struct GNUNET_FS_PublishContext *pc = cls;
1195   struct GNUNET_FS_ProgressInfo pi;
1196   uint64_t off;
1197
1198   if (fi->serialization != NULL)
1199   {
1200     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1201                                  fi->serialization);
1202     GNUNET_free (fi->serialization);
1203     fi->serialization = NULL;
1204   }
1205   off = (fi->chk_uri == NULL) ? 0 : length;
1206   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1207   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1208   *client_info = NULL;
1209   return GNUNET_OK;
1210 }
1211
1212
1213 /**
1214  * Stop an upload.  Will abort incomplete uploads (but
1215  * not remove blocks that have already been publishd) or
1216  * simply clean up the state for completed uploads.
1217  * Must NOT be called from within the event callback!
1218  *
1219  * @param pc context for the upload to stop
1220  */
1221 void
1222 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc)
1223 {
1224 #if DEBUG_PUBLISH
1225   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publish stop called\n");
1226 #endif
1227   GNUNET_FS_end_top (pc->h, pc->top);
1228   if (NULL != pc->qre)
1229   {
1230     GNUNET_DATASTORE_cancel (pc->qre);
1231     pc->qre = NULL;
1232   }
1233   if (NULL != pc->dsh)
1234   {
1235     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1236     pc->dsh = NULL;
1237   }
1238   if (GNUNET_SCHEDULER_NO_TASK != pc->upload_task)
1239   {
1240     GNUNET_SCHEDULER_cancel (pc->upload_task);
1241     pc->upload_task = GNUNET_SCHEDULER_NO_TASK;
1242   }
1243   if (pc->serialization != NULL)
1244   {
1245     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1246                                  pc->serialization);
1247     GNUNET_free (pc->serialization);
1248     pc->serialization = NULL;
1249   }
1250   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_stop, pc);
1251   if (GNUNET_YES == pc->in_network_wait)
1252   {
1253     pc->in_network_wait = GNUNET_SYSERR;
1254     return;
1255   }
1256   publish_cleanup (pc, NULL);
1257 }
1258
1259
1260 /**
1261  * Context for the KSK publication.
1262  */
1263 struct PublishKskContext
1264 {
1265
1266   /**
1267    * Keywords to use.
1268    */
1269   struct GNUNET_FS_Uri *ksk_uri;
1270
1271   /**
1272    * Global FS context.
1273    */
1274   struct GNUNET_FS_Handle *h;
1275
1276   /**
1277    * The master block that we are sending
1278    * (in plaintext), has "mdsize+slen" more
1279    * bytes than the struct would suggest.
1280    */
1281   struct KBlock *kb;
1282
1283   /**
1284    * Buffer of the same size as "kb" for
1285    * the encrypted version.
1286    */
1287   struct KBlock *cpy;
1288
1289   /**
1290    * Handle to the datastore, NULL if we are just
1291    * simulating.
1292    */
1293   struct GNUNET_DATASTORE_Handle *dsh;
1294
1295   /**
1296    * Handle to datastore PUT request.
1297    */
1298   struct GNUNET_DATASTORE_QueueEntry *qre;
1299
1300   /**
1301    * Function to call once we're done.
1302    */
1303   GNUNET_FS_PublishContinuation cont;
1304
1305   /**
1306    * Closure for cont.
1307    */
1308   void *cont_cls;
1309
1310   /**
1311    * When should the KBlocks expire?
1312    */
1313   struct GNUNET_FS_BlockOptions bo;
1314
1315   /**
1316    * Size of the serialized metadata.
1317    */
1318   ssize_t mdsize;
1319
1320   /**
1321    * Size of the (CHK) URI as a string.
1322    */
1323   size_t slen;
1324
1325   /**
1326    * Keyword that we are currently processing.
1327    */
1328   unsigned int i;
1329
1330 };
1331
1332
1333 /**
1334  * Continuation of "GNUNET_FS_publish_ksk" that performs
1335  * the actual publishing operation (iterating over all
1336  * of the keywords).
1337  *
1338  * @param cls closure of type "struct PublishKskContext*"
1339  * @param tc unused
1340  */
1341 static void
1342 publish_ksk_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1343
1344
1345 /**
1346  * Function called by the datastore API with
1347  * the result from the PUT request.
1348  *
1349  * @param cls closure of type "struct PublishKskContext*"
1350  * @param success GNUNET_OK on success
1351  * @param msg error message (or NULL)
1352  */
1353 static void
1354 kb_put_cont (void *cls, int success, const char *msg)
1355 {
1356   struct PublishKskContext *pkc = cls;
1357
1358   pkc->qre = NULL;
1359   if (GNUNET_OK != success)
1360   {
1361 #if DEBUG_PUBLISH
1362     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "KB PUT operation complete\n");
1363 #endif
1364     if (NULL != pkc->dsh)
1365     {
1366       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1367       pkc->dsh = NULL;
1368     }
1369     GNUNET_free (pkc->cpy);
1370     GNUNET_free (pkc->kb);
1371     pkc->cont (pkc->cont_cls, NULL, msg);
1372     GNUNET_FS_uri_destroy (pkc->ksk_uri);
1373     GNUNET_free (pkc);
1374     return;
1375   }
1376   GNUNET_SCHEDULER_add_continuation (&publish_ksk_cont, pkc,
1377                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1378 }
1379
1380
1381 /**
1382  * Continuation of "GNUNET_FS_publish_ksk" that performs the actual
1383  * publishing operation (iterating over all of the keywords).
1384  *
1385  * @param cls closure of type "struct PublishKskContext*"
1386  * @param tc unused
1387  */
1388 static void
1389 publish_ksk_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1390 {
1391   struct PublishKskContext *pkc = cls;
1392   const char *keyword;
1393   GNUNET_HashCode key;
1394   GNUNET_HashCode query;
1395   struct GNUNET_CRYPTO_AesSessionKey skey;
1396   struct GNUNET_CRYPTO_AesInitializationVector iv;
1397   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1398
1399
1400   if ((pkc->i == pkc->ksk_uri->data.ksk.keywordCount) || (NULL == pkc->dsh))
1401   {
1402 #if DEBUG_PUBLISH
1403     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "KSK PUT operation complete\n");
1404 #endif
1405     if (NULL != pkc->dsh)
1406     {
1407       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1408       pkc->dsh = NULL;
1409     }
1410     GNUNET_free (pkc->cpy);
1411     GNUNET_free (pkc->kb);
1412     pkc->cont (pkc->cont_cls, pkc->ksk_uri, NULL);
1413     GNUNET_FS_uri_destroy (pkc->ksk_uri);
1414     GNUNET_free (pkc);
1415     return;
1416   }
1417   keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
1418 #if DEBUG_PUBLISH
1419   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing under keyword `%s'\n",
1420               keyword);
1421 #endif
1422   /* first character of keyword indicates if it is
1423    * mandatory or not -- ignore for hashing */
1424   GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
1425   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
1426   GNUNET_CRYPTO_aes_encrypt (&pkc->kb[1], pkc->slen + pkc->mdsize, &skey, &iv,
1427                              &pkc->cpy[1]);
1428   pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
1429   GNUNET_assert (NULL != pk);
1430   GNUNET_CRYPTO_rsa_key_get_public (pk, &pkc->cpy->keyspace);
1431   GNUNET_CRYPTO_hash (&pkc->cpy->keyspace,
1432                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1433                       &query);
1434   GNUNET_assert (GNUNET_OK ==
1435                  GNUNET_CRYPTO_rsa_sign (pk, &pkc->cpy->purpose,
1436                                          &pkc->cpy->signature));
1437   GNUNET_CRYPTO_rsa_key_free (pk);
1438   pkc->qre =
1439       GNUNET_DATASTORE_put (pkc->dsh, 0, &query,
1440                             pkc->mdsize + sizeof (struct KBlock) + pkc->slen,
1441                             pkc->cpy, GNUNET_BLOCK_TYPE_FS_KBLOCK,
1442                             pkc->bo.content_priority, pkc->bo.anonymity_level,
1443                             pkc->bo.replication_level, pkc->bo.expiration_time,
1444                             -2, 1, GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1445                             &kb_put_cont, pkc);
1446 }
1447
1448
1449 /**
1450  * Publish a CHK under various keywords on GNUnet.
1451  *
1452  * @param h handle to the file sharing subsystem
1453  * @param ksk_uri keywords to use
1454  * @param meta metadata to use
1455  * @param uri URI to refer to in the KBlock
1456  * @param bo per-block options
1457  * @param options publication options
1458  * @param cont continuation
1459  * @param cont_cls closure for cont
1460  */
1461 void
1462 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
1463                        const struct GNUNET_FS_Uri *ksk_uri,
1464                        const struct GNUNET_CONTAINER_MetaData *meta,
1465                        const struct GNUNET_FS_Uri *uri,
1466                        const struct GNUNET_FS_BlockOptions *bo,
1467                        enum GNUNET_FS_PublishOptions options,
1468                        GNUNET_FS_PublishContinuation cont, void *cont_cls)
1469 {
1470   struct PublishKskContext *pkc;
1471   char *uris;
1472   size_t size;
1473   char *kbe;
1474   char *sptr;
1475
1476   GNUNET_assert (NULL != uri);
1477   pkc = GNUNET_malloc (sizeof (struct PublishKskContext));
1478   pkc->h = h;
1479   pkc->bo = *bo;
1480   pkc->cont = cont;
1481   pkc->cont_cls = cont_cls;
1482   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1483   {
1484     pkc->dsh = GNUNET_DATASTORE_connect (h->cfg);
1485     if (pkc->dsh == NULL)
1486     {
1487       cont (cont_cls, NULL, _("Could not connect to datastore."));
1488       GNUNET_free (pkc);
1489       return;
1490     }
1491   }
1492   if (meta == NULL)
1493     pkc->mdsize = 0;
1494   else
1495     pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
1496   GNUNET_assert (pkc->mdsize >= 0);
1497   uris = GNUNET_FS_uri_to_string (uri);
1498   pkc->slen = strlen (uris) + 1;
1499   size = pkc->mdsize + sizeof (struct KBlock) + pkc->slen;
1500   if (size > MAX_KBLOCK_SIZE)
1501   {
1502     size = MAX_KBLOCK_SIZE;
1503     pkc->mdsize = size - sizeof (struct KBlock) - pkc->slen;
1504   }
1505   pkc->kb = GNUNET_malloc (size);
1506   kbe = (char *) &pkc->kb[1];
1507   memcpy (kbe, uris, pkc->slen);
1508   GNUNET_free (uris);
1509   sptr = &kbe[pkc->slen];
1510   if (meta != NULL)
1511     pkc->mdsize =
1512         GNUNET_CONTAINER_meta_data_serialize (meta, &sptr, pkc->mdsize,
1513                                               GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1514   if (pkc->mdsize == -1)
1515   {
1516     GNUNET_break (0);
1517     GNUNET_free (pkc->kb);
1518     if (pkc->dsh != NULL)
1519     {
1520       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1521       pkc->dsh = NULL;
1522     }
1523     cont (cont_cls, NULL, _("Internal error."));
1524     GNUNET_free (pkc);
1525     return;
1526   }
1527   size = sizeof (struct KBlock) + pkc->slen + pkc->mdsize;
1528
1529   pkc->cpy = GNUNET_malloc (size);
1530   pkc->cpy->purpose.size =
1531       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
1532              sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1533              pkc->mdsize + pkc->slen);
1534   pkc->cpy->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_KBLOCK);
1535   pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
1536   GNUNET_SCHEDULER_add_continuation (&publish_ksk_cont, pkc,
1537                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1538 }
1539
1540
1541 /* end of fs_publish.c */