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