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