aboutsummaryrefslogtreecommitdiff
path: root/src/rest-plugin/namestore/plugin_rest_namestore.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rest-plugin/namestore/plugin_rest_namestore.c')
-rw-r--r--src/rest-plugin/namestore/plugin_rest_namestore.c1364
1 files changed, 1364 insertions, 0 deletions
diff --git a/src/rest-plugin/namestore/plugin_rest_namestore.c b/src/rest-plugin/namestore/plugin_rest_namestore.c
new file mode 100644
index 000000000..31e78e6dd
--- /dev/null
+++ b/src/rest-plugin/namestore/plugin_rest_namestore.c
@@ -0,0 +1,1364 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann
23 * @file namestore/plugin_rest_namestore.c
24 * @brief GNUnet Namestore REST plugin
25 */
26
27#include "platform.h"
28#include "gnunet_error_codes.h"
29#include "gnunet_rest_plugin.h"
30#include "gnunet_gns_service.h"
31#include "gnunet_namestore_service.h"
32#include "gnunet_identity_service.h"
33#include "gnunet_rest_lib.h"
34#include "gnunet_gnsrecord_json_lib.h"
35#include "microhttpd.h"
36#include <jansson.h>
37
38/**
39 * Namestore namespace
40 */
41#define GNUNET_REST_API_NS_NAMESTORE "/namestore"
42
43/**
44 * Namestore import API namespace
45 */
46#define GNUNET_REST_API_NS_NAMESTORE_IMPORT "/namestore/import"
47
48/**
49 * State while collecting all egos
50 */
51#define ID_REST_STATE_INIT 0
52
53/**
54 * Done collecting egos
55 */
56#define ID_REST_STATE_POST_INIT 1
57/**
58 * The configuration handle
59 */
60const struct GNUNET_CONFIGURATION_Handle *cfg;
61
62/**
63 * HTTP methods allows for this plugin
64 */
65static char *allow_methods;
66
67/**
68 * Ego list
69 */
70static struct EgoEntry *ego_head;
71
72/**
73 * Ego list
74 */
75static struct EgoEntry *ego_tail;
76
77/**
78 * The processing state
79 */
80static int state;
81
82/**
83 * Handle to NAMESTORE
84 */
85static struct GNUNET_NAMESTORE_Handle *ns_handle;
86
87/**
88 * Handle to Identity service.
89 */
90static struct GNUNET_IDENTITY_Handle *identity_handle;
91
92/**
93 * @brief struct returned by the initialization function of the plugin
94 */
95struct Plugin
96{
97 const struct GNUNET_CONFIGURATION_Handle *cfg;
98};
99
100/**
101 * The default namestore ego
102 */
103struct EgoEntry
104{
105 /**
106 * DLL
107 */
108 struct EgoEntry *next;
109
110 /**
111 * DLL
112 */
113 struct EgoEntry *prev;
114
115 /**
116 * Ego Identifier
117 */
118 char *identifier;
119
120 /**
121 * Public key string
122 */
123 char *keystring;
124
125 /**
126 * The Ego
127 */
128 struct GNUNET_IDENTITY_Ego *ego;
129};
130
131
132enum UpdateStrategy
133{
134 UPDATE_STRATEGY_REPLACE,
135 UPDATE_STRATEGY_APPEND
136};
137
138/**
139 * The request handle
140 */
141struct RequestHandle
142{
143 /**
144 * DLL
145 */
146 struct RequestHandle *next;
147
148 /**
149 * DLL
150 */
151 struct RequestHandle *prev;
152
153 /**
154 * Records to store
155 */
156 char *record_name;
157
158 /**
159 * Record type filter
160 */
161 uint32_t record_type;
162
163 /**
164 * How to update the record set
165 */
166 enum UpdateStrategy update_strategy;
167
168 /**
169 * Records to store
170 */
171 struct GNUNET_GNSRECORD_Data *rd;
172
173 /**
174 * Number of records in rd
175 */
176 unsigned int rd_count;
177
178 /**
179 * RecordInfo array
180 */
181 struct GNUNET_NAMESTORE_RecordInfo *ri;
182
183 /**
184 * Size of record info
185 */
186 unsigned int rd_set_count;
187
188 /**
189 * Position of record info
190 */
191 unsigned int rd_set_pos;
192
193 /**
194 * NAMESTORE Operation
195 */
196 struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
197
198 /**
199 * For bulk import, we need a dedicated Namestore handle
200 */
201 struct GNUNET_NAMESTORE_Handle *nc;
202
203 /**
204 * Response object
205 */
206 json_t *resp_object;
207
208
209 /**
210 * Handle to NAMESTORE it
211 */
212 struct GNUNET_NAMESTORE_ZoneIterator *list_it;
213
214 /**
215 * Private key for the zone
216 */
217 const struct GNUNET_CRYPTO_PrivateKey *zone_pkey;
218
219 /**
220 * IDENTITY Operation
221 */
222 struct EgoEntry *ego_entry;
223
224 /**
225 * IDENTITY Operation
226 */
227 struct GNUNET_IDENTITY_Operation *op;
228
229 /**
230 * Rest connection
231 */
232 struct GNUNET_REST_RequestHandle *rest_handle;
233
234 /**
235 * Desired timeout for the lookup (default is no timeout).
236 */
237 struct GNUNET_TIME_Relative timeout;
238
239 /**
240 * ID of a task associated with the resolution process.
241 */
242 struct GNUNET_SCHEDULER_Task *timeout_task;
243
244 /**
245 * The plugin result processor
246 */
247 GNUNET_REST_ResultProcessor proc;
248
249 /**
250 * The closure of the result processor
251 */
252 void *proc_cls;
253
254 /**
255 * The url
256 */
257 char *url;
258
259 /**
260 * Error code
261 */
262 enum GNUNET_ErrorCode ec;
263
264};
265
266/**
267 * DLL
268 */
269static struct RequestHandle *requests_head;
270
271/**
272 * DLL
273 */
274static struct RequestHandle *requests_tail;
275
276
277/**
278 * Cleanup lookup handle
279 * @param cls Handle to clean up
280 */
281static void
282cleanup_handle (void *cls)
283{
284 struct RequestHandle *handle = cls;
285
286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
287 if (NULL != handle->timeout_task)
288 {
289 GNUNET_SCHEDULER_cancel (handle->timeout_task);
290 handle->timeout_task = NULL;
291 }
292 if (NULL != handle->record_name)
293 GNUNET_free (handle->record_name);
294 if (NULL != handle->url)
295 GNUNET_free (handle->url);
296 if (NULL != handle->rd)
297 {
298 for (int i = 0; i < handle->rd_count; i++)
299 {
300 if (NULL != handle->rd[i].data)
301 GNUNET_free_nz ((void *) handle->rd[i].data);
302 }
303 GNUNET_free (handle->rd);
304 }
305 if (NULL != handle->timeout_task)
306 GNUNET_SCHEDULER_cancel (handle->timeout_task);
307 if (NULL != handle->list_it)
308 GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it);
309 if (NULL != handle->ns_qe)
310 GNUNET_NAMESTORE_cancel (handle->ns_qe);
311 if (NULL != handle->nc)
312 GNUNET_NAMESTORE_disconnect (handle->nc);
313 if (NULL != handle->resp_object)
314 {
315 json_decref (handle->resp_object);
316 }
317 GNUNET_CONTAINER_DLL_remove (requests_head,
318 requests_tail,
319 handle);
320 GNUNET_free (handle);
321}
322
323
324/**
325 * Task run on errors. Reports an error and cleans up everything.
326 *
327 * @param cls the `struct RequestHandle`
328 */
329static void
330do_error (void *cls)
331{
332 struct RequestHandle *handle = cls;
333 struct MHD_Response *resp;
334 json_t *json_error = json_object ();
335 char *response;
336 const char* emsg;
337 int response_code;
338
339 emsg = GNUNET_ErrorCode_get_hint (handle->ec);
340 json_object_set_new (json_error, "error", json_string (emsg));
341 json_object_set_new (json_error, "error_code", json_integer (handle->ec));
342 response_code = GNUNET_ErrorCode_get_http_status (handle->ec);
343 if (0 == response_code)
344 response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
345 response = json_dumps (json_error, 0);
346 resp = GNUNET_REST_create_response (response);
347 GNUNET_assert (MHD_YES ==
348 MHD_add_response_header (resp, "Content-Type",
349 "application/json"));
350 handle->proc (handle->proc_cls, resp, response_code);
351 json_decref (json_error);
352 GNUNET_free (response);
353 cleanup_handle (handle);
354}
355
356
357/**
358 * Get EgoEntry from list with either a public key or a name
359 * If public key and name are not NULL, it returns the public key result first
360 *
361 * @param handle the RequestHandle
362 * @param pubkey the public key of an identity (only one can be NULL)
363 * @param name the name of an identity (only one can be NULL)
364 * @return EgoEntry or NULL if not found
365 */
366struct EgoEntry *
367get_egoentry_namestore (struct RequestHandle *handle, char *name)
368{
369 struct EgoEntry *ego_entry;
370 char *copy = GNUNET_strdup (name);
371 char *tmp;
372
373 if (NULL == name)
374 return NULL;
375 tmp = strtok (copy, "/");
376 if (NULL == tmp)
377 return NULL;
378 for (ego_entry = ego_head; NULL != ego_entry;
379 ego_entry = ego_entry->next)
380 {
381 if (0 != strcasecmp (tmp, ego_entry->identifier))
382 continue;
383 GNUNET_free (copy);
384 return ego_entry;
385 }
386 GNUNET_free (copy);
387 return NULL;
388}
389
390
391/**
392 * Does internal server error when iteration failed.
393 *
394 * @param cls the `struct RequestHandle`
395 */
396static void
397namestore_iteration_error (void *cls)
398{
399 struct RequestHandle *handle = cls;
400
401 handle->ec = GNUNET_EC_NAMESTORE_ITERATION_FAILED;
402 GNUNET_SCHEDULER_add_now (&do_error, handle);
403 return;
404}
405
406
407static void
408create_finished (void *cls, enum GNUNET_ErrorCode ec)
409{
410 struct RequestHandle *handle = cls;
411 struct MHD_Response *resp;
412
413 handle->ns_qe = NULL;
414 handle->ec = ec;
415 if (GNUNET_EC_NONE != ec)
416 {
417 GNUNET_SCHEDULER_add_now (&do_error, handle);
418 return;
419 }
420 resp = GNUNET_REST_create_response (NULL);
421 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
422 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
423}
424
425
426static void
427del_finished (void *cls, enum GNUNET_ErrorCode ec)
428{
429 struct RequestHandle *handle = cls;
430
431 handle->ns_qe = NULL;
432 handle->ec = ec;
433 if (GNUNET_EC_NONE != ec)
434 {
435 GNUNET_SCHEDULER_add_now (&do_error, handle);
436 return;
437 }
438 handle->proc (handle->proc_cls,
439 GNUNET_REST_create_response (NULL),
440 MHD_HTTP_NO_CONTENT);
441 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
442}
443
444
445/**
446 * Iteration over all results finished, build final
447 * response.
448 *
449 * @param cls the `struct RequestHandle`
450 */
451static void
452namestore_list_finished (void *cls)
453{
454 struct RequestHandle *handle = cls;
455 char *result_str;
456 struct MHD_Response *resp;
457
458 handle->list_it = NULL;
459
460 if (NULL == handle->resp_object)
461 {
462 handle->ec = GNUNET_EC_NAMESTORE_ZONE_EMPTY;
463 GNUNET_SCHEDULER_add_now (&do_error, handle);
464 return;
465 }
466 result_str = json_dumps (handle->resp_object, 0);
467 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
468 resp = GNUNET_REST_create_response (result_str);
469 GNUNET_assert (MHD_YES ==
470 MHD_add_response_header (resp, "Content-Type",
471 "application/json"));
472 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
473 GNUNET_free (result_str);
474 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
475}
476
477
478/**
479 * Create a response with requested records
480 *
481 * @param handle the RequestHandle
482 */
483static void
484namestore_list_iteration (void *cls,
485 const struct GNUNET_CRYPTO_PrivateKey *zone_key,
486 const char *rname,
487 unsigned int rd_len,
488 const struct GNUNET_GNSRECORD_Data *rd,
489 struct GNUNET_TIME_Absolute expiry)
490{
491 struct RequestHandle *handle = cls;
492 struct GNUNET_GNSRECORD_Data rd_filtered[rd_len];
493 json_t *record_obj;
494 int i = 0;
495 int j = 0;
496
497 if (rd_len == 0)
498 {
499 /** skip **/
500 GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
501 return;
502 }
503
504 for (i = 0; i < rd_len; i++)
505 {
506 if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) &&
507 (rd[i].record_type != handle->record_type))
508 continue; /* Apply filter */
509 rd_filtered[j] = rd[i];
510 rd_filtered[j].data = rd[i].data;
511 j++;
512 }
513 /** Only add if not empty **/
514 if (j > 0)
515 {
516 if (NULL == handle->resp_object)
517 handle->resp_object = json_array ();
518 record_obj = GNUNET_GNSRECORD_JSON_from_gnsrecord (rname,
519 rd_filtered,
520 j);
521 json_array_append_new (handle->resp_object, record_obj);
522 }
523 GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
524}
525
526
527/**
528 * Handle lookup error
529 *
530 * @param cls the request handle
531 */
532static void
533ns_lookup_error_cb (void *cls)
534{
535 struct RequestHandle *handle = cls;
536
537 handle->ec = GNUNET_EC_NAMESTORE_LOOKUP_ERROR;
538 GNUNET_SCHEDULER_add_now (&do_error, handle);
539}
540
541
542static void
543ns_get_lookup_cb (void *cls,
544 const struct GNUNET_CRYPTO_PrivateKey *zone,
545 const char *label,
546 unsigned int rd_len,
547 const struct GNUNET_GNSRECORD_Data *rd)
548{
549 struct RequestHandle *handle = cls;
550 struct GNUNET_GNSRECORD_Data rd_filtered[rd_len];
551 int i = 0;
552 int j = 0;
553
554 handle->ns_qe = NULL;
555 for (i = 0; i < rd_len; i++)
556 {
557 if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) &&
558 (rd[i].record_type != handle->record_type))
559 continue; /* Apply filter */
560 rd_filtered[j] = rd[i];
561 rd_filtered[j].data = rd[i].data;
562 j++;
563 }
564 /** Return 404 if no set was found **/
565 if (j == 0)
566 {
567 handle->ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND;
568 GNUNET_SCHEDULER_add_now (&do_error, handle);
569 return;
570 }
571 handle->resp_object = GNUNET_GNSRECORD_JSON_from_gnsrecord (label,
572 rd_filtered,
573 j);
574 GNUNET_SCHEDULER_add_now (&namestore_list_finished, handle);
575}
576
577
578/**
579 * Handle namestore GET request
580 *
581 * @param con_handle the connection handle
582 * @param url the url
583 * @param cls the RequestHandle
584 */
585void
586namestore_get (struct GNUNET_REST_RequestHandle *con_handle,
587 const char *url,
588 void *cls)
589{
590 struct RequestHandle *handle = cls;
591 struct EgoEntry *ego_entry;
592 struct GNUNET_HashCode key;
593 enum GNUNET_GNSRECORD_Filter filter_flags;
594 char *egoname;
595 char *labelname;
596 char *typename;
597 char *boolstring;
598
599 egoname = NULL;
600 ego_entry = NULL;
601
602 // set zone to name if given
603 if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
604 {
605 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
606 GNUNET_SCHEDULER_add_now (&do_error, handle);
607 return;
608 }
609 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
610 ego_entry = get_egoentry_namestore (handle, egoname);
611 if (NULL == ego_entry)
612 {
613 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
614 GNUNET_SCHEDULER_add_now (&do_error, handle);
615 return;
616 }
617 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
618
619 GNUNET_CRYPTO_hash ("record_type", strlen ("record_type"), &key);
620 handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
621 if (GNUNET_YES ==
622 GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
623 {
624 typename = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
625 &key);
626 if (NULL != typename)
627 handle->record_type = GNUNET_GNSRECORD_typename_to_number (typename);
628 }
629 GNUNET_CRYPTO_hash ("omit_private", strlen ("omit_private"), &key);
630 filter_flags = GNUNET_GNSRECORD_FILTER_NONE;
631 if (GNUNET_YES ==
632 GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
633 {
634 boolstring = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
635 &key);
636 if ((0 == strcmp (boolstring, "1")) ||
637 (0 == strcmp (boolstring, "yes")) ||
638 (0 == strcmp (boolstring, "true")))
639 filter_flags = GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE;
640 }
641 GNUNET_CRYPTO_hash ("include_maintenance", strlen ("include_maintenance"),
642 &key);
643 if (GNUNET_YES ==
644 GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
645 {
646 boolstring = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
647 &key);
648 if ((0 == strcmp (boolstring, "1")) ||
649 (0 == strcmp (boolstring, "yes")) ||
650 (0 == strcmp (boolstring, "true")))
651 filter_flags |= GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE;
652 }
653 labelname = &egoname[strlen (ego_entry->identifier)];
654 if (1 >= strlen (labelname))
655 {
656 /* Iterate over all records */
657 handle->list_it =
658 GNUNET_NAMESTORE_zone_iteration_start2 (ns_handle,
659 handle->zone_pkey,
660 &namestore_iteration_error,
661 handle,
662 &namestore_list_iteration,
663 handle,
664 &namestore_list_finished,
665 handle,
666 filter_flags);
667 if (NULL == handle->list_it)
668 {
669 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
670 GNUNET_SCHEDULER_add_now (&do_error, handle);
671 return;
672 }
673 return;
674 }
675 handle->record_name = GNUNET_strdup (labelname + 1);
676 handle->ns_qe = GNUNET_NAMESTORE_records_lookup2 (ns_handle,
677 handle->zone_pkey,
678 handle->record_name,
679 &ns_lookup_error_cb,
680 handle,
681 &ns_get_lookup_cb,
682 handle,
683 filter_flags);
684 if (NULL == handle->ns_qe)
685 {
686 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
687 GNUNET_SCHEDULER_add_now (&do_error, handle);
688 return;
689 }
690}
691
692
693static void
694ns_lookup_cb (void *cls,
695 const struct GNUNET_CRYPTO_PrivateKey *zone,
696 const char *label,
697 unsigned int rd_count,
698 const struct GNUNET_GNSRECORD_Data *rd)
699{
700 struct RequestHandle *handle = cls;
701 struct GNUNET_GNSRECORD_Data rd_new[rd_count + handle->rd_count];
702 int i = 0;
703 int j = 0;
704
705 if (UPDATE_STRATEGY_APPEND == handle->update_strategy)
706 {
707 for (i = 0; i < rd_count; i++)
708 rd_new[i] = rd[i];
709 }
710 for (j = 0; j < handle->rd_count; j++)
711 rd_new[i + j] = handle->rd[j];
712 handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
713 handle->zone_pkey,
714 handle->record_name,
715 i + j,
716 rd_new,
717 &create_finished,
718 handle);
719 if (NULL == handle->ns_qe)
720 {
721 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
722 GNUNET_SCHEDULER_add_now (&do_error, handle);
723 return;
724 }
725}
726
727
728static void
729bulk_tx_commit_cb (void *cls, enum GNUNET_ErrorCode ec)
730{
731 struct RequestHandle *handle = cls;
732 struct MHD_Response *resp;
733
734 handle->ns_qe = NULL;
735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736 "Commit finished (%d)\n", ec);
737 handle->ec = ec;
738 if (GNUNET_EC_NONE != ec)
739 {
740 GNUNET_SCHEDULER_add_now (&do_error, handle);
741 return;
742 }
743 resp = GNUNET_REST_create_response (NULL);
744 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
745 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
746}
747
748
749static void
750import_next_cb (void *cls, enum GNUNET_ErrorCode ec)
751{
752 struct RequestHandle *handle = cls;
753
754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
755 "Import finished (%d)\n", ec);
756 handle->ns_qe = NULL;
757 handle->ec = ec;
758 if (GNUNET_EC_NONE != ec)
759 {
760 GNUNET_SCHEDULER_add_now (&do_error, handle);
761 return;
762 }
763 unsigned int remaining = handle->rd_set_count - handle->rd_set_pos;
764 if (0 == remaining)
765 {
766 handle->ns_qe = GNUNET_NAMESTORE_transaction_commit (handle->nc,
767 &bulk_tx_commit_cb,
768 handle);
769 return;
770 }
771 unsigned int sent_rds = 0;
772 // Find the smallest set of records we can send with our message size
773 // restriction of 16 bit
774 handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc,
775 handle->zone_pkey,
776 remaining,
777 &handle->ri[handle->
778 rd_set_pos],
779 &sent_rds,
780 &import_next_cb,
781 handle);
782 if ((NULL == handle->ns_qe) && (0 == sent_rds))
783 {
784 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
785 GNUNET_SCHEDULER_add_now (&do_error, handle);
786 return;
787 }
788 handle->rd_set_pos += sent_rds;
789}
790
791static void
792bulk_tx_start (void *cls, enum GNUNET_ErrorCode ec)
793{
794 struct RequestHandle *handle = cls;
795 json_t *data_js;
796 json_error_t err;
797
798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799 "Transaction started...\n");
800 handle->ec = ec;
801 if (GNUNET_EC_NONE != ec)
802 {
803 GNUNET_SCHEDULER_add_now (&do_error, handle);
804 return;
805 }
806 if (0 >= handle->rest_handle->data_size)
807 {
808 handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN;
809 GNUNET_SCHEDULER_add_now (&do_error, handle);
810 return;
811 }
812 char term_data[handle->rest_handle->data_size + 1];
813 term_data[handle->rest_handle->data_size] = '\0';
814 GNUNET_memcpy (term_data,
815 handle->rest_handle->data,
816 handle->rest_handle->data_size);
817 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
818 if (NULL == data_js)
819 {
820 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
821 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
822 "Error parsing data: %s", err.text);
823 GNUNET_SCHEDULER_add_now (&do_error, handle);
824 return;
825 }
826 if (! json_is_array (data_js))
827 {
828 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
829 GNUNET_SCHEDULER_add_now (&do_error, handle);
830 json_decref (data_js);
831 return;
832 }
833 handle->rd_set_count = json_array_size (data_js);
834 handle->ri = GNUNET_malloc (handle->rd_set_count
835 * sizeof (struct GNUNET_NAMESTORE_RecordInfo));
836 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
837 "Got record set of size %d\n", handle->rd_set_count);
838 char *albl;
839 size_t index;
840 json_t *value;
841 json_array_foreach (data_js, index, value) {
842 {
843 struct GNUNET_GNSRECORD_Data *rd;
844 struct GNUNET_JSON_Specification gnsspec[] =
845 { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&rd,
846 &handle->ri[index].a_rd_count,
847 &albl),
848 GNUNET_JSON_spec_end () };
849 if (GNUNET_OK != GNUNET_JSON_parse (value, gnsspec, NULL, NULL))
850 {
851 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
852 GNUNET_SCHEDULER_add_now (&do_error, handle);
853 json_decref (data_js);
854 return;
855 }
856 handle->ri[index].a_rd = rd;
857 handle->ri[index].a_label = albl;
858 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
859 "Parsed record set for name %s\n",
860 handle->ri[index].a_label);
861 }
862 }
863 // json_decref (data_js);
864
865 unsigned int sent_rds = 0;
866 // Find the smallest set of records we can send with our message size
867 // restriction of 16 bit
868 handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc,
869 handle->zone_pkey,
870 handle->rd_set_count,
871 handle->ri,
872 &sent_rds,
873 &import_next_cb,
874 handle);
875 if ((NULL == handle->ns_qe) && (0 == sent_rds))
876 {
877 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
878 GNUNET_SCHEDULER_add_now (&do_error, handle);
879 return;
880 }
881 handle->rd_set_pos += sent_rds;
882}
883
884
885/**
886 * Handle namestore POST import
887 *
888 * @param con_handle the connection handle
889 * @param url the url
890 * @param cls the RequestHandle
891 */
892void
893namestore_import (struct GNUNET_REST_RequestHandle *con_handle,
894 const char *url,
895 void *cls)
896{
897 struct RequestHandle *handle = cls;
898 struct EgoEntry *ego_entry;
899 char *egoname;
900
901 // set zone to name if given
902 if (strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1 >= strlen (
903 handle->url))
904 {
905 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
906 GNUNET_SCHEDULER_add_now (&do_error, handle);
907 return;
908 }
909 ego_entry = NULL;
910
911 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1];
912 ego_entry = get_egoentry_namestore (handle, egoname);
913
914 if (NULL == ego_entry)
915 {
916 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
917 GNUNET_SCHEDULER_add_now (&do_error, handle);
918 return;
919 }
920 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
921
922 // We need a per-client connection for a transactional bulk import
923 handle->nc = GNUNET_NAMESTORE_connect (cfg);
924 if (NULL == handle->nc)
925 {
926 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
927 GNUNET_SCHEDULER_add_now (&do_error, handle);
928 return;
929 }
930 handle->ns_qe = GNUNET_NAMESTORE_transaction_begin (handle->nc,
931 &bulk_tx_start,
932 handle);
933}
934
935/**
936 * Handle namestore POST/PUT request
937 *
938 * @param con_handle the connection handle
939 * @param url the url
940 * @param cls the RequestHandle
941 */
942void
943namestore_add_or_update (struct GNUNET_REST_RequestHandle *con_handle,
944 const char *url,
945 void *cls)
946{
947 struct RequestHandle *handle = cls;
948 struct EgoEntry *ego_entry;
949 char *egoname;
950 json_t *data_js;
951 json_error_t err;
952
953 char term_data[handle->rest_handle->data_size + 1];
954
955 if (0 >= handle->rest_handle->data_size)
956 {
957 handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN;
958 GNUNET_SCHEDULER_add_now (&do_error, handle);
959 return;
960 }
961 term_data[handle->rest_handle->data_size] = '\0';
962 GNUNET_memcpy (term_data,
963 handle->rest_handle->data,
964 handle->rest_handle->data_size);
965 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
966 struct GNUNET_JSON_Specification gnsspec[] =
967 { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&handle->rd, &handle->rd_count,
968 &handle->record_name),
969 GNUNET_JSON_spec_end () };
970 if (GNUNET_OK != GNUNET_JSON_parse (data_js, gnsspec, NULL, NULL))
971 {
972 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
973 GNUNET_SCHEDULER_add_now (&do_error, handle);
974 json_decref (data_js);
975 return;
976 }
977 GNUNET_JSON_parse_free (gnsspec);
978 if (0 >= strlen (handle->record_name))
979 {
980 handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID;
981 GNUNET_SCHEDULER_add_now (&do_error, handle);
982 json_decref (data_js);
983 return;
984 }
985 json_decref (data_js);
986
987 egoname = NULL;
988 ego_entry = NULL;
989
990 // set zone to name if given
991 if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
992 {
993 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
994 GNUNET_SCHEDULER_add_now (&do_error, handle);
995 return;
996 }
997 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
998 ego_entry = get_egoentry_namestore (handle, egoname);
999
1000 if (NULL == ego_entry)
1001 {
1002 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1003 GNUNET_SCHEDULER_add_now (&do_error, handle);
1004 return;
1005 }
1006 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1007 handle->ns_qe = GNUNET_NAMESTORE_records_lookup (ns_handle,
1008 handle->zone_pkey,
1009 handle->record_name,
1010 &ns_lookup_error_cb,
1011 handle,
1012 &ns_lookup_cb,
1013 handle);
1014 if (NULL == handle->ns_qe)
1015 {
1016 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
1017 GNUNET_SCHEDULER_add_now (&do_error, handle);
1018 return;
1019 }
1020}
1021
1022
1023/**
1024 * Handle namestore PUT request
1025 *
1026 * @param con_handle the connection handle
1027 * @param url the url
1028 * @param cls the RequestHandle
1029 */
1030void
1031namestore_update (struct GNUNET_REST_RequestHandle *con_handle,
1032 const char *url,
1033 void *cls)
1034{
1035 struct RequestHandle *handle = cls;
1036 handle->update_strategy = UPDATE_STRATEGY_REPLACE;
1037 namestore_add_or_update (con_handle, url, cls);
1038}
1039
1040
1041/**
1042 * Handle namestore POST request
1043 *
1044 * @param con_handle the connection handle
1045 * @param url the url
1046 * @param cls the RequestHandle
1047 */
1048void
1049namestore_add (struct GNUNET_REST_RequestHandle *con_handle,
1050 const char *url,
1051 void *cls)
1052{
1053 struct RequestHandle *handle = cls;
1054 handle->update_strategy = UPDATE_STRATEGY_APPEND;
1055 namestore_add_or_update (con_handle, url, cls);
1056}
1057
1058
1059/**
1060 * Handle namestore DELETE request
1061 *
1062 * @param con_handle the connection handle
1063 * @param url the url
1064 * @param cls the RequestHandle
1065 */
1066void
1067namestore_delete (struct GNUNET_REST_RequestHandle *con_handle,
1068 const char *url,
1069 void *cls)
1070{
1071 struct RequestHandle *handle = cls;
1072 struct EgoEntry *ego_entry;
1073 char *egoname;
1074 char *labelname;
1075
1076 egoname = NULL;
1077 ego_entry = NULL;
1078
1079 // set zone to name if given
1080 if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
1081 {
1082 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1083 GNUNET_SCHEDULER_add_now (&do_error, handle);
1084 return;
1085 }
1086 egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
1087 ego_entry = get_egoentry_namestore (handle, egoname);
1088 if (NULL == ego_entry)
1089 {
1090 handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND;
1091 GNUNET_SCHEDULER_add_now (&do_error, handle);
1092 return;
1093 }
1094 handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1095 labelname = &egoname[strlen (ego_entry->identifier)];
1096 // set zone to name if given
1097 if (1 >= strlen (labelname))
1098 {
1099 /* label is only "/" */
1100 handle->ec = GNUNET_EC_NAMESTORE_NO_LABEL_GIVEN;
1101 GNUNET_SCHEDULER_add_now (&do_error, handle);
1102 }
1103
1104 handle->record_name = GNUNET_strdup (labelname + 1);
1105 handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle,
1106 handle->zone_pkey,
1107 handle->record_name,
1108 0,
1109 NULL,
1110 &del_finished,
1111 handle);
1112 if (NULL == handle->ns_qe)
1113 {
1114 handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN;
1115 GNUNET_SCHEDULER_add_now (&do_error, handle);
1116 return;
1117 }
1118}
1119
1120
1121/**
1122 * Respond to OPTIONS request
1123 *
1124 * @param con_handle the connection handle
1125 * @param url the url
1126 * @param cls the RequestHandle
1127 */
1128static void
1129options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1130 const char *url,
1131 void *cls)
1132{
1133 struct MHD_Response *resp;
1134 struct RequestHandle *handle = cls;
1135
1136 // independent of path return all options
1137 resp = GNUNET_REST_create_response (NULL);
1138 GNUNET_assert (MHD_YES ==
1139 MHD_add_response_header (resp,
1140 "Access-Control-Allow-Methods",
1141 allow_methods));
1142 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1143 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
1144 return;
1145}
1146
1147
1148static void
1149list_ego (void *cls,
1150 struct GNUNET_IDENTITY_Ego *ego,
1151 void **ctx,
1152 const char *identifier)
1153{
1154 struct EgoEntry *ego_entry;
1155 struct GNUNET_CRYPTO_PublicKey pk;
1156
1157 if ((NULL == ego) && (ID_REST_STATE_INIT == state))
1158 {
1159 state = ID_REST_STATE_POST_INIT;
1160 return;
1161 }
1162 if (NULL == ego)
1163 {
1164 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1165 "Called with NULL ego\n");
1166 return;
1167 }
1168 if (ID_REST_STATE_INIT == state)
1169 {
1170 ego_entry = GNUNET_new (struct EgoEntry);
1171 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1172 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1173 ego_entry->ego = ego;
1174 ego_entry->identifier = GNUNET_strdup (identifier);
1175 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1176 ego_tail,
1177 ego_entry);
1178 }
1179 /* Ego renamed or added */
1180 if (identifier != NULL)
1181 {
1182 for (ego_entry = ego_head; NULL != ego_entry;
1183 ego_entry = ego_entry->next)
1184 {
1185 if (ego_entry->ego == ego)
1186 {
1187 /* Rename */
1188 GNUNET_free (ego_entry->identifier);
1189 ego_entry->identifier = GNUNET_strdup (identifier);
1190 break;
1191 }
1192 }
1193 if (NULL == ego_entry)
1194 {
1195 /* Add */
1196 ego_entry = GNUNET_new (struct EgoEntry);
1197 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1198 ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk);
1199 ego_entry->ego = ego;
1200 ego_entry->identifier = GNUNET_strdup (identifier);
1201 GNUNET_CONTAINER_DLL_insert_tail (ego_head,
1202 ego_tail,
1203 ego_entry);
1204 }
1205 }
1206 else
1207 {
1208 /* Delete */
1209 for (ego_entry = ego_head; NULL != ego_entry;
1210 ego_entry = ego_entry->next)
1211 {
1212 if (ego_entry->ego == ego)
1213 break;
1214 }
1215 if (NULL == ego_entry)
1216 return; /* Not found */
1217
1218 GNUNET_CONTAINER_DLL_remove (ego_head,
1219 ego_tail,
1220 ego_entry);
1221 GNUNET_free (ego_entry->identifier);
1222 GNUNET_free (ego_entry->keystring);
1223 GNUNET_free (ego_entry);
1224 return;
1225 }
1226
1227}
1228
1229
1230/**
1231 * Function processing the REST call
1232 *
1233 * @param method HTTP method
1234 * @param url URL of the HTTP request
1235 * @param data body of the HTTP request (optional)
1236 * @param data_size length of the body
1237 * @param proc callback function for the result
1238 * @param proc_cls closure for callback function
1239 * @return GNUNET_OK if request accepted
1240 */
1241static enum GNUNET_GenericReturnValue
1242rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
1243 GNUNET_REST_ResultProcessor proc,
1244 void *proc_cls)
1245{
1246 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1247 struct GNUNET_REST_RequestHandlerError err;
1248 static const struct GNUNET_REST_RequestHandler handlers[] =
1249 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get },
1250 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE_IMPORT,
1251 &namestore_import },
1252 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add },
1253 { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_update },
1254 { MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE,
1255 &namestore_delete },
1256 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont },
1257 GNUNET_REST_HANDLER_END };
1258
1259 handle->ec = GNUNET_EC_NONE;
1260 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1261 handle->proc_cls = proc_cls;
1262 handle->proc = proc;
1263 handle->rest_handle = rest_handle;
1264 handle->zone_pkey = NULL;
1265 handle->timeout_task =
1266 GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle);
1267 handle->url = GNUNET_strdup (rest_handle->url);
1268 if (handle->url[strlen (handle->url) - 1] == '/')
1269 handle->url[strlen (handle->url) - 1] = '\0';
1270 GNUNET_CONTAINER_DLL_insert (requests_head,
1271 requests_tail,
1272 handle);
1273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
1274 if (GNUNET_NO ==
1275 GNUNET_REST_handle_request (handle->rest_handle, handlers, &err,
1276 handle))
1277 {
1278 cleanup_handle (handle);
1279 return GNUNET_NO;
1280 }
1281
1282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
1283 return GNUNET_YES;
1284}
1285
1286
1287/**
1288 * Entry point for the plugin.
1289 *
1290 * @param cls Config info
1291 * @return NULL on error, otherwise the plugin context
1292 */
1293void *
1294libgnunet_plugin_rest_namestore_init (void *cls)
1295{
1296 static struct Plugin plugin;
1297 struct GNUNET_REST_Plugin *api;
1298
1299 cfg = cls;
1300 if (NULL != plugin.cfg)
1301 return NULL; /* can only initialize once! */
1302 memset (&plugin, 0, sizeof(struct Plugin));
1303 plugin.cfg = cfg;
1304 api = GNUNET_new (struct GNUNET_REST_Plugin);
1305 api->cls = &plugin;
1306 api->name = GNUNET_REST_API_NS_NAMESTORE;
1307 api->process_request = &rest_process_request;
1308 state = ID_REST_STATE_INIT;
1309 GNUNET_asprintf (&allow_methods,
1310 "%s, %s, %s, %s, %s",
1311 MHD_HTTP_METHOD_GET,
1312 MHD_HTTP_METHOD_POST,
1313 MHD_HTTP_METHOD_PUT,
1314 MHD_HTTP_METHOD_DELETE,
1315 MHD_HTTP_METHOD_OPTIONS);
1316 ns_handle = GNUNET_NAMESTORE_connect (cfg);
1317 identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL);
1318
1319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ (
1320 "Namestore REST API initialized\n"));
1321 return api;
1322}
1323
1324
1325/**
1326 * Exit point from the plugin.
1327 *
1328 * @param cls the plugin context (as returned by "init")
1329 * @return always NULL
1330 */
1331void *
1332libgnunet_plugin_rest_namestore_done (void *cls)
1333{
1334 struct GNUNET_REST_Plugin *api = cls;
1335 struct Plugin *plugin = api->cls;
1336 struct RequestHandle *request;
1337 struct EgoEntry *ego_entry;
1338 struct EgoEntry *ego_tmp;
1339
1340 plugin->cfg = NULL;
1341 while (NULL != (request = requests_head))
1342 do_error (request);
1343 if (NULL != identity_handle)
1344 GNUNET_IDENTITY_disconnect (identity_handle);
1345 if (NULL != ns_handle)
1346 GNUNET_NAMESTORE_disconnect (ns_handle);
1347
1348 for (ego_entry = ego_head; NULL != ego_entry;)
1349 {
1350 ego_tmp = ego_entry;
1351 ego_entry = ego_entry->next;
1352 GNUNET_free (ego_tmp->identifier);
1353 GNUNET_free (ego_tmp->keystring);
1354 GNUNET_free (ego_tmp);
1355 }
1356
1357 GNUNET_free (allow_methods);
1358 GNUNET_free (api);
1359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Namestore REST plugin is finished\n");
1360 return NULL;
1361}
1362
1363
1364/* end of plugin_rest_namestore.c */