kblocks
[oweals/gnunet.git] / src / fs / fs_publish.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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  * - SBlocks
30  * - indexing support
31  * - code-sharing with unindex (can wait)
32  * - persistence support (can wait)
33  * - datastore reservation support (optimization)
34  */
35
36 #include "platform.h"
37 #include "gnunet_constants.h"
38 #include "gnunet_signatures.h"
39 #include "gnunet_util_lib.h"
40 #include "gnunet_fs_service.h"
41 #include "fs.h"
42
43 #define DEBUG_PUBLISH GNUNET_YES
44
45 #define MAX_KBLOCK_SIZE 60000
46
47 /**
48  * Main function that performs the upload.
49  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
50  * @param tc task context
51  */
52 static void
53 do_upload (void *cls,
54            const struct GNUNET_SCHEDULER_TaskContext *tc);
55
56
57 /**
58  * Context for "ds_put_cont".
59  */
60 struct PutContCtx
61 {
62   /**
63    * Current publishing context.
64    */
65   struct GNUNET_FS_PublishContext *sc;
66
67   /**
68    * Specific file with the block.
69    */
70   struct GNUNET_FS_FileInformation *p;
71
72   /**
73    * Function to run next, if any (can be NULL).
74    */
75   GNUNET_SCHEDULER_Task cont;
76
77   /**
78    * Closure for cont.
79    */
80   void *cont_cls;
81 };
82
83
84 /**
85  * Fill in all of the generic fields for 
86  * a publish event.
87  *
88  * @param pc structure to fill in
89  * @param sc overall publishing context
90  * @param p file information for the file being published
91  */
92 static void
93 make_publish_status (struct GNUNET_FS_ProgressInfo *pi,
94                      struct GNUNET_FS_PublishContext *sc,
95                      const struct GNUNET_FS_FileInformation *p)
96 {
97   pi->value.publish.sc = sc;
98   pi->value.publish.fi = p;
99   pi->value.publish.cctx
100     = p->client_info;
101   pi->value.publish.pctx
102     = (NULL == p->dir) ? NULL : p->dir->client_info;
103   pi->value.publish.size
104     = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
105   pi->value.publish.eta 
106     = GNUNET_TIME_calculate_eta (p->start_time,
107                                  p->publish_offset,
108                                  pi->value.publish.size);
109   pi->value.publish.duration = GNUNET_TIME_absolute_get_duration (p->start_time);
110   pi->value.publish.completed = p->publish_offset;
111   pi->value.publish.anonymity = p->anonymity;
112 }
113
114
115 /**
116  * Cleanup the publish context, we're done
117  * with it.
118  *
119  * @param pc struct to clean up after
120  */
121 static void
122 publish_cleanup (struct GNUNET_FS_PublishContext *sc)
123 {
124   GNUNET_FS_file_information_destroy (sc->fi, NULL, NULL);
125   GNUNET_FS_namespace_delete (sc->namespace, GNUNET_NO);
126   GNUNET_free_non_null (sc->nid);  
127   GNUNET_free_non_null (sc->nuid);
128   GNUNET_DATASTORE_disconnect (sc->dsh, GNUNET_NO);
129   GNUNET_free (sc);
130 }
131
132
133 /**
134  * Function called by the datastore API with
135  * the result from the PUT request.
136  *
137  * @param cls our closure
138  * @param success GNUNET_OK on success
139  * @param msg error message (or NULL)
140  */
141 static void
142 ds_put_cont (void *cls,
143              int success,
144              const char *msg)
145 {
146   struct PutContCtx *pcc = cls;
147   struct GNUNET_FS_ProgressInfo pi;
148
149   if (GNUNET_SYSERR == pcc->sc->in_network_wait)
150     {
151       /* we were aborted in the meantime,
152          finish shutdown! */
153       publish_cleanup (pcc->sc);
154       return;
155     }
156   GNUNET_assert (GNUNET_YES == pcc->sc->in_network_wait);
157   pcc->sc->in_network_wait = GNUNET_NO;
158   if (GNUNET_OK != success)
159     {
160       GNUNET_asprintf (&pcc->p->emsg, 
161                        _("Upload failed: %s"),
162                        msg);
163       GNUNET_FS_file_information_sync (pcc->p);
164       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
165       make_publish_status (&pi, pcc->sc, pcc->p);
166       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
167       pi.value.publish.specifics.error.message = pcc->p->emsg;
168       pcc->p->client_info
169         = pcc->sc->h->upcb (pcc->sc->h->upcb_cls,
170                             &pi);
171       return;
172     }
173   GNUNET_FS_file_information_sync (pcc->p);
174   if (NULL != pcc->cont)
175     pcc->sc->upload_task 
176       = GNUNET_SCHEDULER_add_delayed (pcc->sc->h->sched,
177                                       GNUNET_NO,
178                                       GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
179                                       GNUNET_SCHEDULER_NO_TASK,
180                                       GNUNET_TIME_UNIT_ZERO,
181                                       pcc->cont,
182                                       pcc->cont_cls);
183   GNUNET_free (pcc);
184 }
185
186
187 /**
188  * Generate the callback that signals clients
189  * that a file (or directory) has been completely
190  * published.
191  *
192  * @param p the completed upload
193  * @param sc context of the publication
194  */
195 static void 
196 signal_publish_completion (struct GNUNET_FS_FileInformation *p,
197                            struct GNUNET_FS_PublishContext *sc)
198 {
199   struct GNUNET_FS_ProgressInfo pi;
200   
201   pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
202   make_publish_status (&pi, sc, p);
203   pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
204   pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
205   p->client_info
206     = sc->h->upcb (sc->h->upcb_cls,
207                   &pi);
208 }
209
210
211 /**
212  * Generate the callback that signals clients
213  * that a file (or directory) has encountered
214  * a problem during publication.
215  *
216  * @param p the upload that had trouble
217  * @param sc context of the publication
218  * @param emsg error message
219  */
220 static void 
221 signal_publish_error (struct GNUNET_FS_FileInformation *p,
222                       struct GNUNET_FS_PublishContext *sc,
223                       const char *emsg)
224 {
225   struct GNUNET_FS_ProgressInfo pi;
226   
227   p->emsg = GNUNET_strdup (emsg);
228   pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
229   make_publish_status (&pi, sc, p);
230   pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
231   pi.value.publish.specifics.error.message =emsg;
232   p->client_info
233     = sc->h->upcb (sc->h->upcb_cls,
234                   &pi);
235 }
236
237
238 /**
239  * We've finished publishing the SBlock as part of a larger upload.
240  * Check the result and complete the larger upload.
241  *
242  * @param cls the "struct GNUNET_FS_PublishContext*" of the larger upload
243  * @param uri URI of the published SBlock
244  * @param emsg NULL on success, otherwise error message
245  */
246 static void
247 publish_sblocks_cont (void *cls,
248                       const struct GNUNET_FS_Uri *uri,
249                       const char *emsg)
250 {
251   struct GNUNET_FS_PublishContext *sc = cls;
252   if (NULL != emsg)
253     {
254       signal_publish_error (sc->fi,
255                             sc,
256                             emsg);
257       return;
258     }  
259   // FIXME: release the datastore reserve here!
260   signal_publish_completion (sc->fi, sc);
261 }
262
263
264 /**
265  * We are almost done publishing the structure,
266  * add SBlocks (if needed).
267  *
268  * @param sc overall upload data
269  */
270 static void
271 publish_sblock (struct GNUNET_FS_PublishContext *sc)
272 {
273   if (NULL != sc->namespace)
274     GNUNET_FS_publish_sks (sc->h,
275                            sc->namespace,
276                            sc->nid,
277                            sc->nuid,
278                            sc->fi->meta,
279                            sc->fi->chk_uri,
280                            sc->fi->expirationTime,
281                            sc->fi->anonymity,
282                            sc->fi->priority,
283                            sc->options,
284                            &publish_sblocks_cont,
285                            sc);
286   else
287     publish_sblocks_cont (sc, NULL, NULL);
288 }
289
290
291 /**
292  * We've finished publishing a KBlock
293  * as part of a larger upload.  Check
294  * the result and continue the larger
295  * upload.
296  *
297  * @param cls the "struct GNUNET_FS_PublishContext*" of the larger upload
298  * @param uri URI of the published blocks
299  * @param emsg NULL on success, otherwise error message
300  */
301 static void
302 publish_kblocks_cont (void *cls,
303                       const struct GNUNET_FS_Uri *uri,
304                       const char *emsg)
305 {
306   struct GNUNET_FS_PublishContext *sc = cls;
307   struct GNUNET_FS_FileInformation *p = sc->fi_pos;
308
309   if (NULL != emsg)
310     {
311       signal_publish_error (p, sc, emsg);
312       sc->upload_task 
313         = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
314                                         GNUNET_NO,
315                                         GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
316                                         GNUNET_SCHEDULER_NO_TASK,
317                                         GNUNET_TIME_UNIT_ZERO,
318                                         &do_upload,
319                                         sc);
320       return;
321     }
322   GNUNET_FS_file_information_sync (p);
323   if (NULL != p->dir)
324     signal_publish_completion (p, sc);
325   /* move on to next file */
326   if (NULL != p->next)
327     sc->fi_pos = p->next;
328   else
329     sc->fi_pos = p->dir;
330   sc->upload_task 
331     = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
332                                     GNUNET_NO,
333                                     GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
334                                     GNUNET_SCHEDULER_NO_TASK,
335                                     GNUNET_TIME_UNIT_ZERO,
336                                     &do_upload,
337                                     sc);
338 }
339
340
341 /**
342  * Compute the depth of the CHK tree.
343  *
344  * @param flen file length for which to compute the depth
345  * @return depth of the tree
346  */
347 static unsigned int
348 compute_depth (uint64_t flen)
349 {
350   unsigned int treeDepth;
351   uint64_t fl;
352
353   treeDepth = 1;
354   fl = GNUNET_FS_DBLOCK_SIZE;
355   while (fl < flen)
356     {
357       treeDepth++;
358       if (fl * GNUNET_FS_CHK_PER_INODE < fl)
359         {
360           /* integer overflow, this is a HUGE file... */
361           return treeDepth;
362         }
363       fl = fl * GNUNET_FS_CHK_PER_INODE;
364     }
365   return treeDepth;
366 }
367
368
369 /**
370  * Compute the size of the current IBlock.
371  *
372  * @param height height of the IBlock in the tree (aka overall
373  *               number of tree levels minus depth); 0 == DBlock
374  * @param offset current offset in the overall file
375  * @return size of the corresponding IBlock
376  */
377 static uint16_t 
378 compute_iblock_size (unsigned int height,
379                      uint64_t offset)
380 {
381   unsigned int ret;
382   unsigned int i;
383   uint64_t mod;
384   uint64_t bds;
385
386   GNUNET_assert (height > 0);
387   bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
388                                   corresponds to */
389   for (i=0;i<height;i++)
390     bds *= GNUNET_FS_CHK_PER_INODE;
391   mod = offset % bds;
392   if (0 == mod)
393     {
394       /* we were triggered at the end of a full block */
395       ret = GNUNET_FS_CHK_PER_INODE;
396     }
397   else
398     {
399       /* we were triggered at the end of the file */
400       bds /= GNUNET_FS_CHK_PER_INODE;
401       ret = mod / bds;
402       if (0 != mod % bds)
403         ret++; 
404     }
405   return (uint16_t) (ret * sizeof(struct ContentHashKey));
406 }
407
408
409 /**
410  * Compute the offset of the CHK for the
411  * current block in the IBlock above.
412  *
413  * @param height height of the IBlock in the tree (aka overall
414  *               number of tree levels minus depth); 0 == DBlock
415  * @param offset current offset in the overall file
416  * @return (array of CHKs') offset in the above IBlock
417  */
418 static unsigned int
419 compute_chk_offset (unsigned int height,
420                     uint64_t offset)
421 {
422   uint64_t bds;
423   unsigned int ret;
424   unsigned int i;
425
426   bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
427                                   corresponds to */
428   for (i=0;i<height;i++)
429     bds *= GNUNET_FS_CHK_PER_INODE;
430   GNUNET_assert (0 == (offset % bds));
431   ret = offset / bds;
432   return ret % GNUNET_FS_CHK_PER_INODE; 
433 }
434
435
436 /**
437  * We are uploading a file or directory; load (if necessary) the next
438  * block into memory, encrypt it and send it to the FS service.  Then
439  * continue with the main task.
440  *
441  * @param sc overall upload data
442  * @param p specific file or directory for which kblocks
443  *          should be created
444  */
445 static void
446 publish_content (struct GNUNET_FS_PublishContext *sc,
447                  struct GNUNET_FS_FileInformation *p)
448 {
449   struct GNUNET_FS_ProgressInfo pi;
450   struct ContentHashKey *mychk;
451   const void *pt_block;
452   uint16_t pt_size;
453   char *emsg;
454   char iob[GNUNET_FS_DBLOCK_SIZE];
455   char enc[GNUNET_FS_DBLOCK_SIZE];
456   struct GNUNET_CRYPTO_AesSessionKey sk;
457   struct GNUNET_CRYPTO_AesInitializationVector iv;
458   uint64_t size;
459   unsigned int off;
460   struct GNUNET_FS_DirectoryBuilder *db;
461   struct GNUNET_FS_FileInformation *dirpos;
462   void *raw_data;
463   char *dd;
464   struct PutContCtx * dpc_cls;
465
466   // FIXME: figure out how to share this code
467   // with unindex!
468   size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
469   if (NULL == p->chk_tree)
470     {
471       if (p->is_directory)
472         {
473           db = GNUNET_FS_directory_builder_create (p->meta);
474           dirpos = p->data.dir.entries;
475           while (NULL != dirpos)
476             {
477               if (dirpos->is_directory)
478                 {
479                   raw_data = dirpos->data.dir.dir_data;
480                   dirpos->data.dir.dir_data = NULL;
481                 }
482               else
483                 {
484                   raw_data = NULL;
485                   if ( (dirpos->data.file.file_size < GNUNET_FS_MAX_INLINE_SIZE) &&
486                        (dirpos->data.file.file_size > 0) )
487                     {
488                       raw_data = GNUNET_malloc (dirpos->data.file.file_size);
489                       emsg = NULL;
490                       if (dirpos->data.file.file_size !=
491                           dirpos->data.file.reader (dirpos->data.file.reader_cls,
492                                                     0,
493                                                     dirpos->data.file.file_size,
494                                                     raw_data,
495                                                     &emsg))
496                         {
497                           GNUNET_free_non_null (emsg);
498                           GNUNET_free (raw_data);
499                           raw_data = NULL;
500                         } 
501                     }
502                 }
503       GNUNET_FS_directory_builder_add (db,
504                                                dirpos->chk_uri,
505                                                dirpos->meta,
506                                                raw_data);
507               GNUNET_free_non_null (raw_data);
508               dirpos = dirpos->next;
509             }
510           GNUNET_FS_directory_builder_finish (db,
511                                               &p->data.dir.dir_size,
512                                               &p->data.dir.dir_data);
513           size = p->data.dir.dir_size;
514         }
515       p->chk_tree_depth = compute_depth (size);
516       p->chk_tree = GNUNET_malloc (p->chk_tree_depth * 
517                                    sizeof (struct ContentHashKey) *
518                                    GNUNET_FS_CHK_PER_INODE);
519       p->current_depth = p->chk_tree_depth;
520     }
521   if (p->current_depth == p->chk_tree_depth)
522     {
523       if (p->is_directory)
524         {
525           pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
526                                p->data.dir.dir_size - p->publish_offset);
527           dd = p->data.dir.dir_data;
528           pt_block = &dd[p->publish_offset];
529         }
530       else
531         {
532           pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
533                                p->data.file.file_size - p->publish_offset);
534           emsg = NULL;
535           if (pt_size !=
536               p->data.file.reader (p->data.file.reader_cls,
537                                    p->publish_offset,
538                                    pt_size,
539                                    iob,
540                                    &emsg))
541             {
542               GNUNET_asprintf (&p->emsg, 
543                                _("Upload failed: %s"),
544                                emsg);
545               GNUNET_free (emsg);
546               GNUNET_FS_file_information_sync (p);
547               pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
548               make_publish_status (&pi, sc, p);
549               pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
550               pi.value.publish.specifics.error.message = p->emsg;
551               p->client_info
552                 = sc->h->upcb (sc->h->upcb_cls,
553                                &pi);
554               /* continue with main (to propagate error up) */
555               sc->upload_task 
556                 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
557                                                 GNUNET_NO,
558                                                 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
559                                                 GNUNET_SCHEDULER_NO_TASK,
560                                                 GNUNET_TIME_UNIT_ZERO,
561                                                 &do_upload,
562                                                 sc);
563               return;
564             }
565           pt_block = iob;
566         }
567     }
568   else
569     {
570       pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
571                                      p->publish_offset); 
572       pt_block = &p->chk_tree[p->current_depth *
573                               GNUNET_FS_CHK_PER_INODE];
574     }
575   off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
576                             p->publish_offset);
577   mychk = &p->chk_tree[(p->current_depth-1)*GNUNET_FS_CHK_PER_INODE+off];
578   GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
579   GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
580   GNUNET_CRYPTO_aes_encrypt (pt_block,
581                              pt_size,
582                              &sk,
583                              &iv,
584                              enc);
585   // NOTE: this block below is all that really differs
586   // between publish/unindex!  Parameterize & move this code!
587   // FIXME: something around here would need to change
588   // for indexing!
589   if (NULL == sc->dsh)
590     {
591       sc->upload_task
592         = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
593                                         GNUNET_NO,
594                                         GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
595                                         GNUNET_SCHEDULER_NO_TASK,
596                                         GNUNET_TIME_UNIT_ZERO,
597                                         &do_upload,
598                                         sc);
599     }
600   else
601     {
602       GNUNET_assert (GNUNET_NO == sc->in_network_wait);
603       sc->in_network_wait = GNUNET_YES;
604       dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
605       dpc_cls->cont = &do_upload;
606       dpc_cls->cont_cls = sc;
607       dpc_cls->p = p;
608       GNUNET_DATASTORE_put (sc->dsh,
609                             sc->rid,
610                             &mychk->query,
611                             pt_size,
612                             enc,
613                             (p->current_depth == p->chk_tree_depth) 
614                             ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK 
615                             : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
616                             p->priority,
617                             p->anonymity,
618                             p->expirationTime,
619                             GNUNET_CONSTANTS_SERVICE_TIMEOUT,
620                             &ds_put_cont,
621                             dpc_cls);
622     }
623   if (p->current_depth == p->chk_tree_depth)
624     {
625       pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
626       make_publish_status (&pi, sc, p);
627       pi.value.publish.specifics.progress.data = pt_block;
628       pi.value.publish.specifics.progress.offset = p->publish_offset;
629       pi.value.publish.specifics.progress.data_len = pt_size;
630       p->client_info 
631         = sc->h->upcb (sc->h->upcb_cls,
632                        &pi);
633     }
634   GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
635   if (p->current_depth == p->chk_tree_depth) 
636     { 
637       p->publish_offset += pt_size;
638       if ( (p->publish_offset == size) ||
639            (0 == p->publish_offset % (GNUNET_FS_CHK_PER_INODE * GNUNET_FS_DBLOCK_SIZE) ) )
640         p->current_depth--;
641     }
642   else
643     {
644       if ( (off == GNUNET_FS_CHK_PER_INODE) ||
645            (p->publish_offset == size) )
646         p->current_depth--;
647       else
648         p->current_depth = p->chk_tree_depth;
649     }
650   if (0 == p->current_depth)
651     {
652       p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
653       p->chk_uri->type = chk;
654       p->chk_uri->data.chk.chk = p->chk_tree[0];
655       p->chk_uri->data.chk.file_length = size;
656       GNUNET_free (p->chk_tree);
657       p->chk_tree = NULL;
658     }
659 }
660
661
662 /**
663  * Main function that performs the upload.
664  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
665  * @param tc task context
666  */
667 static void
668 do_upload (void *cls,
669            const struct GNUNET_SCHEDULER_TaskContext *tc)
670 {
671   struct GNUNET_FS_PublishContext *sc = cls;
672   struct GNUNET_FS_ProgressInfo pi;
673   struct GNUNET_FS_FileInformation *p;
674   char *fn;
675
676   sc->upload_task = GNUNET_SCHEDULER_NO_TASK;  
677   p = sc->fi_pos;
678   if (NULL == p)
679     {
680       /* upload of entire hierarchy complete,
681          publish namespace entries */
682       publish_sblock (sc);
683       return;
684     }
685   /* find starting position */
686   while ( (p->is_directory) &&
687           (NULL != p->data.dir.entries) &&
688           (NULL == p->emsg) &&
689           (NULL == p->data.dir.entries->chk_uri) )
690     {
691       p = p->data.dir.entries;
692       sc->fi_pos = p;
693     }
694   /* abort on error */
695   if (NULL != p->emsg)
696     {
697       /* error with current file, abort all
698          related files as well! */
699       while (NULL != p->dir)
700         {
701           fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
702                                                        EXTRACTOR_FILENAME);
703           p = p->dir;
704           GNUNET_asprintf (&p->emsg, 
705                            _("Recursive upload failed at `%s'"),
706                            fn);
707           GNUNET_free (fn);
708           GNUNET_FS_file_information_sync (p);
709           pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
710           make_publish_status (&pi, sc, p);
711           pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
712           pi.value.publish.specifics.error.message = p->emsg;
713           p->client_info
714             = sc->h->upcb (sc->h->upcb_cls,
715                            &pi);
716         }
717       return;
718     }
719   /* handle completion */
720   if (NULL != p->chk_uri)
721     {
722       /* upload of "p" complete, publish KBlocks! */
723       GNUNET_FS_publish_ksk (sc->h,
724                              p->keywords,
725                              p->meta,
726                              p->chk_uri,
727                              p->expirationTime,
728                              p->anonymity,
729                              p->priority,
730                              sc->options,
731                              &publish_kblocks_cont,
732                              sc);
733       return;
734     }
735   if ( (!p->is_directory) &&
736        (p->data.file.do_index) )
737     {
738       // FIXME: need to pre-compute hash over
739       // the entire file and ask FS to prepare
740       // for indexing!
741       return;
742     }
743   publish_content (sc, p);
744 }
745
746
747 /**
748  * Signal the FS's progress function that we are starting
749  * an upload.
750  *
751  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
752  * @param fi the entry in the publish-structure
753  * @param length length of the file or directory
754  * @param meta metadata for the file or directory (can be modified)
755  * @param uri pointer to the keywords that will be used for this entry (can be modified)
756  * @param anonymity pointer to selected anonymity level (can be modified)
757  * @param priority pointer to selected priority (can be modified)
758  * @param expirationTime pointer to selected expiration time (can be modified)
759  * @param client_info pointer to client context set upon creation (can be modified)
760  * @return GNUNET_OK to continue (always)
761  */
762 static int
763 fip_signal_start(void *cls,
764                  struct GNUNET_FS_FileInformation *fi,
765                  uint64_t length,
766                  struct GNUNET_CONTAINER_MetaData *meta,
767                  struct GNUNET_FS_Uri **uri,
768                  unsigned int *anonymity,
769                  unsigned int *priority,
770                  struct GNUNET_TIME_Absolute *expirationTime,
771                  void **client_info)
772 {
773   struct GNUNET_FS_PublishContext *sc = cls;
774   struct GNUNET_FS_ProgressInfo pi;
775
776   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
777   make_publish_status (&pi, sc, fi);
778   *client_info = sc->h->upcb (sc->h->upcb_cls,
779                               &pi);
780   return GNUNET_OK;
781 }
782
783
784 /**
785  * Publish a file or directory.
786  *
787  * @param h handle to the file sharing subsystem
788  * @param ctx initial value to use for the '*ctx'
789  *        in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
790  * @param fi information about the file or directory structure to publish
791  * @param namespace namespace to publish the file in, NULL for no namespace
792  * @param nid identifier to use for the publishd content in the namespace
793  *        (can be NULL, must be NULL if namespace is NULL)
794  * @param nuid update-identifier that will be used for future updates 
795  *        (can be NULL, must be NULL if namespace or nid is NULL)
796  * @param options options for the publication 
797  * @return context that can be used to control the publish operation
798  */
799 struct GNUNET_FS_PublishContext *
800 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
801                          void *ctx,
802                          struct GNUNET_FS_FileInformation *fi,
803                          struct GNUNET_FS_Namespace *namespace,
804                          const char *nid,
805                          const char *nuid,
806                          enum GNUNET_FS_PublishOptions options)
807 {
808   struct GNUNET_FS_PublishContext *ret;
809   struct GNUNET_DATASTORE_Handle *dsh;
810
811   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
812     {
813       dsh = GNUNET_DATASTORE_connect (h->cfg,
814                                       h->sched);
815       if (NULL == dsh)
816         return NULL;
817     }
818   else
819     {
820       dsh = NULL;
821     }
822   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
823   ret->dsh = dsh;
824   ret->h = h;
825   ret->client_ctx = ctx;
826   ret->fi = fi;
827   ret->namespace = namespace;
828   if (namespace != NULL)
829     {
830       namespace->rc++;
831       GNUNET_assert (NULL != nid);
832       ret->nid = GNUNET_strdup (nid);
833       if (NULL != nuid)
834         ret->nuid = GNUNET_strdup (nuid);
835     }
836   // FIXME: make upload persistent!
837
838   /* signal start */
839   GNUNET_FS_file_information_inspect (ret->fi,
840                                       &fip_signal_start,
841                                       ret);
842   ret->fi_pos = ret->fi;
843
844   // FIXME: calculate space needed for "fi"
845   // and reserve as first task (then trigger
846   // "do_upload" from that continuation)!
847   ret->upload_task 
848     = GNUNET_SCHEDULER_add_delayed (h->sched,
849                                     GNUNET_NO,
850                                     GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
851                                     GNUNET_SCHEDULER_NO_TASK,
852                                     GNUNET_TIME_UNIT_ZERO,
853                                     &do_upload,
854                                     ret);
855   return ret;
856 }
857
858
859 /**
860  * Signal the FS's progress function that we are stopping
861  * an upload.
862  *
863  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
864  * @param fi the entry in the publish-structure
865  * @param length length of the file or directory
866  * @param meta metadata for the file or directory (can be modified)
867  * @param uri pointer to the keywords that will be used for this entry (can be modified)
868  * @param anonymity pointer to selected anonymity level (can be modified)
869  * @param priority pointer to selected priority (can be modified)
870  * @param expirationTime pointer to selected expiration time (can be modified)
871  * @param client_info pointer to client context set upon creation (can be modified)
872  * @return GNUNET_OK to continue (always)
873  */
874 static int
875 fip_signal_stop(void *cls,
876                 struct GNUNET_FS_FileInformation *fi,
877                 uint64_t length,
878                 struct GNUNET_CONTAINER_MetaData *meta,
879                 struct GNUNET_FS_Uri **uri,
880                 unsigned int *anonymity,
881                 unsigned int *priority,
882                 struct GNUNET_TIME_Absolute *expirationTime,
883                 void **client_info)
884 {
885   struct GNUNET_FS_PublishContext*sc = cls;
886   struct GNUNET_FS_ProgressInfo pi;
887
888   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
889   make_publish_status (&pi, sc, fi);
890   GNUNET_break (NULL ==
891                 sc->h->upcb (sc->h->upcb_cls,
892                              &pi));
893   *client_info = NULL;
894   return GNUNET_OK;
895 }
896
897
898 /**
899  * Stop an upload.  Will abort incomplete uploads (but 
900  * not remove blocks that have already been publishd) or
901  * simply clean up the state for completed uploads.
902  *
903  * @param sc context for the upload to stop
904  */
905 void 
906 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
907 {
908   if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
909     GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
910   // FIXME: remove from persistence DB (?) --- think more about
911   //        shutdown / persistent-resume APIs!!!
912   GNUNET_FS_file_information_inspect (sc->fi,
913                                       &fip_signal_stop,
914                                       sc);
915   if (GNUNET_YES == sc->in_network_wait)
916     {
917       sc->in_network_wait = GNUNET_SYSERR;
918       return;
919     }
920   publish_cleanup (sc);
921 }
922
923
924 /**
925  * Context for the KSK publication.
926  */
927 struct PublishKskContext
928 {
929
930   /**
931    * Keywords to use.
932    */
933   struct GNUNET_FS_Uri *ksk_uri;
934
935   /**
936    * Global FS context.
937    */
938   struct GNUNET_FS_Handle *h;
939
940   /**
941    * The master block that we are sending
942    * (in plaintext), has "mdsize+slen" more
943    * bytes than the struct would suggest.
944    */
945   struct GNUNET_FS_KBlock *kb;
946
947   /**
948    * Buffer of the same size as "kb" for
949    * the encrypted version.
950    */ 
951   struct GNUNET_FS_KBlock *cpy;
952
953   /**
954    * Handle to the datastore, NULL if we are just
955    * simulating.
956    */
957   struct GNUNET_DATASTORE_Handle *dsh;
958
959   /**
960    * Function to call once we're done.
961    */
962   GNUNET_FS_PublishContinuation cont;
963
964   /**
965    * Closure for cont.
966    */ 
967   void *cont_cls;
968
969   /**
970    * When should the KBlocks expire?
971    */
972   struct GNUNET_TIME_Absolute expirationTime;
973
974   /**
975    * Size of the serialized metadata.
976    */
977   ssize_t mdsize;
978
979   /**
980    * Size of the (CHK) URI as a string.
981    */
982   size_t slen;
983
984   /**
985    * Keyword that we are currently processing.
986    */
987   unsigned int i;
988
989   /**
990    * Anonymity level for the KBlocks.
991    */
992   unsigned int anonymity;
993
994   /**
995    * Priority for the KBlocks.
996    */
997   unsigned int priority;
998 };
999
1000
1001 /**
1002  * Continuation of "GNUNET_FS_publish_ksk" that performs
1003  * the actual publishing operation (iterating over all
1004  * of the keywords).
1005  *
1006  * @param cls closure of type "struct PublishKskContext*"
1007  * @param tc unused
1008  */
1009 static void
1010 publish_ksk_cont (void *cls,
1011                   const struct GNUNET_SCHEDULER_TaskContext *tc);
1012
1013
1014 /**
1015  * Function called by the datastore API with
1016  * the result from the PUT request.
1017  *
1018  * @param cls closure of type "struct PublishKskContext*"
1019  * @param success GNUNET_OK on success
1020  * @param msg error message (or NULL)
1021  */
1022 static void
1023 kb_put_cont (void *cls,
1024              int success,
1025              const char *msg)
1026 {
1027   struct PublishKskContext *pkc = cls;
1028
1029   if (GNUNET_OK != success)
1030     {
1031       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1032       GNUNET_free (pkc->cpy);
1033       GNUNET_free (pkc->kb);
1034       pkc->cont (pkc->cont_cls,
1035                  NULL,
1036                  msg);
1037       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1038       GNUNET_free (pkc);
1039       return;
1040     }
1041   GNUNET_SCHEDULER_add_continuation (pkc->h->sched,
1042                                      GNUNET_NO,
1043                                      &publish_ksk_cont,
1044                                      pkc,
1045                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1046 }
1047
1048
1049 /**
1050  * Continuation of "GNUNET_FS_publish_ksk" that performs
1051  * the actual publishing operation (iterating over all
1052  * of the keywords).
1053  *
1054  * @param cls closure of type "struct PublishKskContext*"
1055  * @param tc unused
1056  */
1057 static void
1058 publish_ksk_cont (void *cls,
1059                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1060 {
1061   struct PublishKskContext *pkc = cls;
1062   const char *keyword;
1063   GNUNET_HashCode key;
1064   GNUNET_HashCode query;
1065   struct GNUNET_CRYPTO_AesSessionKey skey;
1066   struct GNUNET_CRYPTO_AesInitializationVector iv;
1067   struct GNUNET_CRYPTO_RsaPrivateKey *pk;
1068
1069
1070   if ( (pkc->i == pkc->ksk_uri->data.ksk.keywordCount) ||
1071        (NULL == pkc->dsh) )
1072     {
1073       if (NULL != pkc->dsh)
1074         GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1075       GNUNET_free (pkc->cpy);
1076       GNUNET_free (pkc->kb);
1077       pkc->cont (pkc->cont_cls,
1078                  pkc->ksk_uri,
1079                  NULL);
1080       GNUNET_FS_uri_destroy (pkc->ksk_uri);
1081       GNUNET_free (pkc);
1082       return;
1083     }
1084   keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
1085   /* first character of keyword indicates if it is
1086      mandatory or not -- ignore for hashing */
1087   GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
1088   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
1089   GNUNET_CRYPTO_aes_encrypt (&pkc->kb[1],
1090                              pkc->slen + pkc->mdsize,
1091                              &skey,
1092                              &iv,
1093                              &pkc->cpy[1]);
1094   pk = GNUNET_CRYPTO_rsa_key_create_from_hash (&key);
1095   GNUNET_CRYPTO_rsa_key_get_public (pk, &pkc->cpy->keyspace);
1096   GNUNET_CRYPTO_hash (&pkc->cpy->keyspace,
1097                       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1098                       &query);
1099   GNUNET_assert (GNUNET_OK == 
1100                  GNUNET_CRYPTO_rsa_sign (pk,
1101                                          &pkc->cpy->purpose,
1102                                          &pkc->cpy->signature));
1103   GNUNET_CRYPTO_rsa_key_free (pk);
1104   GNUNET_DATASTORE_put (pkc->dsh,
1105                         0,
1106                         &query,
1107                         pkc->mdsize + 
1108                         sizeof (struct GNUNET_FS_KBlock) + 
1109                         pkc->slen,
1110                         pkc->cpy,
1111                         GNUNET_DATASTORE_BLOCKTYPE_KBLOCK, 
1112                         pkc->priority,
1113                         pkc->anonymity,
1114                         pkc->expirationTime,
1115                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1116                         &kb_put_cont,
1117                         pkc);
1118 }
1119
1120
1121 /**
1122  * Publish a CHK under various keywords on GNUnet.
1123  *
1124  * @param h handle to the file sharing subsystem
1125  * @param ksk_uri keywords to use
1126  * @param meta metadata to use
1127  * @param uri URI to refer to in the KBlock
1128  * @param expirationTime when the KBlock expires
1129  * @param anonymity anonymity level for the KBlock
1130  * @param priority priority for the KBlock
1131  * @param options publication options
1132  * @param cont continuation
1133  * @param cont_cls closure for cont
1134  */
1135 void
1136 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
1137                        struct GNUNET_FS_Uri *ksk_uri,
1138                        struct GNUNET_CONTAINER_MetaData *meta,
1139                        struct GNUNET_FS_Uri *uri,
1140                        struct GNUNET_TIME_Absolute expirationTime,
1141                        unsigned int anonymity,
1142                        unsigned int priority,
1143                        enum GNUNET_FS_PublishOptions options,
1144                        GNUNET_FS_PublishContinuation cont,
1145                        void *cont_cls)
1146 {
1147   struct PublishKskContext *pkc;
1148   char *uris;
1149   size_t size;
1150   char *kbe;
1151
1152   pkc = GNUNET_malloc (sizeof (struct PublishKskContext));
1153   pkc->h = h;
1154   pkc->expirationTime = expirationTime;
1155   pkc->anonymity = anonymity;
1156   pkc->priority = priority;
1157   pkc->cont = cont;
1158   pkc->cont_cls = cont_cls;
1159   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
1160     {
1161       pkc->dsh = GNUNET_DATASTORE_connect (h->cfg,
1162                                            h->sched);
1163       if (pkc->dsh == NULL)
1164         {
1165           cont (cont_cls, NULL, _("Could not connect to datastore."));
1166           GNUNET_free (pkc);
1167           return;
1168         }
1169     }
1170   pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta,
1171                                                                 GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1172   GNUNET_assert (pkc->mdsize >= 0);
1173   uris = GNUNET_FS_uri_to_string (uri);
1174   pkc->slen = strlen (uris) + 1;
1175   size = pkc->mdsize + sizeof (struct GNUNET_FS_KBlock) + pkc->slen;
1176   if (size > MAX_KBLOCK_SIZE)
1177     {
1178       size = MAX_KBLOCK_SIZE;
1179       pkc->mdsize = size - sizeof (struct GNUNET_FS_KBlock) - pkc->slen;
1180     }
1181   pkc->kb = GNUNET_malloc (size);
1182   kbe = (char *) &pkc->kb[1];
1183   memcpy (kbe, uris, pkc->slen);
1184   GNUNET_free (uris);
1185   pkc->mdsize = GNUNET_CONTAINER_meta_data_serialize (meta,
1186                                                       &kbe[pkc->slen],
1187                                                       pkc->mdsize,
1188                                                       GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
1189   if (pkc->mdsize == -1)
1190     {
1191       GNUNET_break (0);
1192       GNUNET_free (uris);
1193       GNUNET_free (pkc->kb);
1194       if (pkc->dsh != NULL)
1195         GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
1196       cont (cont_cls, NULL, _("Internal error."));
1197       GNUNET_free (pkc);
1198       return;
1199     }
1200   size = sizeof (struct GNUNET_FS_KBlock) + pkc->slen + pkc->mdsize;
1201
1202   pkc->cpy = GNUNET_malloc (size);
1203   pkc->cpy->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + pkc->mdsize + pkc->slen);
1204   pkc->cpy->purpose.purpose = htonl(GNUNET_SIGNATURE_PURPOSE_FS_KBLOCK);
1205   pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
1206   GNUNET_SCHEDULER_add_continuation (h->sched,
1207                                      GNUNET_NO,
1208                                      &publish_ksk_cont,
1209                                      pkc,
1210                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
1211 }
1212
1213
1214 /**
1215  * Publish an SBlock on GNUnet.
1216  *
1217  * @param h handle to the file sharing subsystem
1218  * @param namespace namespace to publish in
1219  * @param identifier identifier to use
1220  * @param update update identifier to use
1221  * @param meta metadata to use
1222  * @param uri URI to refer to in the SBlock
1223  * @param expirationTime when the SBlock expires
1224  * @param anonymity anonymity level for the SBlock
1225  * @param priority priority for the SBlock
1226  * @param options publication options
1227  * @param cont continuation
1228  * @param cont_cls closure for cont
1229  */
1230 void
1231 GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
1232                        struct GNUNET_FS_Namespace *namespace,
1233                        const char *identifier,
1234                        const char *update,
1235                        struct GNUNET_CONTAINER_MetaData *meta,
1236                        struct GNUNET_FS_Uri *uri,
1237                        struct GNUNET_TIME_Absolute expirationTime,
1238                        unsigned int anonymity,
1239                        unsigned int priority,
1240                        enum GNUNET_FS_PublishOptions options,
1241                        GNUNET_FS_PublishContinuation cont,
1242                        void *cont_cls)
1243 {                
1244 #if 0
1245   struct GNUNET_ECRS_URI *uri;
1246   struct GNUNET_ClientServerConnection *sock;
1247   GNUNET_DatastoreValue *value;
1248   unsigned int size;
1249   unsigned int mdsize;
1250   struct GNUNET_RSA_PrivateKey *hk;
1251   GNUNET_EC_SBlock *sb;
1252   char *dstURI;
1253   char *destPos;
1254   GNUNET_HashCode hc;           /* hash of thisId = key */
1255   GNUNET_HashCode hc2;          /* hash of hc = identifier */
1256   int ret;
1257   unsigned int nidlen;
1258
1259   hk = read_namespace_key (cfg, pid);
1260   if (hk == NULL)
1261     return NULL;
1262
1263   /* THEN: construct GNUNET_EC_SBlock */
1264   dstURI = GNUNET_ECRS_uri_to_string (dstU);
1265   mdsize = GNUNET_meta_data_get_serialized_size (md, GNUNET_SERIALIZE_PART);
1266   if (nextId == NULL)
1267     nextId = "";
1268   nidlen = strlen (nextId) + 1;
1269   size = mdsize + sizeof (GNUNET_EC_SBlock) + strlen (dstURI) + 1 + nidlen;
1270   if (size > MAX_SBLOCK_SIZE)
1271     {
1272       size = MAX_SBLOCK_SIZE;
1273       mdsize =
1274         size - (sizeof (GNUNET_EC_SBlock) + strlen (dstURI) + 1 + nidlen);
1275     }
1276   value = GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + size);
1277   sb = (GNUNET_EC_SBlock *) & value[1];
1278   sb->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
1279   destPos = (char *) &sb[1];
1280   memcpy (destPos, nextId, nidlen);
1281   destPos += nidlen;
1282   memcpy (destPos, dstURI, strlen (dstURI) + 1);
1283   destPos += strlen (dstURI) + 1;
1284   mdsize = GNUNET_meta_data_serialize (ectx,
1285                                        md,
1286                                        destPos,
1287                                        mdsize, GNUNET_SERIALIZE_PART);
1288   if (mdsize == -1)
1289     {
1290       GNUNET_GE_BREAK (ectx, 0);
1291       GNUNET_free (dstURI);
1292       GNUNET_RSA_free_key (hk);
1293       GNUNET_free (value);
1294       return NULL;
1295     }
1296   size = sizeof (GNUNET_EC_SBlock) + mdsize + strlen (dstURI) + 1 + nidlen;
1297   value->size = htonl (sizeof (GNUNET_DatastoreValue) + size);
1298   value->type = htonl (GNUNET_ECRS_BLOCKTYPE_SIGNED);
1299   value->priority = htonl (priority);
1300   value->anonymity_level = htonl (anonymityLevel);
1301   value->expiration_time = GNUNET_htonll (expiration);
1302   GNUNET_hash (thisId, strlen (thisId), &hc);
1303   GNUNET_hash (&hc, sizeof (GNUNET_HashCode), &hc2);
1304   uri = GNUNET_malloc (sizeof (URI));
1305   uri->type = sks;
1306   GNUNET_RSA_get_public_key (hk, &sb->subspace);
1307   GNUNET_hash (&sb->subspace,
1308                sizeof (GNUNET_RSA_PublicKey), &uri->data.sks.namespace);
1309   GNUNET_GE_BREAK (ectx, 0 == memcmp (&uri->data.sks.namespace,
1310                                       pid, sizeof (GNUNET_HashCode)));
1311   uri->data.sks.identifier = GNUNET_strdup (thisId);
1312   GNUNET_hash_xor (&hc2, &uri->data.sks.namespace, &sb->identifier);
1313   GNUNET_ECRS_encryptInPlace (&hc, &sb[1], size - sizeof (GNUNET_EC_SBlock));
1314   GNUNET_GE_ASSERT (ectx,
1315                     GNUNET_OK == GNUNET_RSA_sign (hk,
1316                                                   size
1317                                                   -
1318                                                   sizeof
1319                                                   (GNUNET_RSA_Signature) -
1320                                                   sizeof
1321                                                   (GNUNET_RSA_PublicKey) -
1322                                                   sizeof (unsigned int),
1323                                                   &sb->identifier,
1324                                                   &sb->signature));
1325   GNUNET_RSA_free_key (hk);
1326   sock = GNUNET_client_connection_create (ectx, cfg);
1327   ret = GNUNET_FS_insert (sock, value);
1328   if (ret != GNUNET_OK)
1329     {
1330       GNUNET_free (uri);
1331       uri = NULL;
1332     }
1333   GNUNET_client_connection_destroy (sock);
1334   GNUNET_free (value);
1335   GNUNET_free (dstURI);
1336 #endif
1337 }
1338
1339 /* end of fs_publish.c */