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