-fix'
[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, &copy->key, copy,
458                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)
459                );
460   len = ntohl (block->n_proof);
461   {
462     char proof[len + 1];
463
464     memcpy (proof, &block[1], len);
465     proof[len] = '\0';
466     if (GNUNET_OK != GNUNET_REGEX_check_proof (proof, key))
467     {
468       GNUNET_break_op (0);
469       return;
470     }
471   }
472   len = strlen (info->description);
473   if (len == ctx->position) // String processed
474   {
475     if (GNUNET_YES == ntohl (block->accepting))
476     {
477       regex_find_path (key, ctx);
478     }
479     else
480     {
481       LOG (GNUNET_ERROR_TYPE_INFO, "  block not accepting!\n");
482       // FIXME REGEX this block not successful, wait for more? start timeout?
483     }
484     return;
485   }
486
487   regex_next_edge (block, size, ctx);
488
489   return;
490 }
491
492
493 /**
494  * Iterator over found existing mesh regex blocks that match an ongoing search.
495  *
496  * @param cls Closure (current context)-
497  * @param key Current key code (key for cached block).
498  * @param value Value in the hash map (cached RegexBlock).
499  * @return GNUNET_YES: we should always continue to iterate.
500  */
501 static int
502 regex_result_iterator (void *cls,
503                        const struct GNUNET_HashCode * key,
504                        void *value)
505 {
506   struct RegexBlock *block = value;
507   struct RegexSearchContext *ctx = cls;
508
509   if (GNUNET_YES == ntohl(block->accepting) &&
510       ctx->position == strlen (ctx->info->description))
511   {
512     LOG (GNUNET_ERROR_TYPE_INFO, " * Found accepting known block\n");
513     regex_find_path (key, ctx);
514     return GNUNET_YES; // We found an accept state!
515   }
516   LOG (GNUNET_ERROR_TYPE_DEBUG, "* %u, %u, [%u]\n",
517        ctx->position, strlen(ctx->info->description),
518        ntohl(block->accepting));
519
520   regex_next_edge (block, SIZE_MAX, ctx);
521
522   GNUNET_STATISTICS_update (ctx->info->stats, "# regex mesh blocks iterated",
523                             1, GNUNET_NO);
524
525   return GNUNET_YES;
526 }
527
528
529 /**
530  * Iterator over edges in a regex block retrieved from the DHT.
531  *
532  * @param cls Closure (context of the search).
533  * @param token Token that follows to next state.
534  * @param len Lenght of token.
535  * @param key Hash of next state.
536  *
537  * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
538  */
539 static int
540 regex_edge_iterator (void *cls,
541                      const char *token,
542                      size_t len,
543                      const struct GNUNET_HashCode *key)
544 {
545   struct RegexSearchContext *ctx = cls;
546   struct GNUNET_REGEX_search_handle *info = ctx->info;
547   const char *current;
548   size_t current_len;
549
550   GNUNET_STATISTICS_update (info->stats, "# regex edges iterated",
551                             1, GNUNET_NO);
552
553   LOG (GNUNET_ERROR_TYPE_DEBUG, "*    Start of regex edge iterator\n");
554   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     descr : %s\n", info->description);
555   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     posit : %u\n", ctx->position);
556   current = &info->description[ctx->position];
557   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     currt : %s\n", current);
558   current_len = strlen (info->description) - ctx->position;
559   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     ctlen : %u\n", current_len);
560   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     tklen : %u\n", len);
561   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     token : %.*s\n", len, token);
562   LOG (GNUNET_ERROR_TYPE_DEBUG, "*     nextk : %s\n", GNUNET_h2s(key));
563   if (len > current_len)
564   {
565     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token too long, END\n");
566     return GNUNET_YES; // Token too long, wont match
567   }
568   if (0 != strncmp (current, token, len))
569   {
570     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token doesn't match, END\n");
571     return GNUNET_YES; // Token doesn't match
572   }
573
574   if (len > ctx->longest_match)
575   {
576     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token is longer, KEEP\n");
577     ctx->longest_match = len;
578     ctx->hash = *key;
579   }
580   else
581   {
582     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     Token is not longer, IGNORE\n");
583   }
584
585   LOG (GNUNET_ERROR_TYPE_DEBUG, "*    End of regex edge iterator\n");
586   return GNUNET_YES;
587 }
588
589
590 /**
591  * Jump to the next edge, with the longest matching token.
592  *
593  * @param block Block found in the DHT.
594  * @param size Size of the block.
595  * @param ctx Context of the search.
596  *
597  * @return GNUNET_YES if should keep iterating, GNUNET_NO otherwise.
598  */
599 static void
600 regex_next_edge (const struct RegexBlock *block,
601                  size_t size,
602                  struct RegexSearchContext *ctx)
603 {
604   struct RegexSearchContext *new_ctx;
605   struct GNUNET_REGEX_search_handle *info = ctx->info;
606   struct GNUNET_DHT_GetHandle *get_h;
607   struct GNUNET_HashCode *hash;
608   const char *rest;
609   int result;
610
611   /* Find the longest match for the current string position, 
612    * among tokens in the given block */
613   ctx->longest_match = 0;
614   result = GNUNET_REGEX_block_iterate (block, size,
615                                        &regex_edge_iterator, ctx);
616   GNUNET_break (GNUNET_OK == result);
617
618   /* Did anything match? */
619   if (0 == ctx->longest_match)
620   {
621     LOG (GNUNET_ERROR_TYPE_DEBUG, "  no match in block\n");
622     return;
623   }
624
625   hash = &ctx->hash;
626   new_ctx = GNUNET_malloc (sizeof (struct RegexSearchContext));
627   new_ctx->info = info;
628   new_ctx->position = ctx->position + ctx->longest_match;
629   GNUNET_array_append (info->contexts, info->n_contexts, new_ctx);
630
631   /* Check whether we already have a DHT GET running for it */
632   if (GNUNET_YES ==
633       GNUNET_CONTAINER_multihashmap_contains (info->dht_get_handles, hash))
634   {
635     LOG (GNUNET_ERROR_TYPE_DEBUG, "*     GET running, END\n");
636     GNUNET_CONTAINER_multihashmap_get_multiple (info->dht_get_results,
637                                                 hash,
638                                                 &regex_result_iterator,
639                                                 new_ctx);
640     return; /* We are already looking for it */
641   }
642
643   GNUNET_STATISTICS_update (info->stats, "# regex nodes traversed",
644                             1, GNUNET_NO);
645
646   /* Start search in DHT */
647   LOG (GNUNET_ERROR_TYPE_INFO, "   looking for %s\n", GNUNET_h2s (hash));
648   rest = &new_ctx->info->description[new_ctx->position];
649   get_h = 
650       GNUNET_DHT_get_start (info->dht,    /* handle */
651                             GNUNET_BLOCK_TYPE_REGEX, /* type */
652                             hash,     /* key to search */
653                             DHT_REPLICATION, /* replication level */
654                             DHT_OPT,
655                             rest, /* xquery */
656                             // FIXME add BLOOMFILTER to exclude filtered peers
657                             strlen(rest) + 1,     /* xquery bits */
658                             // FIXME add BLOOMFILTER SIZE
659                             &dht_get_string_handler, new_ctx);
660   if (GNUNET_OK !=
661       GNUNET_CONTAINER_multihashmap_put(info->dht_get_handles,
662                                         hash,
663                                         get_h,
664                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
665   {
666     GNUNET_break (0);
667     return;
668   }
669 }
670
671
672 struct GNUNET_REGEX_search_handle *
673 GNUNET_REGEX_search (struct GNUNET_DHT_Handle *dht,
674                      const char *string,
675                      GNUNET_REGEX_Found callback,
676                      void *callback_cls,
677                      struct GNUNET_STATISTICS_Handle *stats)
678 {
679   struct GNUNET_REGEX_search_handle *h;
680   struct GNUNET_DHT_GetHandle *get_h;
681   struct RegexSearchContext *ctx;
682   struct GNUNET_HashCode key;
683   size_t size;
684   size_t len;
685
686   /* Initialize handle */
687   LOG (GNUNET_ERROR_TYPE_INFO, "GNUNET_REGEX_search: %s\n", string);
688   GNUNET_assert (NULL != dht);
689   GNUNET_assert (NULL != callback);
690   h = GNUNET_malloc (sizeof (struct GNUNET_REGEX_search_handle));
691   h->dht = dht;
692   h->description = GNUNET_strdup (string);
693   h->callback = callback;
694   h->callback_cls = callback_cls;
695   h->stats = stats;
696   h->dht_get_handles = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO);
697   h->dht_get_results = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_YES);
698
699   /* Initialize context */
700   len = strlen (string);
701   size = GNUNET_REGEX_get_first_key (string, len, &key);
702   ctx = GNUNET_malloc (sizeof (struct RegexSearchContext));
703   ctx->position = size;
704   ctx->info = h;
705   GNUNET_array_append (h->contexts, h->n_contexts, ctx);
706   LOG (GNUNET_ERROR_TYPE_DEBUG, "  consumed %u bits out of %u\n", size, len);
707   LOG (GNUNET_ERROR_TYPE_INFO, "   looking for %s\n", GNUNET_h2s (&key));
708
709   /* Start search in DHT */
710   get_h = GNUNET_DHT_get_start (h->dht,    /* handle */
711                                 GNUNET_BLOCK_TYPE_REGEX, /* type */
712                                 &key,     /* key to search */
713                                 DHT_REPLICATION, /* replication level */
714                                 DHT_OPT,
715                                 &h->description[size],           /* xquery */
716                                 // FIXME add BLOOMFILTER to exclude filtered peers
717                                 len + 1 - size,                /* xquery bits */
718                                 // FIXME add BLOOMFILTER SIZE
719                                 &dht_get_string_handler, ctx);
720   GNUNET_break (
721     GNUNET_OK ==
722     GNUNET_CONTAINER_multihashmap_put (h->dht_get_handles,
723                                        &key,
724                                        get_h,
725                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)
726                );
727
728   return h;
729 }
730
731
732 /**
733  * Iterator over hash map entries to cancel DHT GET requests after a
734  * successful connect_by_string.
735  *
736  * @param cls Closure (unused).
737  * @param key Current key code (unused).
738  * @param value Value in the hash map (get handle).
739  * @return GNUNET_YES if we should continue to iterate,
740  *         GNUNET_NO if not.
741  */
742 static int
743 regex_cancel_dht_get (void *cls,
744                       const struct GNUNET_HashCode * key,
745                       void *value)
746 {
747   struct GNUNET_DHT_GetHandle *h = value;
748
749   GNUNET_DHT_get_stop (h);
750   return GNUNET_YES;
751 }
752
753
754 /**
755  * Iterator over hash map entries to free MeshRegexBlocks stored during the
756  * search for connect_by_string.
757  *
758  * @param cls Closure (unused).
759  * @param key Current key code (unused).
760  * @param value MeshRegexBlock in the hash map.
761  * @return GNUNET_YES if we should continue to iterate,
762  *         GNUNET_NO if not.
763  */
764 static int
765 regex_free_result (void *cls,
766                    const struct GNUNET_HashCode * key,
767                    void *value)
768 {
769
770   GNUNET_free (value);
771   return GNUNET_YES;
772 }
773
774
775 /**
776  * Cancel an ongoing regex search in the DHT and free all resources.
777  *
778  * @param ctx The search context.
779  */
780 static void
781 regex_cancel_search (struct GNUNET_REGEX_search_handle *ctx)
782 {
783   GNUNET_free (ctx->description);
784   GNUNET_CONTAINER_multihashmap_iterate (ctx->dht_get_handles,
785                                          &regex_cancel_dht_get, NULL);
786   GNUNET_CONTAINER_multihashmap_iterate (ctx->dht_get_results,
787                                          &regex_free_result, NULL);
788   GNUNET_CONTAINER_multihashmap_destroy (ctx->dht_get_results);
789   GNUNET_CONTAINER_multihashmap_destroy (ctx->dht_get_handles);
790   if (0 < ctx->n_contexts)
791   {
792     int i;
793
794     for (i = 0; i < ctx->n_contexts; i++)
795     {
796       GNUNET_free (ctx->contexts[i]);
797     }
798     GNUNET_free (ctx->contexts);
799   }
800 }
801
802 void
803 GNUNET_REGEX_search_cancel (struct GNUNET_REGEX_search_handle *h)
804 {
805   regex_cancel_search (h);
806   GNUNET_free (h);
807 }
808
809
810
811 /* end of regex_dht.c */