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