missing check
[oweals/gnunet.git] / src / regex / regex_api.c
1 /*
2      This file is part of GNUnet
3      (C) 2012, 2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file regex/regex_api.c
22  * @brief access regex service to advertise capabilities via regex and discover
23  *        respective peers using matching strings
24  * @author Maximilian Szengel
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_regex_service.h"
31 #include "regex_ipc.h"
32
33 /**
34  * Handle to store cached data about a regex announce.
35  */
36 struct GNUNET_REGEX_Announcement
37 {
38   /**
39    * Connection to the regex service.
40    */
41   struct GNUNET_CLIENT_Connection *client;
42
43   /** 
44    * Our configuration.
45    */
46   const struct GNUNET_CONFIGURATION_Handle *cfg;
47   
48   /**
49    * Message we're sending to the service.
50    */
51   struct AnnounceMessage msg;
52 };
53
54
55 /**
56  * We got a response (!?) or disconnect after asking regex
57  * to do the announcement.  Retry.
58  * 
59  * @param cls the 'struct GNUNET_REGEX_Announcement' to retry
60  * @param msg NULL on disconnect
61  */
62 static void
63 handle_a_reconnect (void *cls,
64                     const struct GNUNET_MessageHeader *msg);
65
66
67 /**
68  * Try sending the announcement request to regex.  On 
69  * errors (i.e. regex died), try again.
70  *
71  * @param a the announcement to retry
72  */
73 static void
74 retry_announcement (struct GNUNET_REGEX_Announcement *a)
75 {
76   GNUNET_assert (NULL != a->client);
77   GNUNET_assert (GNUNET_OK ==
78                  GNUNET_CLIENT_transmit_and_get_response (a->client,
79                                                           &a->msg.header,
80                                                           GNUNET_TIME_UNIT_FOREVER_REL,
81                                                           GNUNET_YES,
82                                                           &handle_a_reconnect,
83                                                           a));
84 }
85
86
87 /**
88  * We got a response (!?) or disconnect after asking regex
89  * to do the announcement.  Retry.
90  * 
91  * @param cls the 'struct GNUNET_REGEX_Announcement' to retry
92  * @param msg NULL on disconnect
93  */
94 static void
95 handle_a_reconnect (void *cls,
96                     const struct GNUNET_MessageHeader *msg)
97 {
98   struct GNUNET_REGEX_Announcement *a = cls;
99
100   GNUNET_CLIENT_disconnect (a->client);
101   a->client = GNUNET_CLIENT_connect ("regex", a->cfg);
102   retry_announcement (a);
103 }
104
105
106 /**
107  * Announce the given peer under the given regular expression.  Does
108  * not free resources, must call GNUNET_REGEX_announce_cancel for
109  * that.
110  * 
111  * @param cfg configuration to use
112  * @param regex Regular expression to announce.
113  * @param refresh_delay after what delay should the announcement be repeated?
114  * @param compression How many characters per edge can we squeeze?
115  * @return Handle to reuse o free cached resources.
116  *         Must be freed by calling GNUNET_REGEX_announce_cancel.
117  */
118 struct GNUNET_REGEX_Announcement *
119 GNUNET_REGEX_announce (const struct GNUNET_CONFIGURATION_Handle *cfg,
120                        const char *regex,
121                        struct GNUNET_TIME_Relative refresh_delay,
122                        uint16_t compression)
123 {
124   struct GNUNET_REGEX_Announcement *a;
125   size_t slen;
126
127   slen = strlen (regex) + 1;
128   if (slen + sizeof (struct AnnounceMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
129   {
130     GNUNET_break (0);
131     return NULL;
132   }
133   a = GNUNET_malloc (sizeof (struct GNUNET_REGEX_Announcement) + slen);
134   a->cfg = cfg;
135   a->client = GNUNET_CLIENT_connect ("regex", cfg);
136   if (NULL == a->client)
137     return NULL;
138   a->msg.header.type = htons (GNUNET_MESSAGE_TYPE_REGEX_ANNOUNCE);
139   a->msg.header.size = htons (slen + sizeof (struct AnnounceMessage));
140   a->msg.compression = htons (compression);
141   a->msg.reserved = htons (0);
142   a->msg.refresh_delay = GNUNET_TIME_relative_hton (refresh_delay);
143   memcpy (&a[1], regex, slen);
144   retry_announcement (a);
145   return a;
146 }
147
148
149 /**
150  * Stop announcing the regex specified by the given handle.
151  * 
152  * @param a handle returned by a previous GNUNET_REGEX_announce call.
153  */
154 void
155 GNUNET_REGEX_announce_cancel (struct GNUNET_REGEX_Announcement *a)
156 {
157   GNUNET_CLIENT_disconnect (a->client);
158   GNUNET_free (a);
159 }
160
161
162 /**
163  * Handle to store data about a regex search.
164  */
165 struct GNUNET_REGEX_Search
166 {
167   /**
168    * Connection to the regex service.
169    */
170   struct GNUNET_CLIENT_Connection *client;
171
172   /** 
173    * Our configuration.
174    */
175   const struct GNUNET_CONFIGURATION_Handle *cfg;
176   
177   /**
178    * Function to call with results.
179    */
180   GNUNET_REGEX_Found callback;
181
182   /**
183    * Closure for 'callback'.
184    */
185   void *callback_cls;
186   
187   /**
188    * Search message to transmit to the service.
189    */
190   struct SearchMessage *msg;
191 };
192
193
194 /**
195  * We got a response or disconnect after asking regex
196  * to do the search.  Handle it.
197  * 
198  * @param cls the 'struct GNUNET_REGEX_Search' to retry
199  * @param msg NULL on disconnect
200  */
201 static void
202 handle_search_response (void *cls,
203                         const struct GNUNET_MessageHeader *msg);
204
205
206 /**
207  * Try sending the search request to regex.  On 
208  * errors (i.e. regex died), try again.
209  *
210  * @param s the search to retry
211  */
212 static void
213 retry_search (struct GNUNET_REGEX_Search *s)
214 {
215   GNUNET_assert (NULL != s->client);
216   GNUNET_assert (GNUNET_OK ==
217                  GNUNET_CLIENT_transmit_and_get_response (s->client,
218                                                           &s->msg->header,
219                                                           GNUNET_TIME_UNIT_FOREVER_REL,
220                                                           GNUNET_YES,
221                                                           &handle_search_response,
222                                                           s));
223 }
224
225
226 /**
227  * We got a response or disconnect after asking regex
228  * to do the search.  Handle it.
229  * 
230  * @param cls the 'struct GNUNET_REGEX_Search' to retry
231  * @param msg NULL on disconnect, otherwise presumably a response
232  */
233 static void
234 handle_search_response (void *cls,
235                         const struct GNUNET_MessageHeader *msg)
236 {
237   struct GNUNET_REGEX_Search *s = cls;
238   const struct ResultMessage *result;
239   uint16_t size;
240   uint16_t gpl;
241   uint16_t ppl;
242
243   if (NULL == msg)
244   {
245     GNUNET_CLIENT_disconnect (s->client);
246     s->client = GNUNET_CLIENT_connect ("regex", s->cfg);
247     retry_search (s);
248     return;
249   }
250   size = ntohs (msg->size);
251   if ( (GNUNET_MESSAGE_TYPE_REGEX_RESULT == ntohs (msg->type)) &&
252        (size >= sizeof (struct ResultMessage)) )
253   {
254     result = (const struct ResultMessage *) msg;
255     gpl = ntohs (result->get_path_length);
256     ppl = ntohs (result->put_path_length);
257     if (size == (sizeof (struct ResultMessage) + 
258                  (gpl + ppl) * sizeof (struct GNUNET_PeerIdentity)))
259     {
260       const struct GNUNET_PeerIdentity *pid;
261
262       GNUNET_CLIENT_receive (s->client,
263                              &handle_search_response, s,
264                              GNUNET_TIME_UNIT_FOREVER_REL);
265       pid = &result->id;
266       s->callback (s->callback_cls,
267                    pid,
268                    &pid[1], gpl,
269                    &pid[1 + gpl], ppl);            
270       return;
271     }
272   }
273   GNUNET_break (0);
274   GNUNET_CLIENT_disconnect (s->client);
275   s->client = GNUNET_CLIENT_connect ("regex", s->cfg);
276   retry_search (s);
277 }
278
279
280 /**
281  * Search for a peer offering a regex matching certain string in the DHT.
282  * The search runs until GNUNET_REGEX_search_cancel is called, even if results
283  * are returned.
284  *
285  * @param cfg configuration to use
286  * @param string String to match against the regexes in the DHT.
287  * @param callback Callback for found peers.
288  * @param callback_cls Closure for @c callback.
289  * @return Handle to stop search and free resources.
290  *         Must be freed by calling GNUNET_REGEX_search_cancel.
291  */
292 struct GNUNET_REGEX_Search *
293 GNUNET_REGEX_search (const struct GNUNET_CONFIGURATION_Handle *cfg,
294                      const char *string,
295                      GNUNET_REGEX_Found callback,
296                      void *callback_cls)
297 {
298   struct GNUNET_REGEX_Search *s;
299   size_t slen;
300
301   slen = strlen (string) + 1;
302   s = GNUNET_malloc (sizeof (struct GNUNET_REGEX_Search));
303   s->cfg = cfg;
304   s->client = GNUNET_CLIENT_connect ("regex", cfg);
305   if (NULL == s->client)
306     return NULL;
307   s->callback = callback;
308   s->callback_cls = callback_cls;
309   s->msg = GNUNET_malloc (sizeof (struct SearchMessage) + slen);
310   s->msg->header.type = htons (GNUNET_MESSAGE_TYPE_REGEX_SEARCH);
311   s->msg->header.size = htons (sizeof (struct SearchMessage) + slen);
312   memcpy (&s->msg[1], string, slen);
313   retry_search (s);
314   return s;
315 }
316
317
318 /**
319  * Stop search and free all data used by a GNUNET_REGEX_search call.
320  * 
321  * @param s Handle returned by a previous GNUNET_REGEX_search call.
322  */
323 void
324 GNUNET_REGEX_search_cancel (struct GNUNET_REGEX_Search *s)
325 {
326   GNUNET_CLIENT_disconnect (s->client);
327   GNUNET_free (s->msg);
328   GNUNET_free (s);
329 }
330
331
332 /* end of regex_api.c */