fixing bug to ensure that we properly descend into deep directories for the various...
[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   {
1001     /* process entries in directory */
1002     pc->skip_next_fi_callback = GNUNET_YES;
1003     GNUNET_FS_file_information_inspect (fi, &fip_signal_start, pc);
1004   }
1005   return GNUNET_OK;
1006 }
1007
1008
1009 /**
1010  * Signal the FS's progress function that we are suspending
1011  * an upload.
1012  *
1013  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1014  * @param fi the entry in the publish-structure
1015  * @param length length of the file or directory
1016  * @param meta metadata for the file or directory (can be modified)
1017  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1018  * @param bo block options
1019  * @param do_index should we index?
1020  * @param client_info pointer to client context set upon creation (can be modified)
1021  * @return GNUNET_OK to continue (always)
1022  */
1023 static int
1024 fip_signal_suspend (void *cls, struct GNUNET_FS_FileInformation *fi,
1025                     uint64_t length, struct GNUNET_CONTAINER_MetaData *meta,
1026                     struct GNUNET_FS_Uri **uri,
1027                     struct GNUNET_FS_BlockOptions *bo, int *do_index,
1028                     void **client_info)
1029 {
1030   struct GNUNET_FS_PublishContext *pc = cls;
1031   struct GNUNET_FS_ProgressInfo pi;
1032   uint64_t off;
1033
1034   if (GNUNET_YES == pc->skip_next_fi_callback)
1035   {
1036     pc->skip_next_fi_callback = GNUNET_NO;
1037     return GNUNET_OK;
1038   }
1039   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1040   {
1041     /* process entries in directory */
1042     pc->skip_next_fi_callback = GNUNET_YES;
1043     GNUNET_FS_file_information_inspect (fi, &fip_signal_suspend, pc);
1044   }
1045 #if DEBUG_PUBLISH
1046   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Suspending publish operation\n");
1047 #endif
1048   GNUNET_free_non_null (fi->serialization);
1049   fi->serialization = NULL;
1050   off = (fi->chk_uri == NULL) ? 0 : length;
1051   pi.status = GNUNET_FS_STATUS_PUBLISH_SUSPEND;
1052   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1053   *client_info = NULL;
1054   if (NULL != pc->qre)
1055   {
1056     GNUNET_DATASTORE_cancel (pc->qre);
1057     pc->qre = NULL;
1058   }
1059   if (NULL != pc->dsh)
1060   {
1061     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1062     pc->dsh = NULL;
1063   }
1064   pc->rid = 0;
1065   return GNUNET_OK;
1066 }
1067
1068
1069 /**
1070  * Create SUSPEND event for the given publish operation
1071  * and then clean up our state (without stop signal).
1072  *
1073  * @param cls the 'struct GNUNET_FS_PublishContext' to signal for
1074  */
1075 void
1076 GNUNET_FS_publish_signal_suspend_ (void *cls)
1077 {
1078   struct GNUNET_FS_PublishContext *pc = cls;
1079
1080   if (GNUNET_SCHEDULER_NO_TASK != pc->upload_task)
1081   {
1082     GNUNET_SCHEDULER_cancel (pc->upload_task);
1083     pc->upload_task = GNUNET_SCHEDULER_NO_TASK;
1084   }
1085   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_suspend, pc);
1086   GNUNET_FS_end_top (pc->h, pc->top);
1087   pc->top = NULL;
1088   publish_cleanup (pc, NULL);
1089 }
1090
1091
1092 /**
1093  * We have gotten a reply for our space reservation request.
1094  * Either fail (insufficient space) or start publishing for good.
1095  *
1096  * @param cls the 'struct GNUNET_FS_PublishContext*'
1097  * @param success positive reservation ID on success
1098  * @param min_expiration minimum expiration time required for content to be stored
1099  * @param msg error message on error, otherwise NULL
1100  */
1101 static void
1102 finish_reserve (void *cls, int success, 
1103                 struct GNUNET_TIME_Absolute min_expiration,
1104                 const char *msg)
1105 {
1106   struct GNUNET_FS_PublishContext *pc = cls;
1107
1108   pc->qre = NULL;
1109 #if DEBUG_PUBLISH
1110   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Reservation complete (%d)!\n", success);
1111 #endif
1112   if ((msg != NULL) || (success <= 0))
1113   {
1114     GNUNET_asprintf (&pc->fi->emsg, _("Insufficient space for publishing: %s"),
1115                      msg);
1116     signal_publish_error (pc->fi, pc, pc->fi->emsg);
1117     return;
1118   }
1119   pc->rid = success;
1120   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == pc->upload_task);
1121   pc->upload_task =
1122       GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1123                                           &GNUNET_FS_publish_main_, pc);
1124 }
1125
1126
1127 /**
1128  * Publish a file or directory.
1129  *
1130  * @param h handle to the file sharing subsystem
1131  * @param fi information about the file or directory structure to publish
1132  * @param namespace namespace to publish the file in, NULL for no namespace
1133  * @param nid identifier to use for the publishd content in the namespace
1134  *        (can be NULL, must be NULL if namespace is NULL)
1135  * @param nuid update-identifier that will be used for future updates
1136  *        (can be NULL, must be NULL if namespace or nid is NULL)
1137  * @param options options for the publication
1138  * @return context that can be used to control the publish operation
1139  */
1140 struct GNUNET_FS_PublishContext *
1141 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
1142                          struct GNUNET_FS_FileInformation *fi,
1143                          struct GNUNET_FS_Namespace *namespace, const char *nid,
1144                          const char *nuid,
1145                          enum GNUNET_FS_PublishOptions options)
1146 {
1147   struct GNUNET_FS_PublishContext *ret;
1148   struct GNUNET_DATASTORE_Handle *dsh;
1149
1150   GNUNET_assert (NULL != h);
1151   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1152   {
1153     dsh = GNUNET_DATASTORE_connect (h->cfg);
1154     if (NULL == dsh)
1155       return NULL;
1156   }
1157   else
1158   {
1159     dsh = NULL;
1160   }
1161   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
1162   ret->dsh = dsh;
1163   ret->h = h;
1164   ret->fi = fi;
1165   ret->namespace = namespace;
1166   ret->options = options;
1167   if (namespace != NULL)
1168   {
1169     namespace->rc++;
1170     GNUNET_assert (NULL != nid);
1171     ret->nid = GNUNET_strdup (nid);
1172     if (NULL != nuid)
1173       ret->nuid = GNUNET_strdup (nuid);
1174   }
1175   /* signal start */
1176   GNUNET_FS_file_information_inspect (ret->fi, &fip_signal_start, ret);
1177   ret->fi_pos = ret->fi;
1178   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret);
1179   GNUNET_FS_publish_sync_ (ret);
1180   if (NULL != ret->dsh)
1181   {
1182     GNUNET_assert (NULL == ret->qre);
1183     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1184                 _
1185                 ("Reserving space for %u entries and %llu bytes for publication\n"),
1186                 (unsigned int) ret->reserve_entries,
1187                 (unsigned long long) ret->reserve_space);
1188     ret->qre =
1189         GNUNET_DATASTORE_reserve (ret->dsh, ret->reserve_space,
1190                                   ret->reserve_entries, UINT_MAX, UINT_MAX,
1191                                   GNUNET_TIME_UNIT_FOREVER_REL, &finish_reserve,
1192                                   ret);
1193   }
1194   else
1195   {
1196     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == ret->upload_task);
1197     ret->upload_task =
1198         GNUNET_SCHEDULER_add_with_priority
1199         (GNUNET_SCHEDULER_PRIORITY_BACKGROUND, &GNUNET_FS_publish_main_, ret);
1200   }
1201   return ret;
1202 }
1203
1204
1205 /**
1206  * Signal the FS's progress function that we are stopping
1207  * an upload.
1208  *
1209  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1210  * @param fi the entry in the publish-structure
1211  * @param length length of the file or directory
1212  * @param meta metadata for the file or directory (can be modified)
1213  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1214  * @param bo block options (can be modified)
1215  * @param do_index should we index?
1216  * @param client_info pointer to client context set upon creation (can be modified)
1217  * @return GNUNET_OK to continue (always)
1218  */
1219 static int
1220 fip_signal_stop (void *cls, struct GNUNET_FS_FileInformation *fi,
1221                  uint64_t length, struct GNUNET_CONTAINER_MetaData *meta,
1222                  struct GNUNET_FS_Uri **uri, struct GNUNET_FS_BlockOptions *bo,
1223                  int *do_index, void **client_info)
1224 {
1225   struct GNUNET_FS_PublishContext *pc = cls;
1226   struct GNUNET_FS_ProgressInfo pi;
1227   uint64_t off;
1228
1229   if (GNUNET_YES == pc->skip_next_fi_callback)
1230   {
1231     pc->skip_next_fi_callback = GNUNET_NO;
1232     return GNUNET_OK;
1233   }
1234   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta))
1235   {
1236     /* process entries in directory first */
1237     pc->skip_next_fi_callback = GNUNET_YES;
1238     GNUNET_FS_file_information_inspect (fi, &fip_signal_stop, pc);
1239   }
1240   if (fi->serialization != NULL)
1241   {
1242     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_FILE_INFO,
1243                                  fi->serialization);
1244     GNUNET_free (fi->serialization);
1245     fi->serialization = NULL;
1246   }
1247   off = (fi->chk_uri == NULL) ? 0 : length;
1248   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1249   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, pc, fi, off));
1250   *client_info = NULL;
1251   return GNUNET_OK;
1252 }
1253
1254
1255 /**
1256  * Stop an upload.  Will abort incomplete uploads (but
1257  * not remove blocks that have already been publishd) or
1258  * simply clean up the state for completed uploads.
1259  * Must NOT be called from within the event callback!
1260  *
1261  * @param pc context for the upload to stop
1262  */
1263 void
1264 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc)
1265 {
1266 #if DEBUG_PUBLISH
1267   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publish stop called\n");
1268 #endif
1269   GNUNET_FS_end_top (pc->h, pc->top);
1270   if (NULL != pc->qre)
1271   {
1272     GNUNET_DATASTORE_cancel (pc->qre);
1273     pc->qre = NULL;
1274   }
1275   if (NULL != pc->dsh)
1276   {
1277     GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1278     pc->dsh = NULL;
1279   }
1280   if (GNUNET_SCHEDULER_NO_TASK != pc->upload_task)
1281   {
1282     GNUNET_SCHEDULER_cancel (pc->upload_task);
1283     pc->upload_task = GNUNET_SCHEDULER_NO_TASK;
1284   }
1285   if (pc->serialization != NULL)
1286   {
1287     GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH,
1288                                  pc->serialization);
1289     GNUNET_free (pc->serialization);
1290     pc->serialization = NULL;
1291   }
1292   GNUNET_FS_file_information_inspect (pc->fi, &fip_signal_stop, pc);
1293   if (GNUNET_YES == pc->in_network_wait)
1294   {
1295     pc->in_network_wait = GNUNET_SYSERR;
1296     return;
1297   }
1298   publish_cleanup (pc, NULL);
1299 }
1300
1301
1302 /**
1303  * Context for the KSK publication.
1304  */
1305 struct PublishKskContext
1306 {
1307
1308   /**
1309    * Keywords to use.
1310    */
1311   struct GNUNET_FS_Uri *ksk_uri;
1312
1313   /**
1314    * Global FS context.
1315    */
1316   struct GNUNET_FS_Handle *h;
1317
1318   /**
1319    * The master block that we are sending
1320    * (in plaintext), has "mdsize+slen" more
1321    * bytes than the struct would suggest.
1322    */
1323   struct KBlock *kb;
1324
1325   /**
1326    * Buffer of the same size as "kb" for
1327    * the encrypted version.
1328    */
1329   struct KBlock *cpy;
1330
1331   /**
1332    * Handle to the datastore, NULL if we are just
1333    * simulating.
1334    */
1335   struct GNUNET_DATASTORE_Handle *dsh;
1336
1337   /**
1338    * Handle to datastore PUT request.
1339    */
1340   struct GNUNET_DATASTORE_QueueEntry *qre;
1341
1342   /**
1343    * Function to call once we're done.
1344    */
1345   GNUNET_FS_PublishContinuation cont;
1346
1347   /**
1348    * Closure for cont.
1349    */
1350   void *cont_cls;
1351
1352   /**
1353    * When should the KBlocks expire?
1354    */
1355   struct GNUNET_FS_BlockOptions bo;
1356
1357   /**
1358    * Size of the serialized metadata.
1359    */
1360   ssize_t mdsize;
1361
1362   /**
1363    * Size of the (CHK) URI as a string.
1364    */
1365   size_t slen;
1366
1367   /**
1368    * Keyword that we are currently processing.
1369    */
1370   unsigned int i;
1371
1372 };
1373
1374
1375 /**
1376  * Continuation of "GNUNET_FS_publish_ksk" that performs
1377  * the actual publishing operation (iterating over all
1378  * 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
1387 /**
1388  * Function called by the datastore API with
1389  * the result from the PUT request.
1390  *
1391  * @param cls closure of type "struct PublishKskContext*"
1392  * @param success GNUNET_OK on success
1393  * @param min_expiration minimum expiration time required for content to be stored
1394  * @param msg error message (or NULL)
1395  */
1396 static void
1397 kb_put_cont (void *cls, int success, 
1398              struct GNUNET_TIME_Absolute min_expiration,
1399              const char *msg)
1400 {
1401   struct PublishKskContext *pkc = cls;
1402
1403   pkc->qre = NULL;
1404   if (GNUNET_OK != success)
1405   {
1406 #if DEBUG_PUBLISH
1407     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "KB PUT operation complete\n");
1408 #endif
1409     if (NULL != pkc->dsh)
1410     {
1411       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1412       pkc->dsh = NULL;
1413     }
1414     GNUNET_free (pkc->cpy);
1415     GNUNET_free (pkc->kb);
1416     pkc->cont (pkc->cont_cls, NULL, msg);
1417     GNUNET_FS_uri_destroy (pkc->ksk_uri);
1418     GNUNET_free (pkc);
1419     return;
1420   }
1421   GNUNET_SCHEDULER_add_continuation (&publish_ksk_cont, pkc,
1422                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1423 }
1424
1425
1426 /**
1427  * Continuation of "GNUNET_FS_publish_ksk" that performs the actual
1428  * publishing operation (iterating over all of the keywords).
1429  *
1430  * @param cls closure of type "struct PublishKskContext*"
1431  * @param tc unused
1432  */
1433 static void
1434 publish_ksk_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1435 {
1436   struct PublishKskContext *pkc = cls;
1437   const char *keyword;
1438   GNUNET_HashCode key;
1439   GNUNET_HashCode query;
1440   struct GNUNET_CRYPTO_AesSessionKey skey;
1441   struct GNUNET_CRYPTO_AesInitializationVector iv;
1442   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1443
1444
1445   if ((pkc->i == pkc->ksk_uri->data.ksk.keywordCount) || (NULL == pkc->dsh))
1446   {
1447 #if DEBUG_PUBLISH
1448     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "KSK PUT operation complete\n");
1449 #endif
1450     if (NULL != pkc->dsh)
1451     {
1452       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1453       pkc->dsh = NULL;
1454     }
1455     GNUNET_free (pkc->cpy);
1456     GNUNET_free (pkc->kb);
1457     pkc->cont (pkc->cont_cls, pkc->ksk_uri, NULL);
1458     GNUNET_FS_uri_destroy (pkc->ksk_uri);
1459     GNUNET_free (pkc);
1460     return;
1461   }
1462   keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
1463 #if DEBUG_PUBLISH
1464   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing under keyword `%s'\n",
1465               keyword);
1466 #endif
1467   /* first character of keyword indicates if it is
1468    * mandatory or not -- ignore for hashing */
1469   GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
1470   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
1471   GNUNET_CRYPTO_aes_encrypt (&pkc->kb[1], pkc->slen + pkc->mdsize, &skey, &iv,
1472                              &pkc->cpy[1]);
1473   pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
1474   GNUNET_assert (NULL != pk);
1475   GNUNET_CRYPTO_rsa_key_get_public (pk, &pkc->cpy->keyspace);
1476   GNUNET_CRYPTO_hash (&pkc->cpy->keyspace,
1477                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1478                       &query);
1479   GNUNET_assert (GNUNET_OK ==
1480                  GNUNET_CRYPTO_rsa_sign (pk, &pkc->cpy->purpose,
1481                                          &pkc->cpy->signature));
1482   GNUNET_CRYPTO_rsa_key_free (pk);
1483   pkc->qre =
1484       GNUNET_DATASTORE_put (pkc->dsh, 0, &query,
1485                             pkc->mdsize + sizeof (struct KBlock) + pkc->slen,
1486                             pkc->cpy, GNUNET_BLOCK_TYPE_FS_KBLOCK,
1487                             pkc->bo.content_priority, pkc->bo.anonymity_level,
1488                             pkc->bo.replication_level, pkc->bo.expiration_time,
1489                             -2, 1, GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1490                             &kb_put_cont, pkc);
1491 }
1492
1493
1494 /**
1495  * Publish a CHK under various keywords on GNUnet.
1496  *
1497  * @param h handle to the file sharing subsystem
1498  * @param ksk_uri keywords to use
1499  * @param meta metadata to use
1500  * @param uri URI to refer to in the KBlock
1501  * @param bo per-block options
1502  * @param options publication options
1503  * @param cont continuation
1504  * @param cont_cls closure for cont
1505  */
1506 void
1507 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
1508                        const struct GNUNET_FS_Uri *ksk_uri,
1509                        const struct GNUNET_CONTAINER_MetaData *meta,
1510                        const struct GNUNET_FS_Uri *uri,
1511                        const struct GNUNET_FS_BlockOptions *bo,
1512                        enum GNUNET_FS_PublishOptions options,
1513                        GNUNET_FS_PublishContinuation cont, void *cont_cls)
1514 {
1515   struct PublishKskContext *pkc;
1516   char *uris;
1517   size_t size;
1518   char *kbe;
1519   char *sptr;
1520
1521   GNUNET_assert (NULL != uri);
1522   pkc = GNUNET_malloc (sizeof (struct PublishKskContext));
1523   pkc->h = h;
1524   pkc->bo = *bo;
1525   pkc->cont = cont;
1526   pkc->cont_cls = cont_cls;
1527   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1528   {
1529     pkc->dsh = GNUNET_DATASTORE_connect (h->cfg);
1530     if (pkc->dsh == NULL)
1531     {
1532       cont (cont_cls, NULL, _("Could not connect to datastore."));
1533       GNUNET_free (pkc);
1534       return;
1535     }
1536   }
1537   if (meta == NULL)
1538     pkc->mdsize = 0;
1539   else
1540     pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
1541   GNUNET_assert (pkc->mdsize >= 0);
1542   uris = GNUNET_FS_uri_to_string (uri);
1543   pkc->slen = strlen (uris) + 1;
1544   size = pkc->mdsize + sizeof (struct KBlock) + pkc->slen;
1545   if (size > MAX_KBLOCK_SIZE)
1546   {
1547     size = MAX_KBLOCK_SIZE;
1548     pkc->mdsize = size - sizeof (struct KBlock) - pkc->slen;
1549   }
1550   pkc->kb = GNUNET_malloc (size);
1551   kbe = (char *) &pkc->kb[1];
1552   memcpy (kbe, uris, pkc->slen);
1553   GNUNET_free (uris);
1554   sptr = &kbe[pkc->slen];
1555   if (meta != NULL)
1556     pkc->mdsize =
1557         GNUNET_CONTAINER_meta_data_serialize (meta, &sptr, pkc->mdsize,
1558                                               GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1559   if (pkc->mdsize == -1)
1560   {
1561     GNUNET_break (0);
1562     GNUNET_free (pkc->kb);
1563     if (pkc->dsh != NULL)
1564     {
1565       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1566       pkc->dsh = NULL;
1567     }
1568     cont (cont_cls, NULL, _("Internal error."));
1569     GNUNET_free (pkc);
1570     return;
1571   }
1572   size = sizeof (struct KBlock) + pkc->slen + pkc->mdsize;
1573
1574   pkc->cpy = GNUNET_malloc (size);
1575   pkc->cpy->purpose.size =
1576       htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
1577              sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1578              pkc->mdsize + pkc->slen);
1579   pkc->cpy->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_KBLOCK);
1580   pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
1581   GNUNET_SCHEDULER_add_continuation (&publish_ksk_cont, pkc,
1582                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1583 }
1584
1585
1586 /* end of fs_publish.c */