6c933a37d3d4566ee919ca8b2f07a9f9a6d5a5a1
[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->anonymity;
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->expirationTime,
328                            sc->fi->anonymity,
329                            sc->fi->priority,
330                            sc->options,
331                            &publish_sblocks_cont,
332                            sc);
333   else
334     publish_sblocks_cont (sc, NULL, NULL);
335 }
336
337
338 /**
339  * We've finished publishing a KBlock as part of a larger upload.
340  * Check the result and continue the larger upload.
341  *
342  * @param cls the "struct GNUNET_FS_PublishContext*"
343  *        of the larger upload
344  * @param uri URI of the published blocks
345  * @param emsg NULL on success, otherwise error message
346  */
347 static void
348 publish_kblocks_cont (void *cls,
349                       const struct GNUNET_FS_Uri *uri,
350                       const char *emsg)
351 {
352   struct GNUNET_FS_PublishContext *pc = cls;
353   struct GNUNET_FS_FileInformation *p = pc->fi_pos;
354
355   if (NULL != emsg)
356     {
357 #if DEBUG_PUBLISH
358       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
359                   "Error uploading KSK blocks: %s\n",
360                   emsg);
361 #endif
362       signal_publish_error (p, pc, emsg);
363       GNUNET_FS_file_information_sync_ (p);
364       GNUNET_FS_publish_sync_ (pc);
365       pc->upload_task 
366         = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
367                                               &GNUNET_FS_publish_main_,
368                                               pc);
369       return;
370     }
371 #if DEBUG_PUBLISH
372   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373               "KSK blocks published, moving on to next file\n");
374 #endif
375   if (NULL != p->dir)
376     signal_publish_completion (p, pc);    
377   /* move on to next file */
378   if (NULL != p->next)
379     pc->fi_pos = p->next;
380   else
381     pc->fi_pos = p->dir;
382   GNUNET_FS_publish_sync_ (pc);
383   pc->upload_task 
384     = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
385                                           &GNUNET_FS_publish_main_,
386                                           pc);
387 }
388
389
390 /**
391  * Function called by the tree encoder to obtain
392  * a block of plaintext data (for the lowest level
393  * of the tree).
394  *
395  * @param cls our publishing context
396  * @param offset identifies which block to get
397  * @param max (maximum) number of bytes to get; returning
398  *        fewer will also cause errors
399  * @param buf where to copy the plaintext buffer
400  * @param emsg location to store an error message (on error)
401  * @return number of bytes copied to buf, 0 on error
402  */
403 static size_t
404 block_reader (void *cls,
405               uint64_t offset,
406               size_t max, 
407               void *buf,
408               char **emsg)
409 {
410   struct GNUNET_FS_PublishContext *sc = cls;
411   struct GNUNET_FS_FileInformation *p;
412   size_t pt_size;
413   const char *dd;
414
415   p = sc->fi_pos;
416   if (p->is_directory)
417     {
418       pt_size = GNUNET_MIN(max,
419                            p->data.dir.dir_size - offset);
420       dd = p->data.dir.dir_data;
421       memcpy (buf,
422               &dd[offset],
423               pt_size);
424     }
425   else
426     {
427       pt_size = GNUNET_MIN(max,
428                            p->data.file.file_size - offset);
429       if (pt_size == 0)
430         return 0; /* calling reader with pt_size==0 
431                      might free buf, so don't! */
432       if (pt_size !=
433           p->data.file.reader (p->data.file.reader_cls,
434                                offset,
435                                pt_size,
436                                buf,
437                                emsg))
438         return 0;
439     }
440   return pt_size;
441 }
442
443
444 /**
445  * The tree encoder has finished processing a
446  * file.   Call it's finish method and deal with
447  * the final result.
448  *
449  * @param cls our publishing context
450  * @param tc scheduler's task context (not used)
451  */
452 static void 
453 encode_cont (void *cls,
454              const struct GNUNET_SCHEDULER_TaskContext *tc)
455 {
456   struct GNUNET_FS_PublishContext *sc = cls;
457   struct GNUNET_FS_FileInformation *p;
458   struct GNUNET_FS_ProgressInfo pi;
459   char *emsg;
460   uint64_t flen;
461
462   p = sc->fi_pos;
463   GNUNET_FS_tree_encoder_finish (p->te,
464                                  &p->chk_uri,
465                                  &emsg);
466   p->te = NULL;
467   if (NULL != emsg)
468     {
469 #if DEBUG_PUBLISH
470       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471                   "Error during tree walk: %s\n",
472                   emsg);
473 #endif
474       GNUNET_asprintf (&p->emsg, 
475                        _("Publishing failed: %s"),
476                        emsg);
477       GNUNET_free (emsg);
478       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
479       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
480       pi.value.publish.specifics.error.message = p->emsg;
481       p->client_info =  GNUNET_FS_publish_make_status_ (&pi, sc, p, 0);
482     }
483 #if DEBUG_PUBLISH
484   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485               "Finished with tree encoder\n");
486 #endif  
487   /* final progress event */
488   flen = GNUNET_FS_uri_chk_get_file_size (p->chk_uri);
489   pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
490   pi.value.publish.specifics.progress.data = NULL;
491   pi.value.publish.specifics.progress.offset = flen;
492   pi.value.publish.specifics.progress.data_len = 0;
493   pi.value.publish.specifics.progress.depth = GNUNET_FS_compute_depth (flen);
494   p->client_info = GNUNET_FS_publish_make_status_ (&pi, sc, p, flen);
495
496   /* continue with main */
497   sc->upload_task 
498     = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
499                                           &GNUNET_FS_publish_main_,
500                                           sc);
501 }
502
503
504 /**
505  * Function called asking for the current (encoded)
506  * block to be processed.  After processing the
507  * client should either call "GNUNET_FS_tree_encode_next"
508  * or (on error) "GNUNET_FS_tree_encode_finish".
509  *
510  * @param cls closure
511  * @param chk content hash key for the block 
512  * @param offset offset of the block in the file
513  * @param depth depth of the block in the file, 0 for DBLOCK
514  * @param type type of the block (IBLOCK or DBLOCK)
515  * @param block the (encrypted) block
516  * @param block_size size of block (in bytes)
517  */
518 static void 
519 block_proc (void *cls,
520             const struct ContentHashKey *chk,
521             uint64_t offset,
522             unsigned int depth, 
523             enum GNUNET_BLOCK_Type type,
524             const void *block,
525             uint16_t block_size)
526 {
527   struct GNUNET_FS_PublishContext *sc = cls;
528   struct GNUNET_FS_FileInformation *p;
529   struct PutContCtx * dpc_cls;
530   struct OnDemandBlock odb;
531
532   p = sc->fi_pos;
533   if (NULL == sc->dsh)
534     {
535 #if DEBUG_PUBLISH
536       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537                   "Waiting for datastore connection\n");
538 #endif
539       sc->upload_task
540         = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
541                                               &GNUNET_FS_publish_main_,
542                                               sc);
543       return;
544     }
545   
546   GNUNET_assert (GNUNET_NO == sc->in_network_wait);
547   sc->in_network_wait = GNUNET_YES;
548   dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
549   dpc_cls->cont = &GNUNET_FS_publish_main_;
550   dpc_cls->cont_cls = sc;
551   dpc_cls->sc = sc;
552   dpc_cls->p = p;
553   if ( (! p->is_directory) &&
554        (GNUNET_YES == p->data.file.do_index) &&
555        (type == GNUNET_BLOCK_TYPE_FS_DBLOCK) )
556     {
557 #if DEBUG_PUBLISH
558       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
559                   "Indexing block `%s' for offset %llu with index size %u\n",
560                   GNUNET_h2s (&chk->query),
561                   (unsigned long long) offset,
562                   sizeof (struct OnDemandBlock));
563 #endif
564       odb.offset = GNUNET_htonll (offset);
565       odb.file_id = p->data.file.file_id;
566       GNUNET_DATASTORE_put (sc->dsh,
567                             (p->is_directory) ? 0 : sc->rid,
568                             &chk->query,
569                             sizeof (struct OnDemandBlock),
570                             &odb,
571                             GNUNET_BLOCK_TYPE_FS_ONDEMAND,
572                             p->priority,
573                             p->anonymity,
574                             p->expirationTime,
575                             -2, 1,
576                             GNUNET_CONSTANTS_SERVICE_TIMEOUT,
577                             &ds_put_cont,
578                             dpc_cls);     
579       return;
580     }
581 #if DEBUG_PUBLISH
582   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
583               "Publishing block `%s' for offset %llu with size %u\n",
584               GNUNET_h2s (&chk->query),
585               (unsigned long long) offset,
586               (unsigned int) block_size);
587 #endif
588   GNUNET_DATASTORE_put (sc->dsh,
589                         (p->is_directory) ? 0 : sc->rid,
590                         &chk->query,
591                         block_size,
592                         block,
593                         type,
594                         p->priority,
595                         p->anonymity,
596                         p->expirationTime,
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   client = GNUNET_CLIENT_connect ("fs",
842                                   sc->h->cfg);
843   if (NULL == client)
844     {
845       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
846                   _("Can not index file `%s': %s.  Will try to insert instead.\n"),
847                   p->filename,
848                   _("could not connect to `fs' service"));
849       p->data.file.do_index = GNUNET_NO;
850       publish_content (sc);
851       GNUNET_free (fn);
852       return;
853     }
854   if (p->data.file.have_hash != GNUNET_YES)
855     {
856       p->data.file.file_id = *res;
857       p->data.file.have_hash = GNUNET_YES;
858       GNUNET_FS_file_information_sync_ (p);
859     }
860   ism = GNUNET_malloc (sizeof(struct IndexStartMessage) +
861                        slen);
862   ism->header.size = htons(sizeof(struct IndexStartMessage) +
863                            slen);
864   ism->header.type = htons(GNUNET_MESSAGE_TYPE_FS_INDEX_START);
865   if (GNUNET_OK ==
866       GNUNET_DISK_file_get_identifiers (p->filename,
867                                         &dev,
868                                         &ino))
869     {
870       ism->device = GNUNET_htonll (dev);
871       ism->inode = GNUNET_htonll(ino);
872     }
873 #if DEBUG_PUBLISH
874   else
875     {
876       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
877                   _("Failed to get file identifiers for `%s'\n"),
878                   p->filename);
879     }
880 #endif
881   ism->file_id = *res;
882   memcpy (&ism[1],
883           fn,
884           slen);
885   GNUNET_free (fn);
886   sc->client = client;
887   GNUNET_break (GNUNET_YES ==
888                 GNUNET_CLIENT_transmit_and_get_response (client,
889                                                          &ism->header,
890                                                          GNUNET_TIME_UNIT_FOREVER_REL,
891                                                          GNUNET_YES,
892                                                          &process_index_start_response,
893                                                          sc));
894   GNUNET_free (ism);
895 }
896
897
898 /**
899  * Main function that performs the upload.
900  *
901  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
902  * @param tc task context
903  */
904 void
905 GNUNET_FS_publish_main_ (void *cls,
906                          const struct GNUNET_SCHEDULER_TaskContext *tc)
907 {
908   struct GNUNET_FS_PublishContext *pc = cls;
909   struct GNUNET_FS_ProgressInfo pi;
910   struct GNUNET_FS_FileInformation *p;
911   struct GNUNET_FS_Uri *loc;
912   char *fn;
913
914   pc->upload_task = GNUNET_SCHEDULER_NO_TASK;  
915   p = pc->fi_pos;
916   if (NULL == p)
917     {
918 #if DEBUG_PUBLISH
919       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
920                   "Publishing complete, now publishing SKS and KSK blocks.\n");
921 #endif
922       /* upload of entire hierarchy complete,
923          publish namespace entries */
924       GNUNET_FS_publish_sync_ (pc);
925       publish_sblock (pc);
926       return;
927     }
928   /* find starting position */
929   while ( (p->is_directory) &&
930           (NULL != p->data.dir.entries) &&
931           (NULL == p->emsg) &&
932           (NULL == p->data.dir.entries->chk_uri) )
933     {
934       p = p->data.dir.entries;
935       pc->fi_pos = p;
936       GNUNET_FS_publish_sync_ (pc);
937     }
938   /* abort on error */
939   if (NULL != p->emsg)
940     {
941 #if DEBUG_PUBLISH
942       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
943                   "Error uploading: %s\n",
944                   p->emsg);
945 #endif
946       /* error with current file, abort all
947          related files as well! */
948       while (NULL != p->dir)
949         {
950           fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
951                                                        EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
952           p = p->dir;
953           if (fn != NULL)
954             {
955               GNUNET_asprintf (&p->emsg, 
956                                _("Recursive upload failed at `%s': %s"),
957                                fn,
958                                p->emsg);
959               GNUNET_free (fn);
960             }
961           else
962             {
963               GNUNET_asprintf (&p->emsg, 
964                                _("Recursive upload failed: %s"),
965                                p->emsg);              
966             }
967           pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
968           pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
969           pi.value.publish.specifics.error.message = p->emsg;
970           p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
971         }
972       pc->all_done = GNUNET_YES;
973       GNUNET_FS_publish_sync_ (pc);
974       return;
975     }
976   /* handle completion */
977   if (NULL != p->chk_uri)
978     {
979 #if DEBUG_PUBLISH
980       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
981                   "File upload complete, now publishing KSK blocks.\n");
982 #endif
983       if (0 == p->anonymity)
984         {
985           /* zero anonymity, box CHK URI in LOC URI */
986           loc = GNUNET_FS_uri_loc_create (p->chk_uri,
987                                           pc->h->cfg,
988                                           p->expirationTime);
989           GNUNET_FS_uri_destroy (p->chk_uri);
990           p->chk_uri = loc;
991         }
992       GNUNET_FS_publish_sync_ (pc);
993       /* upload of "p" complete, publish KBlocks! */
994       if (p->keywords != NULL)
995         {
996           GNUNET_FS_publish_ksk (pc->h,
997                                  p->keywords,
998                                  p->meta,
999                                  p->chk_uri,
1000                                  p->expirationTime,
1001                                  p->anonymity,
1002                                  p->priority,
1003                                  pc->options,
1004                                  &publish_kblocks_cont,
1005                                  pc);
1006         }
1007       else
1008         {
1009           publish_kblocks_cont (pc,
1010                                 p->chk_uri,
1011                                 NULL);
1012         }
1013       return;
1014     }
1015   if ( (!p->is_directory) &&
1016        (p->data.file.do_index) )
1017     {
1018       if (NULL == p->filename)
1019         {
1020           p->data.file.do_index = GNUNET_NO;
1021           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1022                       _("Can not index file `%s': %s.  Will try to insert instead.\n"),
1023                       "<no-name>",
1024                       _("needs to be an actual file"));
1025           GNUNET_FS_file_information_sync_ (p);
1026           publish_content (pc);
1027           return;
1028         }      
1029       if (p->data.file.have_hash)
1030         {
1031           hash_for_index_cb (pc,
1032                              &p->data.file.file_id);
1033         }
1034       else
1035         {
1036           p->start_time = GNUNET_TIME_absolute_get ();
1037           pc->fhc = GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE,
1038                                              p->filename,
1039                                              HASHING_BLOCKSIZE,
1040                                              &hash_for_index_cb,
1041                                              pc);
1042         }
1043       return;
1044     }
1045   publish_content (pc);
1046 }
1047
1048
1049 /**
1050  * Signal the FS's progress function that we are starting
1051  * an upload.
1052  *
1053  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1054  * @param fi the entry in the publish-structure
1055  * @param length length of the file or directory
1056  * @param meta metadata for the file or directory (can be modified)
1057  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1058  * @param anonymity pointer to selected anonymity level (can be modified)
1059  * @param priority pointer to selected priority (can be modified)
1060  * @param do_index should we index?
1061  * @param expirationTime pointer to selected expiration time (can be modified)
1062  * @param client_info pointer to client context set upon creation (can be modified)
1063  * @return GNUNET_OK to continue (always)
1064  */
1065 static int
1066 fip_signal_start(void *cls,
1067                  struct GNUNET_FS_FileInformation *fi,
1068                  uint64_t length,
1069                  struct GNUNET_CONTAINER_MetaData *meta,
1070                  struct GNUNET_FS_Uri **uri,
1071                  uint32_t *anonymity,
1072                  uint32_t *priority,
1073                  int *do_index,
1074                  struct GNUNET_TIME_Absolute *expirationTime,
1075                  void **client_info)
1076 {
1077   struct GNUNET_FS_PublishContext *sc = cls;
1078   struct GNUNET_FS_ProgressInfo pi;
1079   unsigned int kc;
1080   uint64_t left;
1081
1082   if (*do_index)
1083     {
1084       /* space for on-demand blocks */
1085       sc->reserve_space += ((length + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * sizeof (struct OnDemandBlock);
1086     }
1087   else
1088     {
1089       /* space for DBlocks */
1090       sc->reserve_space += length;
1091     }
1092   /* entries for IBlocks and DBlocks, space for IBlocks */
1093   left = length;
1094   while (1)
1095     {
1096       left = (left + DBLOCK_SIZE - 1) / DBLOCK_SIZE;
1097       sc->reserve_entries += left;
1098       if (left <= 1)
1099         break;
1100       left = left * sizeof (struct ContentHashKey);
1101       sc->reserve_space += left;
1102     }
1103   sc->reserve_entries++;
1104   /* entries and space for keywords */
1105   if (NULL != *uri)
1106     {
1107       kc = GNUNET_FS_uri_ksk_get_keyword_count (*uri);
1108       sc->reserve_entries += kc;
1109       sc->reserve_space += GNUNET_SERVER_MAX_MESSAGE_SIZE * kc;
1110     }  
1111   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
1112   *client_info = GNUNET_FS_publish_make_status_ (&pi, sc, fi, 0);
1113   GNUNET_FS_file_information_sync_ (fi);
1114   return GNUNET_OK;
1115 }
1116
1117
1118 /**
1119  * Signal the FS's progress function that we are suspending
1120  * an upload.
1121  *
1122  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1123  * @param fi the entry in the publish-structure
1124  * @param length length of the file or directory
1125  * @param meta metadata for the file or directory (can be modified)
1126  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1127  * @param anonymity pointer to selected anonymity level (can be modified)
1128  * @param priority pointer to selected priority (can be modified)
1129  * @param do_index should we index?
1130  * @param expirationTime pointer to selected expiration time (can be modified)
1131  * @param client_info pointer to client context set upon creation (can be modified)
1132  * @return GNUNET_OK to continue (always)
1133  */
1134 static int
1135 fip_signal_suspend(void *cls,
1136                    struct GNUNET_FS_FileInformation *fi,
1137                    uint64_t length,
1138                    struct GNUNET_CONTAINER_MetaData *meta,
1139                    struct GNUNET_FS_Uri **uri,
1140                    uint32_t *anonymity,
1141                    uint32_t *priority,
1142                    int *do_index,
1143                    struct GNUNET_TIME_Absolute *expirationTime,
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   if (namespace != NULL)
1265     {
1266       namespace->rc++;
1267       GNUNET_assert (NULL != nid);
1268       ret->nid = GNUNET_strdup (nid);
1269       if (NULL != nuid)
1270         ret->nuid = GNUNET_strdup (nuid);
1271     }
1272   /* signal start */
1273   GNUNET_FS_file_information_inspect (ret->fi,
1274                                       &fip_signal_start,
1275                                       ret);
1276   ret->fi_pos = ret->fi;
1277   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret);
1278   GNUNET_FS_publish_sync_ (ret);
1279   if (NULL != ret->dsh)
1280     {
1281       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1282                   _("Reserving space for %u entries and %llu bytes for publication\n"),
1283                   (unsigned int) ret->reserve_entries,
1284                   (unsigned long long) ret->reserve_space);
1285       ret->qre = GNUNET_DATASTORE_reserve (ret->dsh,
1286                                            ret->reserve_space,
1287                                            ret->reserve_entries,
1288                                            UINT_MAX,
1289                                            UINT_MAX,
1290                                            GNUNET_TIME_UNIT_FOREVER_REL,
1291                                            &finish_reserve,
1292                                            ret);
1293     }
1294   else
1295     {
1296       ret->upload_task 
1297         = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1298                                               &GNUNET_FS_publish_main_,
1299                                               ret);
1300     }
1301   return ret;
1302 }
1303
1304
1305 /**
1306  * Signal the FS's progress function that we are stopping
1307  * an upload.
1308  *
1309  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1310  * @param fi the entry in the publish-structure
1311  * @param length length of the file or directory
1312  * @param meta metadata for the file or directory (can be modified)
1313  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1314  * @param anonymity pointer to selected anonymity level (can be modified)
1315  * @param priority pointer to selected priority (can be modified)
1316  * @param do_index should we index?
1317  * @param expirationTime pointer to selected expiration time (can be modified)
1318  * @param client_info pointer to client context set upon creation (can be modified)
1319  * @return GNUNET_OK to continue (always)
1320  */
1321 static int
1322 fip_signal_stop(void *cls,
1323                 struct GNUNET_FS_FileInformation *fi,
1324                 uint64_t length,
1325                 struct GNUNET_CONTAINER_MetaData *meta,
1326                 struct GNUNET_FS_Uri **uri,
1327                 uint32_t *anonymity,
1328                 uint32_t *priority,
1329                 int *do_index,
1330                 struct GNUNET_TIME_Absolute *expirationTime,
1331                 void **client_info)
1332 {
1333   struct GNUNET_FS_PublishContext*sc = cls;
1334   struct GNUNET_FS_ProgressInfo pi;
1335   uint64_t off;
1336
1337   if (fi->serialization != NULL) 
1338     {
1339       GNUNET_FS_remove_sync_file_ (sc->h,
1340                                    GNUNET_FS_SYNC_PATH_FILE_INFO,
1341                                    fi->serialization);
1342       GNUNET_free (fi->serialization);
1343       fi->serialization = NULL;
1344     }
1345   off = (fi->chk_uri == NULL) ? 0 : length;
1346   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1347   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, sc, fi, off));
1348   *client_info = NULL;
1349   return GNUNET_OK;
1350 }
1351
1352
1353 /**
1354  * Stop an upload.  Will abort incomplete uploads (but 
1355  * not remove blocks that have already been publishd) or
1356  * simply clean up the state for completed uploads.
1357  * Must NOT be called from within the event callback!
1358  *
1359  * @param pc context for the upload to stop
1360  */
1361 void 
1362 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc)
1363 {
1364   GNUNET_FS_end_top (pc->h, pc->top);
1365   if (NULL != pc->qre)
1366     {
1367       GNUNET_DATASTORE_cancel (pc->qre);
1368       pc->qre = NULL;
1369     }
1370   if (NULL != pc->dsh)
1371     {
1372       GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1373       pc->dsh = NULL;
1374     }
1375   if (GNUNET_SCHEDULER_NO_TASK != pc->upload_task)
1376     {
1377       GNUNET_SCHEDULER_cancel (pc->upload_task);
1378       pc->upload_task = GNUNET_SCHEDULER_NO_TASK;
1379     }
1380   if (pc->serialization != NULL) 
1381     {
1382       GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, pc->serialization);
1383       GNUNET_free (pc->serialization);
1384       pc->serialization = NULL;
1385     }
1386   GNUNET_FS_file_information_inspect (pc->fi,
1387                                       &fip_signal_stop,
1388                                       pc);
1389   if (GNUNET_YES == pc->in_network_wait)
1390     {
1391       pc->in_network_wait = GNUNET_SYSERR;
1392       return;
1393     }
1394   publish_cleanup (pc, NULL);
1395 }
1396
1397
1398 /**
1399  * Context for the KSK publication.
1400  */
1401 struct PublishKskContext
1402 {
1403
1404   /**
1405    * Keywords to use.
1406    */
1407   struct GNUNET_FS_Uri *ksk_uri;
1408
1409   /**
1410    * Global FS context.
1411    */
1412   struct GNUNET_FS_Handle *h;
1413
1414   /**
1415    * The master block that we are sending
1416    * (in plaintext), has "mdsize+slen" more
1417    * bytes than the struct would suggest.
1418    */
1419   struct KBlock *kb;
1420
1421   /**
1422    * Buffer of the same size as "kb" for
1423    * the encrypted version.
1424    */ 
1425   struct KBlock *cpy;
1426
1427   /**
1428    * Handle to the datastore, NULL if we are just
1429    * simulating.
1430    */
1431   struct GNUNET_DATASTORE_Handle *dsh;
1432
1433   /**
1434    * Function to call once we're done.
1435    */
1436   GNUNET_FS_PublishContinuation cont;
1437
1438   /**
1439    * Closure for cont.
1440    */ 
1441   void *cont_cls;
1442
1443   /**
1444    * When should the KBlocks expire?
1445    */
1446   struct GNUNET_TIME_Absolute expirationTime;
1447
1448   /**
1449    * Size of the serialized metadata.
1450    */
1451   ssize_t mdsize;
1452
1453   /**
1454    * Size of the (CHK) URI as a string.
1455    */
1456   size_t slen;
1457
1458   /**
1459    * Keyword that we are currently processing.
1460    */
1461   unsigned int i;
1462
1463   /**
1464    * Anonymity level for the KBlocks.
1465    */
1466   uint32_t anonymity;
1467
1468   /**
1469    * Priority for the KBlocks.
1470    */
1471   uint32_t priority;
1472 };
1473
1474
1475 /**
1476  * Continuation of "GNUNET_FS_publish_ksk" that performs
1477  * the actual publishing operation (iterating over all
1478  * of the keywords).
1479  *
1480  * @param cls closure of type "struct PublishKskContext*"
1481  * @param tc unused
1482  */
1483 static void
1484 publish_ksk_cont (void *cls,
1485                   const struct GNUNET_SCHEDULER_TaskContext *tc);
1486
1487
1488 /**
1489  * Function called by the datastore API with
1490  * the result from the PUT request.
1491  *
1492  * @param cls closure of type "struct PublishKskContext*"
1493  * @param success GNUNET_OK on success
1494  * @param msg error message (or NULL)
1495  */
1496 static void
1497 kb_put_cont (void *cls,
1498              int success,
1499              const char *msg)
1500 {
1501   struct PublishKskContext *pkc = cls;
1502
1503   if (GNUNET_OK != success)
1504     {
1505       if (NULL != pkc->dsh)
1506         {
1507           GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1508           pkc->dsh = NULL;
1509         }
1510       GNUNET_free (pkc->cpy);
1511       GNUNET_free (pkc->kb);
1512       pkc->cont (pkc->cont_cls,
1513                  NULL,
1514                  msg);
1515       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1516       GNUNET_free (pkc);
1517       return;
1518     }
1519   GNUNET_SCHEDULER_add_continuation (&publish_ksk_cont,
1520                                      pkc,
1521                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1522 }
1523
1524
1525 /**
1526  * Continuation of "GNUNET_FS_publish_ksk" that performs the actual
1527  * publishing operation (iterating over all of the keywords).
1528  *
1529  * @param cls closure of type "struct PublishKskContext*"
1530  * @param tc unused
1531  */
1532 static void
1533 publish_ksk_cont (void *cls,
1534                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1535 {
1536   struct PublishKskContext *pkc = cls;
1537   const char *keyword;
1538   GNUNET_HashCode key;
1539   GNUNET_HashCode query;
1540   struct GNUNET_CRYPTO_AesSessionKey skey;
1541   struct GNUNET_CRYPTO_AesInitializationVector iv;
1542   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1543
1544
1545   if ( (pkc->i == pkc->ksk_uri->data.ksk.keywordCount) ||
1546        (NULL == pkc->dsh) )
1547     {
1548       if (NULL != pkc->dsh)
1549         {
1550           GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1551           pkc->dsh = NULL;
1552         }
1553       GNUNET_free (pkc->cpy);
1554       GNUNET_free (pkc->kb);
1555       pkc->cont (pkc->cont_cls,
1556                  pkc->ksk_uri,
1557                  NULL);
1558       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1559       GNUNET_free (pkc);
1560       return;
1561     }
1562   keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
1563 #if DEBUG_PUBLISH
1564   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1565               "Publishing under keyword `%s'\n",
1566               keyword);
1567 #endif
1568   /* first character of keyword indicates if it is
1569      mandatory or not -- ignore for hashing */
1570   GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
1571   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
1572   GNUNET_CRYPTO_aes_encrypt (&pkc->kb[1],
1573                              pkc->slen + pkc->mdsize,
1574                              &skey,
1575                              &iv,
1576                              &pkc->cpy[1]);
1577   pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
1578   GNUNET_assert (NULL != pk);
1579   GNUNET_CRYPTO_rsa_key_get_public (pk, &pkc->cpy->keyspace);
1580   GNUNET_CRYPTO_hash (&pkc->cpy->keyspace,
1581                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1582                       &query);
1583   GNUNET_assert (GNUNET_OK == 
1584                  GNUNET_CRYPTO_rsa_sign (pk,
1585                                          &pkc->cpy->purpose,
1586                                          &pkc->cpy->signature));
1587   GNUNET_CRYPTO_rsa_key_free (pk);
1588   GNUNET_DATASTORE_put (pkc->dsh,
1589                         0,
1590                         &query,
1591                         pkc->mdsize + 
1592                         sizeof (struct KBlock) + 
1593                         pkc->slen,
1594                         pkc->cpy,
1595                         GNUNET_BLOCK_TYPE_FS_KBLOCK, 
1596                         pkc->priority,
1597                         pkc->anonymity,
1598                         pkc->expirationTime,
1599                         -2, 1,
1600                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1601                         &kb_put_cont,
1602                         pkc);
1603 }
1604
1605
1606 /**
1607  * Publish a CHK under various keywords on GNUnet.
1608  *
1609  * @param h handle to the file sharing subsystem
1610  * @param ksk_uri keywords to use
1611  * @param meta metadata to use
1612  * @param uri URI to refer to in the KBlock
1613  * @param expirationTime when the KBlock expires
1614  * @param anonymity anonymity level for the KBlock
1615  * @param priority priority for the KBlock
1616  * @param options publication options
1617  * @param cont continuation
1618  * @param cont_cls closure for cont
1619  */
1620 void
1621 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
1622                        const struct GNUNET_FS_Uri *ksk_uri,
1623                        const struct GNUNET_CONTAINER_MetaData *meta,
1624                        const struct GNUNET_FS_Uri *uri,
1625                        struct GNUNET_TIME_Absolute expirationTime,
1626                        uint32_t anonymity,
1627                        uint32_t priority,
1628                        enum GNUNET_FS_PublishOptions options,
1629                        GNUNET_FS_PublishContinuation cont,
1630                        void *cont_cls)
1631 {
1632   struct PublishKskContext *pkc;
1633   char *uris;
1634   size_t size;
1635   char *kbe;
1636   char *sptr;
1637
1638   GNUNET_assert (NULL != uri);
1639   pkc = GNUNET_malloc (sizeof (struct PublishKskContext));
1640   pkc->h = h;
1641   pkc->expirationTime = expirationTime;
1642   pkc->anonymity = anonymity;
1643   pkc->priority = priority;
1644   pkc->cont = cont;
1645   pkc->cont_cls = cont_cls;
1646   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1647     {
1648       pkc->dsh = GNUNET_DATASTORE_connect (h->cfg);
1649       if (pkc->dsh == NULL)
1650         {
1651           cont (cont_cls, NULL, _("Could not connect to datastore."));
1652           GNUNET_free (pkc);
1653           return;
1654         }
1655     }
1656   if (meta == NULL)
1657     pkc->mdsize = 0;
1658   else
1659     pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
1660   GNUNET_assert (pkc->mdsize >= 0);
1661   uris = GNUNET_FS_uri_to_string (uri);
1662   pkc->slen = strlen (uris) + 1;
1663   size = pkc->mdsize + sizeof (struct KBlock) + pkc->slen;
1664   if (size > MAX_KBLOCK_SIZE)
1665     {
1666       size = MAX_KBLOCK_SIZE;
1667       pkc->mdsize = size - sizeof (struct KBlock) - pkc->slen;
1668     }
1669   pkc->kb = GNUNET_malloc (size);
1670   kbe = (char *) &pkc->kb[1];
1671   memcpy (kbe, uris, pkc->slen);
1672   GNUNET_free (uris);
1673   sptr = &kbe[pkc->slen];
1674   if (meta != NULL)
1675     pkc->mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
1676                                                         &sptr,
1677                                                         pkc->mdsize,
1678                                                         GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1679   if (pkc->mdsize == -1)
1680     {
1681       GNUNET_break (0);
1682       GNUNET_free (pkc->kb);
1683       if (pkc->dsh != NULL)
1684         {
1685           GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1686           pkc->dsh = NULL;
1687         }
1688       cont (cont_cls, NULL, _("Internal error."));
1689       GNUNET_free (pkc);
1690       return;
1691     }
1692   size = sizeof (struct KBlock) + pkc->slen + pkc->mdsize;
1693
1694   pkc->cpy = GNUNET_malloc (size);
1695   pkc->cpy->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + 
1696                                   sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1697                                   pkc->mdsize + 
1698                                   pkc->slen);
1699   pkc->cpy->purpose.purpose = htonl(GNUNET_SIGNATURE_PURPOSE_FS_KBLOCK);
1700   pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
1701   GNUNET_SCHEDULER_add_continuation (&publish_ksk_cont,
1702                                      pkc,
1703                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1704 }
1705
1706
1707 /* end of fs_publish.c */