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