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