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