todo
[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  * - KBlocks
30  * - SBlocks
31  * - indexing support
32  * - code-sharing with unindex (can wait)
33  * - persistence support (can wait)
34  * - datastore reservation support (optimization)
35  */
36
37 #include "platform.h"
38 #include "gnunet_constants.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 /**
46  * Main function that performs the upload.
47  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
48  * @param tc task context
49  */
50 static void
51 do_upload (void *cls,
52            const struct GNUNET_SCHEDULER_TaskContext *tc);
53
54
55 /**
56  * Context for "ds_put_cont".
57  */
58 struct PutContCtx
59 {
60   /**
61    * Publishing context for which the datastore
62    * PUT request was executed.
63    */
64   struct GNUNET_FS_PublishContext *sc;
65
66   /**
67    * Specific file with the block.
68    */
69   struct GNUNET_FS_FileInformation *p;
70
71   /**
72    * Function to run next, if any (can be NULL).
73    */
74   GNUNET_SCHEDULER_Task cont;
75 };
76
77
78 /**
79  * Fill in all of the generic fields for 
80  * a publish event.
81  *
82  * @param pc structure to fill in
83  * @param sc overall publishing context
84  * @param p file information for the file being published
85  */
86 static void
87 make_publish_status (struct GNUNET_FS_ProgressInfo *pi,
88                      struct GNUNET_FS_PublishContext *sc,
89                      const struct GNUNET_FS_FileInformation *p)
90 {
91   pi->value.publish.sc = sc;
92   pi->value.publish.fi = p;
93   pi->value.publish.cctx
94     = p->client_info;
95   pi->value.publish.pctx
96     = (NULL == p->dir) ? NULL : p->dir->client_info;
97   pi->value.publish.size
98     = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
99   pi->value.publish.eta 
100     = GNUNET_TIME_calculate_eta (p->start_time,
101                                  p->publish_offset,
102                                  pi->value.publish.size);
103   pi->value.publish.duration = GNUNET_TIME_absolute_get_duration (p->start_time);
104   pi->value.publish.completed = p->publish_offset;
105   pi->value.publish.anonymity = p->anonymity;
106 }
107
108
109 /**
110  * Cleanup the publish context, we're done
111  * with it.
112  *
113  * @param pc struct to clean up after
114  */
115 static void
116 publish_cleanup (struct GNUNET_FS_PublishContext *sc)
117 {
118   GNUNET_FS_file_information_destroy (sc->fi, NULL, NULL);
119   GNUNET_FS_namespace_delete (sc->namespace, GNUNET_NO);
120   GNUNET_free_non_null (sc->nid);  
121   GNUNET_free_non_null (sc->nuid);
122   GNUNET_DATASTORE_disconnect (sc->dsh, GNUNET_NO);
123   GNUNET_free (sc);
124 }
125
126
127 /**
128  * Function called by the datastore API with
129  * the result from the PUT request.
130  *
131  * @param cls our closure
132  * @param success GNUNET_OK on success
133  * @param msg error message (or NULL)
134  */
135 static void
136 ds_put_cont (void *cls,
137              int success,
138              const char *msg)
139 {
140   struct PutContCtx *pcc = cls;
141   struct GNUNET_FS_ProgressInfo pi;
142
143   if (GNUNET_SYSERR == pcc->sc->in_network_wait)
144     {
145       /* we were aborted in the meantime,
146          finish shutdown! */
147       publish_cleanup (pcc->sc);
148       return;
149     }
150   GNUNET_assert (GNUNET_YES == pcc->sc->in_network_wait);
151   pcc->sc->in_network_wait = GNUNET_NO;
152   if (GNUNET_OK != success)
153     {
154       GNUNET_asprintf (&pcc->p->emsg, 
155                        _("Upload failed: %s"),
156                        msg);
157       GNUNET_FS_file_information_sync (pcc->p);
158       pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
159       make_publish_status (&pi, pcc->sc, pcc->p);
160       pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
161       pi.value.publish.specifics.error.message = pcc->p->emsg;
162       pcc->p->client_info
163         = pcc->sc->h->upcb (pcc->sc->h->upcb_cls,
164                             &pi);
165       return;
166     }
167   GNUNET_FS_file_information_sync (pcc->p);
168   if (NULL != pcc->cont)
169     pcc->sc->upload_task 
170       = GNUNET_SCHEDULER_add_delayed (pcc->sc->h->sched,
171                                       GNUNET_NO,
172                                       GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
173                                       GNUNET_SCHEDULER_NO_TASK,
174                                       GNUNET_TIME_UNIT_ZERO,
175                                       pcc->cont,
176                                       pcc->sc);
177   GNUNET_free (pcc);
178 }
179
180
181 /**
182  * We need to publish a specific block.  Do it.  Then continue with
183  * the main task.
184  *
185  * @param sc overall upload data
186  * @param p file that the block belongs to (needed for options!)
187  * @param query what the block should be indexed under
188  * @param blk encoded block to publish
189  * @param blk_size size of the block
190  * @param blk_type type of the block
191  * @param cont function to run when done
192  */
193 static void
194 publish_block (struct GNUNET_FS_PublishContext *sc,
195                struct GNUNET_FS_FileInformation *p,
196                const GNUNET_HashCode *query,
197                const void* blk,
198                uint16_t blk_size,
199                uint32_t blk_type,
200                GNUNET_SCHEDULER_Task cont)
201 {
202   struct PutContCtx * dpc_cls;
203
204   dpc_cls = GNUNET_malloc(sizeof(struct PutContCtx));
205   dpc_cls->cont = cont;
206   dpc_cls->sc = sc;
207   dpc_cls->p = p;
208   GNUNET_assert (GNUNET_NO == sc->in_network_wait);
209   sc->in_network_wait = GNUNET_YES;
210   GNUNET_DATASTORE_put (sc->dsh,
211                         sc->rid,
212                         query,
213                         blk_size,
214                         blk,
215                         blk_type,
216                         p->priority,
217                         p->anonymity,
218                         p->expirationTime,
219                         GNUNET_CONSTANTS_SERVICE_TIMEOUT,
220                         &ds_put_cont,
221                         dpc_cls);
222 }
223
224
225 /**
226  * Generate the callback that signals clients
227  * that a file (or directory) has been completely
228  * published.
229  *
230  * @param p the completed upload
231  * @param sc context of the publication
232  */
233 static void 
234 signal_publish_completion (struct GNUNET_FS_FileInformation *p,
235                            struct GNUNET_FS_PublishContext *sc)
236 {
237   struct GNUNET_FS_ProgressInfo pi;
238   
239   pi.status = GNUNET_FS_STATUS_PUBLISH_COMPLETED;
240   make_publish_status (&pi, sc, p);
241   pi.value.publish.eta = GNUNET_TIME_UNIT_ZERO;
242   pi.value.publish.specifics.completed.chk_uri = p->chk_uri;
243   p->client_info
244     = sc->h->upcb (sc->h->upcb_cls,
245                   &pi);
246 }
247
248
249 /**
250  * We are almost done publishing the structure,
251  * add SBlocks (if needed).
252  *
253  * @param sc overall upload data
254  */
255 static void
256 publish_sblock (struct GNUNET_FS_PublishContext *sc)
257 {
258   struct GNUNET_FS_FileInformation *p;
259   p = sc->fi;
260   
261   if (NULL != sc->namespace)
262     GNUNET_FS_publish_sks (sc->h,
263                            sc->namespace,
264                            sc->nid,
265                            sc->nuid,
266                            p->meta,
267                            p->chk_uri,
268                            p->expirationTime,
269                            p->anonymity,
270                            p->priority);
271   // FIXME: release the datastore reserve here!
272   signal_publish_completion (p, sc);
273 }
274
275
276 /**
277  * We have uploaded a file or directory; now publish
278  * the KBlocks in the global keyword space so that
279  * it can be found.  Then continue with the
280  * main task.
281  *
282  * @param sc overall upload data
283  * @param p specific file or directory for which kblocks
284  *          should be created
285  */
286 static void
287 publish_kblocks (struct GNUNET_FS_PublishContext *sc,
288                  struct GNUNET_FS_FileInformation *p)
289 {
290   unsigned int i;
291
292   // FIXME: use cps here instead...
293   for (i=0;i<p->keywords->data.ksk.keywordCount;i++)
294     GNUNET_FS_publish_ksk (sc->h,
295                            p->keywords->data.ksk.keywords[i],
296                            p->meta,
297                            p->chk_uri,
298                            p->expirationTime,
299                            p->anonymity,
300                            p->priority);
301   GNUNET_FS_file_information_sync (p);
302   if (NULL != p->dir)
303     signal_publish_completion (p, sc);
304   sc->upload_task 
305     = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
306                                     GNUNET_NO,
307                                     GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
308                                     GNUNET_SCHEDULER_NO_TASK,
309                                     GNUNET_TIME_UNIT_ZERO,
310                                     &do_upload,
311                                     sc);
312 }
313
314
315 /**
316  * Compute the depth of the CHK tree.
317  *
318  * @param flen file length for which to compute the depth
319  * @return depth of the tree
320  */
321 static unsigned int
322 compute_depth (uint64_t flen)
323 {
324   unsigned int treeDepth;
325   uint64_t fl;
326
327   treeDepth = 1;
328   fl = GNUNET_FS_DBLOCK_SIZE;
329   while (fl < flen)
330     {
331       treeDepth++;
332       if (fl * GNUNET_FS_CHK_PER_INODE < fl)
333         {
334           /* integer overflow, this is a HUGE file... */
335           return treeDepth;
336         }
337       fl = fl * GNUNET_FS_CHK_PER_INODE;
338     }
339   return treeDepth;
340 }
341
342
343 /**
344  * Compute the size of the current IBlock.
345  *
346  * @param height height of the IBlock in the tree (aka overall
347  *               number of tree levels minus depth); 0 == DBlock
348  * @param offset current offset in the overall file
349  * @return size of the corresponding IBlock
350  */
351 static uint16_t 
352 compute_iblock_size (unsigned int height,
353                      uint64_t offset)
354 {
355   unsigned int ret;
356   unsigned int i;
357   uint64_t mod;
358   uint64_t bds;
359
360   GNUNET_assert (height > 0);
361   bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
362                                   corresponds to */
363   for (i=0;i<height;i++)
364     bds *= GNUNET_FS_CHK_PER_INODE;
365   mod = offset % bds;
366   if (0 == mod)
367     {
368       /* we were triggered at the end of a full block */
369       ret = GNUNET_FS_CHK_PER_INODE;
370     }
371   else
372     {
373       /* we were triggered at the end of the file */
374       bds /= GNUNET_FS_CHK_PER_INODE;
375       ret = mod / bds;
376       if (0 != mod % bds)
377         ret++; 
378     }
379   return (uint16_t) (ret * sizeof(struct ContentHashKey));
380 }
381
382
383 /**
384  * Compute the offset of the CHK for the
385  * current block in the IBlock above.
386  *
387  * @param height height of the IBlock in the tree (aka overall
388  *               number of tree levels minus depth); 0 == DBlock
389  * @param offset current offset in the overall file
390  * @return (array of CHKs') offset in the above IBlock
391  */
392 static unsigned int
393 compute_chk_offset (unsigned int height,
394                     uint64_t offset)
395 {
396   uint64_t bds;
397   unsigned int ret;
398   unsigned int i;
399
400   bds = GNUNET_FS_DBLOCK_SIZE; /* number of bytes each CHK at level "i"
401                                   corresponds to */
402   for (i=0;i<height;i++)
403     bds *= GNUNET_FS_CHK_PER_INODE;
404   GNUNET_assert (0 == (offset % bds));
405   ret = offset / bds;
406   return ret % GNUNET_FS_CHK_PER_INODE; 
407 }
408
409
410 /**
411  * We are uploading a file or directory; load (if necessary) the next
412  * block into memory, encrypt it and send it to the FS service.  Then
413  * continue with the main task.
414  *
415  * @param sc overall upload data
416  * @param p specific file or directory for which kblocks
417  *          should be created
418  */
419 static void
420 publish_content (struct GNUNET_FS_PublishContext *sc,
421                  struct GNUNET_FS_FileInformation *p)
422 {
423   struct GNUNET_FS_ProgressInfo pi;
424   struct ContentHashKey *mychk;
425   const void *pt_block;
426   uint16_t pt_size;
427   char *emsg;
428   char iob[GNUNET_FS_DBLOCK_SIZE];
429   char enc[GNUNET_FS_DBLOCK_SIZE];
430   struct GNUNET_CRYPTO_AesSessionKey sk;
431   struct GNUNET_CRYPTO_AesInitializationVector iv;
432   uint64_t size;
433   unsigned int off;
434   struct GNUNET_FS_DirectoryBuilder *db;
435   struct GNUNET_FS_FileInformation *dirpos;
436   void *raw_data;
437   char *dd;
438
439   // FIXME: figure out how to share this code
440   // with unindex!
441   size = (p->is_directory) ? p->data.dir.dir_size : p->data.file.file_size;
442   if (NULL == p->chk_tree)
443     {
444       if (p->is_directory)
445         {
446           db = GNUNET_FS_directory_builder_create (p->meta);
447           dirpos = p->data.dir.entries;
448           while (NULL != dirpos)
449             {
450               if (dirpos->is_directory)
451                 {
452                   raw_data = dirpos->data.dir.dir_data;
453                   dirpos->data.dir.dir_data = NULL;
454                 }
455               else
456                 {
457                   raw_data = NULL;
458                   if ( (dirpos->data.file.file_size < GNUNET_FS_MAX_INLINE_SIZE) &&
459                        (dirpos->data.file.file_size > 0) )
460                     {
461                       raw_data = GNUNET_malloc (dirpos->data.file.file_size);
462                       emsg = NULL;
463                       if (dirpos->data.file.file_size !=
464                           dirpos->data.file.reader (dirpos->data.file.reader_cls,
465                                                     0,
466                                                     dirpos->data.file.file_size,
467                                                     raw_data,
468                                                     &emsg))
469                         {
470                           GNUNET_free_non_null (emsg);
471                           GNUNET_free (raw_data);
472                           raw_data = NULL;
473                         } 
474                     }
475                 }
476               GNUNET_FS_directory_builder_add (db,
477                                                dirpos->chk_uri,
478                                                dirpos->meta,
479                                                raw_data);
480               GNUNET_free_non_null (raw_data);
481               dirpos = dirpos->next;
482             }
483           GNUNET_FS_directory_builder_finish (db,
484                                               &p->data.dir.dir_size,
485                                               &p->data.dir.dir_data);
486           size = p->data.dir.dir_size;
487         }
488       p->chk_tree_depth = compute_depth (size);
489       p->chk_tree = GNUNET_malloc (p->chk_tree_depth * 
490                                    sizeof (struct ContentHashKey) *
491                                    GNUNET_FS_CHK_PER_INODE);
492       p->current_depth = p->chk_tree_depth;
493     }
494   if (p->current_depth == p->chk_tree_depth)
495     {
496       if (p->is_directory)
497         {
498           pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
499                                p->data.dir.dir_size - p->publish_offset);
500           dd = p->data.dir.dir_data;
501           pt_block = &dd[p->publish_offset];
502         }
503       else
504         {
505           pt_size = GNUNET_MIN(GNUNET_FS_DBLOCK_SIZE,
506                                p->data.file.file_size - p->publish_offset);
507           emsg = NULL;
508           if (pt_size !=
509               p->data.file.reader (p->data.file.reader_cls,
510                                    p->publish_offset,
511                                    pt_size,
512                                    iob,
513                                    &emsg))
514             {
515               GNUNET_asprintf (&p->emsg, 
516                                _("Upload failed: %s"),
517                                emsg);
518               GNUNET_free (emsg);
519               GNUNET_FS_file_information_sync (p);
520               pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
521               make_publish_status (&pi, sc, p);
522               pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
523               pi.value.publish.specifics.error.message = p->emsg;
524               p->client_info
525                 = sc->h->upcb (sc->h->upcb_cls,
526                                &pi);
527               /* continue with main (to propagate error up) */
528               sc->upload_task 
529                 = GNUNET_SCHEDULER_add_delayed (sc->h->sched,
530                                                 GNUNET_NO,
531                                                 GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
532                                                 GNUNET_SCHEDULER_NO_TASK,
533                                                 GNUNET_TIME_UNIT_ZERO,
534                                                 &do_upload,
535                                                 sc);
536               return;
537             }
538           pt_block = iob;
539         }
540     }
541   else
542     {
543       pt_size = compute_iblock_size (p->chk_tree_depth - p->current_depth,
544                                      p->publish_offset); 
545       pt_block = &p->chk_tree[p->current_depth *
546                               GNUNET_FS_CHK_PER_INODE];
547     }
548   off = compute_chk_offset (p->chk_tree_depth - p->current_depth,
549                             p->publish_offset);
550   mychk = &p->chk_tree[(p->current_depth-1)*GNUNET_FS_CHK_PER_INODE+off];
551   GNUNET_CRYPTO_hash (pt_block, pt_size, &mychk->key);
552   GNUNET_CRYPTO_hash_to_aes_key (&mychk->key, &sk, &iv);
553   GNUNET_CRYPTO_aes_encrypt (pt_block,
554                              pt_size,
555                              &sk,
556                              &iv,
557                              enc);
558   // NOTE: this call (and progress below) is all that really differs
559   // between publish/unindex!  Parameterize & move this code!
560   // FIXME: something around here would need to change
561   // for indexing!
562   publish_block (sc, p, 
563                  &mychk->query,
564                  enc, 
565                  pt_size, 
566                  (p->current_depth == p->chk_tree_depth) 
567                  ? GNUNET_DATASTORE_BLOCKTYPE_DBLOCK 
568                  : GNUNET_DATASTORE_BLOCKTYPE_IBLOCK,
569                  &do_upload);
570   if (p->current_depth == p->chk_tree_depth)
571     {
572       pi.status = GNUNET_FS_STATUS_PUBLISH_PROGRESS;
573       make_publish_status (&pi, sc, p);
574       pi.value.publish.specifics.progress.data = pt_block;
575       pi.value.publish.specifics.progress.offset = p->publish_offset;
576       pi.value.publish.specifics.progress.data_len = pt_size;
577       p->client_info 
578         = sc->h->upcb (sc->h->upcb_cls,
579                        &pi);
580     }
581   GNUNET_CRYPTO_hash (enc, pt_size, &mychk->query);
582   if (p->current_depth == p->chk_tree_depth) 
583     { 
584       p->publish_offset += pt_size;
585       if ( (p->publish_offset == size) ||
586            (0 == p->publish_offset % (GNUNET_FS_CHK_PER_INODE * GNUNET_FS_DBLOCK_SIZE) ) )
587         p->current_depth--;
588     }
589   else
590     {
591       if ( (off == GNUNET_FS_CHK_PER_INODE) ||
592            (p->publish_offset == size) )
593         p->current_depth--;
594       else
595         p->current_depth = p->chk_tree_depth;
596     }
597   if (0 == p->current_depth)
598     {
599       p->chk_uri = GNUNET_malloc (sizeof(struct GNUNET_FS_Uri));
600       p->chk_uri->type = chk;
601       p->chk_uri->data.chk.chk = p->chk_tree[0];
602       p->chk_uri->data.chk.file_length = size;
603       GNUNET_free (p->chk_tree);
604       p->chk_tree = NULL;
605     }
606 }
607
608
609 /**
610  * Main function that performs the upload.
611  * @param cls "struct GNUNET_FS_PublishContext" identifies the upload
612  * @param tc task context
613  */
614 static void
615 do_upload (void *cls,
616            const struct GNUNET_SCHEDULER_TaskContext *tc)
617 {
618   struct GNUNET_FS_PublishContext *sc = cls;
619   struct GNUNET_FS_ProgressInfo pi;
620   struct GNUNET_FS_FileInformation *p;
621   char *fn;
622
623   sc->upload_task = GNUNET_SCHEDULER_NO_TASK;  
624   p = sc->fi_pos;
625   if (NULL == p)
626     {
627       /* upload of entire hierarchy complete,
628          publish namespace entries */
629       publish_sblock (sc);
630       return;
631     }
632   if (NULL != p->emsg)
633     {
634       /* error with current file, abort all
635          related files as well! */
636       while (NULL != p->dir)
637         {
638           fn = GNUNET_CONTAINER_meta_data_get_by_type (p->meta,
639                                                        EXTRACTOR_FILENAME);
640           p = p->dir;
641           GNUNET_asprintf (&p->emsg, 
642                            _("Recursive upload failed at `%s'"),
643                            fn);
644           GNUNET_free (fn);
645           GNUNET_FS_file_information_sync (p);
646           pi.status = GNUNET_FS_STATUS_PUBLISH_ERROR;
647           make_publish_status (&pi, sc, p);
648           pi.value.publish.eta = GNUNET_TIME_UNIT_FOREVER_REL;
649           pi.value.publish.specifics.error.message = p->emsg;
650           p->client_info
651             = sc->h->upcb (sc->h->upcb_cls,
652                            &pi);
653         }
654       return;
655     }
656   if (NULL != p->chk_uri)
657     {
658       /* move on to next file */
659       if (NULL != p->next)
660         sc->fi_pos = p->next;
661       else
662         sc->fi_pos = p->dir;
663       /* upload of "p" complete, publish KBlocks! */
664       publish_kblocks (sc, p);
665       return;
666     }
667   if ( (!p->is_directory) &&
668        (p->data.file.do_index) )
669     {
670       // FIXME: need to pre-compute hash over
671       // the entire file and ask FS to prepare
672       // for indexing!
673       return;
674     }
675   publish_content (sc, p);
676 }
677
678
679 /**
680  * Signal the FS's progress function that we are starting
681  * an upload.
682  *
683  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
684  * @param fi the entry in the publish-structure
685  * @param length length of the file or directory
686  * @param meta metadata for the file or directory (can be modified)
687  * @param uri pointer to the keywords that will be used for this entry (can be modified)
688  * @param anonymity pointer to selected anonymity level (can be modified)
689  * @param priority pointer to selected priority (can be modified)
690  * @param expirationTime pointer to selected expiration time (can be modified)
691  * @param client_info pointer to client context set upon creation (can be modified)
692  * @return GNUNET_OK to continue (always)
693  */
694 static int
695 fip_signal_start(void *cls,
696                  struct GNUNET_FS_FileInformation *fi,
697                  uint64_t length,
698                  struct GNUNET_CONTAINER_MetaData *meta,
699                  struct GNUNET_FS_Uri **uri,
700                  unsigned int *anonymity,
701                  unsigned int *priority,
702                  struct GNUNET_TIME_Absolute *expirationTime,
703                  void **client_info)
704 {
705   struct GNUNET_FS_PublishContext *sc = cls;
706   struct GNUNET_FS_ProgressInfo pi;
707
708   pi.status = GNUNET_FS_STATUS_PUBLISH_START;
709   make_publish_status (&pi, sc, fi);
710   *client_info = sc->h->upcb (sc->h->upcb_cls,
711                               &pi);
712   return GNUNET_OK;
713 }
714
715
716 /**
717  * Publish a file or directory.
718  *
719  * @param h handle to the file sharing subsystem
720  * @param ctx initial value to use for the '*ctx'
721  *        in the callback (for the GNUNET_FS_STATUS_PUBLISH_START event).
722  * @param fi information about the file or directory structure to publish
723  * @param namespace namespace to publish the file in, NULL for no namespace
724  * @param nid identifier to use for the publishd content in the namespace
725  *        (can be NULL, must be NULL if namespace is NULL)
726  * @param nuid update-identifier that will be used for future updates 
727  *        (can be NULL, must be NULL if namespace or nid is NULL)
728  * @return context that can be used to control the publish operation
729  */
730 struct GNUNET_FS_PublishContext *
731 GNUNET_FS_publish_start (struct GNUNET_FS_Handle *h,
732                          void *ctx,
733                          struct GNUNET_FS_FileInformation *fi,
734                          struct GNUNET_FS_Namespace *namespace,
735                          const char *nid,
736                          const char *nuid)
737 {
738   struct GNUNET_FS_PublishContext *ret;
739   struct GNUNET_FS_FileInformation *p;
740   struct GNUNET_DATASTORE_Handle *dsh;
741
742   dsh = GNUNET_DATASTORE_connect (h->cfg,
743                                   h->sched);
744   if (NULL == dsh)
745     return NULL;
746   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishContext));
747   ret->dsh = dsh;
748   ret->h = h;
749   ret->client_ctx = ctx;
750   ret->fi = fi;
751   ret->namespace = namespace;
752   if (namespace != NULL)
753     {
754       namespace->rc++;
755       GNUNET_assert (NULL != nid);
756       ret->nid = GNUNET_strdup (nid);
757       if (NULL != nuid)
758         ret->nuid = GNUNET_strdup (nuid);
759     }
760   // FIXME: make upload persistent!
761
762   /* signal start */
763   GNUNET_FS_file_information_inspect (ret->fi,
764                                       &fip_signal_start,
765                                       ret);
766   /* find first leaf, DFS */
767   p = ret->fi;
768   while ( (p->is_directory) &&
769           (NULL != p->data.dir.entries) )
770     p = p->data.dir.entries;          
771   ret->fi_pos = p;
772
773   // FIXME: calculate space needed for "fi"
774   // and reserve as first task (then trigger
775   // "do_upload" from that continuation)!
776   ret->upload_task 
777     = GNUNET_SCHEDULER_add_delayed (h->sched,
778                                     GNUNET_NO,
779                                     GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
780                                     GNUNET_SCHEDULER_NO_TASK,
781                                     GNUNET_TIME_UNIT_ZERO,
782                                     &do_upload,
783                                     ret);
784   return ret;
785 }
786
787
788 /**
789  * Signal the FS's progress function that we are stopping
790  * an upload.
791  *
792  * @param cls closure (of type "struct GNUNET_FS_PublishContext*")
793  * @param fi the entry in the publish-structure
794  * @param length length of the file or directory
795  * @param meta metadata for the file or directory (can be modified)
796  * @param uri pointer to the keywords that will be used for this entry (can be modified)
797  * @param anonymity pointer to selected anonymity level (can be modified)
798  * @param priority pointer to selected priority (can be modified)
799  * @param expirationTime pointer to selected expiration time (can be modified)
800  * @param client_info pointer to client context set upon creation (can be modified)
801  * @return GNUNET_OK to continue (always)
802  */
803 static int
804 fip_signal_stop(void *cls,
805                 struct GNUNET_FS_FileInformation *fi,
806                 uint64_t length,
807                 struct GNUNET_CONTAINER_MetaData *meta,
808                 struct GNUNET_FS_Uri **uri,
809                 unsigned int *anonymity,
810                 unsigned int *priority,
811                 struct GNUNET_TIME_Absolute *expirationTime,
812                 void **client_info)
813 {
814   struct GNUNET_FS_PublishContext*sc = cls;
815   struct GNUNET_FS_ProgressInfo pi;
816
817   pi.status = GNUNET_FS_STATUS_PUBLISH_STOPPED;
818   make_publish_status (&pi, sc, fi);
819   GNUNET_break (NULL ==
820                 sc->h->upcb (sc->h->upcb_cls,
821                              &pi));
822   *client_info = NULL;
823   return GNUNET_OK;
824 }
825
826
827 /**
828  * Stop an upload.  Will abort incomplete uploads (but 
829  * not remove blocks that have already been publishd) or
830  * simply clean up the state for completed uploads.
831  *
832  * @param sc context for the upload to stop
833  */
834 void 
835 GNUNET_FS_publish_stop (struct GNUNET_FS_PublishContext *sc)
836 {
837   if (GNUNET_SCHEDULER_NO_TASK != sc->upload_task)
838     GNUNET_SCHEDULER_cancel (sc->h->sched, sc->upload_task);
839   // FIXME: remove from persistence DB (?) --- think more about
840   //        shutdown / persistent-resume APIs!!!
841   GNUNET_FS_file_information_inspect (sc->fi,
842                                       &fip_signal_stop,
843                                       sc);
844   if (GNUNET_YES == sc->in_network_wait)
845     {
846       sc->in_network_wait = GNUNET_SYSERR;
847       return;
848     }
849   publish_cleanup (sc);
850 }
851
852
853 /**
854  * Publish a KBlock on GNUnet.
855  *
856  * @param h handle to the file sharing subsystem
857  * @param keyword keyword to use
858  * @param meta metadata to use
859  * @param uri URI to refer to in the KBlock
860  * @param expirationTime when the KBlock expires
861  * @param anonymity anonymity level for the KBlock
862  * @param priority priority for the KBlock
863  */
864 // FIXME: cps this one
865 void
866 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
867                        const char *keyword,
868                        struct GNUNET_CONTAINER_MetaData *meta,
869                        struct GNUNET_FS_Uri *uri,
870                        struct GNUNET_TIME_Absolute expirationTime,
871                        unsigned int anonymity,
872                        unsigned int priority)
873 {
874   // FIXME!
875 }
876
877
878
879 /**
880  * Publish an SBlock on GNUnet.
881  *
882  * @param h handle to the file sharing subsystem
883  * @param namespace namespace to publish in
884  * @param identifier identifier to use
885  * @param update update identifier to use
886  * @param meta metadata to use
887  * @param uri URI to refer to in the SBlock
888  * @param expirationTime when the SBlock expires
889  * @param anonymity anonymity level for the SBlock
890  * @param priority priority for the SBlock
891  */
892 // FIXME: cps this one
893 void
894 GNUNET_FS_publish_sks (struct GNUNET_FS_Handle *h,
895                        struct GNUNET_FS_Namespace *namespace,
896                        const char *identifier,
897                        const char *update,
898                        struct GNUNET_CONTAINER_MetaData *meta,
899                        struct GNUNET_FS_Uri *uri,
900                        struct GNUNET_TIME_Absolute expirationTime,
901                        unsigned int anonymity,
902                        unsigned int priority)
903 {                
904   // FIXME
905 }
906
907 /* end of fs_publish.c */