-fix
[oweals/gnunet.git] / src / fs / fs_publish_ksk.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2012, 2013 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_ksk.c
23  * @brief publish a URI under a keyword in GNUnet
24  * @see https://gnunet.org/encoding and #2564 
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_api.h"
35 #include "fs_tree.h"
36
37
38 /**
39  * Context for the KSK publication.
40  */
41 struct GNUNET_FS_PublishKskContext
42 {
43
44   /**
45    * Keywords to use.
46    */
47   struct GNUNET_FS_Uri *ksk_uri;
48
49   /**
50    * Global FS context.
51    */
52   struct GNUNET_FS_Handle *h;
53
54   /**
55    * The master block that we are sending
56    * (in plaintext), has "mdsize+slen" more
57    * bytes than the struct would suggest.
58    */
59   struct UBlock *ub;
60
61   /**
62    * Buffer of the same size as "kb" for
63    * the encrypted version.
64    */
65   struct UBlock *cpy;
66
67   /**
68    * Handle to the datastore, NULL if we are just
69    * simulating.
70    */
71   struct GNUNET_DATASTORE_Handle *dsh;
72
73   /**
74    * Handle to datastore PUT request.
75    */
76   struct GNUNET_DATASTORE_QueueEntry *qre;
77
78   /**
79    * Current task.
80    */
81   GNUNET_SCHEDULER_TaskIdentifier ksk_task;
82
83   /**
84    * Function to call once we're done.
85    */
86   GNUNET_FS_PublishContinuation cont;
87
88   /**
89    * Closure for cont.
90    */
91   void *cont_cls;
92
93   /**
94    * When should the KBlocks expire?
95    */
96   struct GNUNET_FS_BlockOptions bo;
97
98   /**
99    * Size of the serialized metadata.
100    */
101   ssize_t mdsize;
102
103   /**
104    * Size of the (CHK) URI as a string.
105    */
106   size_t slen;
107
108   /**
109    * Keyword that we are currently processing.
110    */
111   unsigned int i;
112
113 };
114
115
116 /**
117  * Continuation of "GNUNET_FS_publish_ksk" that performs
118  * the actual publishing operation (iterating over all
119  * of the keywords).
120  *
121  * @param cls closure of type "struct PublishKskContext*"
122  * @param tc unused
123  */
124 static void
125 publish_ksk_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
126
127
128 /**
129  * Function called by the datastore API with
130  * the result from the PUT request.
131  *
132  * @param cls closure of type "struct GNUNET_FS_PublishKskContext*"
133  * @param success GNUNET_OK on success
134  * @param min_expiration minimum expiration time required for content to be stored
135  * @param msg error message (or NULL)
136  */
137 static void
138 kb_put_cont (void *cls, int success, 
139              struct GNUNET_TIME_Absolute min_expiration,
140              const char *msg)
141 {
142   struct GNUNET_FS_PublishKskContext *pkc = cls;
143
144   pkc->qre = NULL;
145   if (GNUNET_OK != success)
146   {
147     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
148                 "KBlock PUT operation failed: %s\n", msg);
149     pkc->cont (pkc->cont_cls, NULL, msg);
150     GNUNET_FS_publish_ksk_cancel (pkc);
151     return;
152   }
153   pkc->ksk_task = GNUNET_SCHEDULER_add_now (&publish_ksk_cont, pkc);
154 }
155
156
157 /**
158  * Continuation of "GNUNET_FS_publish_ksk" that performs the actual
159  * publishing operation (iterating over all of the keywords).
160  *
161  * @param cls closure of type "struct GNUNET_FS_PublishKskContext*"
162  * @param tc unused
163  */
164 static void
165 publish_ksk_cont (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
166 {
167   struct GNUNET_FS_PublishKskContext *pkc = cls;
168   const char *keyword;
169   struct GNUNET_HashCode key;
170   struct GNUNET_HashCode seed;
171   struct GNUNET_HashCode signing_key;
172   struct GNUNET_HashCode query;
173   struct GNUNET_CRYPTO_AesSessionKey skey;
174   struct GNUNET_CRYPTO_AesInitializationVector iv;
175   struct GNUNET_FS_PseudonymHandle *ph;
176   struct GNUNET_FS_PseudonymIdentifier pseudonym;
177   struct UBlock *ub_dst;
178
179   pkc->ksk_task = GNUNET_SCHEDULER_NO_TASK;
180   if ((pkc->i == pkc->ksk_uri->data.ksk.keywordCount) || (NULL == pkc->dsh))
181   {
182     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "KSK PUT operation complete\n");
183     pkc->cont (pkc->cont_cls, pkc->ksk_uri, NULL);
184     GNUNET_FS_publish_ksk_cancel (pkc);
185     return;
186   }
187   /* derive signing seed from plaintext */
188   GNUNET_CRYPTO_hash (&pkc->ub[1],
189                       1 + pkc->slen + pkc->mdsize,
190                       &seed);
191   keyword = pkc->ksk_uri->data.ksk.keywords[pkc->i++];
192   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Publishing under keyword `%s'\n",
193               &keyword[1]);  
194   /* first character of keyword indicates if it is
195    * mandatory or not -- ignore for hashing */
196   GNUNET_CRYPTO_hash (&keyword[1], strlen (&keyword[1]), &key);
197   GNUNET_CRYPTO_hash_to_aes_key (&key, &skey, &iv);
198   ub_dst = pkc->cpy;
199   GNUNET_CRYPTO_aes_encrypt (&pkc->ub[1], 
200                              1 + pkc->slen + pkc->mdsize,
201                              &skey, &iv,
202                              &ub_dst[1]);
203   ph = GNUNET_FS_pseudonym_get_anonymous_pseudonym_handle ();
204   GNUNET_CRYPTO_hash (&key, sizeof (key), &signing_key);
205   ub_dst->purpose.size = htonl (1 + pkc->slen + pkc->mdsize + sizeof (struct UBlock)
206                                   - sizeof (struct GNUNET_FS_PseudonymSignature));
207   ub_dst->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_FS_UBLOCK);
208   
209   GNUNET_FS_pseudonym_get_identifier (ph, &pseudonym);
210   GNUNET_FS_pseudonym_derive_verification_key (&pseudonym, 
211                                                &signing_key,
212                                                &ub_dst->verification_key);
213   GNUNET_FS_pseudonym_sign (ph,
214                             &ub_dst->purpose,
215                             &seed,
216                             &signing_key,
217                             &ub_dst->signature);
218
219   GNUNET_CRYPTO_hash (&ub_dst->verification_key,
220                       sizeof (ub_dst->verification_key),
221                       &query);
222   GNUNET_FS_pseudonym_destroy (ph);
223   pkc->qre =
224       GNUNET_DATASTORE_put (pkc->dsh, 0, &query,
225                             1 + pkc->slen + pkc->mdsize + sizeof (struct UBlock),
226                             ub_dst, GNUNET_BLOCK_TYPE_FS_UBLOCK,
227                             pkc->bo.content_priority, pkc->bo.anonymity_level,
228                             pkc->bo.replication_level, pkc->bo.expiration_time,
229                             -2, 1, GNUNET_CONSTANTS_SERVICE_TIMEOUT,
230                             &kb_put_cont, pkc);
231 }
232
233
234 /**
235  * Publish a CHK under various keywords on GNUnet.
236  *
237  * @param h handle to the file sharing subsystem
238  * @param ksk_uri keywords to use
239  * @param meta metadata to use
240  * @param uri URI to refer to in the KBlock
241  * @param bo per-block options
242  * @param options publication options
243  * @param cont continuation
244  * @param cont_cls closure for cont
245  * @return NULL on error ('cont' will still be called)
246  */
247 struct GNUNET_FS_PublishKskContext *
248 GNUNET_FS_publish_ksk (struct GNUNET_FS_Handle *h,
249                        const struct GNUNET_FS_Uri *ksk_uri,
250                        const struct GNUNET_CONTAINER_MetaData *meta,
251                        const struct GNUNET_FS_Uri *uri,
252                        const struct GNUNET_FS_BlockOptions *bo,
253                        enum GNUNET_FS_PublishOptions options,
254                        GNUNET_FS_PublishContinuation cont, void *cont_cls)
255 {
256   struct GNUNET_FS_PublishKskContext *pkc;
257   char *uris;
258   size_t size;
259   char *kbe;
260   char *sptr;
261
262   GNUNET_assert (NULL != uri);
263   pkc = GNUNET_malloc (sizeof (struct GNUNET_FS_PublishKskContext));
264   pkc->h = h;
265   pkc->bo = *bo;
266   pkc->cont = cont;
267   pkc->cont_cls = cont_cls;
268   if (0 == (options & GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY))
269   {
270     pkc->dsh = GNUNET_DATASTORE_connect (h->cfg);
271     if (NULL == pkc->dsh)
272     {
273       cont (cont_cls, NULL, _("Could not connect to datastore."));
274       GNUNET_free (pkc);
275       return NULL;
276     }
277   }
278   if (meta == NULL)
279     pkc->mdsize = 0;
280   else
281     pkc->mdsize = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
282   GNUNET_assert (pkc->mdsize >= 0);
283   uris = GNUNET_FS_uri_to_string (uri);
284   pkc->slen = strlen (uris) + 1;
285   size = pkc->mdsize + sizeof (struct UBlock) + pkc->slen + 1;
286   if (size > MAX_UBLOCK_SIZE)
287   {
288     size = MAX_UBLOCK_SIZE;
289     pkc->mdsize = size - sizeof (struct UBlock) - pkc->slen + 1;
290   }
291   pkc->ub = GNUNET_malloc (size);
292   kbe = (char *) &pkc->ub[1];
293   kbe++; /* leave one '\0' for the update identifier */
294   memcpy (kbe, uris, pkc->slen);
295   GNUNET_free (uris);
296   sptr = &kbe[pkc->slen];
297   if (meta != NULL)
298     pkc->mdsize =
299         GNUNET_CONTAINER_meta_data_serialize (meta, &sptr, pkc->mdsize,
300                                               GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
301   if (-1 == pkc->mdsize)
302   {
303     GNUNET_break (0);
304     GNUNET_free (pkc->ub);
305     if (NULL != pkc->dsh)
306     {
307       GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
308       pkc->dsh = NULL;
309     }
310     GNUNET_free (pkc);
311     cont (cont_cls, NULL, _("Internal error."));
312     return NULL;
313   }
314   size = sizeof (struct UBlock) + pkc->slen + pkc->mdsize + 1;
315   pkc->cpy = GNUNET_malloc (size);
316   pkc->ksk_uri = GNUNET_FS_uri_dup (ksk_uri);
317   pkc->ksk_task = GNUNET_SCHEDULER_add_now (&publish_ksk_cont, pkc);
318   return pkc;
319 }
320
321
322 /**
323  * Abort the KSK publishing operation.
324  *
325  * @param pkc context of the operation to abort.
326  */
327 void
328 GNUNET_FS_publish_ksk_cancel (struct GNUNET_FS_PublishKskContext *pkc)
329 {
330   if (GNUNET_SCHEDULER_NO_TASK != pkc->ksk_task)
331   {
332     GNUNET_SCHEDULER_cancel (pkc->ksk_task);
333     pkc->ksk_task = GNUNET_SCHEDULER_NO_TASK;
334   }
335   if (NULL != pkc->qre)
336   {
337     GNUNET_DATASTORE_cancel (pkc->qre);
338     pkc->qre = NULL;
339   }
340   if (NULL != pkc->dsh)
341   {
342     GNUNET_DATASTORE_disconnect (pkc->dsh, GNUNET_NO);
343     pkc->dsh = NULL;
344   }
345   GNUNET_free (pkc->cpy);
346   GNUNET_free (pkc->ub);
347   GNUNET_FS_uri_destroy (pkc->ksk_uri);
348   GNUNET_free (pkc);
349 }
350
351
352 /* end of fs_publish_ksk.c */