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