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