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