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