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