(no commit message)
[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 3, 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   uint64_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 = GNUNET_htonll (dev);
813       ism->inode = GNUNET_htonll(ino);
814     }
815 #if DEBUG_PUBLISH
816   else
817     {
818       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819                   _("Failed to get file identifiers for `%s'\n"),
820                   p->filename);
821     }
822 #endif
823   ism->file_id = *res;
824   memcpy (&ism[1],
825           fn,
826           slen);
827   GNUNET_free (fn);
828   sc->client = client;
829   GNUNET_break (GNUNET_YES ==
830                 GNUNET_CLIENT_transmit_and_get_response (client,
831                                                          &ism->header,
832                                                          GNUNET_TIME_UNIT_FOREVER_REL,
833                                                          GNUNET_YES,
834                                                          &process_index_start_response,
835                                                          sc));
836   GNUNET_free (ism);
837 }
838
839
840 /**
841  * Main function that performs the upload.
842  *
843  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
844  * @param tc task context
845  */
846 void
847 GNUNET_FS_publish_main_ (void *cls,
848                          const struct GNUNET_SCHEDULER_TaskContext *tc)
849 {
850   struct GNUNET_FS_PublishContext *pc = cls;
851   struct GNUNET_FS_ProgressInfo pi;
852   struct GNUNET_FS_FileInformation *p;
853   char *fn;
854
855   pc->upload_task = GNUNET_SCHEDULER_NO_TASK;  
856   p = pc->fi_pos;
857   if (NULL == p)
858     {
859 #if DEBUG_PUBLISH
860       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
861                   "Upload complete, now publishing SKS and KSK blocks.\n");
862 #endif
863       /* upload of entire hierarchy complete,
864          publish namespace entries */
865       GNUNET_FS_publish_sync_ (pc);
866       publish_sblock (pc);
867       return;
868     }
869   /* find starting position */
870   while ( (p->is_directory) &&
871           (NULL != p->data.dir.entries) &&
872           (NULL == p->emsg) &&
873           (NULL == p->data.dir.entries->chk_uri) )
874     {
875       p = p->data.dir.entries;
876       pc->fi_pos = p;
877       GNUNET_FS_publish_sync_ (pc);
878     }
879   /* abort on error */
880   if (NULL != p->emsg)
881     {
882 #if DEBUG_PUBLISH
883       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
884                   "Error uploading: %s\n",
885                   p->emsg);
886 #endif
887       /* error with current file, abort all
888          related files as well! */
889       while (NULL != p->dir)
890         {
891           fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
892                                                        EXTRACTOR_METATYPE_FILENAME);
893           p = p->dir;
894           if (fn != NULL)
895             {
896               GNUNET_asprintf (&p->emsg, 
897                                _("Recursive upload failed at `%s': %s"),
898                                fn,
899                                p->emsg);
900               GNUNET_free (fn);
901             }
902           else
903             {
904               GNUNET_asprintf (&p->emsg, 
905                                _("Recursive upload failed: %s"),
906                                p->emsg);              
907             }
908           pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
909           pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
910           pi.value.publish.specifics.error.message = p->emsg;
911           p->client_info = GNUNET_FS_publish_make_status_ (&pi, pc, p, 0);
912         }
913       pc->all_done = GNUNET_YES;
914       GNUNET_FS_publish_sync_ (pc);
915       return;
916     }
917   /* handle completion */
918   if (NULL != p->chk_uri)
919     {
920 #if DEBUG_PUBLISH
921       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
922                   "File upload complete, now publishing KSK blocks.\n");
923 #endif
924       GNUNET_FS_publish_sync_ (pc);
925       /* upload of "p" complete, publish KBlocks! */
926       if (p->keywords != NULL)
927         {
928           GNUNET_FS_publish_ksk (pc->h,
929                                  p->keywords,
930                                  p->meta,
931                                  p->chk_uri,
932                                  p->expirationTime,
933                                  p->anonymity,
934                                  p->priority,
935                                  pc->options,
936                                  &publish_kblocks_cont,
937                                  pc);
938         }
939       else
940         {
941           publish_kblocks_cont (pc,
942                                 p->chk_uri,
943                                 NULL);
944         }
945       return;
946     }
947   if ( (!p->is_directory) &&
948        (p->data.file.do_index) )
949     {
950       if (NULL == p->filename)
951         {
952           p->data.file.do_index = GNUNET_NO;
953           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
954                       _("Can not index file `%s': %s.  Will try to insert instead.\n"),
955                       "<no-name>",
956                       _("needs to be an actual file"));
957           GNUNET_FS_file_information_sync_ (p);
958           publish_content (pc);
959           return;
960         }      
961       if (p->data.file.have_hash)
962         {
963           hash_for_index_cb (pc,
964                              &p->data.file.file_id);
965         }
966       else
967         {
968           p->start_time = GNUNET_TIME_absolute_get ();
969           GNUNET_CRYPTO_hash_file (pc->h->sched,
970                                    GNUNET_SCHEDULER_PRIORITY_IDLE,
971                                    p->filename,
972                                    HASHING_BLOCKSIZE,
973                                    &hash_for_index_cb,
974                                    pc);
975         }
976       return;
977     }
978   publish_content (pc);
979 }
980
981
982 /**
983  * Signal the FS's progress function that we are starting
984  * an upload.
985  *
986  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
987  * @param fi the entry in the publish-structure
988  * @param length length of the file or directory
989  * @param meta metadata for the file or directory (can be modified)
990  * @param uri pointer to the keywords that will be used for this entry (can be modified)
991  * @param anonymity pointer to selected anonymity level (can be modified)
992  * @param priority pointer to selected priority (can be modified)
993  * @param do_index should we index?
994  * @param expirationTime pointer to selected expiration time (can be modified)
995  * @param client_info pointer to client context set upon creation (can be modified)
996  * @return GNUNET_OK to continue (always)
997  */
998 static int
999 fip_signal_start(void *cls,
1000                  struct GNUNET_FS_FileInformation *fi,
1001                  uint64_t length,
1002                  struct GNUNET_CONTAINER_MetaData *meta,
1003                  struct GNUNET_FS_Uri **uri,
1004                  uint32_t *anonymity,
1005                  uint32_t *priority,
1006                  int *do_index,
1007                  struct GNUNET_TIME_Absolute *expirationTime,
1008                  void **client_info)
1009 {
1010   struct GNUNET_FS_PublishContext *sc = cls;
1011   struct GNUNET_FS_ProgressInfo pi;
1012
1013   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
1014   *client_info = GNUNET_FS_publish_make_status_ (&pi, sc, fi, 0);
1015   GNUNET_FS_file_information_sync_ (fi);
1016   return GNUNET_OK;
1017 }
1018
1019
1020 /**
1021  * Signal the FS's progress function that we are suspending
1022  * an upload.
1023  *
1024  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1025  * @param fi the entry in the publish-structure
1026  * @param length length of the file or directory
1027  * @param meta metadata for the file or directory (can be modified)
1028  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1029  * @param anonymity pointer to selected anonymity level (can be modified)
1030  * @param priority pointer to selected priority (can be modified)
1031  * @param do_index should we index?
1032  * @param expirationTime pointer to selected expiration time (can be modified)
1033  * @param client_info pointer to client context set upon creation (can be modified)
1034  * @return GNUNET_OK to continue (always)
1035  */
1036 static int
1037 fip_signal_suspend(void *cls,
1038                    struct GNUNET_FS_FileInformation *fi,
1039                    uint64_t length,
1040                    struct GNUNET_CONTAINER_MetaData *meta,
1041                    struct GNUNET_FS_Uri **uri,
1042                    uint32_t *anonymity,
1043                    uint32_t *priority,
1044                    int *do_index,
1045                    struct GNUNET_TIME_Absolute *expirationTime,
1046                    void **client_info)
1047 {
1048   struct GNUNET_FS_PublishContext*sc = cls;
1049   struct GNUNET_FS_ProgressInfo pi;
1050   uint64_t off;
1051
1052   GNUNET_free_non_null (fi->serialization);
1053   fi->serialization = NULL;    
1054   off = (fi->chk_uri == NULL) ? 0 : length;
1055   pi.status = GNUNET_FS_STATUS_PUBLISH_SUSPEND;
1056   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, sc, fi, off));
1057   *client_info = NULL;
1058   if (NULL != sc->dsh)
1059     {
1060       GNUNET_DATASTORE_disconnect (sc->dsh, GNUNET_NO);
1061       sc->dsh = NULL;
1062     }
1063   return GNUNET_OK;
1064 }
1065
1066
1067 /**
1068  * Create SUSPEND event for the given publish operation
1069  * and then clean up our state (without stop signal).
1070  *
1071  * @param cls the 'struct GNUNET_FS_PublishContext' to signal for
1072  */
1073 void
1074 GNUNET_FS_publish_signal_suspend_ (void *cls)
1075 {
1076   struct GNUNET_FS_PublishContext *pc = cls;
1077
1078   if (GNUNET_SCHEDULER_NO_TASK != pc->upload_task)
1079     {
1080       GNUNET_SCHEDULER_cancel (pc->h->sched, pc->upload_task);
1081       pc->upload_task = GNUNET_SCHEDULER_NO_TASK;
1082     }
1083   GNUNET_FS_file_information_inspect (pc->fi,
1084                                       &fip_signal_suspend,
1085                                       pc);
1086   GNUNET_FS_end_top (pc->h, pc->top);
1087   publish_cleanup (pc, NULL);
1088 }
1089
1090
1091 /**
1092  * Publish a file or directory.
1093  *
1094  * @param h handle to the file sharing subsystem
1095  * @param fi information about the file or directory structure to publish
1096  * @param namespace namespace to publish the file in, NULL for no namespace
1097  * @param nid identifier to use for the publishd content in the namespace
1098  *        (can be NULL, must be NULL if namespace is NULL)
1099  * @param nuid update-identifier that will be used for future updates 
1100  *        (can be NULL, must be NULL if namespace or nid is NULL)
1101  * @param options options for the publication 
1102  * @return context that can be used to control the publish operation
1103  */
1104 struct GNUNET_FS_PublishContext *
1105 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
1106                          struct GNUNET_FS_FileInformation *fi,
1107                          struct GNUNET_FS_Namespace *namespace,
1108                          const char *nid,
1109                          const char *nuid,
1110                          enum GNUNET_FS_PublishOptions options)
1111 {
1112   struct GNUNET_FS_PublishContext *ret;
1113   struct GNUNET_DATASTORE_Handle *dsh;
1114
1115   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1116     {
1117       dsh = GNUNET_DATASTORE_connect (h->cfg,
1118                                       h->sched);
1119       if (NULL == dsh)
1120         return NULL;
1121     }
1122   else
1123     {
1124       dsh = NULL;
1125     }
1126   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
1127   ret->dsh = dsh;
1128   ret->h = h;
1129   ret->fi = fi;
1130   ret->namespace = namespace;
1131   if (namespace != NULL)
1132     {
1133       namespace->rc++;
1134       GNUNET_assert (NULL != nid);
1135       ret->nid = GNUNET_strdup (nid);
1136       if (NULL != nuid)
1137         ret->nuid = GNUNET_strdup (nuid);
1138     }
1139   /* signal start */
1140   GNUNET_FS_file_information_inspect (ret->fi,
1141                                       &fip_signal_start,
1142                                       ret);
1143   ret->fi_pos = ret->fi;
1144   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_publish_signal_suspend_, ret);
1145   GNUNET_FS_publish_sync_ (ret);
1146   // FIXME: calculate space needed for "fi"
1147   // and reserve as first task (then trigger
1148   // "publish_main" from that continuation)!
1149   ret->upload_task 
1150     = GNUNET_SCHEDULER_add_with_priority (h->sched,
1151                                           GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
1152                                           &GNUNET_FS_publish_main_,
1153                                           ret);
1154   return ret;
1155 }
1156
1157
1158 /**
1159  * Signal the FS's progress function that we are stopping
1160  * an upload.
1161  *
1162  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
1163  * @param fi the entry in the publish-structure
1164  * @param length length of the file or directory
1165  * @param meta metadata for the file or directory (can be modified)
1166  * @param uri pointer to the keywords that will be used for this entry (can be modified)
1167  * @param anonymity pointer to selected anonymity level (can be modified)
1168  * @param priority pointer to selected priority (can be modified)
1169  * @param do_index should we index?
1170  * @param expirationTime pointer to selected expiration time (can be modified)
1171  * @param client_info pointer to client context set upon creation (can be modified)
1172  * @return GNUNET_OK to continue (always)
1173  */
1174 static int
1175 fip_signal_stop(void *cls,
1176                 struct GNUNET_FS_FileInformation *fi,
1177                 uint64_t length,
1178                 struct GNUNET_CONTAINER_MetaData *meta,
1179                 struct GNUNET_FS_Uri **uri,
1180                 uint32_t *anonymity,
1181                 uint32_t *priority,
1182                 int *do_index,
1183                 struct GNUNET_TIME_Absolute *expirationTime,
1184                 void **client_info)
1185 {
1186   struct GNUNET_FS_PublishContext*sc = cls;
1187   struct GNUNET_FS_ProgressInfo pi;
1188   uint64_t off;
1189
1190   if (fi->serialization != NULL) 
1191     {
1192       GNUNET_FS_remove_sync_file_ (sc->h,
1193                                    GNUNET_FS_SYNC_PATH_FILE_INFO,
1194                                    fi->serialization);
1195       GNUNET_free (fi->serialization);
1196       fi->serialization = NULL;
1197     }
1198   off = (fi->chk_uri == NULL) ? 0 : length;
1199   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
1200   GNUNET_break (NULL == GNUNET_FS_publish_make_status_ (&pi, sc, fi, off));
1201   *client_info = NULL;
1202   return GNUNET_OK;
1203 }
1204
1205
1206 /**
1207  * Stop an upload.  Will abort incomplete uploads (but 
1208  * not remove blocks that have already been publishd) or
1209  * simply clean up the state for completed uploads.
1210  * Must NOT be called from within the event callback!
1211  *
1212  * @param pc context for the upload to stop
1213  */
1214 void 
1215 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *pc)
1216 {
1217   GNUNET_FS_end_top (pc->h, pc->top);
1218   if (NULL != pc->dsh)
1219     {
1220       GNUNET_DATASTORE_disconnect (pc->dsh, GNUNET_NO);
1221       pc->dsh = NULL;
1222     }
1223   if (GNUNET_SCHEDULER_NO_TASK != pc->upload_task)
1224     {
1225       GNUNET_SCHEDULER_cancel (pc->h->sched, pc->upload_task);
1226       pc->upload_task = GNUNET_SCHEDULER_NO_TASK;
1227     }
1228   if (pc->serialization != NULL) 
1229     {
1230       GNUNET_FS_remove_sync_file_ (pc->h, GNUNET_FS_SYNC_PATH_MASTER_PUBLISH, pc->serialization);
1231       GNUNET_free (pc->serialization);
1232       pc->serialization = NULL;
1233     }
1234   GNUNET_FS_file_information_inspect (pc->fi,
1235                                       &fip_signal_stop,
1236                                       pc);
1237   if (GNUNET_YES == pc->in_network_wait)
1238     {
1239       pc->in_network_wait = GNUNET_SYSERR;
1240       return;
1241     }
1242   publish_cleanup (pc, NULL);
1243 }
1244
1245
1246 /**
1247  * Context for the KSK publication.
1248  */
1249 struct PublishKskContext
1250 {
1251
1252   /**
1253    * Keywords to use.
1254    */
1255   struct GNUNET_FS_Uri *ksk_uri;
1256
1257   /**
1258    * Global FS context.
1259    */
1260   struct GNUNET_FS_Handle *h;
1261
1262   /**
1263    * The master block that we are sending
1264    * (in plaintext), has "mdsize+slen" more
1265    * bytes than the struct would suggest.
1266    */
1267   struct KBlock *kb;
1268
1269   /**
1270    * Buffer of the same size as "kb" for
1271    * the encrypted version.
1272    */ 
1273   struct KBlock *cpy;
1274
1275   /**
1276    * Handle to the datastore, NULL if we are just
1277    * simulating.
1278    */
1279   struct GNUNET_DATASTORE_Handle *dsh;
1280
1281   /**
1282    * Function to call once we're done.
1283    */
1284   GNUNET_FS_PublishContinuation cont;
1285
1286   /**
1287    * Closure for cont.
1288    */ 
1289   void *cont_cls;
1290
1291   /**
1292    * When should the KBlocks expire?
1293    */
1294   struct GNUNET_TIME_Absolute expirationTime;
1295
1296   /**
1297    * Size of the serialized metadata.
1298    */
1299   ssize_t mdsize;
1300
1301   /**
1302    * Size of the (CHK) URI as a string.
1303    */
1304   size_t slen;
1305
1306   /**
1307    * Keyword that we are currently processing.
1308    */
1309   unsigned int i;
1310
1311   /**
1312    * Anonymity level for the KBlocks.
1313    */
1314   uint32_t anonymity;
1315
1316   /**
1317    * Priority for the KBlocks.
1318    */
1319   uint32_t priority;
1320 };
1321
1322
1323 /**
1324  * Continuation of "GNUNET_FS_publish_ksk" that performs
1325  * the actual publishing operation (iterating over all
1326  * of the keywords).
1327  *
1328  * @param cls closure of type "struct PublishKskContext*"
1329  * @param tc unused
1330  */
1331 static void
1332 publish_ksk_cont (void *cls,
1333                   const struct GNUNET_SCHEDULER_TaskContext *tc);
1334
1335
1336 /**
1337  * Function called by the datastore API with
1338  * the result from the PUT request.
1339  *
1340  * @param cls closure of type "struct PublishKskContext*"
1341  * @param success GNUNET_OK on success
1342  * @param msg error message (or NULL)
1343  */
1344 static void
1345 kb_put_cont (void *cls,
1346              int success,
1347              const char *msg)
1348 {
1349   struct PublishKskContext *pkc = cls;
1350
1351   if (GNUNET_OK != success)
1352     {
1353       if (NULL != pkc->dsh)
1354         {
1355           GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1356           pkc->dsh = NULL;
1357         }
1358       GNUNET_free (pkc->cpy);
1359       GNUNET_free (pkc->kb);
1360       pkc->cont (pkc->cont_cls,
1361                  NULL,
1362                  msg);
1363       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1364       GNUNET_free (pkc);
1365       return;
1366     }
1367   GNUNET_SCHEDULER_add_continuation (pkc->h->sched,
1368                                      &publish_ksk_cont,
1369                                      pkc,
1370                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1371 }
1372
1373
1374 /**
1375  * Continuation of "GNUNET_FS_publish_ksk" that performs the actual
1376  * publishing operation (iterating over all of the keywords).
1377  *
1378  * @param cls closure of type "struct PublishKskContext*"
1379  * @param tc unused
1380  */
1381 static void
1382 publish_ksk_cont (void *cls,
1383                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1384 {
1385   struct PublishKskContext *pkc = cls;
1386   const char *keyword;
1387   GNUNET_HashCode key;
1388   GNUNET_HashCode query;
1389   struct GNUNET_CRYPTO_AesSessionKey skey;
1390   struct GNUNET_CRYPTO_AesInitializationVector iv;
1391   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1392
1393
1394   if ( (pkc->i == pkc->ksk_uri->data.ksk.keywordCount) ||
1395        (NULL == pkc->dsh) )
1396     {
1397       if (NULL != pkc->dsh)
1398         {
1399           GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1400           pkc->dsh = NULL;
1401         }
1402       GNUNET_free (pkc->cpy);
1403       GNUNET_free (pkc->kb);
1404       pkc->cont (pkc->cont_cls,
1405                  pkc->ksk_uri,
1406                  NULL);
1407       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1408       GNUNET_free (pkc);
1409       return;
1410     }
1411   keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
1412   /* first character of keyword indicates if it is
1413      mandatory or not -- ignore for hashing */
1414   GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
1415   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
1416   GNUNET_CRYPTO_aes_encrypt (&pkc->kb[1],
1417                              pkc->slen + pkc->mdsize,
1418                              &skey,
1419                              &iv,
1420                              &pkc->cpy[1]);
1421   pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
1422   GNUNET_assert (NULL != pk);
1423   GNUNET_CRYPTO_rsa_key_get_public (pk, &pkc->cpy->keyspace);
1424   GNUNET_CRYPTO_hash (&pkc->cpy->keyspace,
1425                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1426                       &query);
1427   GNUNET_assert (GNUNET_OK == 
1428                  GNUNET_CRYPTO_rsa_sign (pk,
1429                                          &pkc->cpy->purpose,
1430                                          &pkc->cpy->signature));
1431   GNUNET_CRYPTO_rsa_key_free (pk);
1432   GNUNET_DATASTORE_put (pkc->dsh,
1433                         0,
1434                         &query,
1435                         pkc->mdsize + 
1436                         sizeof (struct KBlock) + 
1437                         pkc->slen,
1438                         pkc->cpy,
1439                         GNUNET_BLOCK_TYPE_KBLOCK, 
1440                         pkc->priority,
1441                         pkc->anonymity,
1442                         pkc->expirationTime,
1443                         -2, 1,
1444                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1445                         &kb_put_cont,
1446                         pkc);
1447 }
1448
1449
1450 /**
1451  * Publish a CHK under various keywords on GNUnet.
1452  *
1453  * @param h handle to the file sharing subsystem
1454  * @param ksk_uri keywords to use
1455  * @param meta metadata to use
1456  * @param uri URI to refer to in the KBlock
1457  * @param expirationTime when the KBlock expires
1458  * @param anonymity anonymity level for the KBlock
1459  * @param priority priority for the KBlock
1460  * @param options publication options
1461  * @param cont continuation
1462  * @param cont_cls closure for cont
1463  */
1464 void
1465 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
1466                        const struct GNUNET_FS_Uri *ksk_uri,
1467                        const struct GNUNET_CONTAINER_MetaData *meta,
1468                        const struct GNUNET_FS_Uri *uri,
1469                        struct GNUNET_TIME_Absolute expirationTime,
1470                        uint32_t anonymity,
1471                        uint32_t priority,
1472                        enum GNUNET_FS_PublishOptions options,
1473                        GNUNET_FS_PublishContinuation cont,
1474                        void *cont_cls)
1475 {
1476   struct PublishKskContext *pkc;
1477   char *uris;
1478   size_t size;
1479   char *kbe;
1480   char *sptr;
1481
1482   pkc = GNUNET_malloc (sizeof (struct PublishKskContext));
1483   pkc->h = h;
1484   pkc->expirationTime = expirationTime;
1485   pkc->anonymity = anonymity;
1486   pkc->priority = priority;
1487   pkc->cont = cont;
1488   pkc->cont_cls = cont_cls;
1489   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1490     {
1491       pkc->dsh = GNUNET_DATASTORE_connect (h->cfg,
1492                                            h->sched);
1493       if (pkc->dsh == NULL)
1494         {
1495           cont (cont_cls, NULL, _("Could not connect to datastore."));
1496           GNUNET_free (pkc);
1497           return;
1498         }
1499     }
1500   if (meta == NULL)
1501     pkc->mdsize = 0;
1502   else
1503     pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
1504   GNUNET_assert (pkc->mdsize >= 0);
1505   uris = GNUNET_FS_uri_to_string (uri);
1506   pkc->slen = strlen (uris) + 1;
1507   size = pkc->mdsize + sizeof (struct KBlock) + pkc->slen;
1508   if (size > MAX_KBLOCK_SIZE)
1509     {
1510       size = MAX_KBLOCK_SIZE;
1511       pkc->mdsize = size - sizeof (struct KBlock) - pkc->slen;
1512     }
1513   pkc->kb = GNUNET_malloc (size);
1514   kbe = (char *) &pkc->kb[1];
1515   memcpy (kbe, uris, pkc->slen);
1516   GNUNET_free (uris);
1517   sptr = &kbe[pkc->slen];
1518   if (meta != NULL)
1519     pkc->mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
1520                                                         &sptr,
1521                                                         pkc->mdsize,
1522                                                         GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1523   if (pkc->mdsize == -1)
1524     {
1525       GNUNET_break (0);
1526       GNUNET_free (pkc->kb);
1527       if (pkc->dsh != NULL)
1528         {
1529           GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1530           pkc->dsh = NULL;
1531         }
1532       cont (cont_cls, NULL, _("Internal error."));
1533       GNUNET_free (pkc);
1534       return;
1535     }
1536   size = sizeof (struct KBlock) + pkc->slen + pkc->mdsize;
1537
1538   pkc->cpy = GNUNET_malloc (size);
1539   pkc->cpy->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + 
1540                                   sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1541                                   pkc->mdsize + 
1542                                   pkc->slen);
1543   pkc->cpy->purpose.purpose = htonl(GNUNET_SIGNATURE_PURPOSE_FS_KBLOCK);
1544   pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
1545   GNUNET_SCHEDULER_add_continuation (h->sched,
1546                                      &publish_ksk_cont,
1547                                      pkc,
1548                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1549 }
1550
1551
1552 /**
1553  * Context for the SKS publication.
1554  */
1555 struct PublishSksContext
1556 {
1557
1558   /**
1559    * Global FS context.
1560    */
1561   struct GNUNET_FS_Uri *uri;
1562
1563   /**
1564    * Handle to the datastore.
1565    */
1566   struct GNUNET_DATASTORE_Handle *dsh;
1567
1568   /**
1569    * Function to call once we're done.
1570    */
1571   GNUNET_FS_PublishContinuation cont;
1572
1573   /**
1574    * Closure for cont.
1575    */ 
1576   void *cont_cls;
1577
1578 };
1579
1580
1581 /**
1582  * Function called by the datastore API with
1583  * the result from the PUT (SBlock) request.
1584  *
1585  * @param cls closure of type "struct PublishSksContext*"
1586  * @param success GNUNET_OK on success
1587  * @param msg error message (or NULL)
1588  */
1589 static void
1590 sb_put_cont (void *cls,
1591              int success,
1592              const char *msg)
1593 {
1594   struct PublishSksContext *psc = cls;
1595
1596   if (NULL != psc->dsh)
1597     {
1598       GNUNET_DATASTORE_disconnect (psc->dsh, GNUNET_NO);
1599       psc->dsh = NULL;
1600     }
1601   if (GNUNET_OK != success)
1602     psc->cont (psc->cont_cls,
1603                NULL,
1604                msg);
1605   else
1606     psc->cont (psc->cont_cls,
1607                psc->uri,
1608                NULL);
1609   GNUNET_FS_uri_destroy (psc->uri);
1610   GNUNET_free (psc);
1611 }
1612
1613
1614 /**
1615  * Publish an SBlock on GNUnet.
1616  *
1617  * @param h handle to the file sharing subsystem
1618  * @param namespace namespace to publish in
1619  * @param identifier identifier to use
1620  * @param update update identifier to use
1621  * @param meta metadata to use
1622  * @param uri URI to refer to in the SBlock
1623  * @param expirationTime when the SBlock expires
1624  * @param anonymity anonymity level for the SBlock
1625  * @param priority priority for the SBlock
1626  * @param options publication options
1627  * @param cont continuation
1628  * @param cont_cls closure for cont
1629  */
1630 void
1631 GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
1632                        struct GNUNET_FS_Namespace *namespace,
1633                        const char *identifier,
1634                        const char *update,
1635                        const struct GNUNET_CONTAINER_MetaData *meta,
1636                        const struct GNUNET_FS_Uri *uri,
1637                        struct GNUNET_TIME_Absolute expirationTime,
1638                        uint32_t anonymity,
1639                        uint32_t priority,
1640                        enum GNUNET_FS_PublishOptions options,
1641                        GNUNET_FS_PublishContinuation cont,
1642                        void *cont_cls)
1643 {
1644   struct PublishSksContext *psc;
1645   struct GNUNET_CRYPTO_AesSessionKey sk;
1646   struct GNUNET_CRYPTO_AesInitializationVector iv;
1647   struct GNUNET_FS_Uri *sks_uri;
1648   char *uris;
1649   size_t size;
1650   size_t slen;
1651   size_t nidlen;
1652   size_t idlen;
1653   ssize_t mdsize;
1654   struct SBlock *sb;
1655   struct SBlock *sb_enc;
1656   char *dest;
1657   struct GNUNET_CONTAINER_MetaData *mmeta;
1658   GNUNET_HashCode key;         /* hash of thisId = key */
1659   GNUNET_HashCode id;          /* hash of hc = identifier */
1660   GNUNET_HashCode query;       /* id ^ nsid = DB query */
1661
1662   if (NULL == meta)
1663     mmeta = GNUNET_CONTAINER_meta_data_create ();
1664   else
1665     mmeta = GNUNET_CONTAINER_meta_data_duplicate (meta);
1666   uris = GNUNET_FS_uri_to_string (uri);
1667   slen = strlen (uris) + 1;
1668   idlen = strlen (identifier);
1669   if (update == NULL)
1670     update = "";
1671   nidlen = strlen (update) + 1;
1672   mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (mmeta);
1673   size = sizeof (struct SBlock) + slen + nidlen + mdsize;
1674   if (size > MAX_SBLOCK_SIZE)
1675     {
1676       size = MAX_SBLOCK_SIZE;
1677       mdsize = size - (sizeof (struct SBlock) + slen + nidlen);
1678     }
1679   sb = GNUNET_malloc (sizeof (struct SBlock) + size);
1680   dest = (char *) &sb[1];
1681   memcpy (dest, update, nidlen);
1682   dest += nidlen;
1683   memcpy (dest, uris, slen);
1684   GNUNET_free (uris);
1685   dest += slen;
1686   mdsize = GNUNET_CONTAINER_meta_data_serialize (mmeta,
1687                                                  &dest,
1688                                                  mdsize, 
1689                                                  GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1690   GNUNET_CONTAINER_meta_data_destroy (mmeta);
1691   if (mdsize == -1)
1692     {
1693       GNUNET_break (0);
1694       GNUNET_free (sb);
1695       cont (cont_cls,
1696             NULL,
1697             _("Internal error."));
1698       return;
1699     }
1700   size = sizeof (struct SBlock) + mdsize + slen + nidlen;
1701   sb_enc = GNUNET_malloc (size);
1702   GNUNET_CRYPTO_hash (identifier, idlen, &key);
1703   GNUNET_CRYPTO_hash (&key, sizeof (GNUNET_HashCode), &id);
1704   sks_uri = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
1705   sks_uri->type = sks;
1706   GNUNET_CRYPTO_rsa_key_get_public (namespace->key, &sb_enc->subspace);
1707   GNUNET_CRYPTO_hash (&sb_enc->subspace,
1708                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1709                       &sks_uri->data.sks.namespace);
1710   sks_uri->data.sks.identifier = GNUNET_strdup (identifier);
1711   GNUNET_CRYPTO_hash_xor (&id, 
1712                           &sks_uri->data.sks.namespace, 
1713                           &sb_enc->identifier);
1714   GNUNET_CRYPTO_hash_to_aes_key (&key, &sk, &iv);
1715   GNUNET_CRYPTO_aes_encrypt (&sb[1],
1716                              size - sizeof (struct SBlock),
1717                              &sk,
1718                              &iv,
1719                              &sb_enc[1]);
1720   sb_enc->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_SBLOCK);
1721   sb_enc->purpose.size = htonl(slen + mdsize + nidlen
1722                                + sizeof(struct SBlock)
1723                                - sizeof(struct GNUNET_CRYPTO_RsaSignature));
1724   GNUNET_assert (GNUNET_OK == 
1725                  GNUNET_CRYPTO_rsa_sign (namespace->key,
1726                                          &sb_enc->purpose,
1727                                          &sb_enc->signature));
1728   psc = GNUNET_malloc (sizeof(struct PublishSksContext));
1729   psc->uri = sks_uri;
1730   psc->cont = cont;
1731   psc->cont_cls = cont_cls;
1732   if (0 != (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1733     {
1734       GNUNET_free (sb_enc);
1735       GNUNET_free (sb);
1736       sb_put_cont (psc,
1737                    GNUNET_OK,
1738                    NULL);
1739       return;
1740     }
1741   psc->dsh = GNUNET_DATASTORE_connect (h->cfg, h->sched);
1742   if (NULL == psc->dsh)
1743     {
1744       GNUNET_free (sb_enc);
1745       GNUNET_free (sb);
1746       sb_put_cont (psc,
1747                    GNUNET_NO,
1748                    _("Failed to connect to datastore."));
1749       return;
1750     }
1751   GNUNET_CRYPTO_hash_xor (&sks_uri->data.sks.namespace,
1752                           &id,
1753                           &query);  
1754   GNUNET_DATASTORE_put (psc->dsh,
1755                         0,
1756                         &sb_enc->identifier,
1757                         size,
1758                         sb_enc,
1759                         GNUNET_BLOCK_TYPE_SBLOCK, 
1760                         priority,
1761                         anonymity,
1762                         expirationTime,
1763                         -2, 1,
1764                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1765                         &sb_put_cont,
1766                         psc);
1767   GNUNET_free (sb);
1768   GNUNET_free (sb_enc);
1769 }
1770
1771 /* end of fs_publish.c */