- simplify start
[oweals/gnunet.git] / src / regex / regex_dht.c
1 /*
2      This file is part of GNUnet
3      (C) 2012 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  * @file src/regex/regex_dht.c
22  * @brief library to announce regexes in the network and match strings
23  * against published regexes.
24  * @author Bartlomiej Polot
25  */
26 #include "platform.h"
27 #include "gnunet_regex_lib.h"
28 #include "regex_block_lib.h"
29 #include "gnunet_dht_service.h"
30 #include "gnunet_statistics_service.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind,"regex-dht",__VA_ARGS__)
33
34 /* FIXME: OPTION (API, CONFIG) */
35 #define DHT_REPLICATION 5
36 #define DHT_TTL         GNUNET_TIME_UNIT_HOURS
37 #define DEBUG_DHT       GNUNET_YES
38
39 #if DEBUG_DHT
40 #define DHT_OPT         GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE | GNUNET_DHT_RO_RECORD_ROUTE
41 #else
42 #define DHT_OPT         GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE
43 #endif
44
45 struct GNUNET_REGEX_announce_handle
46 {
47   /**
48    * DHT handle to use, must be initialized externally.
49    */
50   struct GNUNET_DHT_Handle *dht;
51
52   /**
53    * Regular expression.
54    */
55   const char *regex;
56
57   /**
58    * Automaton representation of the regex (expensive to build).
59    */
60   struct GNUNET_REGEX_Automaton* dfa;
61
62   /**
63    * Identity under which to announce the regex.
64    */
65   struct GNUNET_PeerIdentity *id;
66
67   /**
68    * Optional statistics handle to report usage. Can be NULL.
69    */
70   struct GNUNET_STATISTICS_Handle *stats;
71 };
72
73
74 /**
75  * Regex callback iterator to store own service description in the DHT.
76  *
77  * @param cls closure.
78  * @param key hash for current state.
79  * @param proof proof for current state.
80  * @param accepting GNUNET_YES if this is an accepting state, GNUNET_NO if not.
81  * @param num_edges number of edges leaving current state.
82  * @param edges edges leaving current state.
83  */
84 static void
85 regex_iterator (void *cls,
86                 const struct GNUNET_HashCode *key,
87                 const char *proof,
88                 int accepting,
89                 unsigned int num_edges,
90                 const struct GNUNET_REGEX_Edge *edges)
91 {
92   struct GNUNET_REGEX_announce_handle *h = cls;
93   struct RegexBlock *block;
94   struct RegexEdge *block_edge;
95   size_t size;
96   size_t len;
97   unsigned int i;
98   unsigned int offset;
99   char *aux;
100
101   LOG (GNUNET_ERROR_TYPE_DEBUG,
102        "  regex dht put for state %s\n",
103        GNUNET_h2s (key));
104   LOG (GNUNET_ERROR_TYPE_DEBUG, "   proof: %s\n", proof);
105   LOG (GNUNET_ERROR_TYPE_DEBUG, "   num edges: %u\n", num_edges);
106
107   if (GNUNET_YES == accepting)
108   {
109     struct RegexAccept block;
110
111     LOG (GNUNET_ERROR_TYPE_DEBUG,
112          "   state %s is accepting, putting own id\n",
113          GNUNET_h2s(key));
114     size = sizeof (block);
115     block.key = *key;
116     block.id = *(h->id);
117     GNUNET_STATISTICS_update (h->stats, "# regex accepting blocks stored",
118                               1, GNUNET_NO);
119     GNUNET_STATISTICS_update (h->stats, "# regex accepting block bytes stored",
120                               sizeof (block), GNUNET_NO);
121     (void)
122     GNUNET_DHT_put (h->dht, key,
123                     DHT_REPLICATION,
124                     DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE,
125                     GNUNET_BLOCK_TYPE_REGEX_ACCEPT,
126                     size,
127                     (char *) &block,
128                     GNUNET_TIME_relative_to_absolute (DHT_TTL),
129                     DHT_TTL,
130                     NULL, NULL);
131   }
132   len = strlen(proof);
133   size = sizeof (struct RegexBlock) + len;
134   block = GNUNET_malloc (size);
135
136   block->key = *key;
137   block->n_proof = htonl (len);
138   block->n_edges = htonl (num_edges);
139   block->accepting = htonl (accepting);
140
141   /* Store the proof at the end of the block. */
142   aux = (char *) &block[1];
143   memcpy (aux, proof, len);
144   aux = &aux[len];
145
146   /* Store each edge in a variable length MeshEdge struct at the
147    * very end of the MeshRegexBlock structure.
148    */
149   for (i = 0; i < num_edges; i++)
150   {
151     LOG (GNUNET_ERROR_TYPE_DEBUG, "    edge %s towards %s\n",
152          edges[i].label, GNUNET_h2s(&edges[i].destination));
153
154     /* aux points at the end of the last block */
155     len = strlen (edges[i].label);
156     size += sizeof (struct RegexEdge) + len;
157     // Calculate offset FIXME is this ok? use size instead?
158     offset = aux - (char *) block;
159     block = GNUNET_realloc (block, size);
160     aux = &((char *) block)[offset];
161     block_edge = (struct RegexEdge *) aux;
162     block_edge->key = edges[i].destination;
163     block_edge->n_token = htonl (len);
164     aux = (char *) &block_edge[1];
165     memcpy (aux, edges[i].label, len);
166     aux = &aux[len];
167   }
168   (void)
169   GNUNET_DHT_put (h->dht, key,
170                   DHT_REPLICATION,
171                   DHT_OPT,
172                   GNUNET_BLOCK_TYPE_REGEX, size,
173                   (char *) block,
174                   GNUNET_TIME_relative_to_absolute (DHT_TTL),
175                   DHT_TTL,
176                   NULL, NULL);
177   GNUNET_STATISTICS_update (h->stats, "# regex blocks stored",
178                             1, GNUNET_NO);
179   GNUNET_STATISTICS_update (h->stats, "# regex block bytes stored",
180                             size, GNUNET_NO);
181   
182   GNUNET_free (block);
183 }
184
185
186 struct GNUNET_REGEX_announce_handle *
187 GNUNET_REGEX_announce (struct GNUNET_DHT_Handle *dht,
188                        struct GNUNET_PeerIdentity *id,
189                        const char *regex,
190                        uint16_t compression,
191                        struct GNUNET_STATISTICS_Handle *stats)
192 {
193   struct GNUNET_REGEX_announce_handle *h;
194
195   GNUNET_assert (NULL != dht);
196   h = GNUNET_malloc (sizeof (struct GNUNET_REGEX_announce_handle));
197   h->regex = regex;
198   h->dht = dht;
199   h->stats = stats;
200   h->id = id;
201   h->dfa = GNUNET_REGEX_construct_dfa (regex,
202                                        strlen (regex),
203                                        compression);
204   GNUNET_REGEX_reannounce (h);
205   return h;
206 }
207
208 void
209 GNUNET_REGEX_reannounce (struct GNUNET_REGEX_announce_handle *h)
210 {
211   LOG (GNUNET_ERROR_TYPE_INFO, "GNUNET_REGEX_reannounce: %s\n", h->regex);
212   GNUNET_REGEX_iterate_all_edges (h->dfa, &regex_iterator, h);
213 }
214
215 void
216 GNUNET_REGEX_announce_cancel (struct GNUNET_REGEX_announce_handle *h)
217 {
218   GNUNET_REGEX_automaton_destroy (h->dfa);
219   GNUNET_free (h);
220 }
221
222
223 /******************************************************************************/
224
225
226 /**
227  * Struct to keep state of running searches that have consumed a part of
228  * the inital string.
229  */
230 struct RegexSearchContext
231 {
232     /**
233      * Part of the description already consumed by
234      * this particular search branch.
235      */
236   size_t position;
237
238     /**
239      * Information about the search.
240      */
241   struct GNUNET_REGEX_search_handle *info;
242
243     /**
244      * We just want to look for one edge, the longer the better.
245      * Keep its length.
246      */
247   unsigned int longest_match;
248
249     /**
250      * Destination hash of the longest match.
251      */
252   struct GNUNET_HashCode hash;
253 };
254
255
256 /**
257  * Struct to keep information of searches of services described by a regex
258  * using a user-provided string service description.
259  */
260 struct GNUNET_REGEX_search_handle
261 {
262     /**
263      * DHT handle to use, must be initialized externally.
264      */
265   struct GNUNET_DHT_Handle *dht;
266
267     /**
268      * Optional statistics handle to report usage. Can be NULL.
269      */
270   struct GNUNET_STATISTICS_Handle *stats;
271
272     /**
273      * User provided description of the searched service.
274      */
275   char *description;
276
277     /**
278      * Running DHT GETs.
279      */
280   struct GNUNET_CONTAINER_MultiHashMap *dht_get_handles;
281
282     /**
283      * Results from running DHT GETs.
284      */
285   struct GNUNET_CONTAINER_MultiHashMap *dht_get_results;
286
287     /**
288      * Contexts, for each running DHT GET. Free all on end of search.
289      */
290   struct RegexSearchContext **contexts;
291
292     /**
293      * Number of contexts (branches/steps in search).
294      */
295   unsigned int n_contexts;
296   
297   /**
298    * @param callback Callback for found peers.
299    */
300   GNUNET_REGEX_Found callback;
301
302   /**
303    * @param callback_cls Closure for @c callback.
304    */
305   void *callback_cls;
306 };
307
308
309
310 /**
311  * Jump to the next edge, with the longest matching token.
312  *
313  * @param block Block found in the DHT.
314  * @param size Size of the block.
315  * @param ctx Context of the search.
316  *
317  * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
318  */
319 static void
320 regex_next_edge (const struct RegexBlock *block,
321                  size_t size,
322                  struct RegexSearchContext *ctx);
323
324
325 /**
326  * Function to process DHT string to regex matching.
327  * Called on each result obtained for the DHT search.
328  *
329  * @param cls Closure (search context).
330  * @param exp When will this value expire.
331  * @param key Key of the result.
332  * @param get_path Path of the get request.
333  * @param get_path_length Lenght of get_path.
334  * @param put_path Path of the put request.
335  * @param put_path_length Length of the put_path.
336  * @param type Type of the result.
337  * @param size Number of bytes in data.
338  * @param data Pointer to the result data.
339  */
340 static void
341 dht_get_string_accept_handler (void *cls, struct GNUNET_TIME_Absolute exp,
342                                const struct GNUNET_HashCode * key,
343                                const struct GNUNET_PeerIdentity *get_path,
344                                unsigned int get_path_length,
345                                const struct GNUNET_PeerIdentity *put_path,
346                                unsigned int put_path_length,
347                                enum GNUNET_BLOCK_Type type,
348                                size_t size, const void *data)
349 {
350   const struct RegexAccept *block = data;
351   struct RegexSearchContext *ctx = cls;
352   struct GNUNET_REGEX_search_handle *info = ctx->info;
353
354   LOG (GNUNET_ERROR_TYPE_DEBUG, "Got regex results from DHT!\n");
355   LOG (GNUNET_ERROR_TYPE_INFO, "   accept for %s (key %s)\n",
356        info->description, GNUNET_h2s(key));
357
358   GNUNET_STATISTICS_update (info->stats, "# regex accepting blocks found",
359                             1, GNUNET_NO);
360   GNUNET_STATISTICS_update (info->stats, "# regex accepting block bytes found",
361                             size, GNUNET_NO);
362
363   info->callback (info->callback_cls,
364                   &block->id,
365                   get_path, get_path_length,
366                   put_path, put_path_length);
367
368   return;
369 }
370
371 /**
372  * Find a path to a peer that offers a regex servcie compatible
373  * with a given string.
374  * 
375  * @param key The key of the accepting state.
376  * @param ctx Context containing info about the string, tunnel, etc.
377  */
378 static void
379 regex_find_path (const struct GNUNET_HashCode *key,
380                  struct RegexSearchContext *ctx)
381 {
382   struct GNUNET_DHT_GetHandle *get_h;
383
384   LOG (GNUNET_ERROR_TYPE_DEBUG, "Found peer by service\n");
385   LOG (GNUNET_ERROR_TYPE_INFO, "   find accept for %s\n", GNUNET_h2s (key));
386   get_h = GNUNET_DHT_get_start (ctx->info->dht,    /* handle */
387                                 GNUNET_BLOCK_TYPE_REGEX_ACCEPT, /* type */
388                                 key,     /* key to search */
389                                 DHT_REPLICATION, /* replication level */
390                                 DHT_OPT | GNUNET_DHT_RO_RECORD_ROUTE,
391                                 NULL,       /* xquery */ // FIXME BLOOMFILTER
392                                 0,     /* xquery bits */ // FIXME BLOOMFILTER SIZE
393                                 &dht_get_string_accept_handler, ctx);
394   GNUNET_break (GNUNET_OK ==
395                 GNUNET_CONTAINER_multihashmap_put(ctx->info->dht_get_handles,
396                                                   key,
397                                                   get_h,
398                                                   GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
399 }
400
401
402 /**
403  * Function to process DHT string to regex matching.
404  * Called on each result obtained for the DHT search.
405  *
406  * @param cls closure (search context)
407  * @param exp when will this value expire
408  * @param key key of the result
409  * @param get_path path of the get request (not used)
410  * @param get_path_length lenght of get_path (not used)
411  * @param put_path path of the put request (not used)
412  * @param put_path_length length of the put_path (not used)
413  * @param type type of the result
414  * @param size number of bytes in data
415  * @param data pointer to the result data
416  *
417  * TODO: re-issue the request after certain time? cancel after X results?
418  */
419 static void
420 dht_get_string_handler (void *cls, struct GNUNET_TIME_Absolute exp,
421                         const struct GNUNET_HashCode * key,
422                         const struct GNUNET_PeerIdentity *get_path,
423                         unsigned int get_path_length,
424                         const struct GNUNET_PeerIdentity *put_path,
425                         unsigned int put_path_length,
426                         enum GNUNET_BLOCK_Type type,
427                         size_t size, const void *data)
428 {
429   const struct RegexBlock *block = data;
430   struct RegexSearchContext *ctx = cls;
431   struct GNUNET_REGEX_search_handle *info = ctx->info;
432   void *copy;
433   size_t len;
434   char *datastore;
435
436 #if DEBUG_DHT
437   if (NULL != put_path && 0 != put_path_length)
438   {
439     datastore = GNUNET_strdup (GNUNET_i2s (&put_path[put_path_length - 1]));
440   }
441   else
442   {
443     GNUNET_asprintf (&datastore, "?? %u/%u", put_path_length, get_path_length);
444   }
445 #else
446   datastore = GNUNET_strdup ("N/A");
447 #endif
448
449   LOG (GNUNET_ERROR_TYPE_INFO, " DHT GET result for %s (%s) at %s\n",
450        GNUNET_h2s (key), ctx->info->description, datastore);
451   GNUNET_free (datastore);
452
453   copy = GNUNET_malloc (size);
454   memcpy (copy, data, size);
455   GNUNET_break (
456     GNUNET_OK ==
457     GNUNET_CONTAINER_multihashmap_put (info->dht_get_results,
458                                        &((struct RegexBlock *)copy)->key, copy,
459                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)
460                );
461   len = ntohl (block->n_proof);
462   {
463     char proof[len + 1];
464
465     memcpy (proof, &block[1], len);
466     proof[len] = '\0';
467     if (GNUNET_OK != GNUNET_REGEX_check_proof (proof, key))
468     {
469       GNUNET_break_op (0);
470       return;
471     }
472   }
473   len = strlen (info->description);
474   if (len == ctx->position) // String processed
475   {
476     if (GNUNET_YES == ntohl (block->accepting))
477     {
478       regex_find_path (key, ctx);
479     }
480     else
481     {
482       LOG (GNUNET_ERROR_TYPE_INFO, "  block not accepting!\n");
483       // FIXME REGEX this block not successful, wait for more? start timeout?
484     }
485     return;
486   }
487
488   regex_next_edge (block, size, ctx);
489
490   return;
491 }
492
493
494 /**
495  * Iterator over found existing mesh regex blocks that match an ongoing search.
496  *
497  * @param cls Closure (current context)-
498  * @param key Current key code (key for cached block).
499  * @param value Value in the hash map (cached RegexBlock).
500  * @return GNUNET_YES: we should always continue to iterate.
501  */
502 static int
503 regex_result_iterator (void *cls,
504                        const struct GNUNET_HashCode * key,
505                        void *value)
506 {
507   struct RegexBlock *block = value;
508   struct RegexSearchContext *ctx = cls;
509
510   if (GNUNET_YES == ntohl(block->accepting) &&
511       ctx->position == strlen (ctx->info->description))
512   {
513     LOG (GNUNET_ERROR_TYPE_INFO, " * Found accepting known block\n");
514     regex_find_path (key, ctx);
515     return GNUNET_YES; // We found an accept state!
516   }
517   LOG (GNUNET_ERROR_TYPE_DEBUG, "* %u, %u, [%u]\n",
518        ctx->position, strlen(ctx->info->description),
519        ntohl(block->accepting));
520
521   regex_next_edge (block, SIZE_MAX, ctx);
522
523   GNUNET_STATISTICS_update (ctx->info->stats, "# regex mesh blocks iterated",
524                             1, GNUNET_NO);
525
526   return GNUNET_YES;
527 }
528
529
530 /**
531  * Iterator over edges in a regex block retrieved from the DHT.
532  *
533  * @param cls Closure (context of the search).
534  * @param token Token that follows to next state.
535  * @param len Lenght of token.
536  * @param key Hash of next state.
537  *
538  * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
539  */
540 static int
541 regex_edge_iterator (void *cls,
542                      const char *token,
543                      size_t len,
544                      const struct GNUNET_HashCode *key)
545 {
546   struct RegexSearchContext *ctx = cls;
547   struct GNUNET_REGEX_search_handle *info = ctx->info;
548   const char *current;
549   size_t current_len;
550
551   GNUNET_STATISTICS_update (info->stats, "# regex edges iterated",
552                             1, GNUNET_NO);
553
554   LOG (GNUNET_ERROR_TYPE_DEBUG, "*    Start of regex edge iterator\n");
555   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     descr : %s\n", info->description);
556   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     posit : %u\n", ctx->position);
557   current = &info->description[ctx->position];
558   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     currt : %s\n", current);
559   current_len = strlen (info->description) - ctx->position;
560   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     ctlen : %u\n", current_len);
561   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     tklen : %u\n", len);
562   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     token : %.*s\n", len, token);
563   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     nextk : %s\n", GNUNET_h2s(key));
564   if (len > current_len)
565   {
566     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token too long, END\n");
567     return GNUNET_YES; // Token too long, wont match
568   }
569   if (0 != strncmp (current, token, len))
570   {
571     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token doesn't match, END\n");
572     return GNUNET_YES; // Token doesn't match
573   }
574
575   if (len > ctx->longest_match)
576   {
577     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token is longer, KEEP\n");
578     ctx->longest_match = len;
579     ctx->hash = *key;
580   }
581   else
582   {
583     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token is not longer, IGNORE\n");
584   }
585
586   LOG (GNUNET_ERROR_TYPE_DEBUG, "*    End of regex edge iterator\n");
587   return GNUNET_YES;
588 }
589
590
591 /**
592  * Jump to the next edge, with the longest matching token.
593  *
594  * @param block Block found in the DHT.
595  * @param size Size of the block.
596  * @param ctx Context of the search.
597  *
598  * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
599  */
600 static void
601 regex_next_edge (const struct RegexBlock *block,
602                  size_t size,
603                  struct RegexSearchContext *ctx)
604 {
605   struct RegexSearchContext *new_ctx;
606   struct GNUNET_REGEX_search_handle *info = ctx->info;
607   struct GNUNET_DHT_GetHandle *get_h;
608   struct GNUNET_HashCode *hash;
609   const char *rest;
610   int result;
611
612   /* Find the longest match for the current string position, 
613    * among tokens in the given block */
614   ctx->longest_match = 0;
615   result = GNUNET_REGEX_block_iterate (block, size,
616                                        &regex_edge_iterator, ctx);
617   GNUNET_break (GNUNET_OK == result);
618
619   /* Did anything match? */
620   if (0 == ctx->longest_match)
621   {
622     LOG (GNUNET_ERROR_TYPE_DEBUG, "  no match in block\n");
623     return;
624   }
625
626   hash = &ctx->hash;
627   new_ctx = GNUNET_malloc (sizeof (struct RegexSearchContext));
628   new_ctx->info = info;
629   new_ctx->position = ctx->position + ctx->longest_match;
630   GNUNET_array_append (info->contexts, info->n_contexts, new_ctx);
631
632   /* Check whether we already have a DHT GET running for it */
633   if (GNUNET_YES ==
634       GNUNET_CONTAINER_multihashmap_contains (info->dht_get_handles, hash))
635   {
636     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     GET for %s running, END\n",
637          GNUNET_h2s (hash));
638     GNUNET_CONTAINER_multihashmap_get_multiple (info->dht_get_results,
639                                                 hash,
640                                                 &regex_result_iterator,
641                                                 new_ctx);
642     return; /* We are already looking for it */
643   }
644
645   GNUNET_STATISTICS_update (info->stats, "# regex nodes traversed",
646                             1, GNUNET_NO);
647
648   /* Start search in DHT */
649   LOG (GNUNET_ERROR_TYPE_INFO, "   looking for %s\n", GNUNET_h2s (hash));
650   rest = &new_ctx->info->description[new_ctx->position];
651   get_h = 
652       GNUNET_DHT_get_start (info->dht,    /* handle */
653                             GNUNET_BLOCK_TYPE_REGEX, /* type */
654                             hash,     /* key to search */
655                             DHT_REPLICATION, /* replication level */
656                             DHT_OPT,
657                             rest, /* xquery */
658                             // FIXME add BLOOMFILTER to exclude filtered peers
659                             strlen(rest) + 1,     /* xquery bits */
660                             // FIXME add BLOOMFILTER SIZE
661                             &dht_get_string_handler, new_ctx);
662   if (GNUNET_OK !=
663       GNUNET_CONTAINER_multihashmap_put(info->dht_get_handles,
664                                         hash,
665                                         get_h,
666                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
667   {
668     GNUNET_break (0);
669     return;
670   }
671 }
672
673
674 struct GNUNET_REGEX_search_handle *
675 GNUNET_REGEX_search (struct GNUNET_DHT_Handle *dht,
676                      const char *string,
677                      GNUNET_REGEX_Found callback,
678                      void *callback_cls,
679                      struct GNUNET_STATISTICS_Handle *stats)
680 {
681   struct GNUNET_REGEX_search_handle *h;
682   struct GNUNET_DHT_GetHandle *get_h;
683   struct RegexSearchContext *ctx;
684   struct GNUNET_HashCode key;
685   size_t size;
686   size_t len;
687
688   /* Initialize handle */
689   LOG (GNUNET_ERROR_TYPE_INFO, "GNUNET_REGEX_search: %s\n", string);
690   GNUNET_assert (NULL != dht);
691   GNUNET_assert (NULL != callback);
692   h = GNUNET_malloc (sizeof (struct GNUNET_REGEX_search_handle));
693   h->dht = dht;
694   h->description = GNUNET_strdup (string);
695   h->callback = callback;
696   h->callback_cls = callback_cls;
697   h->stats = stats;
698   h->dht_get_handles = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
699   h->dht_get_results = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_YES);
700
701   /* Initialize context */
702   len = strlen (string);
703   size = GNUNET_REGEX_get_first_key (string, len, &key);
704   ctx = GNUNET_malloc (sizeof (struct RegexSearchContext));
705   ctx->position = size;
706   ctx->info = h;
707   GNUNET_array_append (h->contexts, h->n_contexts, ctx);
708   LOG (GNUNET_ERROR_TYPE_DEBUG, "  consumed %u bits out of %u\n", size, len);
709   LOG (GNUNET_ERROR_TYPE_INFO, "   looking for %s\n", GNUNET_h2s (&key));
710
711   /* Start search in DHT */
712   get_h = GNUNET_DHT_get_start (h->dht,    /* handle */
713                                 GNUNET_BLOCK_TYPE_REGEX, /* type */
714                                 &key,     /* key to search */
715                                 DHT_REPLICATION, /* replication level */
716                                 DHT_OPT,
717                                 &h->description[size],           /* xquery */
718                                 // FIXME add BLOOMFILTER to exclude filtered peers
719                                 len + 1 - size,                /* xquery bits */
720                                 // FIXME add BLOOMFILTER SIZE
721                                 &dht_get_string_handler, ctx);
722   GNUNET_break (
723     GNUNET_OK ==
724     GNUNET_CONTAINER_multihashmap_put (h->dht_get_handles,
725                                        &key,
726                                        get_h,
727                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)
728                );
729
730   return h;
731 }
732
733
734 /**
735  * Iterator over hash map entries to cancel DHT GET requests after a
736  * successful connect_by_string.
737  *
738  * @param cls Closure (unused).
739  * @param key Current key code (unused).
740  * @param value Value in the hash map (get handle).
741  * @return GNUNET_YES if we should continue to iterate,
742  *         GNUNET_NO if not.
743  */
744 static int
745 regex_cancel_dht_get (void *cls,
746                       const struct GNUNET_HashCode * key,
747                       void *value)
748 {
749   struct GNUNET_DHT_GetHandle *h = value;
750
751   GNUNET_DHT_get_stop (h);
752   return GNUNET_YES;
753 }
754
755
756 /**
757  * Iterator over hash map entries to free MeshRegexBlocks stored during the
758  * search for connect_by_string.
759  *
760  * @param cls Closure (unused).
761  * @param key Current key code (unused).
762  * @param value MeshRegexBlock in the hash map.
763  * @return GNUNET_YES if we should continue to iterate,
764  *         GNUNET_NO if not.
765  */
766 static int
767 regex_free_result (void *cls,
768                    const struct GNUNET_HashCode * key,
769                    void *value)
770 {
771
772   GNUNET_free (value);
773   return GNUNET_YES;
774 }
775
776
777 /**
778  * Cancel an ongoing regex search in the DHT and free all resources.
779  *
780  * @param ctx The search context.
781  */
782 static void
783 regex_cancel_search (struct GNUNET_REGEX_search_handle *ctx)
784 {
785   GNUNET_free (ctx->description);
786   GNUNET_CONTAINER_multihashmap_iterate (ctx->dht_get_handles,
787                                          &regex_cancel_dht_get, NULL);
788   GNUNET_CONTAINER_multihashmap_iterate (ctx->dht_get_results,
789                                          &regex_free_result, NULL);
790   GNUNET_CONTAINER_multihashmap_destroy (ctx->dht_get_results);
791   GNUNET_CONTAINER_multihashmap_destroy (ctx->dht_get_handles);
792   if (0 < ctx->n_contexts)
793   {
794     int i;
795
796     for (i = 0; i < ctx->n_contexts; i++)
797     {
798       GNUNET_free (ctx->contexts[i]);
799     }
800     GNUNET_free (ctx->contexts);
801   }
802 }
803
804 void
805 GNUNET_REGEX_search_cancel (struct GNUNET_REGEX_search_handle *h)
806 {
807   regex_cancel_search (h);
808   GNUNET_free (h);
809 }
810
811
812
813 /* end of regex_dht.c */