clarification
[oweals/gnunet.git] / src / fs / fs_publish.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 2, 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 http://gnunet.org/encoding.php3
25  * @author Krista Bennett
26  * @author Christian Grothoff
27  *
28  * TODO:
29  * - indexing support
30  * - code-sharing with unindex (can wait)
31  * - persistence support (can wait)
32  * - datastore reservation support (optimization)
33  * - location URIs (publish with anonymity-level zero)
34  */
35
36 #include "platform.h"
37 #include "gnunet_constants.h"
38 #include "gnunet_signatures.h"
39 #include "gnunet_util_lib.h"
40 #include "gnunet_fs_service.h"
41 #include "fs.h"
42
43 #define DEBUG_PUBLISH GNUNET_YES
44
45 /**
46  * Maximum allowed size for a KBlock.
47  */
48 #define MAX_KBLOCK_SIZE 60000
49
50 /**
51  * Maximum allowed size for an SBlock.
52  */
53 #define MAX_SBLOCK_SIZE 60000
54
55
56 /**
57  * Main function that performs the upload.
58  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
59  * @param tc task context
60  */
61 static void
62 do_upload (void *cls,
63            const struct GNUNET_SCHEDULER_TaskContext *tc);
64
65
66 /**
67  * Context for "ds_put_cont".
68  */
69 struct PutContCtx
70 {
71   /**
72    * Current publishing context.
73    */
74   struct GNUNET_FS_PublishContext *sc;
75
76   /**
77    * Specific file with the block.
78    */
79   struct GNUNET_FS_FileInformation *p;
80
81   /**
82    * Function to run next, if any (can be NULL).
83    */
84   GNUNET_SCHEDULER_Task cont;
85
86   /**
87    * Closure for cont.
88    */
89   void *cont_cls;
90 };
91
92
93 /**
94  * Fill in all of the generic fields for 
95  * a publish event.
96  *
97  * @param pc structure to fill in
98  * @param sc overall publishing context
99  * @param p file information for the file being published
100  */
101 static void
102 make_publish_status (struct GNUNET_FS_ProgressInfo *pi,
103                      struct GNUNET_FS_PublishContext *sc,
104                      const struct GNUNET_FS_FileInformation *p)
105 {
106   pi->value.publish.sc = sc;
107   pi->value.publish.fi = p;
108   pi->value.publish.cctx
109     = p->client_info;
110   pi->value.publish.pctx
111     = (NULL == p->dir) ? NULL : p->dir->client_info;
112   pi->value.publish.size
113     = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
114   pi->value.publish.eta 
115     = GNUNET_TIME_calculate_eta (p->start_time,
116                                  p->publish_offset,
117                                  pi->value.publish.size);
118   pi->value.publish.duration = GNUNET_TIME_absolute_get_duration (p->start_time);
119   pi->value.publish.completed = p->publish_offset;
120   pi->value.publish.anonymity = p->anonymity;
121 }
122
123
124 /**
125  * Cleanup the publish context, we're done
126  * with it.
127  *
128  * @param pc struct to clean up after
129  */
130 static void
131 publish_cleanup (struct GNUNET_FS_PublishContext *sc)
132 {
133   GNUNET_FS_file_information_destroy (sc->fi, NULL, NULL);
134   GNUNET_FS_namespace_delete (sc->namespace, GNUNET_NO);
135   GNUNET_free_non_null (sc->nid);  
136   GNUNET_free_non_null (sc->nuid);
137   GNUNET_DATASTORE_disconnect (sc->dsh, GNUNET_NO);
138   GNUNET_free (sc);
139 }
140
141
142 /**
143  * Function called by the datastore API with
144  * the result from the PUT request.
145  *
146  * @param cls our closure
147  * @param success GNUNET_OK on success
148  * @param msg error message (or NULL)
149  */
150 static void
151 ds_put_cont (void *cls,
152              int success,
153              const char *msg)
154 {
155   struct PutContCtx *pcc = cls;
156   struct GNUNET_FS_ProgressInfo pi;
157
158   if (GNUNET_SYSERR == pcc->sc->in_network_wait)
159     {
160       /* we were aborted in the meantime,
161          finish shutdown! */
162       publish_cleanup (pcc->sc);
163       return;
164     }
165   GNUNET_assert (GNUNET_YES == pcc->sc->in_network_wait);
166   pcc->sc->in_network_wait = GNUNET_NO;
167   if (GNUNET_OK != success)
168     {
169       GNUNET_asprintf (&pcc->p->emsg, 
170                        _("Upload failed: %s"),
171                        msg);
172       GNUNET_FS_file_information_sync (pcc->p);
173       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
174       make_publish_status (&pi, pcc->sc, pcc->p);
175       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
176       pi.value.publish.specifics.error.message = pcc->p->emsg;
177       pcc->p->client_info
178         = pcc->sc->h->upcb (pcc->sc->h->upcb_cls,
179                             &pi);
180       return;
181     }
182   GNUNET_FS_file_information_sync (pcc->p);
183   if (NULL != pcc->cont)
184     pcc->sc->upload_task 
185       = GNUNET_SCHEDULER_add_delayed (pcc->sc->h->sched,
186                                       GNUNET_NO,
187                                       GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
188                                       GNUNET_SCHEDULER_NO_TASK,
189                                       GNUNET_TIME_UNIT_ZERO,
190                                       pcc->cont,
191                                       pcc->cont_cls);
192   GNUNET_free (pcc);
193 }
194
195
196 /**
197  * Generate the callback that signals clients
198  * that a file (or directory) has been completely
199  * published.
200  *
201  * @param p the completed upload
202  * @param sc context of the publication
203  */
204 static void 
205 signal_publish_completion (struct GNUNET_FS_FileInformation *p,
206                            struct GNUNET_FS_PublishContext *sc)
207 {
208   struct GNUNET_FS_ProgressInfo pi;
209   
210   pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
211   make_publish_status (&pi, sc, p);
212   pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
213   pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
214   p->client_info
215     = sc->h->upcb (sc->h->upcb_cls,
216                   &pi);
217 }
218
219
220 /**
221  * Generate the callback that signals clients
222  * that a file (or directory) has encountered
223  * a problem during publication.
224  *
225  * @param p the upload that had trouble
226  * @param sc context of the publication
227  * @param emsg error message
228  */
229 static void 
230 signal_publish_error (struct GNUNET_FS_FileInformation *p,
231                       struct GNUNET_FS_PublishContext *sc,
232                       const char *emsg)
233 {
234   struct GNUNET_FS_ProgressInfo pi;
235   
236   p->emsg = GNUNET_strdup (emsg);
237   pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
238   make_publish_status (&pi, sc, p);
239   pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
240   pi.value.publish.specifics.error.message =emsg;
241   p->client_info
242     = sc->h->upcb (sc->h->upcb_cls,
243                   &pi);
244 }
245
246
247 /**
248  * We've finished publishing the SBlock as part of a larger upload.
249  * Check the result and complete the larger upload.
250  *
251  * @param cls the "struct GNUNET_FS_PublishContext*" of the larger upload
252  * @param uri URI of the published SBlock
253  * @param emsg NULL on success, otherwise error message
254  */
255 static void
256 publish_sblocks_cont (void *cls,
257                       const struct GNUNET_FS_Uri *uri,
258                       const char *emsg)
259 {
260   struct GNUNET_FS_PublishContext *sc = cls;
261   if (NULL != emsg)
262     {
263       signal_publish_error (sc->fi,
264                             sc,
265                             emsg);
266       return;
267     }  
268   // FIXME: release the datastore reserve here!
269   signal_publish_completion (sc->fi, sc);
270 }
271
272
273 /**
274  * We are almost done publishing the structure,
275  * add SBlocks (if needed).
276  *
277  * @param sc overall upload data
278  */
279 static void
280 publish_sblock (struct GNUNET_FS_PublishContext *sc)
281 {
282   if (NULL != sc->namespace)
283     GNUNET_FS_publish_sks (sc->h,
284                            sc->namespace,
285                            sc->nid,
286                            sc->nuid,
287                            sc->fi->meta,
288                            sc->fi->chk_uri,
289                            sc->fi->expirationTime,
290                            sc->fi->anonymity,
291                            sc->fi->priority,
292                            sc->options,
293                            &publish_sblocks_cont,
294                            sc);
295   else
296     publish_sblocks_cont (sc, NULL, NULL);
297 }
298
299
300 /**
301  * We've finished publishing a KBlock
302  * as part of a larger upload.  Check
303  * the result and continue the larger
304  * upload.
305  *
306  * @param cls the "struct GNUNET_FS_PublishContext*" of the larger upload
307  * @param uri URI of the published blocks
308  * @param emsg NULL on success, otherwise error message
309  */
310 static void
311 publish_kblocks_cont (void *cls,
312                       const struct GNUNET_FS_Uri *uri,
313                       const char *emsg)
314 {
315   struct GNUNET_FS_PublishContext *sc = cls;
316   struct GNUNET_FS_FileInformation *p = sc->fi_pos;
317
318   if (NULL != emsg)
319     {
320       signal_publish_error (p, sc, emsg);
321       sc->upload_task 
322         = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
323                                         GNUNET_NO,
324                                         GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
325                                         GNUNET_SCHEDULER_NO_TASK,
326                                         GNUNET_TIME_UNIT_ZERO,
327                                         &do_upload,
328                                         sc);
329       return;
330     }
331   GNUNET_FS_file_information_sync (p);
332   if (NULL != p->dir)
333     signal_publish_completion (p, sc);
334   /* move on to next file */
335   if (NULL != p->next)
336     sc->fi_pos = p->next;
337   else
338     sc->fi_pos = p->dir;
339   sc->upload_task 
340     = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
341                                     GNUNET_NO,
342                                     GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
343                                     GNUNET_SCHEDULER_NO_TASK,
344                                     GNUNET_TIME_UNIT_ZERO,
345                                     &do_upload,
346                                     sc);
347 }
348
349
350 /**
351  * Compute the depth of the CHK tree.
352  *
353  * @param flen file length for which to compute the depth
354  * @return depth of the tree
355  */
356 static unsigned int
357 compute_depth (uint64_t flen)
358 {
359   unsigned int treeDepth;
360   uint64_t fl;
361
362   treeDepth = 1;
363   fl = DBLOCK_SIZE;
364   while (fl < flen)
365     {
366       treeDepth++;
367       if (fl * CHK_PER_INODE < fl)
368         {
369           /* integer overflow, this is a HUGE file... */
370           return treeDepth;
371         }
372       fl = fl * CHK_PER_INODE;
373     }
374   return treeDepth;
375 }
376
377
378 /**
379  * Compute the size of the current IBlock.
380  *
381  * @param height height of the IBlock in the tree (aka overall
382  *               number of tree levels minus depth); 0 == DBlock
383  * @param offset current offset in the overall file
384  * @return size of the corresponding IBlock
385  */
386 static uint16_t 
387 compute_iblock_size (unsigned int height,
388                      uint64_t offset)
389 {
390   unsigned int ret;
391   unsigned int i;
392   uint64_t mod;
393   uint64_t bds;
394
395   GNUNET_assert (height > 0);
396   bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i"
397                                   corresponds to */
398   for (i=0;i<height;i++)
399     bds *= CHK_PER_INODE;
400   mod = offset % bds;
401   if (0 == mod)
402     {
403       /* we were triggered at the end of a full block */
404       ret = CHK_PER_INODE;
405     }
406   else
407     {
408       /* we were triggered at the end of the file */
409       bds /= CHK_PER_INODE;
410       ret = mod / bds;
411       if (0 != mod % bds)
412         ret++; 
413     }
414   return (uint16_t) (ret * sizeof(struct ContentHashKey));
415 }
416
417
418 /**
419  * Compute the offset of the CHK for the
420  * current block in the IBlock above.
421  *
422  * @param height height of the IBlock in the tree (aka overall
423  *               number of tree levels minus depth); 0 == DBlock
424  * @param offset current offset in the overall file
425  * @return (array of CHKs') offset in the above IBlock
426  */
427 static unsigned int
428 compute_chk_offset (unsigned int height,
429                     uint64_t offset)
430 {
431   uint64_t bds;
432   unsigned int ret;
433   unsigned int i;
434
435   bds = DBLOCK_SIZE; /* number of bytes each CHK at level "i"
436                                   corresponds to */
437   for (i=0;i<height;i++)
438     bds *= CHK_PER_INODE;
439   GNUNET_assert (0 == (offset % bds));
440   ret = offset / bds;
441   return ret % CHK_PER_INODE; 
442 }
443
444
445 /**
446  * We are uploading a file or directory; load (if necessary) the next
447  * block into memory, encrypt it and send it to the FS service.  Then
448  * continue with the main task.
449  *
450  * @param sc overall upload data
451  * @param p specific file or directory for which kblocks
452  *          should be created
453  */
454 static void
455 publish_content (struct GNUNET_FS_PublishContext *sc,
456                  struct GNUNET_FS_FileInformation *p)
457 {
458   struct GNUNET_FS_ProgressInfo pi;
459   struct ContentHashKey *mychk;
460   const void *pt_block;
461   uint16_t pt_size;
462   char *emsg;
463   char iob[DBLOCK_SIZE];
464   char enc[DBLOCK_SIZE];
465   struct GNUNET_CRYPTO_AesSessionKey sk;
466   struct GNUNET_CRYPTO_AesInitializationVector iv;
467   uint64_t size;
468   unsigned int off;
469   struct GNUNET_FS_DirectoryBuilder *db;
470   struct GNUNET_FS_FileInformation *dirpos;
471   void *raw_data;
472   char *dd;
473   struct PutContCtx * dpc_cls;
474
475   // FIXME: figure out how to share this code
476   // with unindex!
477   size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
478   if (NULL == p->chk_tree)
479     {
480       if (p->is_directory)
481         {
482           db = GNUNET_FS_directory_builder_create (p->meta);
483           dirpos = p->data.dir.entries;
484           while (NULL != dirpos)
485             {
486               if (dirpos->is_directory)
487                 {
488                   raw_data = dirpos->data.dir.dir_data;
489                   dirpos->data.dir.dir_data = NULL;
490                 }
491               else
492                 {
493                   raw_data = NULL;
494                   if ( (dirpos->data.file.file_size < MAX_INLINE_SIZE) &&
495                        (dirpos->data.file.file_size > 0) )
496                     {
497                       raw_data = GNUNET_malloc (dirpos->data.file.file_size);
498                       emsg = NULL;
499                       if (dirpos->data.file.file_size !=
500                           dirpos->data.file.reader (dirpos->data.file.reader_cls,
501                                                     0,
502                                                     dirpos->data.file.file_size,
503                                                     raw_data,
504                                                     &emsg))
505                         {
506                           GNUNET_free_non_null (emsg);
507                           GNUNET_free (raw_data);
508                           raw_data = NULL;
509                         } 
510                     }
511                 }
512       GNUNET_FS_directory_builder_add (db,
513                                                dirpos->chk_uri,
514                                                dirpos->meta,
515                                                raw_data);
516               GNUNET_free_non_null (raw_data);
517               dirpos = dirpos->next;
518             }
519           GNUNET_FS_directory_builder_finish (db,
520                                               &p->data.dir.dir_size,
521                                               &p->data.dir.dir_data);
522           size = p->data.dir.dir_size;
523         }
524       p->chk_tree_depth = compute_depth (size);
525       p->chk_tree = GNUNET_malloc (p->chk_tree_depth * 
526                                    sizeof (struct ContentHashKey) *
527                                    CHK_PER_INODE);
528       p->current_depth = p->chk_tree_depth;
529     }
530   if (p->current_depth == p->chk_tree_depth)
531     {
532       if (p->is_directory)
533         {
534           pt_size = GNUNET_MIN(DBLOCK_SIZE,
535                                p->data.dir.dir_size - p->publish_offset);
536           dd = p->data.dir.dir_data;
537           pt_block = &dd[p->publish_offset];
538         }
539       else
540         {
541           pt_size = GNUNET_MIN(DBLOCK_SIZE,
542                                p->data.file.file_size - p->publish_offset);
543           emsg = NULL;
544           if (pt_size !=
545               p->data.file.reader (p->data.file.reader_cls,
546                                    p->publish_offset,
547                                    pt_size,
548                                    iob,
549                                    &emsg))
550             {
551               GNUNET_asprintf (&p->emsg, 
552                                _("Upload failed: %s"),
553                                emsg);
554               GNUNET_free (emsg);
555               GNUNET_FS_file_information_sync (p);
556               pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
557               make_publish_status (&pi, sc, p);
558               pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
559               pi.value.publish.specifics.error.message = p->emsg;
560               p->client_info
561                 = sc->h->upcb (sc->h->upcb_cls,
562                                &pi);
563               /* continue with main (to propagate error up) */
564               sc->upload_task 
565                 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
566                                                 GNUNET_NO,
567                                                 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
568                                                 GNUNET_SCHEDULER_NO_TASK,
569                                                 GNUNET_TIME_UNIT_ZERO,
570                                                 &do_upload,
571                                                 sc);
572               return;
573             }
574           pt_block = iob;
575         }
576     }
577   else
578     {
579       pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
580                                      p->publish_offset); 
581       pt_block = &p->chk_tree[p->current_depth *
582                               CHK_PER_INODE];
583     }
584   off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
585                             p->publish_offset);
586   mychk = &p->chk_tree[(p->current_depth-1)*CHK_PER_INODE+off];
587   GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
588   GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
589   GNUNET_CRYPTO_aes_encrypt (pt_block,
590                              pt_size,
591                              &sk,
592                              &iv,
593                              enc);
594   // NOTE: this block below is all that really differs
595   // between publish/unindex!  Parameterize & move this code!
596   // FIXME: something around here would need to change
597   // for indexing!
598   if (NULL == sc->dsh)
599     {
600       sc->upload_task
601         = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
602                                         GNUNET_NO,
603                                         GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
604                                         GNUNET_SCHEDULER_NO_TASK,
605                                         GNUNET_TIME_UNIT_ZERO,
606                                         &do_upload,
607                                         sc);
608     }
609   else
610     {
611       GNUNET_assert (GNUNET_NO == sc->in_network_wait);
612       sc->in_network_wait = GNUNET_YES;
613       dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
614       dpc_cls->cont = &do_upload;
615       dpc_cls->cont_cls = sc;
616       dpc_cls->p = p;
617       GNUNET_DATASTORE_put (sc->dsh,
618                             sc->rid,
619                             &mychk->query,
620                             pt_size,
621                             enc,
622                             (p->current_depth == p->chk_tree_depth) 
623                             ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK 
624                             : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
625                             p->priority,
626                             p->anonymity,
627                             p->expirationTime,
628                             GNUNET_CONSTANTS_SERVICE_TIMEOUT,
629                             &ds_put_cont,
630                             dpc_cls);
631     }
632   if (p->current_depth == p->chk_tree_depth)
633     {
634       pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
635       make_publish_status (&pi, sc, p);
636       pi.value.publish.specifics.progress.data = pt_block;
637       pi.value.publish.specifics.progress.offset = p->publish_offset;
638       pi.value.publish.specifics.progress.data_len = pt_size;
639       p->client_info 
640         = sc->h->upcb (sc->h->upcb_cls,
641                        &pi);
642     }
643   GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
644   if (p->current_depth == p->chk_tree_depth) 
645     { 
646       p->publish_offset += pt_size;
647       if ( (p->publish_offset == size) ||
648            (0 == p->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE) ) )
649         p->current_depth--;
650     }
651   else
652     {
653       if ( (off == CHK_PER_INODE) ||
654            (p->publish_offset == size) )
655         p->current_depth--;
656       else
657         p->current_depth = p->chk_tree_depth;
658     }
659   if (0 == p->current_depth)
660     {
661       p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
662       p->chk_uri->type = chk;
663       p->chk_uri->data.chk.chk = p->chk_tree[0];
664       p->chk_uri->data.chk.file_length = size;
665       GNUNET_free (p->chk_tree);
666       p->chk_tree = NULL;
667     }
668 }
669
670
671 /**
672  * Main function that performs the upload.
673  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
674  * @param tc task context
675  */
676 static void
677 do_upload (void *cls,
678            const struct GNUNET_SCHEDULER_TaskContext *tc)
679 {
680   struct GNUNET_FS_PublishContext *sc = cls;
681   struct GNUNET_FS_ProgressInfo pi;
682   struct GNUNET_FS_FileInformation *p;
683   char *fn;
684
685   sc->upload_task = GNUNET_SCHEDULER_NO_TASK;  
686   p = sc->fi_pos;
687   if (NULL == p)
688     {
689       /* upload of entire hierarchy complete,
690          publish namespace entries */
691       publish_sblock (sc);
692       return;
693     }
694   /* find starting position */
695   while ( (p->is_directory) &&
696           (NULL != p->data.dir.entries) &&
697           (NULL == p->emsg) &&
698           (NULL == p->data.dir.entries->chk_uri) )
699     {
700       p = p->data.dir.entries;
701       sc->fi_pos = p;
702     }
703   /* abort on error */
704   if (NULL != p->emsg)
705     {
706       /* error with current file, abort all
707          related files as well! */
708       while (NULL != p->dir)
709         {
710           fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
711                                                        EXTRACTOR_FILENAME);
712           p = p->dir;
713           GNUNET_asprintf (&p->emsg, 
714                            _("Recursive upload failed at `%s'"),
715                            fn);
716           GNUNET_free (fn);
717           GNUNET_FS_file_information_sync (p);
718           pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
719           make_publish_status (&pi, sc, p);
720           pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
721           pi.value.publish.specifics.error.message = p->emsg;
722           p->client_info
723             = sc->h->upcb (sc->h->upcb_cls,
724                            &pi);
725         }
726       return;
727     }
728   /* handle completion */
729   if (NULL != p->chk_uri)
730     {
731       /* upload of "p" complete, publish KBlocks! */
732       GNUNET_FS_publish_ksk (sc->h,
733                              p->keywords,
734                              p->meta,
735                              p->chk_uri,
736                              p->expirationTime,
737                              p->anonymity,
738                              p->priority,
739                              sc->options,
740                              &publish_kblocks_cont,
741                              sc);
742       return;
743     }
744   if ( (!p->is_directory) &&
745        (p->data.file.do_index) )
746     {
747       // FIXME: need to pre-compute hash over
748       // the entire file and ask FS to prepare
749       // for indexing!
750       return;
751     }
752   publish_content (sc, p);
753 }
754
755
756 /**
757  * Signal the FS's progress function that we are starting
758  * an upload.
759  *
760  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
761  * @param fi the entry in the publish-structure
762  * @param length length of the file or directory
763  * @param meta metadata for the file or directory (can be modified)
764  * @param uri pointer to the keywords that will be used for this entry (can be modified)
765  * @param anonymity pointer to selected anonymity level (can be modified)
766  * @param priority pointer to selected priority (can be modified)
767  * @param expirationTime pointer to selected expiration time (can be modified)
768  * @param client_info pointer to client context set upon creation (can be modified)
769  * @return GNUNET_OK to continue (always)
770  */
771 static int
772 fip_signal_start(void *cls,
773                  struct GNUNET_FS_FileInformation *fi,
774                  uint64_t length,
775                  struct GNUNET_CONTAINER_MetaData *meta,
776                  struct GNUNET_FS_Uri **uri,
777                  unsigned int *anonymity,
778                  unsigned int *priority,
779                  struct GNUNET_TIME_Absolute *expirationTime,
780                  void **client_info)
781 {
782   struct GNUNET_FS_PublishContext *sc = cls;
783   struct GNUNET_FS_ProgressInfo pi;
784
785   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
786   make_publish_status (&pi, sc, fi);
787   *client_info = sc->h->upcb (sc->h->upcb_cls,
788                               &pi);
789   return GNUNET_OK;
790 }
791
792
793 /**
794  * Publish a file or directory.
795  *
796  * @param h handle to the file sharing subsystem
797  * @param ctx initial value to use for the '*ctx'
798  *        in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
799  * @param fi information about the file or directory structure to publish
800  * @param namespace namespace to publish the file in, NULL for no namespace
801  * @param nid identifier to use for the publishd content in the namespace
802  *        (can be NULL, must be NULL if namespace is NULL)
803  * @param nuid update-identifier that will be used for future updates 
804  *        (can be NULL, must be NULL if namespace or nid is NULL)
805  * @param options options for the publication 
806  * @return context that can be used to control the publish operation
807  */
808 struct GNUNET_FS_PublishContext *
809 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
810                          void *ctx,
811                          struct GNUNET_FS_FileInformation *fi,
812                          struct GNUNET_FS_Namespace *namespace,
813                          const char *nid,
814                          const char *nuid,
815                          enum GNUNET_FS_PublishOptions options)
816 {
817   struct GNUNET_FS_PublishContext *ret;
818   struct GNUNET_DATASTORE_Handle *dsh;
819
820   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
821     {
822       dsh = GNUNET_DATASTORE_connect (h->cfg,
823                                       h->sched);
824       if (NULL == dsh)
825         return NULL;
826     }
827   else
828     {
829       dsh = NULL;
830     }
831   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
832   ret->dsh = dsh;
833   ret->h = h;
834   ret->client_ctx = ctx;
835   ret->fi = fi;
836   ret->namespace = namespace;
837   if (namespace != NULL)
838     {
839       namespace->rc++;
840       GNUNET_assert (NULL != nid);
841       ret->nid = GNUNET_strdup (nid);
842       if (NULL != nuid)
843         ret->nuid = GNUNET_strdup (nuid);
844     }
845   // FIXME: make upload persistent!
846
847   /* signal start */
848   GNUNET_FS_file_information_inspect (ret->fi,
849                                       &fip_signal_start,
850                                       ret);
851   ret->fi_pos = ret->fi;
852
853   // FIXME: calculate space needed for "fi"
854   // and reserve as first task (then trigger
855   // "do_upload" from that continuation)!
856   ret->upload_task 
857     = GNUNET_SCHEDULER_add_delayed (h->sched,
858                                     GNUNET_NO,
859                                     GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
860                                     GNUNET_SCHEDULER_NO_TASK,
861                                     GNUNET_TIME_UNIT_ZERO,
862                                     &do_upload,
863                                     ret);
864   return ret;
865 }
866
867
868 /**
869  * Signal the FS's progress function that we are stopping
870  * an upload.
871  *
872  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
873  * @param fi the entry in the publish-structure
874  * @param length length of the file or directory
875  * @param meta metadata for the file or directory (can be modified)
876  * @param uri pointer to the keywords that will be used for this entry (can be modified)
877  * @param anonymity pointer to selected anonymity level (can be modified)
878  * @param priority pointer to selected priority (can be modified)
879  * @param expirationTime pointer to selected expiration time (can be modified)
880  * @param client_info pointer to client context set upon creation (can be modified)
881  * @return GNUNET_OK to continue (always)
882  */
883 static int
884 fip_signal_stop(void *cls,
885                 struct GNUNET_FS_FileInformation *fi,
886                 uint64_t length,
887                 struct GNUNET_CONTAINER_MetaData *meta,
888                 struct GNUNET_FS_Uri **uri,
889                 unsigned int *anonymity,
890                 unsigned int *priority,
891                 struct GNUNET_TIME_Absolute *expirationTime,
892                 void **client_info)
893 {
894   struct GNUNET_FS_PublishContext*sc = cls;
895   struct GNUNET_FS_ProgressInfo pi;
896
897   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
898   make_publish_status (&pi, sc, fi);
899   GNUNET_break (NULL ==
900                 sc->h->upcb (sc->h->upcb_cls,
901                              &pi));
902   *client_info = NULL;
903   return GNUNET_OK;
904 }
905
906
907 /**
908  * Stop an upload.  Will abort incomplete uploads (but 
909  * not remove blocks that have already been publishd) or
910  * simply clean up the state for completed uploads.
911  *
912  * @param sc context for the upload to stop
913  */
914 void 
915 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
916 {
917   if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
918     GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
919   // FIXME: remove from persistence DB (?) --- think more about
920   //        shutdown / persistent-resume APIs!!!
921   GNUNET_FS_file_information_inspect (sc->fi,
922                                       &fip_signal_stop,
923                                       sc);
924   if (GNUNET_YES == sc->in_network_wait)
925     {
926       sc->in_network_wait = GNUNET_SYSERR;
927       return;
928     }
929   publish_cleanup (sc);
930 }
931
932
933 /**
934  * Context for the KSK publication.
935  */
936 struct PublishKskContext
937 {
938
939   /**
940    * Keywords to use.
941    */
942   struct GNUNET_FS_Uri *ksk_uri;
943
944   /**
945    * Global FS context.
946    */
947   struct GNUNET_FS_Handle *h;
948
949   /**
950    * The master block that we are sending
951    * (in plaintext), has "mdsize+slen" more
952    * bytes than the struct would suggest.
953    */
954   struct KBlock *kb;
955
956   /**
957    * Buffer of the same size as "kb" for
958    * the encrypted version.
959    */ 
960   struct KBlock *cpy;
961
962   /**
963    * Handle to the datastore, NULL if we are just
964    * simulating.
965    */
966   struct GNUNET_DATASTORE_Handle *dsh;
967
968   /**
969    * Function to call once we're done.
970    */
971   GNUNET_FS_PublishContinuation cont;
972
973   /**
974    * Closure for cont.
975    */ 
976   void *cont_cls;
977
978   /**
979    * When should the KBlocks expire?
980    */
981   struct GNUNET_TIME_Absolute expirationTime;
982
983   /**
984    * Size of the serialized metadata.
985    */
986   ssize_t mdsize;
987
988   /**
989    * Size of the (CHK) URI as a string.
990    */
991   size_t slen;
992
993   /**
994    * Keyword that we are currently processing.
995    */
996   unsigned int i;
997
998   /**
999    * Anonymity level for the KBlocks.
1000    */
1001   unsigned int anonymity;
1002
1003   /**
1004    * Priority for the KBlocks.
1005    */
1006   unsigned int priority;
1007 };
1008
1009
1010 /**
1011  * Continuation of "GNUNET_FS_publish_ksk" that performs
1012  * the actual publishing operation (iterating over all
1013  * of the keywords).
1014  *
1015  * @param cls closure of type "struct PublishKskContext*"
1016  * @param tc unused
1017  */
1018 static void
1019 publish_ksk_cont (void *cls,
1020                   const struct GNUNET_SCHEDULER_TaskContext *tc);
1021
1022
1023 /**
1024  * Function called by the datastore API with
1025  * the result from the PUT request.
1026  *
1027  * @param cls closure of type "struct PublishKskContext*"
1028  * @param success GNUNET_OK on success
1029  * @param msg error message (or NULL)
1030  */
1031 static void
1032 kb_put_cont (void *cls,
1033              int success,
1034              const char *msg)
1035 {
1036   struct PublishKskContext *pkc = cls;
1037
1038   if (GNUNET_OK != success)
1039     {
1040       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1041       GNUNET_free (pkc->cpy);
1042       GNUNET_free (pkc->kb);
1043       pkc->cont (pkc->cont_cls,
1044                  NULL,
1045                  msg);
1046       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1047       GNUNET_free (pkc);
1048       return;
1049     }
1050   GNUNET_SCHEDULER_add_continuation (pkc->h->sched,
1051                                      GNUNET_NO,
1052                                      &publish_ksk_cont,
1053                                      pkc,
1054                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1055 }
1056
1057
1058 /**
1059  * Continuation of "GNUNET_FS_publish_ksk" that performs
1060  * the actual publishing operation (iterating over all
1061  * of the keywords).
1062  *
1063  * @param cls closure of type "struct PublishKskContext*"
1064  * @param tc unused
1065  */
1066 static void
1067 publish_ksk_cont (void *cls,
1068                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1069 {
1070   struct PublishKskContext *pkc = cls;
1071   const char *keyword;
1072   GNUNET_HashCode key;
1073   GNUNET_HashCode query;
1074   struct GNUNET_CRYPTO_AesSessionKey skey;
1075   struct GNUNET_CRYPTO_AesInitializationVector iv;
1076   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1077
1078
1079   if ( (pkc->i == pkc->ksk_uri->data.ksk.keywordCount) ||
1080        (NULL == pkc->dsh) )
1081     {
1082       if (NULL != pkc->dsh)
1083         GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1084       GNUNET_free (pkc->cpy);
1085       GNUNET_free (pkc->kb);
1086       pkc->cont (pkc->cont_cls,
1087                  pkc->ksk_uri,
1088                  NULL);
1089       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1090       GNUNET_free (pkc);
1091       return;
1092     }
1093   keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
1094   /* first character of keyword indicates if it is
1095      mandatory or not -- ignore for hashing */
1096   GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
1097   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
1098   GNUNET_CRYPTO_aes_encrypt (&pkc->kb[1],
1099                              pkc->slen + pkc->mdsize,
1100                              &skey,
1101                              &iv,
1102                              &pkc->cpy[1]);
1103   pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
1104   GNUNET_CRYPTO_rsa_key_get_public (pk, &pkc->cpy->keyspace);
1105   GNUNET_CRYPTO_hash (&pkc->cpy->keyspace,
1106                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1107                       &query);
1108   GNUNET_assert (GNUNET_OK == 
1109                  GNUNET_CRYPTO_rsa_sign (pk,
1110                                          &pkc->cpy->purpose,
1111                                          &pkc->cpy->signature));
1112   GNUNET_CRYPTO_rsa_key_free (pk);
1113   GNUNET_DATASTORE_put (pkc->dsh,
1114                         0,
1115                         &query,
1116                         pkc->mdsize + 
1117                         sizeof (struct KBlock) + 
1118                         pkc->slen,
1119                         pkc->cpy,
1120                         GNUNET_DATASTORE_BLOCKTYPE_KBLOCK, 
1121                         pkc->priority,
1122                         pkc->anonymity,
1123                         pkc->expirationTime,
1124                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1125                         &kb_put_cont,
1126                         pkc);
1127 }
1128
1129
1130 /**
1131  * Publish a CHK under various keywords on GNUnet.
1132  *
1133  * @param h handle to the file sharing subsystem
1134  * @param ksk_uri keywords to use
1135  * @param meta metadata to use
1136  * @param uri URI to refer to in the KBlock
1137  * @param expirationTime when the KBlock expires
1138  * @param anonymity anonymity level for the KBlock
1139  * @param priority priority for the KBlock
1140  * @param options publication options
1141  * @param cont continuation
1142  * @param cont_cls closure for cont
1143  */
1144 void
1145 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
1146                        struct GNUNET_FS_Uri *ksk_uri,
1147                        struct GNUNET_CONTAINER_MetaData *meta,
1148                        struct GNUNET_FS_Uri *uri,
1149                        struct GNUNET_TIME_Absolute expirationTime,
1150                        unsigned int anonymity,
1151                        unsigned int priority,
1152                        enum GNUNET_FS_PublishOptions options,
1153                        GNUNET_FS_PublishContinuation cont,
1154                        void *cont_cls)
1155 {
1156   struct PublishKskContext *pkc;
1157   char *uris;
1158   size_t size;
1159   char *kbe;
1160
1161   pkc = GNUNET_malloc (sizeof (struct PublishKskContext));
1162   pkc->h = h;
1163   pkc->expirationTime = expirationTime;
1164   pkc->anonymity = anonymity;
1165   pkc->priority = priority;
1166   pkc->cont = cont;
1167   pkc->cont_cls = cont_cls;
1168   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1169     {
1170       pkc->dsh = GNUNET_DATASTORE_connect (h->cfg,
1171                                            h->sched);
1172       if (pkc->dsh == NULL)
1173         {
1174           cont (cont_cls, NULL, _("Could not connect to datastore."));
1175           GNUNET_free (pkc);
1176           return;
1177         }
1178     }
1179   pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta,
1180                                                                 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1181   GNUNET_assert (pkc->mdsize >= 0);
1182   uris = GNUNET_FS_uri_to_string (uri);
1183   pkc->slen = strlen (uris) + 1;
1184   size = pkc->mdsize + sizeof (struct KBlock) + pkc->slen;
1185   if (size > MAX_KBLOCK_SIZE)
1186     {
1187       size = MAX_KBLOCK_SIZE;
1188       pkc->mdsize = size - sizeof (struct KBlock) - pkc->slen;
1189     }
1190   pkc->kb = GNUNET_malloc (size);
1191   kbe = (char *) &pkc->kb[1];
1192   memcpy (kbe, uris, pkc->slen);
1193   GNUNET_free (uris);
1194   pkc->mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
1195                                                       &kbe[pkc->slen],
1196                                                       pkc->mdsize,
1197                                                       GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1198   if (pkc->mdsize == -1)
1199     {
1200       GNUNET_break (0);
1201       GNUNET_free (uris);
1202       GNUNET_free (pkc->kb);
1203       if (pkc->dsh != NULL)
1204         GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1205       cont (cont_cls, NULL, _("Internal error."));
1206       GNUNET_free (pkc);
1207       return;
1208     }
1209   size = sizeof (struct KBlock) + pkc->slen + pkc->mdsize;
1210
1211   pkc->cpy = GNUNET_malloc (size);
1212   pkc->cpy->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + 
1213                                   sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1214                                   pkc->mdsize + 
1215                                   pkc->slen);
1216   pkc->cpy->purpose.purpose = htonl(GNUNET_SIGNATURE_PURPOSE_FS_KBLOCK);
1217   pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
1218   GNUNET_SCHEDULER_add_continuation (h->sched,
1219                                      GNUNET_NO,
1220                                      &publish_ksk_cont,
1221                                      pkc,
1222                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1223 }
1224
1225
1226 /**
1227  * Context for the SKS publication.
1228  */
1229 struct PublishSksContext
1230 {
1231
1232   /**
1233    * Global FS context.
1234    */
1235   struct GNUNET_FS_Uri *uri;
1236
1237   /**
1238    * Handle to the datastore.
1239    */
1240   struct GNUNET_DATASTORE_Handle *dsh;
1241
1242   /**
1243    * Function to call once we're done.
1244    */
1245   GNUNET_FS_PublishContinuation cont;
1246
1247   /**
1248    * Closure for cont.
1249    */ 
1250   void *cont_cls;
1251
1252 };
1253
1254
1255 /**
1256  * Function called by the datastore API with
1257  * the result from the PUT (SBlock) request.
1258  *
1259  * @param cls closure of type "struct PublishSksContext*"
1260  * @param success GNUNET_OK on success
1261  * @param msg error message (or NULL)
1262  */
1263 static void
1264 sb_put_cont (void *cls,
1265              int success,
1266              const char *msg)
1267 {
1268   struct PublishSksContext *psc = cls;
1269
1270   if (NULL != psc->dsh)
1271     GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO);
1272   if (GNUNET_OK != success)
1273     psc->cont (psc->cont_cls,
1274                NULL,
1275                msg);
1276   else
1277     psc->cont (psc->cont_cls,
1278                psc->uri,
1279                NULL);
1280   GNUNET_FS_uri_destroy (psc->uri);
1281   GNUNET_free (psc);
1282 }
1283
1284
1285 /**
1286  * Publish an SBlock on GNUnet.
1287  *
1288  * @param h handle to the file sharing subsystem
1289  * @param namespace namespace to publish in
1290  * @param identifier identifier to use
1291  * @param update update identifier to use
1292  * @param meta metadata to use
1293  * @param uri URI to refer to in the SBlock
1294  * @param expirationTime when the SBlock expires
1295  * @param anonymity anonymity level for the SBlock
1296  * @param priority priority for the SBlock
1297  * @param options publication options
1298  * @param cont continuation
1299  * @param cont_cls closure for cont
1300  */
1301 void
1302 GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
1303                        struct GNUNET_FS_Namespace *namespace,
1304                        const char *identifier,
1305                        const char *update,
1306                        struct GNUNET_CONTAINER_MetaData *meta,
1307                        struct GNUNET_FS_Uri *uri,
1308                        struct GNUNET_TIME_Absolute expirationTime,
1309                        unsigned int anonymity,
1310                        unsigned int priority,
1311                        enum GNUNET_FS_PublishOptions options,
1312                        GNUNET_FS_PublishContinuation cont,
1313                        void *cont_cls)
1314 {
1315   struct PublishSksContext *psc;
1316   struct GNUNET_CRYPTO_AesSessionKey sk;
1317   struct GNUNET_CRYPTO_AesInitializationVector iv;
1318   struct GNUNET_FS_Uri *sks_uri;
1319   char *uris;
1320   size_t size;
1321   size_t slen;
1322   size_t nidlen;
1323   size_t idlen;
1324   ssize_t mdsize;
1325   struct SBlock *sb;
1326   struct SBlock *sb_enc;
1327   char *dest;
1328   GNUNET_HashCode key;           /* hash of thisId = key */
1329   GNUNET_HashCode id;          /* hash of hc = identifier */
1330
1331   uris = GNUNET_FS_uri_to_string (uri);
1332   slen = strlen (uris) + 1;
1333   idlen = strlen (identifier);
1334   if (update == NULL)
1335     update = "";
1336   nidlen = strlen (update) + 1;
1337   mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta, 
1338                                                            GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1339
1340   size = sizeof (struct SBlock) + slen + nidlen + mdsize;
1341   if (size > MAX_SBLOCK_SIZE)
1342     {
1343       size = MAX_SBLOCK_SIZE;
1344       mdsize = size - (sizeof (struct SBlock) + slen + nidlen);
1345     }
1346   sb = GNUNET_malloc (sizeof (struct SBlock) + size);
1347   dest = (char *) &sb[1];
1348   memcpy (dest, update, nidlen);
1349   dest += nidlen;
1350   memcpy (dest, uris, slen);
1351   dest += slen;
1352   mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
1353                                                  dest,
1354                                                  mdsize, 
1355                                                  GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1356   if (mdsize == -1)
1357     {
1358       GNUNET_break (0);
1359       GNUNET_free (uris);
1360       GNUNET_free (sb);
1361       cont (cont_cls,
1362             NULL,
1363             _("Internal error."));
1364       return;
1365     }
1366   size = sizeof (struct SBlock) + mdsize + slen + nidlen;
1367   sb_enc = GNUNET_malloc (sizeof (struct SBlock) + size);
1368   GNUNET_CRYPTO_hash (identifier, idlen, &key);
1369   GNUNET_CRYPTO_hash (&key, sizeof (GNUNET_HashCode), &id);
1370   sks_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1371   sks_uri->type = sks;
1372   GNUNET_CRYPTO_rsa_key_get_public (namespace->key, &sb_enc->subspace);
1373   GNUNET_CRYPTO_hash (&sb_enc->subspace,
1374                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1375                       &sks_uri->data.sks.namespace);
1376   sks_uri->data.sks.identifier = GNUNET_strdup (identifier);
1377   GNUNET_CRYPTO_hash_xor (&id, 
1378                           &sks_uri->data.sks.namespace, 
1379                           &sb_enc->identifier);
1380   GNUNET_CRYPTO_hash_to_aes_key (&key, &sk, &iv);
1381   GNUNET_CRYPTO_aes_encrypt (&sb[1],
1382                              size - sizeof (struct SBlock),
1383                              &sk,
1384                              &iv,
1385                              &sb_enc[1]);
1386   GNUNET_free (sb);
1387   sb_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_SBLOCK);
1388   sb_enc->purpose.size = htonl(slen + mdsize + nidlen
1389                                + sizeof(struct SBlock)
1390                                - sizeof(struct GNUNET_CRYPTO_RsaSignature));
1391   GNUNET_assert (GNUNET_OK == 
1392                  GNUNET_CRYPTO_rsa_sign (namespace->key,
1393                                          &sb_enc->purpose,
1394                                          &sb_enc->signature));
1395   psc = GNUNET_malloc (sizeof(struct PublishSksContext));
1396   psc->uri = sks_uri;
1397   psc->cont = cont;
1398   psc->cont_cls = cont_cls;
1399   if (0 != (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1400     {
1401       GNUNET_free (sb_enc);
1402       sb_put_cont (psc,
1403                    GNUNET_OK,
1404                    NULL);
1405       return;
1406     }
1407   psc->dsh = GNUNET_DATASTORE_connect (h->cfg, h->sched);
1408   if (NULL == psc->dsh)
1409     {
1410       GNUNET_free (sb_enc);
1411       sb_put_cont (psc,
1412                    GNUNET_NO,
1413                    _("Failed to connect to datastore."));
1414       return;
1415     }
1416
1417   GNUNET_DATASTORE_put (psc->dsh,
1418                         0,
1419                         &sb->identifier,
1420                         size,
1421                         sb_enc,
1422                         GNUNET_DATASTORE_BLOCKTYPE_SBLOCK, 
1423                         priority,
1424                         anonymity,
1425                         expirationTime,
1426                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1427                         &sb_put_cont,
1428                         psc);
1429   GNUNET_free (sb_enc);
1430 }
1431
1432
1433 /* end of fs_publish.c */