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