5a7e8c6cf8b809fadd4f66533cb375d5307c7248
[oweals/gnunet.git] / src / namestore / plugin_rest_namestore.c
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_rest_plugin.h"
29 #include "gnunet_gns_service.h"
30 #include "gnunet_namestore_service.h"
31 #include "gnunet_identity_service.h"
32 #include "gnunet_rest_lib.h"
33 #include "gnunet_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  * Error message Unknown Error
44  */
45 #define GNUNET_REST_NAMESTORE_ERROR_UNKNOWN "Unknown Error"
46
47 /**
48  * Error message No identity found
49  */
50 #define GNUNET_REST_IDENTITY_NOT_FOUND "No identity found"
51
52
53 /**
54  * Error message Failed request
55  */
56 #define GNUNET_REST_NAMESTORE_FAILED "Namestore action failed"
57
58 /**
59  * Error message invalid data
60  */
61 #define GNUNET_REST_NAMESTORE_INVALID_DATA "Data invalid"
62
63 /**
64  * Error message No data
65  */
66 #define GNUNET_REST_NAMESTORE_NO_DATA "No data"
67
68 /**
69  * State while collecting all egos
70  */
71 #define ID_REST_STATE_INIT 0
72
73 /**
74  * Done collecting egos
75  */
76 #define ID_REST_STATE_POST_INIT 1
77 /**
78  * The configuration handle
79  */
80 const struct GNUNET_CONFIGURATION_Handle *cfg;
81
82 /**
83  * HTTP methods allows for this plugin
84  */
85 static char *allow_methods;
86
87 /**
88  * @brief struct returned by the initialization function of the plugin
89  */
90 struct Plugin
91 {
92   const struct GNUNET_CONFIGURATION_Handle *cfg;
93 };
94
95 /**
96  * The default namestore ego
97  */
98 struct EgoEntry
99 {
100   /**
101    * DLL
102    */
103   struct EgoEntry *next;
104
105   /**
106    * DLL
107    */
108   struct EgoEntry *prev;
109
110   /**
111    * Ego Identifier
112    */
113   char *identifier;
114
115   /**
116    * Public key string
117    */
118   char *keystring;
119
120   /**
121    * The Ego
122    */
123   struct GNUNET_IDENTITY_Ego *ego;
124 };
125
126
127 enum UpdateStrategy
128 {
129   UPDATE_STRATEGY_REPLACE,
130   UPDATE_STRATEGY_APPEND
131 };
132
133 /**
134  * The request handle
135  */
136 struct RequestHandle
137 {
138   /**
139    * Records to store
140    */
141   char *record_name;
142
143   /**
144    * Record type filter
145    */
146   uint32_t record_type;
147
148   /**
149    * How to update the record set
150    */
151   enum UpdateStrategy update_strategy;
152
153   /**
154    * Records to store
155    */
156   struct GNUNET_GNSRECORD_Data *rd;
157
158   /**
159    * Number of records in rd
160    */
161   unsigned int rd_count;
162
163   /**
164    * NAMESTORE Operation
165    */
166   struct GNUNET_NAMESTORE_QueueEntry *add_qe;
167
168   /**
169    * Response object
170    */
171   json_t *resp_object;
172
173   /**
174    * The processing state
175    */
176   int state;
177
178   /**
179    * Handle to NAMESTORE
180    */
181   struct GNUNET_NAMESTORE_Handle *ns_handle;
182
183   /**
184    * Handle to NAMESTORE it
185    */
186   struct GNUNET_NAMESTORE_ZoneIterator *list_it;
187
188   /**
189    * Private key for the zone
190    */
191   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_pkey;
192
193   /**
194    * IDENTITY Operation
195    */
196   struct EgoEntry *ego_entry;
197
198   /**
199    * Ego list
200    */
201   struct EgoEntry *ego_head;
202
203   /**
204    * Ego list
205    */
206   struct EgoEntry *ego_tail;
207
208   /**
209    * IDENTITY Operation
210    */
211   struct GNUNET_IDENTITY_Operation *op;
212
213   /**
214    * Handle to Identity service.
215    */
216   struct GNUNET_IDENTITY_Handle *identity_handle;
217
218   /**
219    * Rest connection
220    */
221   struct GNUNET_REST_RequestHandle *rest_handle;
222
223   /**
224    * Desired timeout for the lookup (default is no timeout).
225    */
226   struct GNUNET_TIME_Relative timeout;
227
228   /**
229    * ID of a task associated with the resolution process.
230    */
231   struct GNUNET_SCHEDULER_Task *timeout_task;
232
233   /**
234    * The plugin result processor
235    */
236   GNUNET_REST_ResultProcessor proc;
237
238   /**
239    * The closure of the result processor
240    */
241   void *proc_cls;
242
243   /**
244    * The url
245    */
246   char *url;
247
248   /**
249    * Error response message
250    */
251   char *emsg;
252
253   /**
254    * Response code
255    */
256   int response_code;
257 };
258
259 /**
260  * Cleanup lookup handle
261  * @param handle Handle to clean up
262  */
263 static void
264 cleanup_handle (void *cls)
265 {
266   struct RequestHandle *handle = cls;
267   struct EgoEntry *ego_entry;
268   struct EgoEntry *ego_tmp;
269
270   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
271   if (NULL != handle->timeout_task)
272   {
273     GNUNET_SCHEDULER_cancel (handle->timeout_task);
274     handle->timeout_task = NULL;
275   }
276   if (NULL != handle->record_name)
277     GNUNET_free (handle->record_name);
278   if (NULL != handle->url)
279     GNUNET_free (handle->url);
280   if (NULL != handle->emsg)
281     GNUNET_free (handle->emsg);
282   if (NULL != handle->rd)
283   {
284     for (int i = 0; i < handle->rd_count; i++)
285     {
286       if (NULL != handle->rd[i].data)
287         GNUNET_free_nz ((void *) handle->rd[i].data);
288     }
289     GNUNET_free (handle->rd);
290   }
291   if (NULL != handle->timeout_task)
292     GNUNET_SCHEDULER_cancel (handle->timeout_task);
293   if (NULL != handle->list_it)
294     GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it);
295   if (NULL != handle->add_qe)
296     GNUNET_NAMESTORE_cancel (handle->add_qe);
297   if (NULL != handle->identity_handle)
298     GNUNET_IDENTITY_disconnect (handle->identity_handle);
299   if (NULL != handle->ns_handle)
300   {
301     GNUNET_NAMESTORE_disconnect (handle->ns_handle);
302   }
303
304   for (ego_entry = handle->ego_head; NULL != ego_entry;)
305   {
306     ego_tmp = ego_entry;
307     ego_entry = ego_entry->next;
308     GNUNET_free (ego_tmp->identifier);
309     GNUNET_free (ego_tmp->keystring);
310     GNUNET_free (ego_tmp);
311   }
312
313   if (NULL != handle->resp_object)
314   {
315     json_decref (handle->resp_object);
316   }
317
318   GNUNET_free (handle);
319 }
320
321
322 /**
323  * Task run on errors.  Reports an error and cleans up everything.
324  *
325  * @param cls the `struct RequestHandle`
326  */
327 static void
328 do_error (void *cls)
329 {
330   struct RequestHandle *handle = cls;
331   struct MHD_Response *resp;
332   json_t *json_error = json_object ();
333   char *response;
334
335   if (NULL == handle->emsg)
336     handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_ERROR_UNKNOWN);
337
338   json_object_set_new (json_error, "error", json_string (handle->emsg));
339
340   if (0 == handle->response_code)
341     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
342   response = json_dumps (json_error, 0);
343   resp = GNUNET_REST_create_response (response);
344   MHD_add_response_header (resp, "Content-Type", "application/json");
345   handle->proc (handle->proc_cls, resp, handle->response_code);
346   json_decref (json_error);
347   GNUNET_free (response);
348   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
349 }
350
351
352 /**
353  * Get EgoEntry from list with either a public key or a name
354  * If public key and name are not NULL, it returns the public key result first
355  *
356  * @param handle the RequestHandle
357  * @param pubkey the public key of an identity (only one can be NULL)
358  * @param name the name of an identity (only one can be NULL)
359  * @return EgoEntry or NULL if not found
360  */
361 struct EgoEntry *
362 get_egoentry_namestore (struct RequestHandle *handle, char *name)
363 {
364   struct EgoEntry *ego_entry;
365   char *copy = GNUNET_strdup (name);
366   char *tmp;
367
368   if (NULL == name)
369     return NULL;
370   tmp = strtok (copy, "/");
371   for (ego_entry = handle->ego_head; NULL != ego_entry;
372        ego_entry = ego_entry->next)
373   {
374     if (0 != strcasecmp (tmp, ego_entry->identifier))
375       continue;
376     GNUNET_free (copy);
377     return ego_entry;
378   }
379   GNUNET_free (copy);
380   return NULL;
381 }
382
383
384 /**
385  * Does internal server error when iteration failed.
386  *
387  * @param cls the `struct RequestHandle`
388  */
389 static void
390 namestore_iteration_error (void *cls)
391 {
392   struct RequestHandle *handle = cls;
393
394   handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_FAILED);
395   GNUNET_SCHEDULER_add_now (&do_error, handle);
396   return;
397 }
398
399
400 /**
401  * Create finished callback
402  *
403  * @param cls the `struct RequestHandle`
404  * @param success the success indicating integer, GNUNET_OK on success
405  * @param emsg the error message (can be NULL)
406  */
407 static void
408 create_finished (void *cls, int32_t success, const char *emsg)
409 {
410   struct RequestHandle *handle = cls;
411   struct MHD_Response *resp;
412
413   handle->add_qe = NULL;
414   if (GNUNET_YES != success)
415   {
416     if (NULL != emsg)
417     {
418       handle->emsg = GNUNET_strdup (emsg);
419       GNUNET_SCHEDULER_add_now (&do_error, handle);
420       return;
421     }
422     handle->emsg = GNUNET_strdup ("Error storing records");
423     GNUNET_SCHEDULER_add_now (&do_error, handle);
424     return;
425   }
426   resp = GNUNET_REST_create_response (NULL);
427   handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
428   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
429 }
430
431
432 /**
433  * Delete finished callback
434  *
435  * @param cls the `struct RequestHandle`
436  * @param success the success indicating integer, GNUNET_OK on success
437  * @param emsg the error message (can be NULL)
438  */
439 static void
440 del_finished (void *cls, int32_t success, const char *emsg)
441 {
442   struct RequestHandle *handle = cls;
443
444   handle->add_qe = NULL;
445   if (GNUNET_NO == success)
446   {
447     handle->response_code = MHD_HTTP_NOT_FOUND;
448     handle->emsg = GNUNET_strdup ("No record found");
449     GNUNET_SCHEDULER_add_now (&do_error, handle);
450     return;
451   }
452   if (GNUNET_SYSERR == success)
453   {
454     if (NULL != emsg)
455     {
456       handle->emsg = GNUNET_strdup (emsg);
457       GNUNET_SCHEDULER_add_now (&do_error, handle);
458       return;
459     }
460     handle->emsg = GNUNET_strdup ("Deleting record failed");
461     GNUNET_SCHEDULER_add_now (&do_error, handle);
462     return;
463   }
464   handle->proc (handle->proc_cls,
465                 GNUNET_REST_create_response (NULL),
466                 MHD_HTTP_NO_CONTENT);
467   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
468 }
469
470
471 /**
472  * Iteration over all results finished, build final
473  * response.
474  *
475  * @param cls the `struct RequestHandle`
476  */
477 static void
478 namestore_list_finished (void *cls)
479 {
480   struct RequestHandle *handle = cls;
481   char *result_str;
482   struct MHD_Response *resp;
483
484   handle->list_it = NULL;
485
486   if (NULL == handle->resp_object)
487     handle->resp_object = json_array ();
488
489   result_str = json_dumps (handle->resp_object, 0);
490   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
491   resp = GNUNET_REST_create_response (result_str);
492   MHD_add_response_header (resp, "Content-Type", "application/json");
493   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
494   GNUNET_free_non_null (result_str);
495   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
496 }
497
498
499 /**
500  * Create a response with requested records
501  *
502  * @param handle the RequestHandle
503  */
504 static void
505 namestore_list_iteration (void *cls,
506                           const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
507                           const char *rname,
508                           unsigned int rd_len,
509                           const struct GNUNET_GNSRECORD_Data *rd)
510 {
511   struct RequestHandle *handle = cls;
512   struct GNUNET_GNSRECORD_Data rd_filtered[rd_len];
513   json_t *record_obj;
514   int i = 0;
515   int j = 0;
516
517   if (NULL == handle->resp_object)
518     handle->resp_object = json_array ();
519   for (i = 0; i < rd_len; i++)
520   {
521     if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) &&
522         (rd[i].record_type != handle->record_type))
523       continue; /* Apply filter */
524     rd_filtered[j] = rd[i];
525     rd_filtered[j].data = rd[i].data;
526     j++;
527   }
528   /** Only add if not empty **/
529   if (j > 0)
530   {
531     record_obj = GNUNET_JSON_from_gnsrecord (rname,
532                                              rd_filtered,
533                                              j);
534     json_array_append_new (handle->resp_object, record_obj);
535   }
536   GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
537 }
538
539
540 /**
541  * Handle namestore GET request
542  *
543  * @param con_handle the connection handle
544  * @param url the url
545  * @param cls the RequestHandle
546  */
547 void
548 namestore_get (struct GNUNET_REST_RequestHandle *con_handle,
549                const char *url,
550                void *cls)
551 {
552   struct RequestHandle *handle = cls;
553   struct EgoEntry *ego_entry;
554   struct GNUNET_HashCode key;
555   char *egoname;
556   char *typename;
557
558   egoname = NULL;
559   ego_entry = NULL;
560
561   // set zone to name if given
562   if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1  >= strlen (handle->url))
563   {
564     handle->response_code = MHD_HTTP_NOT_FOUND;
565     handle->emsg = GNUNET_strdup (GNUNET_REST_IDENTITY_NOT_FOUND);
566     GNUNET_SCHEDULER_add_now (&do_error, handle);
567     return;
568   }
569   egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
570   ego_entry = get_egoentry_namestore (handle, egoname);
571   if (NULL == ego_entry)
572   {
573     handle->response_code = MHD_HTTP_NOT_FOUND;
574     handle->emsg = GNUNET_strdup (GNUNET_REST_IDENTITY_NOT_FOUND);
575     GNUNET_SCHEDULER_add_now (&do_error, handle);
576     return;
577   }
578   handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
579
580   GNUNET_CRYPTO_hash ("record_type", strlen ("record_type"), &key);
581   if (GNUNET_NO ==
582       GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key))
583   {
584     handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
585   }
586   else
587   {
588     typename = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
589                                                   &key);
590     handle->record_type = GNUNET_GNSRECORD_typename_to_number (typename);
591   }
592
593   handle->list_it =
594     GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle,
595                                            handle->zone_pkey,
596                                            &namestore_iteration_error,
597                                            handle,
598                                            &namestore_list_iteration,
599                                            handle,
600                                            &namestore_list_finished,
601                                            handle);
602   if (NULL == handle->list_it)
603   {
604     handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_FAILED);
605     GNUNET_SCHEDULER_add_now (&do_error, handle);
606     return;
607   }
608 }
609
610
611 static void
612 ns_lookup_error_cb (void *cls)
613 {
614   struct RequestHandle *handle = cls;
615
616   handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_FAILED);
617   GNUNET_SCHEDULER_add_now (&do_error, handle);
618 }
619
620
621 static void
622 ns_lookup_cb (void *cls,
623               const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
624               const char *label,
625               unsigned int rd_count,
626               const struct GNUNET_GNSRECORD_Data *rd)
627 {
628   struct RequestHandle *handle = cls;
629   struct GNUNET_GNSRECORD_Data rd_new[rd_count + handle->rd_count];
630   int i = 0;
631   int j = 0;
632
633   if (UPDATE_STRATEGY_APPEND == handle->update_strategy)
634   {
635     for (i = 0; i < rd_count; i++)
636       rd_new[i] = rd[i];
637   }
638   for (j = 0; j < handle->rd_count; j++)
639     rd_new[i + j] = handle->rd[j];
640   handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
641                                                    handle->zone_pkey,
642                                                    handle->record_name,
643                                                    i + j,
644                                                    rd_new,
645                                                    &create_finished,
646                                                    handle);
647   if (NULL == handle->add_qe)
648   {
649     handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_FAILED);
650     GNUNET_SCHEDULER_add_now (&do_error, handle);
651     return;
652   }
653 }
654
655
656 /**
657  * Handle namestore POST/PUT request
658  *
659  * @param con_handle the connection handle
660  * @param url the url
661  * @param cls the RequestHandle
662  */
663 void
664 namestore_add_or_update (struct GNUNET_REST_RequestHandle *con_handle,
665                          const char *url,
666                          void *cls)
667 {
668   struct RequestHandle *handle = cls;
669   struct EgoEntry *ego_entry;
670   char *egoname;
671   json_t *data_js;
672   json_error_t err;
673
674   char term_data[handle->rest_handle->data_size + 1];
675
676   if (0 >= handle->rest_handle->data_size)
677   {
678     handle->response_code = MHD_HTTP_BAD_REQUEST;
679     handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_NO_DATA);
680     GNUNET_SCHEDULER_add_now (&do_error, handle);
681     return;
682   }
683   term_data[handle->rest_handle->data_size] = '\0';
684   GNUNET_memcpy (term_data,
685                  handle->rest_handle->data,
686                  handle->rest_handle->data_size);
687   data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
688   struct GNUNET_JSON_Specification gnsspec[] =
689   { GNUNET_JSON_spec_gnsrecord (&handle->rd, &handle->rd_count,
690                                 &handle->record_name),
691     GNUNET_JSON_spec_end () };
692   if (GNUNET_OK != GNUNET_JSON_parse (data_js, gnsspec, NULL, NULL))
693   {
694     handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_INVALID_DATA);
695     GNUNET_SCHEDULER_add_now (&do_error, handle);
696     json_decref (data_js);
697     return;
698   }
699   GNUNET_JSON_parse_free (gnsspec);
700   if (0 >= strlen (handle->record_name))
701   {
702     handle->response_code = MHD_HTTP_BAD_REQUEST;
703     handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_INVALID_DATA);
704     GNUNET_SCHEDULER_add_now (&do_error, handle);
705     json_decref (data_js);
706     return;
707   }
708   json_decref (data_js);
709
710   egoname = NULL;
711   ego_entry = NULL;
712
713   // set zone to name if given
714   if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
715   {
716     handle->response_code = MHD_HTTP_NOT_FOUND;
717     handle->emsg = GNUNET_strdup (GNUNET_REST_IDENTITY_NOT_FOUND);
718     GNUNET_SCHEDULER_add_now (&do_error, handle);
719     return;
720   }
721   egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
722   ego_entry = get_egoentry_namestore (handle, egoname);
723
724   if (NULL == ego_entry)
725   {
726     handle->response_code = MHD_HTTP_NOT_FOUND;
727     handle->emsg = GNUNET_strdup (GNUNET_REST_IDENTITY_NOT_FOUND);
728     GNUNET_SCHEDULER_add_now (&do_error, handle);
729     return;
730   }
731   handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
732   handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
733                                                     handle->zone_pkey,
734                                                     handle->record_name,
735                                                     &ns_lookup_error_cb,
736                                                     handle,
737                                                     &ns_lookup_cb,
738                                                     handle);
739   if (NULL == handle->add_qe)
740   {
741     handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_FAILED);
742     GNUNET_SCHEDULER_add_now (&do_error, handle);
743     return;
744   }
745 }
746
747
748 /**
749  * Handle namestore PUT request
750  *
751  * @param con_handle the connection handle
752  * @param url the url
753  * @param cls the RequestHandle
754  */
755 void
756 namestore_update (struct GNUNET_REST_RequestHandle *con_handle,
757                   const char *url,
758                   void *cls)
759 {
760   struct RequestHandle *handle = cls;
761   handle->update_strategy = UPDATE_STRATEGY_REPLACE;
762   namestore_add_or_update (con_handle, url, cls);
763 }
764
765
766 /**
767  * Handle namestore POST request
768  *
769  * @param con_handle the connection handle
770  * @param url the url
771  * @param cls the RequestHandle
772  */
773 void
774 namestore_add (struct GNUNET_REST_RequestHandle *con_handle,
775                const char *url,
776                void *cls)
777 {
778   struct RequestHandle *handle = cls;
779   handle->update_strategy = UPDATE_STRATEGY_APPEND;
780   namestore_add_or_update (con_handle, url, cls);
781 }
782
783
784 /**
785  * Handle namestore DELETE request
786  *
787  * @param con_handle the connection handle
788  * @param url the url
789  * @param cls the RequestHandle
790  */
791 void
792 namestore_delete (struct GNUNET_REST_RequestHandle *con_handle,
793                   const char *url,
794                   void *cls)
795 {
796   struct RequestHandle *handle = cls;
797   struct EgoEntry *ego_entry;
798   char *egoname;
799   char *labelname;
800
801   egoname = NULL;
802   ego_entry = NULL;
803
804   // set zone to name if given
805   if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url))
806   {
807     handle->response_code = MHD_HTTP_NOT_FOUND;
808     handle->emsg = GNUNET_strdup (GNUNET_REST_IDENTITY_NOT_FOUND);
809     GNUNET_SCHEDULER_add_now (&do_error, handle);
810     return;
811   }
812   egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1];
813   ego_entry = get_egoentry_namestore (handle, egoname);
814   if (NULL == ego_entry)
815   {
816     handle->response_code = MHD_HTTP_NOT_FOUND;
817     handle->emsg = GNUNET_strdup (GNUNET_REST_IDENTITY_NOT_FOUND);
818     GNUNET_SCHEDULER_add_now (&do_error, handle);
819     return;
820   }
821   handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
822   labelname = &egoname[strlen (ego_entry->identifier)];
823   // set zone to name if given
824   if (1 >= strlen (labelname))
825   {
826     /* label is only "/" */
827     handle->response_code = MHD_HTTP_BAD_REQUEST;
828     handle->emsg = GNUNET_strdup ("Label missing");
829     GNUNET_SCHEDULER_add_now (&do_error, handle);
830   }
831
832   handle->record_name = GNUNET_strdup (labelname + 1);
833   handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
834                                                    handle->zone_pkey,
835                                                    handle->record_name,
836                                                    0,
837                                                    NULL,
838                                                    &del_finished,
839                                                    handle);
840   if (NULL == handle->add_qe)
841   {
842     handle->emsg = GNUNET_strdup (GNUNET_REST_NAMESTORE_FAILED);
843     GNUNET_SCHEDULER_add_now (&do_error, handle);
844     return;
845   }
846 }
847
848
849 /**
850  * Respond to OPTIONS request
851  *
852  * @param con_handle the connection handle
853  * @param url the url
854  * @param cls the RequestHandle
855  */
856 static void
857 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
858               const char *url,
859               void *cls)
860 {
861   struct MHD_Response *resp;
862   struct RequestHandle *handle = cls;
863
864   // independent of path return all options
865   resp = GNUNET_REST_create_response (NULL);
866   MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
867   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
868   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
869   return;
870 }
871
872
873 /**
874  * Handle rest request
875  *
876  * @param handle the request handle
877  */
878 static void
879 init_cont (struct RequestHandle *handle)
880 {
881   struct GNUNET_REST_RequestHandlerError err;
882   static const struct GNUNET_REST_RequestHandler handlers[] =
883   { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get },
884     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add },
885     { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_update },
886     { MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, &namestore_delete },
887     { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont },
888     GNUNET_REST_HANDLER_END };
889
890   if (GNUNET_NO ==
891       GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
892   {
893     handle->response_code = err.error_code;
894     GNUNET_SCHEDULER_add_now (&do_error, handle);
895   }
896 }
897
898
899 /**
900  * This function is initially called for all egos and then again
901  * whenever a ego's identifier changes or if it is deleted.  At the
902  * end of the initial pass over all egos, the function is once called
903  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
904  * be invoked in the future or that there was an error.
905  *
906  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
907  * this function is only called ONCE, and 'NULL' being passed in
908  * 'ego' does indicate an error (i.e. name is taken or no default
909  * value is known).  If 'ego' is non-NULL and if '*ctx'
910  * is set in those callbacks, the value WILL be passed to a subsequent
911  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
912  * that one was not NULL).
913  *
914  * When an identity is renamed, this function is called with the
915  * (known) ego but the NEW identifier.
916  *
917  * When an identity is deleted, this function is called with the
918  * (known) ego and "NULL" for the 'identifier'.  In this case,
919  * the 'ego' is henceforth invalid (and the 'ctx' should also be
920  * cleaned up).
921  *
922  * @param cls closure
923  * @param ego ego handle
924  * @param ctx context for application to store data for this ego
925  *                 (during the lifetime of this process, initially NULL)
926  * @param name identifier assigned by the user for this ego,
927  *                   NULL if the user just deleted the ego and it
928  *                   must thus no longer be used
929  */
930 static void
931 id_connect_cb (void *cls,
932                struct GNUNET_IDENTITY_Ego *ego,
933                void **ctx,
934                const char *name)
935 {
936   struct RequestHandle *handle = cls;
937   struct EgoEntry *ego_entry;
938   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
939
940   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
941   {
942     handle->state = ID_REST_STATE_POST_INIT;
943     init_cont (handle);
944     return;
945   }
946   if (ID_REST_STATE_INIT == handle->state)
947   {
948     ego_entry = GNUNET_new (struct EgoEntry);
949     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
950     ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
951     ego_entry->ego = ego;
952     GNUNET_asprintf (&ego_entry->identifier, "%s", name);
953     GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
954                                       handle->ego_tail,
955                                       ego_entry);
956   }
957 }
958
959
960 /**
961  * Function processing the REST call
962  *
963  * @param method HTTP method
964  * @param url URL of the HTTP request
965  * @param data body of the HTTP request (optional)
966  * @param data_size length of the body
967  * @param proc callback function for the result
968  * @param proc_cls closure for callback function
969  * @return GNUNET_OK if request accepted
970  */
971 static void
972 rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
973                       GNUNET_REST_ResultProcessor proc,
974                       void *proc_cls)
975 {
976   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
977
978   handle->response_code = 0;
979   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
980   handle->proc_cls = proc_cls;
981   handle->proc = proc;
982   handle->rest_handle = rest_handle;
983   handle->zone_pkey = NULL;
984
985   handle->url = GNUNET_strdup (rest_handle->url);
986   if (handle->url[strlen (handle->url) - 1] == '/')
987     handle->url[strlen (handle->url) - 1] = '\0';
988   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
989
990   handle->ns_handle = GNUNET_NAMESTORE_connect (cfg);
991   handle->identity_handle =
992     GNUNET_IDENTITY_connect (cfg, &id_connect_cb, handle);
993   handle->timeout_task =
994     GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle);
995
996   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
997 }
998
999
1000 /**
1001  * Entry point for the plugin.
1002  *
1003  * @param cls Config info
1004  * @return NULL on error, otherwise the plugin context
1005  */
1006 void *
1007 libgnunet_plugin_rest_namestore_init (void *cls)
1008 {
1009   static struct Plugin plugin;
1010   struct GNUNET_REST_Plugin *api;
1011
1012   cfg = cls;
1013   if (NULL != plugin.cfg)
1014     return NULL; /* can only initialize once! */
1015   memset (&plugin, 0, sizeof(struct Plugin));
1016   plugin.cfg = cfg;
1017   api = GNUNET_new (struct GNUNET_REST_Plugin);
1018   api->cls = &plugin;
1019   api->name = GNUNET_REST_API_NS_NAMESTORE;
1020   api->process_request = &rest_process_request;
1021   GNUNET_asprintf (&allow_methods,
1022                    "%s, %s, %s, %s, %s",
1023                    MHD_HTTP_METHOD_GET,
1024                    MHD_HTTP_METHOD_POST,
1025                    MHD_HTTP_METHOD_PUT,
1026                    MHD_HTTP_METHOD_DELETE,
1027                    MHD_HTTP_METHOD_OPTIONS);
1028
1029   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ("Namestore REST API initialized\n"));
1030   return api;
1031 }
1032
1033
1034 /**
1035  * Exit point from the plugin.
1036  *
1037  * @param cls the plugin context (as returned by "init")
1038  * @return always NULL
1039  */
1040 void *
1041 libgnunet_plugin_rest_namestore_done (void *cls)
1042 {
1043   struct GNUNET_REST_Plugin *api = cls;
1044   struct Plugin *plugin = api->cls;
1045
1046   plugin->cfg = NULL;
1047
1048   GNUNET_free_non_null (allow_methods);
1049   GNUNET_free (api);
1050   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Namestore REST plugin is finished\n");
1051   return NULL;
1052 }
1053
1054
1055 /* end of plugin_rest_namestore.c */