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