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