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