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