fe991241646453a34c0742f163aff9dc146ccb76
[oweals/gnunet.git] / src / psycutil / psyc_slicer.c
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 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., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * @author Gabor X Toth
23  *
24  * @file
25  * PSYC Slicer API
26  */
27
28 #include <inttypes.h>
29
30 #include "platform.h"
31 #include "gnunet_util_lib.h"
32 #include "gnunet_psyc_util_lib.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "psyc-util-slicer",__VA_ARGS__)
35
36
37 /**
38  * Handle for a try-and-slice instance.
39  */
40 struct GNUNET_PSYC_Slicer
41 {
42   /**
43    * Method handlers: H(method_name) -> SlicerMethodCallbacks
44    */
45   struct GNUNET_CONTAINER_MultiHashMap *method_handlers;
46
47   /**
48    * Modifier handlers: H(modifier_name) -> SlicerModifierCallbacks
49    */
50   struct GNUNET_CONTAINER_MultiHashMap *modifier_handlers;
51
52   /**
53    * Currently being processed message part.
54    */
55   const struct GNUNET_MessageHeader *msg;
56
57   /**
58    * ID of currently being received message.
59    */
60   uint64_t message_id;
61
62   /**
63    * Method name of currently being received message.
64    */
65   char *method_name;
66
67   /**
68    * Name of currently processed modifier.
69    */
70   char *mod_name;
71
72   /**
73    * Value of currently processed modifier.
74    */
75   char *mod_value;
76
77   /**
78    * Public key of the nym the current message originates from.
79    */
80   struct GNUNET_CRYPTO_EcdsaPublicKey nym_pub_key;
81
82   /**
83    * Size of @a method_name (including terminating \0).
84    */
85   uint16_t method_name_size;
86
87   /**
88    * Size of @a modifier_name (including terminating \0).
89    */
90   uint16_t mod_name_size;
91
92   /**
93    * Size of modifier value fragment.
94    */
95   uint16_t mod_value_size;
96
97   /**
98    * Full size of modifier value.
99    */
100   uint16_t mod_full_value_size;
101
102   /**
103    * Remaining bytes from the value of the current modifier.
104    */
105   uint16_t mod_value_remaining;
106
107   /**
108    * Operator of currently processed modifier.
109    */
110   uint8_t mod_oper;
111 };
112
113
114 /**
115  * Callbacks for a slicer method handler.
116  */
117 struct SlicerMethodCallbacks
118 {
119   GNUNET_PSYC_MethodCallback method_cb;
120   GNUNET_PSYC_ModifierCallback modifier_cb;
121   GNUNET_PSYC_DataCallback data_cb;
122   GNUNET_PSYC_EndOfMessageCallback eom_cb;
123   void *cls;
124 };
125
126
127 struct SlicerMethodRemoveClosure
128 {
129   struct GNUNET_PSYC_Slicer *slicer;
130   struct SlicerMethodCallbacks rm_cbs;
131 };
132
133
134 /**
135  * Callbacks for a slicer method handler.
136  */
137 struct SlicerModifierCallbacks
138 {
139   GNUNET_PSYC_ModifierCallback modifier_cb;
140   void *cls;
141 };
142
143
144 struct SlicerModifierRemoveClosure
145 {
146   struct GNUNET_PSYC_Slicer *slicer;
147   struct SlicerModifierCallbacks rm_cbs;
148 };
149
150
151 /**
152  * Call a method handler for an incoming message part.
153  */
154 int
155 slicer_method_handler_notify (void *cls, const struct GNUNET_HashCode *key,
156                               void *value)
157 {
158   struct GNUNET_PSYC_Slicer *slicer = cls;
159   const struct GNUNET_MessageHeader *msg = slicer->msg;
160   struct SlicerMethodCallbacks *cbs = value;
161   uint16_t ptype = ntohs (msg->type);
162
163   switch (ptype)
164   {
165   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
166   {
167     if (NULL == cbs->method_cb)
168       break;
169     struct GNUNET_PSYC_MessageMethod *
170       meth = (struct GNUNET_PSYC_MessageMethod *) msg;
171     cbs->method_cb (cbs->cls, meth, slicer->message_id,
172                     ntohl (meth->flags),
173                     &slicer->nym_pub_key,
174                     slicer->method_name);
175     break;
176   }
177
178   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
179   {
180     if (NULL == cbs->modifier_cb)
181       break;
182     struct GNUNET_PSYC_MessageModifier *
183       mod = (struct GNUNET_PSYC_MessageModifier *) msg;
184     cbs->modifier_cb (cbs->cls, &mod->header, slicer->message_id,
185                       mod->oper, (const char *) &mod[1],
186                       (const void *) &mod[1] + ntohs (mod->name_size),
187                       ntohs (mod->header.size) - sizeof (*mod) - ntohs (mod->name_size),
188                       ntohs (mod->value_size));
189     break;
190   }
191
192   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
193   {
194     if (NULL == cbs->modifier_cb)
195       break;
196     cbs->modifier_cb (cbs->cls, msg, slicer->message_id,
197                       slicer->mod_oper, slicer->mod_name, &msg[1],
198                       ntohs (msg->size) - sizeof (*msg),
199                       slicer->mod_full_value_size);
200     break;
201   }
202
203   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
204   {
205     if (NULL == cbs->data_cb)
206       break;
207     uint64_t data_offset = 0; // FIXME
208     cbs->data_cb (cbs->cls, msg, slicer->message_id,
209                   data_offset, &msg[1], ntohs (msg->size) - sizeof (*msg));
210     break;
211   }
212
213   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
214     if (NULL == cbs->eom_cb)
215       break;
216     cbs->eom_cb (cbs->cls, msg, slicer->message_id, GNUNET_NO);
217     break;
218
219   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
220     if (NULL == cbs->eom_cb)
221       break;
222     cbs->eom_cb (cbs->cls, msg, slicer->message_id, GNUNET_YES);
223     break;
224   }
225   return GNUNET_YES;
226 }
227
228
229 /**
230  * Call a method handler for an incoming message part.
231  */
232 int
233 slicer_modifier_handler_notify (void *cls, const struct GNUNET_HashCode *key,
234                                 void *value)
235 {
236   struct GNUNET_PSYC_Slicer *slicer = cls;
237   struct SlicerModifierCallbacks *cbs = value;
238
239   cbs->modifier_cb (cbs->cls, slicer->msg, slicer->message_id, slicer->mod_oper,
240                     slicer->mod_name, slicer->mod_value,
241                     slicer->mod_value_size, slicer->mod_full_value_size);
242   return GNUNET_YES;
243 }
244
245
246 /**
247  * Process an incoming message part and call matching handlers.
248  *
249  * @param cls
250  *        Closure.
251  * @param message_id
252  *        ID of the message.
253  * @param flags
254  *        Flags for the message.
255  *        @see enum GNUNET_PSYC_MessageFlags
256  * @param msg
257  *        The message part. as it arrived from the network.
258  */
259 void
260 GNUNET_PSYC_slicer_message (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_pub_key,
261                             uint64_t message_id, uint32_t flags, uint64_t fragment_offset,
262                             const struct GNUNET_MessageHeader *msg)
263 {
264   struct GNUNET_PSYC_Slicer *slicer = cls;
265   slicer->nym_pub_key = *slave_pub_key;
266
267   uint16_t ptype = ntohs (msg->type);
268   if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype)
269   {
270     struct GNUNET_PSYC_MessageMethod *
271       meth = (struct GNUNET_PSYC_MessageMethod *) msg;
272     slicer->method_name_size = ntohs (meth->header.size) - sizeof (*meth);
273     slicer->method_name = GNUNET_malloc (slicer->method_name_size);
274     memcpy (slicer->method_name, &meth[1], slicer->method_name_size);
275     slicer->message_id = message_id;
276   }
277   else
278   {
279     GNUNET_assert (message_id == slicer->message_id);
280   }
281
282   char *nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (slave_pub_key);
283   LOG (GNUNET_ERROR_TYPE_DEBUG,
284        "Slicer received message of type %u and size %u, "
285        "with ID %" PRIu64 " and method %s from %s\n",
286        ptype, ntohs (msg->size), message_id, slicer->method_name, nym_str);
287   GNUNET_free (nym_str);
288
289   slicer->msg = msg;
290
291   /* try-and-slice modifier */
292
293   switch (ptype)
294   {
295   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
296   {
297     struct GNUNET_PSYC_MessageModifier *
298       mod = (struct GNUNET_PSYC_MessageModifier *) msg;
299     slicer->mod_oper = mod->oper;
300     slicer->mod_name_size = ntohs (mod->name_size);
301     slicer->mod_name = GNUNET_malloc (slicer->mod_name_size);
302     memcpy (slicer->mod_name, &mod[1], slicer->mod_name_size);
303     slicer->mod_value = (char *) &mod[1] + slicer->mod_name_size;
304     slicer->mod_full_value_size = ntohs (mod->value_size);
305     slicer->mod_value_remaining = slicer->mod_full_value_size;
306     slicer->mod_value_size
307       = ntohs (mod->header.size) - sizeof (*mod) - slicer->mod_name_size;
308   }
309   case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
310     if (ptype == GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT)
311     {
312       slicer->mod_value = (char *) &msg[1];
313       slicer->mod_value_size = ntohs (msg->size) - sizeof (*msg);
314     }
315     slicer->mod_value_remaining -= slicer->mod_value_size;
316     char *name = GNUNET_malloc (slicer->mod_name_size);
317     memcpy (name, slicer->mod_name, slicer->mod_name_size);
318     do
319     {
320       struct GNUNET_HashCode key;
321       uint16_t name_len = strlen (name);
322       GNUNET_CRYPTO_hash (name, name_len, &key);
323       GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, &key,
324                                                   slicer_modifier_handler_notify,
325                                                   slicer);
326       char *p = strrchr (name, '_');
327       if (NULL == p)
328         break;
329       *p = '\0';
330     } while (1);
331     GNUNET_free (name);
332   }
333
334   /* try-and-slice method */
335
336   char *name = GNUNET_malloc (slicer->method_name_size);
337   memcpy (name, slicer->method_name, slicer->method_name_size);
338   do
339   {
340     struct GNUNET_HashCode key;
341     uint16_t name_len = strlen (name);
342     GNUNET_CRYPTO_hash (name, name_len, &key);
343     GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key,
344                                                 slicer_method_handler_notify,
345                                                 slicer);
346     char *p = strrchr (name, '_');
347     if (NULL == p)
348       break;
349     *p = '\0';
350   } while (1);
351   GNUNET_free (name);
352
353   if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
354     GNUNET_free (slicer->method_name);
355
356   if (0 == slicer->mod_value_remaining && NULL != slicer->mod_name)
357   {
358     GNUNET_free (slicer->mod_name);
359     slicer->mod_name = NULL;
360     slicer->mod_name_size = 0;
361     slicer->mod_value_size = 0;
362     slicer->mod_full_value_size = 0;
363     slicer->mod_oper = 0;
364   }
365
366   slicer->msg = NULL;
367 }
368
369
370 /**
371  * Create a try-and-slice instance.
372  *
373  * A slicer processes incoming messages and notifies callbacks about matching
374  * methods or modifiers encountered.
375  *
376  * @return A new try-and-slice construct.
377  */
378 struct GNUNET_PSYC_Slicer *
379 GNUNET_PSYC_slicer_create (void)
380 {
381   struct GNUNET_PSYC_Slicer *slicer = GNUNET_malloc (sizeof (*slicer));
382   slicer->method_handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
383   slicer->modifier_handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
384   return slicer;
385 }
386
387
388 /**
389  * Add a method to the try-and-slice instance.
390  *
391  * The callbacks are called for messages with a matching @a method_name prefix.
392  *
393  * @param slicer
394  *        The try-and-slice instance to extend.
395  * @param method_name
396  *        Name of the given method, use empty string to match all.
397  * @param method_cb
398  *        Method handler invoked upon a matching message.
399  * @param modifier_cb
400  *        Modifier handler, invoked after @a method_cb
401  *        for each modifier in the message.
402  * @param data_cb
403  *        Data handler, invoked after @a modifier_cb for each data fragment.
404  * @param eom_cb
405  *        Invoked upon reaching the end of a matching message.
406  * @param cls
407  *        Closure for the callbacks.
408  */
409 void
410 GNUNET_PSYC_slicer_method_add (struct GNUNET_PSYC_Slicer *slicer,
411                                const char *method_name,
412                                GNUNET_PSYC_MethodCallback method_cb,
413                                GNUNET_PSYC_ModifierCallback modifier_cb,
414                                GNUNET_PSYC_DataCallback data_cb,
415                                GNUNET_PSYC_EndOfMessageCallback eom_cb,
416                                void *cls)
417 {
418   struct GNUNET_HashCode key;
419   GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
420
421   struct SlicerMethodCallbacks *cbs = GNUNET_malloc (sizeof (*cbs));
422   cbs->method_cb = method_cb;
423   cbs->modifier_cb = modifier_cb;
424   cbs->data_cb = data_cb;
425   cbs->eom_cb = eom_cb;
426   cbs->cls = cls;
427
428   GNUNET_CONTAINER_multihashmap_put (slicer->method_handlers, &key, cbs,
429                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
430 }
431
432
433 int
434 slicer_method_remove (void *cls, const struct GNUNET_HashCode *key, void *value)
435 {
436   struct SlicerMethodRemoveClosure *rm_cls = cls;
437   struct GNUNET_PSYC_Slicer *slicer = rm_cls->slicer;
438   struct SlicerMethodCallbacks *rm_cbs = &rm_cls->rm_cbs;
439   struct SlicerMethodCallbacks *cbs = value;
440
441   if (cbs->method_cb == rm_cbs->method_cb
442       && cbs->modifier_cb == rm_cbs->modifier_cb
443       && cbs->data_cb == rm_cbs->data_cb
444       && cbs->eom_cb == rm_cbs->eom_cb)
445   {
446     GNUNET_CONTAINER_multihashmap_remove (slicer->method_handlers, key, cbs);
447     GNUNET_free (cbs);
448     return GNUNET_NO;
449   }
450   return GNUNET_YES;
451 }
452
453
454 /**
455  * Remove a registered method from the try-and-slice instance.
456  *
457  * Removes one matching handler registered with the given
458  * @a method_name and  callbacks.
459  *
460  * @param slicer
461  *        The try-and-slice instance.
462  * @param method_name
463  *        Name of the method to remove.
464  * @param method_cb
465  *        Method handler.
466  * @param modifier_cb
467  *        Modifier handler.
468  * @param data_cb
469  *        Data handler.
470  * @param eom_cb
471  *        End of message handler.
472  *
473  * @return #GNUNET_OK if a method handler was removed,
474  *         #GNUNET_NO if no handler matched the given method name and callbacks.
475  */
476 int
477 GNUNET_PSYC_slicer_method_remove (struct GNUNET_PSYC_Slicer *slicer,
478                                   const char *method_name,
479                                   GNUNET_PSYC_MethodCallback method_cb,
480                                   GNUNET_PSYC_ModifierCallback modifier_cb,
481                                   GNUNET_PSYC_DataCallback data_cb,
482                                   GNUNET_PSYC_EndOfMessageCallback eom_cb)
483 {
484   struct GNUNET_HashCode key;
485   GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
486
487   struct SlicerMethodRemoveClosure rm_cls;
488   rm_cls.slicer = slicer;
489   struct SlicerMethodCallbacks *rm_cbs = &rm_cls.rm_cbs;
490   rm_cbs->method_cb = method_cb;
491   rm_cbs->modifier_cb = modifier_cb;
492   rm_cbs->data_cb = data_cb;
493   rm_cbs->eom_cb = eom_cb;
494
495   return
496     (GNUNET_SYSERR
497      == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key,
498                                                     slicer_method_remove,
499                                                     &rm_cls))
500     ? GNUNET_NO
501     : GNUNET_OK;
502 }
503
504
505 /**
506  * Watch a place for changed objects.
507  *
508  * @param slicer
509  *        The try-and-slice instance.
510  * @param object_filter
511  *        Object prefix to match.
512  * @param modifier_cb
513  *        Function to call when encountering a state modifier.
514  * @param cls
515  *        Closure for callback.
516  */
517 void
518 GNUNET_PSYC_slicer_modifier_add (struct GNUNET_PSYC_Slicer *slicer,
519                                  const char *object_filter,
520                                  GNUNET_PSYC_ModifierCallback modifier_cb,
521                                  void *cls)
522 {
523   struct SlicerModifierCallbacks *cbs = GNUNET_malloc (sizeof *cbs);
524   cbs->modifier_cb = modifier_cb;
525   cbs->cls = cls;
526
527   struct GNUNET_HashCode key;
528   GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key);
529   GNUNET_CONTAINER_multihashmap_put (slicer->modifier_handlers, &key, cbs,
530                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
531 }
532
533
534 int
535 slicer_modifier_remove (void *cls, const struct GNUNET_HashCode *key, void *value)
536 {
537   struct SlicerModifierRemoveClosure *rm_cls = cls;
538   struct GNUNET_PSYC_Slicer *slicer = rm_cls->slicer;
539   struct SlicerModifierCallbacks *rm_cbs = &rm_cls->rm_cbs;
540   struct SlicerModifierCallbacks *cbs = value;
541
542   if (cbs->modifier_cb == rm_cbs->modifier_cb)
543   {
544     GNUNET_CONTAINER_multihashmap_remove (slicer->modifier_handlers, key, cbs);
545     GNUNET_free (cbs);
546     return GNUNET_NO;
547   }
548   return GNUNET_YES;
549 }
550
551
552 /**
553  * Remove a registered modifier from the try-and-slice instance.
554  *
555  * Removes one matching handler registered with the given
556  * @a object_filter and @a modifier_cb.
557  *
558  * @param slicer
559  *        The try-and-slice instance.
560  * @param object_filter
561  *        Object prefix to match.
562  * @param modifier_cb
563  *        Function to call when encountering a state modifier changes.
564  */
565 int
566 GNUNET_PSYC_slicer_modifier_remove (struct GNUNET_PSYC_Slicer *slicer,
567                                     const char *object_filter,
568                                     GNUNET_PSYC_ModifierCallback modifier_cb)
569 {
570   struct GNUNET_HashCode key;
571   GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key);
572
573   struct SlicerModifierRemoveClosure rm_cls;
574   rm_cls.slicer = slicer;
575   struct SlicerModifierCallbacks *rm_cbs = &rm_cls.rm_cbs;
576   rm_cbs->modifier_cb = modifier_cb;
577
578   return
579     (GNUNET_SYSERR
580      == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, &key,
581                                                     slicer_modifier_remove,
582                                                     &rm_cls))
583     ? GNUNET_NO
584     : GNUNET_OK;
585  }
586
587
588 int
589 slicer_method_free (void *cls, const struct GNUNET_HashCode *key, void *value)
590 {
591   struct SlicerMethodCallbacks *cbs = value;
592   GNUNET_free (cbs);
593   return GNUNET_YES;
594 }
595
596
597 /**
598  * Destroy a given try-and-slice instance.
599  *
600  * @param slicer
601  *        Slicer to destroy
602  */
603 void
604 GNUNET_PSYC_slicer_destroy (struct GNUNET_PSYC_Slicer *slicer)
605 {
606   GNUNET_CONTAINER_multihashmap_iterate (slicer->method_handlers,
607                                          slicer_method_free, NULL);
608   GNUNET_CONTAINER_multihashmap_destroy (slicer->method_handlers);
609   GNUNET_free (slicer);
610 }