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