fix uninit variable causing crash on 0 value
[oweals/gnunet.git] / src / gns / plugin_rest_gns.c
index 34529884916475c4b421ea3d66b6580929ac8f0e..3cddbc2469a2754ab04266bca1201611669fbe5c 100644 (file)
@@ -1,6 +1,6 @@
 /*
    This file is part of GNUnet.
-   Copyright (C) 2012-2015 Christian Grothoff (and other contributing authors)
+   Copyright (C) 2012-2015 GNUnet e.V.
 
    GNUnet is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published
@@ -14,8 +14,8 @@
 
    You should have received a copy of the GNU General Public License
    along with GNUnet; see the file COPYING.  If not, write to the
-   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.
+   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
    */
 /**
  * @author Martin Schanzenbach
 #include <gnunet_gnsrecord_lib.h>
 #include <gnunet_namestore_service.h>
 #include <gnunet_gns_service.h>
+#include <gnunet_rest_lib.h>
+#include <gnunet_jsonapi_lib.h>
+#include <gnunet_jsonapi_util.h>
 #include <jansson.h>
 
-#define API_NAMESPACE "/gns"
+#define GNUNET_REST_API_NS_GNS "/gns"
 
-#define GNUNET_REST_JSON_ATTR_ID "id"
+#define GNUNET_REST_JSONAPI_GNS_RECORD_TYPE "record_type"
 
-#define GNUNET_REST_JSON_ATTR_TYPE "type"
+#define GNUNET_REST_JSONAPI_GNS_TYPEINFO "gns_name"
 
-#define GNUNET_GNS_JSON_RECORD_TYPE "rtype"
+#define GNUNET_REST_JSONAPI_GNS_RECORD "records"
 
-#define GNUNET_REST_JSON_ATTR_DATA "data"
+#define GNUNET_REST_JSONAPI_GNS_EGO "ego"
+
+#define GNUNET_REST_JSONAPI_GNS_PKEY "pkey"
+
+#define GNUNET_REST_JSONAPI_GNS_OPTIONS "options"
 
 /**
  * @brief struct returned by the initialization function of the plugin
@@ -70,6 +77,11 @@ struct LookupHandle
    */
   struct GNUNET_GNS_LookupRequest *lookup_request;
 
+  /**
+   * Handle to rest request
+   */
+  struct GNUNET_REST_RequestHandle *rest_handle;
+
   /**
    * Lookup an ego with the identity service.
    */
@@ -88,7 +100,7 @@ struct LookupHandle
   /**
    * ID of a task associated with the resolution process.
    */
-  struct GNUNET_SCHEDULER_Task * timeout_task;    
+  struct GNUNET_SCHEDULER_Task * timeout_task;
 
   /**
    * The root of the received JSON or NULL
@@ -118,12 +130,12 @@ struct LookupHandle
 
   /**
    * The Pkey to use
-   * In stirng representation from JSON
+   * In string representation from JSON
    */
   const char *pkey_str;
 
   /**
-   * The record type to look up
+   * The record type
    */
   int type;
 
@@ -147,13 +159,20 @@ struct LookupHandle
    */
   struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
 
+  /**
+   * HTTP response code
+   */
+  int response_code;
+
 };
 
+
 /**
- * Cleanup lookup handle
- * @praram handle Handle to clean up
+ * Cleanup lookup handle.
+ *
+ * @param handle Handle to clean up
  */
-void
+static void
 cleanup_handle (struct LookupHandle *handle)
 {
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -197,27 +216,6 @@ cleanup_handle (struct LookupHandle *handle)
 }
 
 
-/**
- * Create s JSON Response for MHD
- * TODO move to lib
- * @param data the JSON to return (can be NULL)
- * @return a MHD_Response handle
- */
-struct MHD_Response*
-create_json_response (const char *data)
-{
-  size_t len;
-  if (NULL == data)
-    len = 0;
-  else
-    len = strlen (data);
-  struct MHD_Response *resp = MHD_create_response_from_buffer (len,
-                                                               (void*)data,
-                                                               MHD_RESPMEM_MUST_COPY);
-  MHD_add_response_header (resp,MHD_HTTP_HEADER_CONTENT_TYPE,"application/json");
-  return resp;
-}
-
 /**
  * Task run on shutdown.  Cleans up everything.
  *
@@ -225,21 +223,23 @@ create_json_response (const char *data)
  * @param tc scheduler context
  */
 static void
-do_error (void *cls,
-          const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_error (void *cls)
 {
   struct LookupHandle *handle = cls;
-  struct MHD_Response *resp = create_json_response (NULL);
-  handle->proc (handle->proc_cls, resp, GNUNET_SYSERR);
+  struct MHD_Response *resp;
+
+  resp = GNUNET_REST_create_response (NULL);
+  handle->proc (handle->proc_cls, resp, handle->response_code);
   cleanup_handle (handle);
 }
 
+
 /**
  * Create json representation of a GNSRECORD
  *
  * @param rd the GNSRECORD_Data
  */
-json_t *
+static json_t *
 gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd)
 {
   const char *typename;
@@ -259,7 +259,7 @@ gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd)
                 (int) rd->record_type);
     return NULL;
   }
-  record_obj = json_object();
+  record_obj = json_object ();
   json_object_set_new (record_obj, "type", json_string (typename));
   json_object_set_new (record_obj, "value", json_string (string_val));
   GNUNET_free (string_val);
@@ -296,39 +296,35 @@ process_lookup_result (void *cls, uint32_t rd_count,
 {
   struct LookupHandle *handle = cls;
   struct MHD_Response *resp;
+  struct GNUNET_JSONAPI_Document *json_document;
+  struct GNUNET_JSONAPI_Resource *json_resource;
   uint32_t i;
   char *result;
-  json_t *result_root;
-  json_t *result_data;
   json_t *result_array;
   json_t *record_obj;
 
-  result_root = json_object();
   result_array = json_array();
-  result_data = json_object();
-  json_object_set_new (result_root, GNUNET_REST_JSON_ATTR_ID, json_string (handle->name));
-  json_object_set (result_root,
-                   GNUNET_REST_JSON_ATTR_TYPE,
-                   json_string (GNUNET_GNS_JSON_RECORD_TYPE));
+  json_document = GNUNET_JSONAPI_document_new ();
+  json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_GNS_TYPEINFO, handle->name);
   handle->lookup_request = NULL;
   for (i=0; i<rd_count; i++)
   {
     if ( (rd[i].record_type != handle->type) &&
          (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
       continue;
-
     record_obj = gnsrecord_to_json (&(rd[i]));
     json_array_append (result_array, record_obj);
     json_decref (record_obj);
   }
-  json_object_set (result_root, GNUNET_REST_JSON_ATTR_DATA, result_data);
-  json_decref (result_data);
-  json_object_set (result_data, "query_result", result_array);
-  json_decref (result_array);
-  result = json_dumps (result_root, JSON_COMPACT);
+  GNUNET_JSONAPI_resource_add_attr (json_resource,
+                                         GNUNET_REST_JSONAPI_GNS_RECORD,
+                                         result_array);
+  GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
+  GNUNET_JSONAPI_document_serialize (json_document, &result);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
-  json_decref (result_root);
-  resp = create_json_response (result);
+  json_decref (result_array);
+  GNUNET_JSONAPI_document_delete (json_document);
+  resp = GNUNET_REST_create_response (result);
   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
   GNUNET_free (result);
   cleanup_handle (handle);
@@ -497,11 +493,10 @@ identity_master_cb (void *cls,
  * @param handle lookup handle to populate
  * @return GNUNET_SYSERR on error
  */
-int
+static int
 parse_url (const char *url, struct LookupHandle *handle)
 {
   char *name;
-  char *type;
   char tmp_url[strlen(url)+1];
   char *tok;
 
@@ -517,113 +512,24 @@ parse_url (const char *url, struct LookupHandle *handle)
                    name);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Got name: %s\n", handle->name);
-  type = strtok (NULL, "/");
-  if (NULL == type)
-  {
-    handle->type = GNUNET_GNSRECORD_TYPE_ANY;
-    return GNUNET_OK;
-  }
-  handle->type = GNUNET_GNSRECORD_typename_to_number (type);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got type: %s\n", type);   
-  return GNUNET_OK;
-}
-
-/**
- * Parse json from REST request
- * TODO  1. this leaks 2. Rework JSON API. This is confusing
- *
- * @param data REST data
- * @param data_size data size
- * @param handle Handle to populate
- * @return GNUNET_SYSERR on error
- */
-int
-parse_json (const char *data, size_t data_size, struct LookupHandle *handle)
-{
-  json_error_t error;
-  json_t *pkey_json;
-  json_t *ego_json;
-  json_t *options_json;
-
-  char term_data[data_size+1];
-  term_data[data_size] = '\0';
-
-  memcpy (term_data, data, data_size);
-
-  handle->json_root = json_loads (term_data, 0, &error);
-
-  if (NULL == handle->json_root)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, error.text);
-    return GNUNET_SYSERR;
-  }
-
-  if(!json_is_object(handle->json_root))
-  {
-    return GNUNET_SYSERR;
-  }
-
-  ego_json = json_object_get (handle->json_root, "ego");
-
-  if(json_is_string(ego_json))
-  {
-    handle->ego_str = json_string_value (ego_json);
-  }
-
-  pkey_json = json_object_get (handle->json_root, "pkey");
-  if(json_is_string(pkey_json))
-  {
-    handle->pkey_str = json_string_value (pkey_json);
-  }
-
-  options_json = json_object_get (handle->json_root, "options");
-  if(json_is_integer (options_json))
-  {
-    handle->options = json_integer_value (options_json);
-  }
   return GNUNET_OK;
 }
 
-
-/**
- * Function processing the REST call
- *
- * @param method HTTP method
- * @param url URL of the HTTP request
- * @param data body of the HTTP request (optional)
- * @param data_size length of the body
- * @param proc callback function for the result
- * @param proc_cls closure for callback function
- * @return GNUNET_OK if request accepted
- */
-void
-rest_gns_process_request(struct RestConnectionDataHandle *conndata_handle,
-                         GNUNET_REST_ResultProcessor proc,
-                         void *proc_cls)
+static void
+get_gns_cont (struct GNUNET_REST_RequestHandle *conndata_handle,
+              const char* url,
+              void *cls)
 {
-  struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
-  handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+  struct LookupHandle *handle = cls;
+  struct GNUNET_HashCode key;
 
   //parse name and type from url
-  if (GNUNET_OK != parse_url (conndata_handle->url, handle))
+  if (GNUNET_OK != parse_url (url, handle))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing url...\n");
     GNUNET_SCHEDULER_add_now (&do_error, handle);
     return;
   }
-
-  handle->proc_cls = proc_cls;
-  handle->proc = proc;
-  if (0 < conndata_handle->data_size)
-  {
-    if (GNUNET_OK != parse_json (conndata_handle->data, conndata_handle->data_size, handle))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing json...\n");
-      GNUNET_SCHEDULER_add_now (&do_error, handle);
-      return;
-    }
-  }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Connecting...\n");
   handle->gns = GNUNET_GNS_connect (cfg);
@@ -632,7 +538,6 @@ rest_gns_process_request(struct RestConnectionDataHandle *conndata_handle,
                                                        &do_error, handle);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Connected\n");
-
   if (NULL == handle->gns)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -640,9 +545,40 @@ rest_gns_process_request(struct RestConnectionDataHandle *conndata_handle,
     GNUNET_SCHEDULER_add_now (&do_error, handle);
     return;
   }
+  GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_OPTIONS,
+                      strlen (GNUNET_REST_JSONAPI_GNS_OPTIONS),
+                      &key);
+  handle->options = GNUNET_GNS_LO_DEFAULT;
+  if ( GNUNET_YES ==
+       GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
+                                               &key) )
+  {
+    handle->options = GNUNET_GNS_LO_DEFAULT;//TODO(char*) GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
+    //&key);
+  }
+  GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE,
+                      strlen (GNUNET_REST_JSONAPI_GNS_RECORD_TYPE),
+                      &key);
+  if ( GNUNET_YES ==
+       GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
+                                               &key) )
+  {
+    handle->type = GNUNET_GNSRECORD_typename_to_number
+      (GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
+                                          &key));
+  }
+  else
+    handle->type = GNUNET_GNSRECORD_TYPE_ANY;
 
-  if (NULL != handle->pkey_str)
+  GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_PKEY,
+                      strlen (GNUNET_REST_JSONAPI_GNS_PKEY),
+                      &key);
+  if ( GNUNET_YES ==
+       GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
+                                               &key) )
   {
+    handle->pkey_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
+                                                          &key);
     if (GNUNET_OK !=
         GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->pkey_str,
                                                     strlen(handle->pkey_str),
@@ -652,10 +588,17 @@ rest_gns_process_request(struct RestConnectionDataHandle *conndata_handle,
       return;
     }
     lookup_with_public_key (handle);
-
+    return;
   }
-  if (NULL != handle->ego_str)
+  GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_GNS_EGO,
+                      strlen (GNUNET_REST_JSONAPI_GNS_EGO),
+                      &key);
+  if ( GNUNET_YES ==
+       GNUNET_CONTAINER_multihashmap_contains (conndata_handle->url_param_map,
+                                               &key) )
   {
+    handle->ego_str = GNUNET_CONTAINER_multihashmap_get (conndata_handle->url_param_map,
+                                                         &key);
     handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
                                              handle->ego_str,
                                              &identity_zone_cb,
@@ -667,7 +610,6 @@ rest_gns_process_request(struct RestConnectionDataHandle *conndata_handle,
        (0 == strcmp (".zkey",
                      &handle->name[strlen (handle->name) - 4])) )
   {
-    /* no zone required, use 'anonymous' zone */
     GNUNET_CRYPTO_ecdsa_key_get_public
       (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
        &(handle->pkey));
@@ -684,6 +626,72 @@ rest_gns_process_request(struct RestConnectionDataHandle *conndata_handle,
   }
 }
 
+/**
+ * Handle rest request
+ *
+ * @param handle the lookup handle
+ */
+static void
+options_cont (struct GNUNET_REST_RequestHandle *con_handle,
+              const char* url,
+              void *cls)
+{
+  struct MHD_Response *resp;
+  struct LookupHandle *handle = cls;
+
+  //For GNS, independent of path return all options
+  resp = GNUNET_REST_create_response (NULL);
+  MHD_add_response_header (resp,
+                           "Access-Control-Allow-Methods",
+                           MHD_HTTP_METHOD_GET);
+  handle->proc (handle->proc_cls,
+               resp,
+               MHD_HTTP_OK);
+  cleanup_handle (handle);
+}
+
+
+/**
+ * Function processing the REST call
+ *
+ * @param method HTTP method
+ * @param url URL of the HTTP request
+ * @param data body of the HTTP request (optional)
+ * @param data_size length of the body
+ * @param proc callback function for the result
+ * @param proc_cls closure for callback function
+ * @return GNUNET_OK if request accepted
+ */
+static void
+rest_gns_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
+                         GNUNET_REST_ResultProcessor proc,
+                         void *proc_cls)
+{
+  struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
+  struct GNUNET_REST_RequestHandlerError err;
+
+  handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+  handle->proc_cls = proc_cls;
+  handle->proc = proc;
+  handle->rest_handle = conndata_handle;
+
+  static const struct GNUNET_REST_RequestHandler handlers[] = {
+    {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont},
+    {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont},
+    GNUNET_REST_HANDLER_END
+  };
+
+  if (GNUNET_NO == GNUNET_JSONAPI_handle_request (conndata_handle,
+                                                  handlers,
+                                                  &err,
+                                                  handle))
+  {
+    handle->response_code = err.error_code;
+    GNUNET_SCHEDULER_add_now (&do_error, handle);
+  }
+}
+
+
 /**
  * Entry point for the plugin.
  *
@@ -703,7 +711,7 @@ libgnunet_plugin_rest_gns_init (void *cls)
   plugin.cfg = cfg;
   api = GNUNET_new (struct GNUNET_REST_Plugin);
   api->cls = &plugin;
-  api->name = API_NAMESPACE;
+  api->name = GNUNET_REST_API_NS_GNS;
   api->process_request = &rest_gns_process_request;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               _("GNS REST API initialized\n"));