aboutsummaryrefslogtreecommitdiff
path: root/src/gns/plugin_rest_gns.c
diff options
context:
space:
mode:
authorMartin Schanzenbach <mschanzenbach@posteo.de>2015-09-14 11:10:11 +0000
committerMartin Schanzenbach <mschanzenbach@posteo.de>2015-09-14 11:10:11 +0000
commitb0a03eeebfdedbb7b0070ef00514f321aafa49db (patch)
tree06233b8dc0c1bf8ee580367e772c9951a3d7cee6 /src/gns/plugin_rest_gns.c
parent134da182130592c78bdb5d348826825665a2ffab (diff)
downloadgnunet-b0a03eeebfdedbb7b0070ef00514f321aafa49db.tar.gz
gnunet-b0a03eeebfdedbb7b0070ef00514f321aafa49db.zip
- revert plugin move. Add new identity-token
Diffstat (limited to 'src/gns/plugin_rest_gns.c')
-rw-r--r--src/gns/plugin_rest_gns.c720
1 files changed, 720 insertions, 0 deletions
diff --git a/src/gns/plugin_rest_gns.c b/src/gns/plugin_rest_gns.c
new file mode 100644
index 000000000..3ebbfb925
--- /dev/null
+++ b/src/gns/plugin_rest_gns.c
@@ -0,0 +1,720 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 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 * @author Martin Schanzenbach
22 * @file gns/plugin_rest_gns.c
23 * @brief GNUnet GNS REST plugin
24 *
25 */
26
27#include "platform.h"
28#include "gnunet_rest_plugin.h"
29#include <gnunet_dnsparser_lib.h>
30#include <gnunet_identity_service.h>
31#include <gnunet_gnsrecord_lib.h>
32#include <gnunet_namestore_service.h>
33#include <gnunet_gns_service.h>
34#include <gnunet_rest_lib.h>
35#include <jansson.h>
36
37#define GNUNET_REST_API_NS_GNS "/gns"
38
39#define GNUNET_REST_JSONAPI_GNS_RECORD_TYPE "record_type"
40
41#define GNUNET_REST_JSONAPI_GNS_TYPEINFO "gns_name"
42
43#define GNUNET_REST_JSONAPI_GNS_RECORD "records"
44
45#define GNUNET_REST_JSONAPI_GNS_EGO "ego"
46
47#define GNUNET_REST_JSONAPI_GNS_PKEY "pkey"
48
49#define GNUNET_REST_JSONAPI_GNS_OPTIONS "options"
50
51/**
52 * @brief struct returned by the initialization function of the plugin
53 */
54struct Plugin
55{
56 const struct GNUNET_CONFIGURATION_Handle *cfg;
57};
58
59const struct GNUNET_CONFIGURATION_Handle *cfg;
60
61struct LookupHandle
62{
63 /**
64 * Handle to GNS service.
65 */
66 struct GNUNET_GNS_Handle *gns;
67
68 /**
69 * Desired timeout for the lookup (default is no timeout).
70 */
71 struct GNUNET_TIME_Relative timeout;
72
73 /**
74 * Handle to lookup request
75 */
76 struct GNUNET_GNS_LookupRequest *lookup_request;
77
78 /**
79 * Lookup an ego with the identity service.
80 */
81 struct GNUNET_IDENTITY_EgoLookup *el;
82
83 /**
84 * Handle for identity service.
85 */
86 struct GNUNET_IDENTITY_Handle *identity;
87
88 /**
89 * Active operation on identity service.
90 */
91 struct GNUNET_IDENTITY_Operation *id_op;
92
93 /**
94 * ID of a task associated with the resolution process.
95 */
96 struct GNUNET_SCHEDULER_Task * timeout_task;
97
98 /**
99 * The root of the received JSON or NULL
100 */
101 json_t *json_root;
102
103 /**
104 * The plugin result processor
105 */
106 GNUNET_REST_ResultProcessor proc;
107
108 /**
109 * The closure of the result processor
110 */
111 void *proc_cls;
112
113 /**
114 * The name to look up
115 */
116 char *name;
117
118 /**
119 * The ego to use
120 * In string representation from JSON
121 */
122 const char *ego_str;
123
124 /**
125 * The Pkey to use
126 * In string representation from JSON
127 */
128 const char *pkey_str;
129
130 /**
131 * The record type
132 */
133 int type;
134
135 /**
136 * The public key of to use for lookup
137 */
138 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
139
140 /**
141 * The public key to use for lookup
142 */
143 struct GNUNET_CRYPTO_EcdsaPublicKey pkeym;
144
145 /**
146 * The resolver options
147 */
148 enum GNUNET_GNS_LocalOptions options;
149
150 /**
151 * the shorten key
152 */
153 struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
154
155};
156
157
158/**
159 * Cleanup lookup handle.
160 *
161 * @param handle Handle to clean up
162 */
163static void
164cleanup_handle (struct LookupHandle *handle)
165{
166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
167 "Cleaning up\n");
168 if (NULL != handle->json_root)
169 json_decref (handle->json_root);
170
171 if (NULL != handle->name)
172 GNUNET_free (handle->name);
173 if (NULL != handle->el)
174 {
175 GNUNET_IDENTITY_ego_lookup_cancel (handle->el);
176 handle->el = NULL;
177 }
178 if (NULL != handle->id_op)
179 {
180 GNUNET_IDENTITY_cancel (handle->id_op);
181 handle->id_op = NULL;
182 }
183 if (NULL != handle->lookup_request)
184 {
185 GNUNET_GNS_lookup_cancel (handle->lookup_request);
186 handle->lookup_request = NULL;
187 }
188 if (NULL != handle->identity)
189 {
190 GNUNET_IDENTITY_disconnect (handle->identity);
191 handle->identity = NULL;
192 }
193 if (NULL != handle->gns)
194 {
195 GNUNET_GNS_disconnect (handle->gns);
196 handle->gns = NULL;
197 }
198
199 if (NULL != handle->timeout_task)
200 {
201 GNUNET_SCHEDULER_cancel (handle->timeout_task);
202 }
203 GNUNET_free (handle);
204}
205
206
207/**
208 * Task run on shutdown. Cleans up everything.
209 *
210 * @param cls unused
211 * @param tc scheduler context
212 */
213static void
214do_error (void *cls,
215 const struct GNUNET_SCHEDULER_TaskContext *tc)
216{
217 struct LookupHandle *handle = cls;
218 struct MHD_Response *resp = GNUNET_REST_create_json_response (NULL);
219 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
220 cleanup_handle (handle);
221}
222
223
224/**
225 * Create json representation of a GNSRECORD
226 *
227 * @param rd the GNSRECORD_Data
228 */
229static json_t *
230gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd)
231{
232 const char *typename;
233 char *string_val;
234 const char *exp_str;
235 json_t *record_obj;
236
237 typename = GNUNET_GNSRECORD_number_to_typename (rd->record_type);
238 string_val = GNUNET_GNSRECORD_value_to_string (rd->record_type,
239 rd->data,
240 rd->data_size);
241
242 if (NULL == string_val)
243 {
244 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
245 "Record of type %d malformed, skipping\n",
246 (int) rd->record_type);
247 return NULL;
248 }
249 record_obj = json_object();
250 json_object_set_new (record_obj, "type", json_string (typename));
251 json_object_set_new (record_obj, "value", json_string (string_val));
252 GNUNET_free (string_val);
253
254 if (GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION & rd->flags)
255 {
256 struct GNUNET_TIME_Relative time_rel;
257 time_rel.rel_value_us = rd->expiration_time;
258 exp_str = GNUNET_STRINGS_relative_time_to_string (time_rel, 1);
259 }
260 else
261 {
262 struct GNUNET_TIME_Absolute time_abs;
263 time_abs.abs_value_us = rd->expiration_time;
264 exp_str = GNUNET_STRINGS_absolute_time_to_string (time_abs);
265 }
266 json_object_set_new (record_obj, "expiration_time", json_string (exp_str));
267
268 json_object_set_new (record_obj, "expired",
269 json_boolean (GNUNET_YES == GNUNET_GNSRECORD_is_expired (rd)));
270 return record_obj;
271}
272
273/**
274 * Function called with the result of a GNS lookup.
275 *
276 * @param cls the 'const char *' name that was resolved
277 * @param rd_count number of records returned
278 * @param rd array of @a rd_count records with the results
279 */
280static void
281process_lookup_result (void *cls, uint32_t rd_count,
282 const struct GNUNET_GNSRECORD_Data *rd)
283{
284 struct LookupHandle *handle = cls;
285 struct MHD_Response *resp;
286 struct JsonApiObject *json_object;
287 struct JsonApiResource *json_resource;
288 uint32_t i;
289 char *result;
290 json_t *result_array;
291 json_t *record_obj;
292
293 result_array = json_array();
294 json_object = GNUNET_REST_jsonapi_object_new ();
295 json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_GNS_TYPEINFO, handle->name);
296 handle->lookup_request = NULL;
297 for (i=0; i<rd_count; i++)
298 {
299 if ( (rd[i].record_type != handle->type) &&
300 (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
301 continue;
302 record_obj = gnsrecord_to_json (&(rd[i]));
303 json_array_append (result_array, record_obj);
304 json_decref (record_obj);
305 }
306 GNUNET_REST_jsonapi_resource_add_attr (json_resource,
307 GNUNET_REST_JSONAPI_GNS_RECORD,
308 result_array);
309 GNUNET_REST_jsonapi_object_resource_add (json_object, json_resource);
310 GNUNET_REST_jsonapi_data_serialize (json_object, &result);
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
312 json_decref (result_array);
313 GNUNET_REST_jsonapi_object_delete (json_object);
314 resp = GNUNET_REST_create_json_response (result);
315 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
316 GNUNET_free (result);
317 cleanup_handle (handle);
318}
319
320
321/**
322 * Perform the actual resolution, starting with the zone
323 * identified by the given public key and the shorten zone.
324 *
325 * @param pkey public key to use for the zone, can be NULL
326 * @param shorten_key private key used for shortening, can be NULL
327 */
328static void
329lookup_with_keys (struct LookupHandle *handle, const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key)
330{
331 if (UINT32_MAX == handle->type)
332 {
333 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
334 _("Invalid typename specified, assuming `ANY'\n"));
335 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
336 }
337 if (NULL != handle->name)
338 {
339 handle->lookup_request = GNUNET_GNS_lookup (handle->gns,
340 handle->name,
341 &handle->pkey,
342 handle->type,
343 handle->options,
344 shorten_key,
345 &process_lookup_result,
346 handle);
347 }
348 else
349 {
350 GNUNET_SCHEDULER_add_now (&do_error, handle);
351 return;
352 }
353}
354
355/**
356 * Method called to with the ego we are to use for shortening
357 * during the lookup.
358 *
359 * @param cls closure contains the public key to use
360 * @param ego ego handle, NULL if not found
361 * @param ctx context for application to store data for this ego
362 * (during the lifetime of this process, initially NULL)
363 * @param name name assigned by the user for this ego,
364 * NULL if the user just deleted the ego and it
365 * must thus no longer be used
366 */
367static void
368identity_shorten_cb (void *cls,
369 struct GNUNET_IDENTITY_Ego *ego,
370 void **ctx,
371 const char *name)
372{
373 struct LookupHandle *handle = cls;
374
375 handle->id_op = NULL;
376 if (NULL == ego)
377 lookup_with_keys (handle, NULL);
378 else
379 lookup_with_keys (handle,
380 GNUNET_IDENTITY_ego_get_private_key (ego));
381}
382
383/**
384 * Perform the actual resolution, starting with the zone
385 * identified by the given public key.
386 *
387 * @param pkey public key to use for the zone
388 */
389static void
390lookup_with_public_key (struct LookupHandle *handle)
391{
392 handle->pkeym = handle->pkey;
393 GNUNET_break (NULL == handle->id_op);
394 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
395 "gns-short",
396 &identity_shorten_cb,
397 handle);
398 if (NULL == handle->id_op)
399 {
400 GNUNET_break (0);
401 lookup_with_keys (handle, NULL);
402 }
403}
404
405/**
406 * Method called to with the ego we are to use for the lookup,
407 * when the ego is determined by a name.
408 *
409 * @param cls closure (NULL, unused)
410 * @param ego ego handle, NULL if not found
411 */
412static void
413identity_zone_cb (void *cls,
414 const struct GNUNET_IDENTITY_Ego *ego)
415{
416 struct LookupHandle *handle = cls;
417
418 handle->el = NULL;
419 if (NULL == ego)
420 {
421 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
422 _("Ego for not found, cannot perform lookup.\n"));
423 GNUNET_SCHEDULER_add_now (&do_error, handle);
424 return;
425 }
426 else
427 {
428 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
429 lookup_with_public_key (handle);
430 }
431 json_decref(handle->json_root);
432}
433
434/**
435 * Method called to with the ego we are to use for the lookup,
436 * when the ego is the one for the default master zone.
437 *
438 * @param cls closure (NULL, unused)
439 * @param ego ego handle, NULL if not found
440 * @param ctx context for application to store data for this ego
441 * (during the lifetime of this process, initially NULL)
442 * @param name name assigned by the user for this ego,
443 * NULL if the user just deleted the ego and it
444 * must thus no longer be used
445 */
446static void
447identity_master_cb (void *cls,
448 struct GNUNET_IDENTITY_Ego *ego,
449 void **ctx,
450 const char *name)
451{
452 const char *dot;
453 struct LookupHandle *handle = cls;
454
455 handle->id_op = NULL;
456 if (NULL == ego)
457 {
458 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
459 _("Ego for `gns-master' not found, cannot perform lookup. Did you run gnunet-gns-import.sh?\n"));
460 GNUNET_SCHEDULER_add_now (&do_error, handle);
461 return;
462 }
463 GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
464 /* main name is our own master zone, do no look for that in the DHT */
465 handle->options = GNUNET_GNS_LO_LOCAL_MASTER;
466 /* if the name is of the form 'label.gnu', never go to the DHT */
467 dot = NULL;
468 if (NULL != handle->name)
469 dot = strchr (handle->name, '.');
470 if ( (NULL != dot) &&
471 (0 == strcasecmp (dot, ".gnu")) )
472 handle->options = GNUNET_GNS_LO_NO_DHT;
473 lookup_with_public_key (handle);
474}
475
476/**
477 * Parse REST uri for name and record type
478 *
479 * @param url Url to parse
480 * @param handle lookup handle to populate
481 * @return GNUNET_SYSERR on error
482 */
483static int
484parse_url (const char *url, struct LookupHandle *handle)
485{
486 char *name;
487 char tmp_url[strlen(url)+1];
488 char *tok;
489
490 strcpy (tmp_url, url);
491 tok = strtok ((char*)tmp_url, "/");
492 if (NULL == tok)
493 return GNUNET_SYSERR;
494 name = strtok (NULL, "/");
495 if (NULL == name)
496 return GNUNET_SYSERR;
497 GNUNET_asprintf (&handle->name,
498 "%s",
499 name);
500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501 "Got name: %s\n", handle->name);
502 return GNUNET_OK;
503}
504
505static void
506get_gns_cont (struct RestConnectionDataHandle *conndata_handle,
507 const char* url,
508 void *cls)
509{
510 struct LookupHandle *handle = cls;
511 struct GNUNET_HashCode key;
512
513 //parse name and type from url
514 if (GNUNET_OK != parse_url (url, handle))
515 {
516 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing url...\n");
517 GNUNET_SCHEDULER_add_now (&do_error, handle);
518 return;
519 }
520 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
521 "Connecting...\n");
522 handle->gns = GNUNET_GNS_connect (cfg);
523 handle->identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
524 handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
525 &do_error, handle);
526 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
527 "Connected\n");
528 if (NULL == handle->gns)
529 {
530 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
531 "Connecting to GNS failed\n");
532 GNUNET_SCHEDULER_add_now (&do_error, handle);
533 return;
534 }
535 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_OPTIONS,
536 strlen (GNUNET_REST_JSONAPI_GNS_OPTIONS),
537 &key);
538 handle->options = GNUNET_GNS_LO_DEFAULT;
539 if ( GNUNET_YES ==
540 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
541 &key) )
542 {
543 handle->options = GNUNET_GNS_LO_DEFAULT;//TODO(char*) GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
544 //&key);
545 }
546 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE,
547 strlen (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE),
548 &key);
549 if ( GNUNET_YES ==
550 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
551 &key) )
552 {
553 handle->type = GNUNET_GNSRECORD_typename_to_number
554 (GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
555 &key));
556 }
557 else
558 handle->type = GNUNET_GNSRECORD_TYPE_ANY;
559
560 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_PKEY,
561 strlen (GNUNET_REST_JSONAPI_GNS_PKEY),
562 &key);
563 if ( GNUNET_YES ==
564 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
565 &key) )
566 {
567 handle->pkey_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
568 &key);
569 if (GNUNET_OK !=
570 GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->pkey_str,
571 strlen(handle->pkey_str),
572 &(handle->pkey)))
573 {
574 GNUNET_SCHEDULER_add_now (&do_error, handle);
575 return;
576 }
577 lookup_with_public_key (handle);
578 return;
579 }
580 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_EGO,
581 strlen (GNUNET_REST_JSONAPI_GNS_EGO),
582 &key);
583 if ( GNUNET_YES ==
584 GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
585 &key) )
586 {
587 handle->ego_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
588 &key);
589 handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
590 handle->ego_str,
591 &identity_zone_cb,
592 handle);
593 return;
594 }
595 if ( (NULL != handle->name) &&
596 (strlen (handle->name) > 4) &&
597 (0 == strcmp (".zkey",
598 &handle->name[strlen (handle->name) - 4])) )
599 {
600 GNUNET_CRYPTO_ecdsa_key_get_public
601 (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
602 &(handle->pkey));
603 lookup_with_public_key (handle);
604 }
605 else
606 {
607 GNUNET_break (NULL == handle->id_op);
608 handle->id_op = GNUNET_IDENTITY_get (handle->identity,
609 "gns-master",
610 &identity_master_cb,
611 handle);
612 GNUNET_assert (NULL != handle->id_op);
613 }
614}
615
616/**
617 * Handle rest request
618 *
619 * @param handle the lookup handle
620 */
621static void
622options_cont (struct RestConnectionDataHandle *con_handle,
623 const char* url,
624 void *cls)
625{
626 struct MHD_Response *resp;
627 struct LookupHandle *handle = cls;
628
629 //For GNS, independent of path return all options
630 resp = GNUNET_REST_create_json_response (NULL);
631 MHD_add_response_header (resp,
632 "Access-Control-Allow-Methods",
633 MHD_HTTP_METHOD_GET);
634 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
635 cleanup_handle (handle);
636 return;
637}
638
639
640/**
641 * Function processing the REST call
642 *
643 * @param method HTTP method
644 * @param url URL of the HTTP request
645 * @param data body of the HTTP request (optional)
646 * @param data_size length of the body
647 * @param proc callback function for the result
648 * @param proc_cls closure for callback function
649 * @return GNUNET_OK if request accepted
650 */
651static void
652rest_gns_process_request(struct RestConnectionDataHandle *conndata_handle,
653 GNUNET_REST_ResultProcessor proc,
654 void *proc_cls)
655{
656 struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
657
658 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
659 handle->proc_cls = proc_cls;
660 handle->proc = proc;
661
662 static const struct GNUNET_REST_RestConnectionHandler handlers[] = {
663 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont},
664 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont},
665 GNUNET_REST_HANDLER_END
666 };
667
668 if (GNUNET_NO == GNUNET_REST_handle_request (conndata_handle, handlers, handle))
669 GNUNET_SCHEDULER_add_now (&do_error, handle);
670}
671
672
673
674/**
675 * Entry point for the plugin.
676 *
677 * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
678 * @return NULL on error, otherwise the plugin context
679 */
680void *
681libgnunet_plugin_rest_gns_init (void *cls)
682{
683 static struct Plugin plugin;
684 cfg = cls;
685 struct GNUNET_REST_Plugin *api;
686
687 if (NULL != plugin.cfg)
688 return NULL; /* can only initialize once! */
689 memset (&plugin, 0, sizeof (struct Plugin));
690 plugin.cfg = cfg;
691 api = GNUNET_new (struct GNUNET_REST_Plugin);
692 api->cls = &plugin;
693 api->name = GNUNET_REST_API_NS_GNS;
694 api->process_request = &rest_gns_process_request;
695 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
696 _("GNS REST API initialized\n"));
697 return api;
698}
699
700
701/**
702 * Exit point from the plugin.
703 *
704 * @param cls the plugin context (as returned by "init")
705 * @return always NULL
706 */
707void *
708libgnunet_plugin_rest_gns_done (void *cls)
709{
710 struct GNUNET_REST_Plugin *api = cls;
711 struct Plugin *plugin = api->cls;
712
713 plugin->cfg = NULL;
714 GNUNET_free (api);
715 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
716 "GNS REST plugin is finished\n");
717 return NULL;
718}
719
720/* end of plugin_rest_gns.c */