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