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