working on unindex
[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  * 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 = DBLOCK_SIZE;
363   while (fl < flen)
364     {
365       treeDepth++;
366       if (fl * CHK_PER_INODE < fl)
367         {
368           /* integer overflow, this is a HUGE file... */
369           return treeDepth;
370         }
371       fl = fl * 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 = DBLOCK_SIZE; /* number of bytes each CHK at level "i"
396                                   corresponds to */
397   for (i=0;i<height;i++)
398     bds *= 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 = CHK_PER_INODE;
404     }
405   else
406     {
407       /* we were triggered at the end of the file */
408       bds /= 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 = DBLOCK_SIZE; /* number of bytes each CHK at level "i"
435                                   corresponds to */
436   for (i=0;i<height;i++)
437     bds *= CHK_PER_INODE;
438   GNUNET_assert (0 == (offset % bds));
439   ret = offset / bds;
440   return ret % 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[DBLOCK_SIZE];
463   char enc[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   struct OnDemandBlock odb;
474
475   // FIXME: figure out how to share this code
476   // with unindex!
477   size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
478   if (NULL == p->chk_tree)
479     {
480       if (p->is_directory)
481         {
482           db = GNUNET_FS_directory_builder_create (p->meta);
483           dirpos = p->data.dir.entries;
484           while (NULL != dirpos)
485             {
486               if (dirpos->is_directory)
487                 {
488                   raw_data = dirpos->data.dir.dir_data;
489                   dirpos->data.dir.dir_data = NULL;
490                 }
491               else
492                 {
493                   raw_data = NULL;
494                   if ( (dirpos->data.file.file_size < MAX_INLINE_SIZE) &&
495                        (dirpos->data.file.file_size > 0) )
496                     {
497                       raw_data = GNUNET_malloc (dirpos->data.file.file_size);
498                       emsg = NULL;
499                       if (dirpos->data.file.file_size !=
500                           dirpos->data.file.reader (dirpos->data.file.reader_cls,
501                                                     0,
502                                                     dirpos->data.file.file_size,
503                                                     raw_data,
504                                                     &emsg))
505                         {
506                           GNUNET_free_non_null (emsg);
507                           GNUNET_free (raw_data);
508                           raw_data = NULL;
509                         } 
510                     }
511                 }
512       GNUNET_FS_directory_builder_add (db,
513                                                dirpos->chk_uri,
514                                                dirpos->meta,
515                                                raw_data);
516               GNUNET_free_non_null (raw_data);
517               dirpos = dirpos->next;
518             }
519           GNUNET_FS_directory_builder_finish (db,
520                                               &p->data.dir.dir_size,
521                                               &p->data.dir.dir_data);
522           size = p->data.dir.dir_size;
523         }
524       p->chk_tree_depth = compute_depth (size);
525       p->chk_tree = GNUNET_malloc (p->chk_tree_depth * 
526                                    sizeof (struct ContentHashKey) *
527                                    CHK_PER_INODE);
528       p->current_depth = p->chk_tree_depth;
529     }
530   if (p->current_depth == p->chk_tree_depth)
531     {
532       if (p->is_directory)
533         {
534           pt_size = GNUNET_MIN(DBLOCK_SIZE,
535                                p->data.dir.dir_size - p->publish_offset);
536           dd = p->data.dir.dir_data;
537           pt_block = &dd[p->publish_offset];
538         }
539       else
540         {
541           pt_size = GNUNET_MIN(DBLOCK_SIZE,
542                                p->data.file.file_size - p->publish_offset);
543           emsg = NULL;
544           if (pt_size !=
545               p->data.file.reader (p->data.file.reader_cls,
546                                    p->publish_offset,
547                                    pt_size,
548                                    iob,
549                                    &emsg))
550             {
551               GNUNET_asprintf (&p->emsg, 
552                                _("Upload failed: %s"),
553                                emsg);
554               GNUNET_free (emsg);
555               GNUNET_FS_file_information_sync (p);
556               pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
557               make_publish_status (&pi, sc, p);
558               pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
559               pi.value.publish.specifics.error.message = p->emsg;
560               p->client_info
561                 = sc->h->upcb (sc->h->upcb_cls,
562                                &pi);
563               /* continue with main (to propagate error up) */
564               sc->upload_task 
565                 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
566                                                 GNUNET_NO,
567                                                 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
568                                                 GNUNET_SCHEDULER_NO_TASK,
569                                                 GNUNET_TIME_UNIT_ZERO,
570                                                 &do_upload,
571                                                 sc);
572               return;
573             }
574           pt_block = iob;
575         }
576     }
577   else
578     {
579       pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
580                                      p->publish_offset); 
581       pt_block = &p->chk_tree[p->current_depth *
582                               CHK_PER_INODE];
583     }
584   off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
585                             p->publish_offset);
586   mychk = &p->chk_tree[(p->current_depth-1)*CHK_PER_INODE+off];
587   GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
588   GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
589   GNUNET_CRYPTO_aes_encrypt (pt_block,
590                              pt_size,
591                              &sk,
592                              &iv,
593                              enc);
594   // NOTE: this block below is all that really differs
595   // between publish/unindex!  Parameterize & move this code!
596   if (NULL == sc->dsh)
597     {
598       sc->upload_task
599         = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
600                                         GNUNET_NO,
601                                         GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
602                                         GNUNET_SCHEDULER_NO_TASK,
603                                         GNUNET_TIME_UNIT_ZERO,
604                                         &do_upload,
605                                         sc);
606     }
607   else
608     {
609       GNUNET_assert (GNUNET_NO == sc->in_network_wait);
610       sc->in_network_wait = GNUNET_YES;
611       dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
612       dpc_cls->cont = &do_upload;
613       dpc_cls->cont_cls = sc;
614       dpc_cls->p = p;
615       if ( (p->is_directory) &&
616            (p->data.file.do_index) &&
617            (p->current_depth == p->chk_tree_depth) )
618         {
619           odb.offset = p->publish_offset;
620           odb.file_id = p->data.file.file_id;
621           GNUNET_DATASTORE_put (sc->dsh,
622                                 sc->rid,
623                                 &mychk->query,
624                                 sizeof(struct OnDemandBlock),
625                                 &odb,
626                                 GNUNET_DATASTORE_BLOCKTYPE_ONDEMAND,
627                                 p->priority,
628                                 p->anonymity,
629                                 p->expirationTime,
630                                 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
631                                 &ds_put_cont,
632                                 dpc_cls);         
633         }
634       else
635         {
636           GNUNET_DATASTORE_put (sc->dsh,
637                                 sc->rid,
638                                 &mychk->query,
639                                 pt_size,
640                                 enc,
641                                 (p->current_depth == p->chk_tree_depth) 
642                                 ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK 
643                                 : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
644                                 p->priority,
645                                 p->anonymity,
646                                 p->expirationTime,
647                                 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
648                                 &ds_put_cont,
649                                 dpc_cls);
650         }
651     }
652   if (p->current_depth == p->chk_tree_depth)
653     {
654       pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
655       make_publish_status (&pi, sc, p);
656       pi.value.publish.specifics.progress.data = pt_block;
657       pi.value.publish.specifics.progress.offset = p->publish_offset;
658       pi.value.publish.specifics.progress.data_len = pt_size;
659       p->client_info 
660         = sc->h->upcb (sc->h->upcb_cls,
661                        &pi);
662     }
663   GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
664   if (p->current_depth == p->chk_tree_depth) 
665     { 
666       p->publish_offset += pt_size;
667       if ( (p->publish_offset == size) ||
668            (0 == p->publish_offset % (CHK_PER_INODE * DBLOCK_SIZE) ) )
669         p->current_depth--;
670     }
671   else
672     {
673       if ( (off == CHK_PER_INODE) ||
674            (p->publish_offset == size) )
675         p->current_depth--;
676       else
677         p->current_depth = p->chk_tree_depth;
678     }
679   if (0 == p->current_depth)
680     {
681       p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
682       p->chk_uri->type = chk;
683       p->chk_uri->data.chk.chk = p->chk_tree[0];
684       p->chk_uri->data.chk.file_length = size;
685       GNUNET_free (p->chk_tree);
686       p->chk_tree = NULL;
687     }
688 }
689
690
691
692
693 /**
694  * Process the response (or lack thereof) from
695  * the "fs" service to our 'start index' request.
696  *
697  * @param cls closure (of type "struct GNUNET_FS_PublishContext*"_)
698  * @param msg the response we got
699  */
700 static void
701 process_index_start_response (void *cls,
702                               const struct GNUNET_MessageHeader *msg)
703 {
704   struct GNUNET_FS_PublishContext *sc = cls;
705   struct GNUNET_FS_FileInformation *p;
706   const char *emsg;
707   uint16_t msize;
708
709   GNUNET_CLIENT_disconnect (sc->client);
710   sc->client = NULL;
711   p = sc->fi_pos;
712   if (msg == NULL)
713     {
714       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
715                   _("Can not index file `%s': %s.  Will try to insert instead.\n"),
716                   p->data.file.filename,
717                   _("timeout on index-start request to `fs' service"));
718       p->data.file.do_index = GNUNET_NO;
719       publish_content (sc, p);
720       return;
721     }
722   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_INDEX_START_OK)
723     {
724       msize = ntohs (msg->size);
725       emsg = (const char *) &msg[1];
726       if ( (msize <= sizeof (struct GNUNET_MessageHeader)) ||
727            (emsg[msize - sizeof(struct GNUNET_MessageHeader) - 1] != '\0') )
728         emsg = gettext_noop ("unknown error");
729       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
730                   _("Can not index file `%s': %s.  Will try to insert instead.\n"),
731                   p->data.file.filename,
732                   gettext (emsg));
733       p->data.file.do_index = GNUNET_NO;
734       publish_content (sc, p);
735       return;
736     }
737   /* success! continue with indexing */
738   publish_content (sc, p);
739 }
740
741
742 #if LINUX
743 #include <sys/statvfs.h>
744 #endif
745
746 /**
747  * Function called once the hash computation over an
748  * indexed file has completed.
749  *
750  * @param cls closure, our publishing context
751  * @param res resulting hash, NULL on error
752  */
753 static void 
754 hash_for_index_cb (void *cls,
755                    const GNUNET_HashCode *
756                    res)
757 {
758   struct GNUNET_FS_PublishContext *sc = cls;
759   struct GNUNET_FS_FileInformation *p;
760   struct IndexStartMessage *ism;
761   size_t slen;
762   struct GNUNET_CLIENT_Connection *client;
763 #if LINUX
764   struct stat sbuf;
765   struct statvfs fbuf;
766 #endif
767
768   p = sc->fi_pos;
769   if (NULL == res) 
770     {
771       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
772                   _("Can not index file `%s': %s.  Will try to insert instead.\n"),
773                   p->data.file.filename,
774                   _("failed to compute hash"));
775       p->data.file.do_index = GNUNET_NO;
776       publish_content (sc, p);
777       return;
778     }
779   slen = strlen (p->data.file.filename) + 1;
780   if (slen > GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof(struct IndexStartMessage))
781     {
782       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
783                   _("Can not index file `%s': %s.  Will try to insert instead.\n"),
784                   p->data.file.filename,
785                   _("filename too long"));
786       p->data.file.do_index = GNUNET_NO;
787       publish_content (sc, p);
788       return;
789     }
790   client = GNUNET_CLIENT_connect (sc->h->sched,
791                                   "fs",
792                                   sc->h->cfg);
793   if (NULL == client)
794     {
795       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
796                   _("Can not index file `%s': %s.  Will try to insert instead.\n"),
797                   p->data.file.filename,
798                   _("could not connect to `fs' service"));
799       p->data.file.do_index = GNUNET_NO;
800       publish_content (sc, p);
801       return;
802     }
803   p->data.file.file_id = *res;
804   ism = GNUNET_malloc (sizeof(struct IndexStartMessage) +
805                        slen);
806   ism->header.size = htons(sizeof(struct IndexStartMessage) +
807                            slen);
808   ism->header.type = htons(GNUNET_MESSAGE_TYPE_FS_INDEX_START);
809   /* FIXME: activate this on other OSes that
810      support it (or something very similar; make
811      sure to also adjust corresponding code
812      on the service-side) */
813   /* FIXME: the block below should probably be
814      abstracted into a function in the DISK API */
815 #if LINUX
816   if ( (0 == stat(p->data.file.filename,
817                   &sbuf)) &&
818        (0 == statvfs (p->data.file.filename,
819                       &fbuf) ) )
820     {
821       ism->device = htonl ((uint32_t) fbuf.f_fsid);
822       ism->inode = GNUNET_htonll( (uint64_t) sbuf.st_ino);
823     }
824 #endif
825   memcpy (&ism[1],
826           p->data.file.filename,
827           slen);
828   sc->client = client;
829   GNUNET_CLIENT_transmit_and_get_response (client,
830                                            &ism->header,
831                                            GNUNET_TIME_UNIT_FOREVER_REL,
832                                            &process_index_start_response,
833                                            sc);
834   GNUNET_free (ism);
835 }
836
837
838 /**
839  * Main function that performs the upload.
840  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
841  * @param tc task context
842  */
843 static void
844 do_upload (void *cls,
845            const struct GNUNET_SCHEDULER_TaskContext *tc)
846 {
847   struct GNUNET_FS_PublishContext *sc = cls;
848   struct GNUNET_FS_ProgressInfo pi;
849   struct GNUNET_FS_FileInformation *p;
850   char *fn;
851
852   sc->upload_task = GNUNET_SCHEDULER_NO_TASK;  
853   p = sc->fi_pos;
854   if (NULL == p)
855     {
856       /* upload of entire hierarchy complete,
857          publish namespace entries */
858       publish_sblock (sc);
859       return;
860     }
861   /* find starting position */
862   while ( (p->is_directory) &&
863           (NULL != p->data.dir.entries) &&
864           (NULL == p->emsg) &&
865           (NULL == p->data.dir.entries->chk_uri) )
866     {
867       p = p->data.dir.entries;
868       sc->fi_pos = p;
869     }
870   /* abort on error */
871   if (NULL != p->emsg)
872     {
873       /* error with current file, abort all
874          related files as well! */
875       while (NULL != p->dir)
876         {
877           fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
878                                                        EXTRACTOR_FILENAME);
879           p = p->dir;
880           GNUNET_asprintf (&p->emsg, 
881                            _("Recursive upload failed at `%s'"),
882                            fn);
883           GNUNET_free (fn);
884           GNUNET_FS_file_information_sync (p);
885           pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
886           make_publish_status (&pi, sc, p);
887           pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
888           pi.value.publish.specifics.error.message = p->emsg;
889           p->client_info
890             = sc->h->upcb (sc->h->upcb_cls,
891                            &pi);
892         }
893       return;
894     }
895   /* handle completion */
896   if (NULL != p->chk_uri)
897     {
898       /* upload of "p" complete, publish KBlocks! */
899       GNUNET_FS_publish_ksk (sc->h,
900                              p->keywords,
901                              p->meta,
902                              p->chk_uri,
903                              p->expirationTime,
904                              p->anonymity,
905                              p->priority,
906                              sc->options,
907                              &publish_kblocks_cont,
908                              sc);
909       return;
910     }
911   if ( (!p->is_directory) &&
912        (p->data.file.do_index) )
913     {
914       if (NULL == p->data.file.filename)
915         {
916           p->data.file.do_index = GNUNET_NO;
917           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
918                       _("Can not index file `%s': %s.  Will try to insert instead.\n"),
919                       "<no-name>",
920                       _("needs to be an actual file"));
921           publish_content (sc, p);
922           return;
923         }      
924       GNUNET_CRYPTO_hash_file (sc->h->sched,
925                                GNUNET_SCHEDULER_PRIORITY_IDLE,
926                                GNUNET_NO,
927                                p->data.file.filename,
928                                HASHING_BLOCKSIZE,
929                                &hash_for_index_cb,
930                                sc);
931       return;
932     }
933   publish_content (sc, p);
934 }
935
936
937 /**
938  * Signal the FS's progress function that we are starting
939  * an upload.
940  *
941  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
942  * @param fi the entry in the publish-structure
943  * @param length length of the file or directory
944  * @param meta metadata for the file or directory (can be modified)
945  * @param uri pointer to the keywords that will be used for this entry (can be modified)
946  * @param anonymity pointer to selected anonymity level (can be modified)
947  * @param priority pointer to selected priority (can be modified)
948  * @param expirationTime pointer to selected expiration time (can be modified)
949  * @param client_info pointer to client context set upon creation (can be modified)
950  * @return GNUNET_OK to continue (always)
951  */
952 static int
953 fip_signal_start(void *cls,
954                  struct GNUNET_FS_FileInformation *fi,
955                  uint64_t length,
956                  struct GNUNET_CONTAINER_MetaData *meta,
957                  struct GNUNET_FS_Uri **uri,
958                  unsigned int *anonymity,
959                  unsigned int *priority,
960                  struct GNUNET_TIME_Absolute *expirationTime,
961                  void **client_info)
962 {
963   struct GNUNET_FS_PublishContext *sc = cls;
964   struct GNUNET_FS_ProgressInfo pi;
965
966   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
967   make_publish_status (&pi, sc, fi);
968   *client_info = sc->h->upcb (sc->h->upcb_cls,
969                               &pi);
970   return GNUNET_OK;
971 }
972
973
974 /**
975  * Publish a file or directory.
976  *
977  * @param h handle to the file sharing subsystem
978  * @param ctx initial value to use for the '*ctx'
979  *        in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
980  * @param fi information about the file or directory structure to publish
981  * @param namespace namespace to publish the file in, NULL for no namespace
982  * @param nid identifier to use for the publishd content in the namespace
983  *        (can be NULL, must be NULL if namespace is NULL)
984  * @param nuid update-identifier that will be used for future updates 
985  *        (can be NULL, must be NULL if namespace or nid is NULL)
986  * @param options options for the publication 
987  * @return context that can be used to control the publish operation
988  */
989 struct GNUNET_FS_PublishContext *
990 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
991                          void *ctx,
992                          struct GNUNET_FS_FileInformation *fi,
993                          struct GNUNET_FS_Namespace *namespace,
994                          const char *nid,
995                          const char *nuid,
996                          enum GNUNET_FS_PublishOptions options)
997 {
998   struct GNUNET_FS_PublishContext *ret;
999   struct GNUNET_DATASTORE_Handle *dsh;
1000
1001   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1002     {
1003       dsh = GNUNET_DATASTORE_connect (h->cfg,
1004                                       h->sched);
1005       if (NULL == dsh)
1006         return NULL;
1007     }
1008   else
1009     {
1010       dsh = NULL;
1011     }
1012   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
1013   ret->dsh = dsh;
1014   ret->h = h;
1015   ret->client_ctx = ctx;
1016   ret->fi = fi;
1017   ret->namespace = namespace;
1018   if (namespace != NULL)
1019     {
1020       namespace->rc++;
1021       GNUNET_assert (NULL != nid);
1022       ret->nid = GNUNET_strdup (nid);
1023       if (NULL != nuid)
1024         ret->nuid = GNUNET_strdup (nuid);
1025     }
1026   // FIXME: make upload persistent!
1027
1028   /* signal start */
1029   GNUNET_FS_file_information_inspect (ret->fi,
1030                                       &fip_signal_start,
1031                                       ret);
1032   ret->fi_pos = ret->fi;
1033
1034   // FIXME: calculate space needed for "fi"
1035   // and reserve as first task (then trigger
1036   // "do_upload" from that continuation)!
1037   ret->upload_task 
1038     = GNUNET_SCHEDULER_add_delayed (h->sched,
1039                                     GNUNET_NO,
1040                                     GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1041                                     GNUNET_SCHEDULER_NO_TASK,
1042                                     GNUNET_TIME_UNIT_ZERO,
1043                                     &do_upload,
1044                                     ret);
1045   return ret;
1046 }
1047
1048
1049 /**
1050  * Signal the FS's progress function that we are stopping
1051  * an upload.
1052  *
1053  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1054  * @param fi the entry in the publish-structure
1055  * @param length length of the file or directory
1056  * @param meta metadata for the file or directory (can be modified)
1057  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1058  * @param anonymity pointer to selected anonymity level (can be modified)
1059  * @param priority pointer to selected priority (can be modified)
1060  * @param expirationTime pointer to selected expiration time (can be modified)
1061  * @param client_info pointer to client context set upon creation (can be modified)
1062  * @return GNUNET_OK to continue (always)
1063  */
1064 static int
1065 fip_signal_stop(void *cls,
1066                 struct GNUNET_FS_FileInformation *fi,
1067                 uint64_t length,
1068                 struct GNUNET_CONTAINER_MetaData *meta,
1069                 struct GNUNET_FS_Uri **uri,
1070                 unsigned int *anonymity,
1071                 unsigned int *priority,
1072                 struct GNUNET_TIME_Absolute *expirationTime,
1073                 void **client_info)
1074 {
1075   struct GNUNET_FS_PublishContext*sc = cls;
1076   struct GNUNET_FS_ProgressInfo pi;
1077
1078   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1079   make_publish_status (&pi, sc, fi);
1080   GNUNET_break (NULL ==
1081                 sc->h->upcb (sc->h->upcb_cls,
1082                              &pi));
1083   *client_info = NULL;
1084   return GNUNET_OK;
1085 }
1086
1087
1088 /**
1089  * Stop an upload.  Will abort incomplete uploads (but 
1090  * not remove blocks that have already been publishd) or
1091  * simply clean up the state for completed uploads.
1092  *
1093  * @param sc context for the upload to stop
1094  */
1095 void 
1096 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
1097 {
1098   if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
1099     GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
1100   // FIXME: remove from persistence DB (?) --- think more about
1101   //        shutdown / persistent-resume APIs!!!
1102   GNUNET_FS_file_information_inspect (sc->fi,
1103                                       &fip_signal_stop,
1104                                       sc);
1105   if (GNUNET_YES == sc->in_network_wait)
1106     {
1107       sc->in_network_wait = GNUNET_SYSERR;
1108       return;
1109     }
1110   publish_cleanup (sc);
1111 }
1112
1113
1114 /**
1115  * Context for the KSK publication.
1116  */
1117 struct PublishKskContext
1118 {
1119
1120   /**
1121    * Keywords to use.
1122    */
1123   struct GNUNET_FS_Uri *ksk_uri;
1124
1125   /**
1126    * Global FS context.
1127    */
1128   struct GNUNET_FS_Handle *h;
1129
1130   /**
1131    * The master block that we are sending
1132    * (in plaintext), has "mdsize+slen" more
1133    * bytes than the struct would suggest.
1134    */
1135   struct KBlock *kb;
1136
1137   /**
1138    * Buffer of the same size as "kb" for
1139    * the encrypted version.
1140    */ 
1141   struct KBlock *cpy;
1142
1143   /**
1144    * Handle to the datastore, NULL if we are just
1145    * simulating.
1146    */
1147   struct GNUNET_DATASTORE_Handle *dsh;
1148
1149   /**
1150    * Function to call once we're done.
1151    */
1152   GNUNET_FS_PublishContinuation cont;
1153
1154   /**
1155    * Closure for cont.
1156    */ 
1157   void *cont_cls;
1158
1159   /**
1160    * When should the KBlocks expire?
1161    */
1162   struct GNUNET_TIME_Absolute expirationTime;
1163
1164   /**
1165    * Size of the serialized metadata.
1166    */
1167   ssize_t mdsize;
1168
1169   /**
1170    * Size of the (CHK) URI as a string.
1171    */
1172   size_t slen;
1173
1174   /**
1175    * Keyword that we are currently processing.
1176    */
1177   unsigned int i;
1178
1179   /**
1180    * Anonymity level for the KBlocks.
1181    */
1182   unsigned int anonymity;
1183
1184   /**
1185    * Priority for the KBlocks.
1186    */
1187   unsigned int priority;
1188 };
1189
1190
1191 /**
1192  * Continuation of "GNUNET_FS_publish_ksk" that performs
1193  * the actual publishing operation (iterating over all
1194  * of the keywords).
1195  *
1196  * @param cls closure of type "struct PublishKskContext*"
1197  * @param tc unused
1198  */
1199 static void
1200 publish_ksk_cont (void *cls,
1201                   const struct GNUNET_SCHEDULER_TaskContext *tc);
1202
1203
1204 /**
1205  * Function called by the datastore API with
1206  * the result from the PUT request.
1207  *
1208  * @param cls closure of type "struct PublishKskContext*"
1209  * @param success GNUNET_OK on success
1210  * @param msg error message (or NULL)
1211  */
1212 static void
1213 kb_put_cont (void *cls,
1214              int success,
1215              const char *msg)
1216 {
1217   struct PublishKskContext *pkc = cls;
1218
1219   if (GNUNET_OK != success)
1220     {
1221       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1222       GNUNET_free (pkc->cpy);
1223       GNUNET_free (pkc->kb);
1224       pkc->cont (pkc->cont_cls,
1225                  NULL,
1226                  msg);
1227       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1228       GNUNET_free (pkc);
1229       return;
1230     }
1231   GNUNET_SCHEDULER_add_continuation (pkc->h->sched,
1232                                      GNUNET_NO,
1233                                      &publish_ksk_cont,
1234                                      pkc,
1235                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1236 }
1237
1238
1239 /**
1240  * Continuation of "GNUNET_FS_publish_ksk" that performs
1241  * the actual publishing operation (iterating over all
1242  * of the keywords).
1243  *
1244  * @param cls closure of type "struct PublishKskContext*"
1245  * @param tc unused
1246  */
1247 static void
1248 publish_ksk_cont (void *cls,
1249                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1250 {
1251   struct PublishKskContext *pkc = cls;
1252   const char *keyword;
1253   GNUNET_HashCode key;
1254   GNUNET_HashCode query;
1255   struct GNUNET_CRYPTO_AesSessionKey skey;
1256   struct GNUNET_CRYPTO_AesInitializationVector iv;
1257   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1258
1259
1260   if ( (pkc->i == pkc->ksk_uri->data.ksk.keywordCount) ||
1261        (NULL == pkc->dsh) )
1262     {
1263       if (NULL != pkc->dsh)
1264         GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1265       GNUNET_free (pkc->cpy);
1266       GNUNET_free (pkc->kb);
1267       pkc->cont (pkc->cont_cls,
1268                  pkc->ksk_uri,
1269                  NULL);
1270       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1271       GNUNET_free (pkc);
1272       return;
1273     }
1274   keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
1275   /* first character of keyword indicates if it is
1276      mandatory or not -- ignore for hashing */
1277   GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
1278   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
1279   GNUNET_CRYPTO_aes_encrypt (&pkc->kb[1],
1280                              pkc->slen + pkc->mdsize,
1281                              &skey,
1282                              &iv,
1283                              &pkc->cpy[1]);
1284   pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
1285   GNUNET_CRYPTO_rsa_key_get_public (pk, &pkc->cpy->keyspace);
1286   GNUNET_CRYPTO_hash (&pkc->cpy->keyspace,
1287                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1288                       &query);
1289   GNUNET_assert (GNUNET_OK == 
1290                  GNUNET_CRYPTO_rsa_sign (pk,
1291                                          &pkc->cpy->purpose,
1292                                          &pkc->cpy->signature));
1293   GNUNET_CRYPTO_rsa_key_free (pk);
1294   GNUNET_DATASTORE_put (pkc->dsh,
1295                         0,
1296                         &query,
1297                         pkc->mdsize + 
1298                         sizeof (struct KBlock) + 
1299                         pkc->slen,
1300                         pkc->cpy,
1301                         GNUNET_DATASTORE_BLOCKTYPE_KBLOCK, 
1302                         pkc->priority,
1303                         pkc->anonymity,
1304                         pkc->expirationTime,
1305                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1306                         &kb_put_cont,
1307                         pkc);
1308 }
1309
1310
1311 /**
1312  * Publish a CHK under various keywords on GNUnet.
1313  *
1314  * @param h handle to the file sharing subsystem
1315  * @param ksk_uri keywords to use
1316  * @param meta metadata to use
1317  * @param uri URI to refer to in the KBlock
1318  * @param expirationTime when the KBlock expires
1319  * @param anonymity anonymity level for the KBlock
1320  * @param priority priority for the KBlock
1321  * @param options publication options
1322  * @param cont continuation
1323  * @param cont_cls closure for cont
1324  */
1325 void
1326 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
1327                        struct GNUNET_FS_Uri *ksk_uri,
1328                        struct GNUNET_CONTAINER_MetaData *meta,
1329                        struct GNUNET_FS_Uri *uri,
1330                        struct GNUNET_TIME_Absolute expirationTime,
1331                        unsigned int anonymity,
1332                        unsigned int priority,
1333                        enum GNUNET_FS_PublishOptions options,
1334                        GNUNET_FS_PublishContinuation cont,
1335                        void *cont_cls)
1336 {
1337   struct PublishKskContext *pkc;
1338   char *uris;
1339   size_t size;
1340   char *kbe;
1341
1342   pkc = GNUNET_malloc (sizeof (struct PublishKskContext));
1343   pkc->h = h;
1344   pkc->expirationTime = expirationTime;
1345   pkc->anonymity = anonymity;
1346   pkc->priority = priority;
1347   pkc->cont = cont;
1348   pkc->cont_cls = cont_cls;
1349   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1350     {
1351       pkc->dsh = GNUNET_DATASTORE_connect (h->cfg,
1352                                            h->sched);
1353       if (pkc->dsh == NULL)
1354         {
1355           cont (cont_cls, NULL, _("Could not connect to datastore."));
1356           GNUNET_free (pkc);
1357           return;
1358         }
1359     }
1360   pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta,
1361                                                                 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1362   GNUNET_assert (pkc->mdsize >= 0);
1363   uris = GNUNET_FS_uri_to_string (uri);
1364   pkc->slen = strlen (uris) + 1;
1365   size = pkc->mdsize + sizeof (struct KBlock) + pkc->slen;
1366   if (size > MAX_KBLOCK_SIZE)
1367     {
1368       size = MAX_KBLOCK_SIZE;
1369       pkc->mdsize = size - sizeof (struct KBlock) - pkc->slen;
1370     }
1371   pkc->kb = GNUNET_malloc (size);
1372   kbe = (char *) &pkc->kb[1];
1373   memcpy (kbe, uris, pkc->slen);
1374   GNUNET_free (uris);
1375   pkc->mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
1376                                                       &kbe[pkc->slen],
1377                                                       pkc->mdsize,
1378                                                       GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1379   if (pkc->mdsize == -1)
1380     {
1381       GNUNET_break (0);
1382       GNUNET_free (uris);
1383       GNUNET_free (pkc->kb);
1384       if (pkc->dsh != NULL)
1385         GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1386       cont (cont_cls, NULL, _("Internal error."));
1387       GNUNET_free (pkc);
1388       return;
1389     }
1390   size = sizeof (struct KBlock) + pkc->slen + pkc->mdsize;
1391
1392   pkc->cpy = GNUNET_malloc (size);
1393   pkc->cpy->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + 
1394                                   sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1395                                   pkc->mdsize + 
1396                                   pkc->slen);
1397   pkc->cpy->purpose.purpose = htonl(GNUNET_SIGNATURE_PURPOSE_FS_KBLOCK);
1398   pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
1399   GNUNET_SCHEDULER_add_continuation (h->sched,
1400                                      GNUNET_NO,
1401                                      &publish_ksk_cont,
1402                                      pkc,
1403                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1404 }
1405
1406
1407 /**
1408  * Context for the SKS publication.
1409  */
1410 struct PublishSksContext
1411 {
1412
1413   /**
1414    * Global FS context.
1415    */
1416   struct GNUNET_FS_Uri *uri;
1417
1418   /**
1419    * Handle to the datastore.
1420    */
1421   struct GNUNET_DATASTORE_Handle *dsh;
1422
1423   /**
1424    * Function to call once we're done.
1425    */
1426   GNUNET_FS_PublishContinuation cont;
1427
1428   /**
1429    * Closure for cont.
1430    */ 
1431   void *cont_cls;
1432
1433 };
1434
1435
1436 /**
1437  * Function called by the datastore API with
1438  * the result from the PUT (SBlock) request.
1439  *
1440  * @param cls closure of type "struct PublishSksContext*"
1441  * @param success GNUNET_OK on success
1442  * @param msg error message (or NULL)
1443  */
1444 static void
1445 sb_put_cont (void *cls,
1446              int success,
1447              const char *msg)
1448 {
1449   struct PublishSksContext *psc = cls;
1450
1451   if (NULL != psc->dsh)
1452     GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO);
1453   if (GNUNET_OK != success)
1454     psc->cont (psc->cont_cls,
1455                NULL,
1456                msg);
1457   else
1458     psc->cont (psc->cont_cls,
1459                psc->uri,
1460                NULL);
1461   GNUNET_FS_uri_destroy (psc->uri);
1462   GNUNET_free (psc);
1463 }
1464
1465
1466 /**
1467  * Publish an SBlock on GNUnet.
1468  *
1469  * @param h handle to the file sharing subsystem
1470  * @param namespace namespace to publish in
1471  * @param identifier identifier to use
1472  * @param update update identifier to use
1473  * @param meta metadata to use
1474  * @param uri URI to refer to in the SBlock
1475  * @param expirationTime when the SBlock expires
1476  * @param anonymity anonymity level for the SBlock
1477  * @param priority priority for the SBlock
1478  * @param options publication options
1479  * @param cont continuation
1480  * @param cont_cls closure for cont
1481  */
1482 void
1483 GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
1484                        struct GNUNET_FS_Namespace *namespace,
1485                        const char *identifier,
1486                        const char *update,
1487                        struct GNUNET_CONTAINER_MetaData *meta,
1488                        struct GNUNET_FS_Uri *uri,
1489                        struct GNUNET_TIME_Absolute expirationTime,
1490                        unsigned int anonymity,
1491                        unsigned int priority,
1492                        enum GNUNET_FS_PublishOptions options,
1493                        GNUNET_FS_PublishContinuation cont,
1494                        void *cont_cls)
1495 {
1496   struct PublishSksContext *psc;
1497   struct GNUNET_CRYPTO_AesSessionKey sk;
1498   struct GNUNET_CRYPTO_AesInitializationVector iv;
1499   struct GNUNET_FS_Uri *sks_uri;
1500   char *uris;
1501   size_t size;
1502   size_t slen;
1503   size_t nidlen;
1504   size_t idlen;
1505   ssize_t mdsize;
1506   struct SBlock *sb;
1507   struct SBlock *sb_enc;
1508   char *dest;
1509   GNUNET_HashCode key;           /* hash of thisId = key */
1510   GNUNET_HashCode id;          /* hash of hc = identifier */
1511
1512   uris = GNUNET_FS_uri_to_string (uri);
1513   slen = strlen (uris) + 1;
1514   idlen = strlen (identifier);
1515   if (update == NULL)
1516     update = "";
1517   nidlen = strlen (update) + 1;
1518   mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta, 
1519                                                            GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1520
1521   size = sizeof (struct SBlock) + slen + nidlen + mdsize;
1522   if (size > MAX_SBLOCK_SIZE)
1523     {
1524       size = MAX_SBLOCK_SIZE;
1525       mdsize = size - (sizeof (struct SBlock) + slen + nidlen);
1526     }
1527   sb = GNUNET_malloc (sizeof (struct SBlock) + size);
1528   dest = (char *) &sb[1];
1529   memcpy (dest, update, nidlen);
1530   dest += nidlen;
1531   memcpy (dest, uris, slen);
1532   dest += slen;
1533   mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
1534                                                  dest,
1535                                                  mdsize, 
1536                                                  GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1537   if (mdsize == -1)
1538     {
1539       GNUNET_break (0);
1540       GNUNET_free (uris);
1541       GNUNET_free (sb);
1542       cont (cont_cls,
1543             NULL,
1544             _("Internal error."));
1545       return;
1546     }
1547   size = sizeof (struct SBlock) + mdsize + slen + nidlen;
1548   sb_enc = GNUNET_malloc (sizeof (struct SBlock) + size);
1549   GNUNET_CRYPTO_hash (identifier, idlen, &key);
1550   GNUNET_CRYPTO_hash (&key, sizeof (GNUNET_HashCode), &id);
1551   sks_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1552   sks_uri->type = sks;
1553   GNUNET_CRYPTO_rsa_key_get_public (namespace->key, &sb_enc->subspace);
1554   GNUNET_CRYPTO_hash (&sb_enc->subspace,
1555                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1556                       &sks_uri->data.sks.namespace);
1557   sks_uri->data.sks.identifier = GNUNET_strdup (identifier);
1558   GNUNET_CRYPTO_hash_xor (&id, 
1559                           &sks_uri->data.sks.namespace, 
1560                           &sb_enc->identifier);
1561   GNUNET_CRYPTO_hash_to_aes_key (&key, &sk, &iv);
1562   GNUNET_CRYPTO_aes_encrypt (&sb[1],
1563                              size - sizeof (struct SBlock),
1564                              &sk,
1565                              &iv,
1566                              &sb_enc[1]);
1567   GNUNET_free (sb);
1568   sb_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_SBLOCK);
1569   sb_enc->purpose.size = htonl(slen + mdsize + nidlen
1570                                + sizeof(struct SBlock)
1571                                - sizeof(struct GNUNET_CRYPTO_RsaSignature));
1572   GNUNET_assert (GNUNET_OK == 
1573                  GNUNET_CRYPTO_rsa_sign (namespace->key,
1574                                          &sb_enc->purpose,
1575                                          &sb_enc->signature));
1576   psc = GNUNET_malloc (sizeof(struct PublishSksContext));
1577   psc->uri = sks_uri;
1578   psc->cont = cont;
1579   psc->cont_cls = cont_cls;
1580   if (0 != (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1581     {
1582       GNUNET_free (sb_enc);
1583       sb_put_cont (psc,
1584                    GNUNET_OK,
1585                    NULL);
1586       return;
1587     }
1588   psc->dsh = GNUNET_DATASTORE_connect (h->cfg, h->sched);
1589   if (NULL == psc->dsh)
1590     {
1591       GNUNET_free (sb_enc);
1592       sb_put_cont (psc,
1593                    GNUNET_NO,
1594                    _("Failed to connect to datastore."));
1595       return;
1596     }
1597
1598   GNUNET_DATASTORE_put (psc->dsh,
1599                         0,
1600                         &sb->identifier,
1601                         size,
1602                         sb_enc,
1603                         GNUNET_DATASTORE_BLOCKTYPE_SBLOCK, 
1604                         priority,
1605                         anonymity,
1606                         expirationTime,
1607                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1608                         &sb_put_cont,
1609                         psc);
1610   GNUNET_free (sb_enc);
1611 }
1612
1613
1614 /* end of fs_publish.c */