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