Cloud API added 76/189776/4
authorMichal Kolodziejski <m.kolodziejs@samsung.com>
Thu, 20 Sep 2018 10:52:52 +0000 (12:52 +0200)
committerMichal Kolodziejski <m.kolodziejs@samsung.com>
Tue, 25 Sep 2018 16:43:22 +0000 (18:43 +0200)
Change-Id: If689e3f336be2b13ddc6ac597135c3692c28a51e
Signed-off-by: Michal Kolodziejski <m.kolodziejs@samsung.com>
CMakeLists.txt
inc/cloud/car_info.h [new file with mode: 0644]
inc/cloud/car_info_serializer.h [new file with mode: 0644]
inc/cloud/cloud_request.h [new file with mode: 0644]
inc/cloud/http_request.h [new file with mode: 0644]
packaging/car-app.spec
src/cloud/car_info.c [new file with mode: 0644]
src/cloud/car_info_serializer.c [new file with mode: 0644]
src/cloud/cloud_request.c [new file with mode: 0644]
src/cloud/http_request.c [new file with mode: 0644]

index 9e5aac8..37f165f 100644 (file)
@@ -13,6 +13,8 @@ pkg_check_modules(APP_PKGS REQUIRED
        capi-appfw-application
        capi-appfw-service-application
        capi-system-peripheral-io
+       libcurl
+       json-glib-1.0
        glib-2.0
        gio-2.0
        capi-network-connection
@@ -42,6 +44,10 @@ ADD_EXECUTABLE(${PROJECT_NAME}
        ${PROJECT_ROOT_DIR}/src/resource/resource_motor_driver_L298N.c
        ${PROJECT_ROOT_DIR}/src/resource/resource_PCA9685.c
        ${PROJECT_ROOT_DIR}/src/resource/resource_servo_motor.c
+       ${PROJECT_ROOT_DIR}/src/cloud/car_info.c
+       ${PROJECT_ROOT_DIR}/src/cloud/car_info_serializer.c
+       ${PROJECT_ROOT_DIR}/src/cloud/cloud_request.c
+       ${PROJECT_ROOT_DIR}/src/cloud/http_request.c
 )
 
 TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} -lm)
diff --git a/inc/cloud/car_info.h b/inc/cloud/car_info.h
new file mode 100644 (file)
index 0000000..da95734
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CAR_INFO_H_
+#define __CAR_INFO_H_
+
+#include <stdbool.h>
+
+/**
+ * @brief The car info structure template.
+ */
+typedef struct car_info car_info_t;
+
+/**
+ * @brief Creates new instance of car_info_t.
+ * @return new car_info_t object, or NULL on error.
+ */
+car_info_t *car_info_create();
+
+/**
+ * @brief Crates deep copy of given car_info_t object.
+ * @return Copied car_info_t object, or NULL on error.
+ */
+car_info_t *car_info_copy(const car_info_t *car_info);
+
+/**
+ * @brief Releases car_info_t.
+ * @param[in] car_info Car info struct.
+ */
+void car_info_destroy(car_info_t *car_info);
+
+/**
+ * @brief Checks if car_info_t structure core fields are set.
+ * @param[in] car_info Car info struct.
+ * @returns Returns true if object is valid, otherwise false.
+ */
+bool car_info_is_valid(car_info_t *car_info);
+
+/**
+ * @brief Gets car id.
+ * @param[in] car_info Car info struct.
+ * @param[out] car_id The car id.
+ * @return Returns car id or NULL on error.
+ * @remark This value is valid only during car_info life.
+ */
+const char *car_info_get_car_id(car_info_t *car_info);
+
+/**
+ * @brief Sets car id.
+ * @param[in] car_info Car info struct.
+ * @param[in] car_id The car id.
+ * @return Returns 0 on success, -1 otherwise.
+ */
+int car_info_set_car_id(car_info_t *car_info, const char *car_id);
+
+/**
+ * @brief Gets car name.
+ * @param[in] car_info Car info struct.
+ * @param[out] car_id The car id.
+ * @return Returns car name or NULL on error.
+ * @remark This value is valid only during car_info life.
+ */
+const char *car_info_get_car_name(car_info_t *car_info);
+
+/**
+ * @brief Sets car name.
+ * @param[in] car_info Car info struct.
+ * @param[in] car_name The car name.
+ * @return Returns 0 on success, -1 otherwise.
+ */
+int car_info_set_car_name(car_info_t *car_info, const char *car_name);
+
+/**
+ * @brief Gets car ip.
+ * @param[in] car_info Car info struct.
+ * @param[out] car_ip The car ip.
+ * @return Returns car ip or NULL on error.
+ * @remark This value is valid only during car_info life.
+ */
+const char *car_info_get_car_ip(car_info_t *car_info);
+
+/**
+ * @brief Sets car ip.
+ * @param[in] car_info Car info struct.
+ * @param[in] car_ip The car ip.
+ * @return Returns 0 on success, -1 otherwise.
+ * @remarks Param car_ip should be valid ip address.
+ */
+int car_info_set_car_ip(car_info_t *car_info, const char *car_ip);
+
+/**
+ * @brief Gets access point mac.
+ * @param[in] car_info Car info struct.
+ * @param[out] ap_mac The access point mac address.
+ * @return Returns access po id or NULL on error.
+ * @remark This value is valid only during car_info life.
+ */
+const char *car_info_get_ap_mac(car_info_t *car_info);
+
+/**
+ * @brief Sets access point mac.
+ * @param[in] car_info Car info struct.
+ * @param[in] ap_mac The access point mac address.
+ * @return Returns 0 on success, -1 otherwise.
+ * @remarks Param ap_max should be valid mac address.
+ */
+int car_info_set_car_ap_mac(car_info_t *car_info, const char *ap_mac);
+
+/**
+ * @brief Gets access point ssid.
+ * @param[in] car_info Car info struct.
+ * @param[out] ap_ssid The access point ssid.
+ * @return Returns -1 if any error occurred, 0 otherwise.
+ */
+const char *car_info_get_ap_ssid(car_info_t *car_info);
+
+/**
+ * @brief Sets access point ssid.
+ * @param[out] ap_ssid The access point ssid.
+ * @param[in] car_id The car id.
+ * @return Returns 0 on success, -1 otherwise.
+ */
+int car_info_set_ap_ssid(car_info_t *car_info, const char *ap_ssid);
+
+#endif
\ No newline at end of file
diff --git a/inc/cloud/car_info_serializer.h b/inc/cloud/car_info_serializer.h
new file mode 100644 (file)
index 0000000..ef7885d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CAR_INFO_SERIALIZER_H_
+#define __CAR_INFO_SERIALIZER_H_
+
+#include "car_info.h"
+
+/**
+ * @brief Serializes car_info_t struct into json string.
+ * @param[in] car_info The struct with car data.
+ * @return Json with car data.
+ * @remarks Returned value should be freed.
+ */
+char *car_info_serializer_serialize(car_info_t *car_info);
+
+/**
+ * @brief Deserializes json string to array of car_info_t structs.
+ * @param[in] json_data Json string with config.
+ * @param[out] size Length of car_info_t array.
+ * @return Dynamically allocated car_info_t array or NULL on error.
+ * @remarks Returned array should be released with @free and its every element with @car_info_destroy.
+ */
+car_info_t **car_info_serializer_deserialize_array(const char *json_data, int *size);
+
+#endif
\ No newline at end of file
diff --git a/inc/cloud/cloud_request.h b/inc/cloud/cloud_request.h
new file mode 100644 (file)
index 0000000..8137fed
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CLOUD_REQUEST_H_
+#define __CLOUD_REQUEST_H_
+
+#include <gio/gio.h>
+#include "car_info.h"
+
+/**
+ * @brief Enum that indicates if HTTP request finished succesfully or not.
+*/
+typedef enum
+{
+    SUCCESS = 0,
+    FAILURE
+} request_result_e;
+
+/**
+ * @brief Called when data from HTTP /api/racing GET request was obtained.
+ *
+ * @param[in] result Result of request.
+ * @param[in] car_info The array of car_info struct.
+ * @param[in] size Length of the car_info array
+ * @param[in] user_data User data passed in @cloud_request_api_racing_get function.
+ */
+typedef void(*cloud_request_car_list_data_cb)(request_result_e result, car_info_t **car_info, int size, void *user_data);
+
+/**
+ * @brief Called when data from HTTP /api/racing POST request was obtained.
+ *
+ * @param[in] result Result of request.
+ * @param[in] user_data User data passed in @cloud_request_api_racing_post function.
+ */
+typedef void(*cloud_request_car_post_finish_cb)(request_result_e result, void *user_data);
+
+/**
+ * @brief Sends cloud request that obtains list of registered cars.
+ *
+ * @param[in] ap_mac Mac address of access point that device is connected to.
+ * @param[in] callback Function that will be invoked, when request will be finished.
+ *
+ * @return Returns @GCancellable object that allows to cancel this request.
+ * @remark To cancel task function g_cancellable_cancel should be called.
+ */
+GCancellable *cloud_request_api_racing_get(const char *ap_mac, cloud_request_car_list_data_cb callback, void *user_data);
+
+/**
+ * @brief Sends cloud request registering the car.
+ *
+ * @param[in] car_info The car_info object with car data.
+ * @param[in] callback Function that will be invoked, when request will be finished.
+ *
+ * @return Returns @GCancellable object that allows to cancel this request.
+ * @remark To cancel task function g_cancellable_cancel should be called.
+ */
+GCancellable *cloud_request_api_racing_post(const car_info_t *car_info, cloud_request_car_post_finish_cb callback, void *user_data);
+
+#endif
\ No newline at end of file
diff --git a/inc/cloud/http_request.h b/inc/cloud/http_request.h
new file mode 100644 (file)
index 0000000..ddaf958
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __HTTP_REQUEST_H_
+#define __HTTP_REQUEST_H_
+
+/**
+ * @brief Generic HTTP GET.
+ * @param[in] url Url of request.
+ * @param[out] response Response from the request.
+ * @param[out] response_code HTTP response code.
+ * @return Returns 0 on success, -1 otherwise.
+ * @remarks Response should be released with @free.
+ */
+int http_request_get(const char *url, char **response, long *response_code);
+
+/**
+ * @brief Generic HTTP POST.
+ * @param[in] url URL of request.
+ * @param[in] json Content of the response in json format.
+ * @param[out] response Response from the request.
+ * @param[out] response_code HTTP response code.
+ * @return Returns 0 on success, -1 otherwise.
+ * @remarks Response should be released with @free.
+ */
+int http_request_post(const char *url, const char *json, char **response, long *response_code);
+
+#endif
\ No newline at end of file
index b957a3e..62ea0a2 100644 (file)
@@ -20,7 +20,9 @@ BuildRequires:  pkgconfig(capi-appfw-service-application)
 BuildRequires:  pkgconfig(capi-system-peripheral-io)
 BuildRequires:  pkgconfig(gio-2.0)
 BuildRequires:  pkgconfig(glib-2.0)
-BuildRequires: pkgconfig(capi-network-connection)
+BuildRequires:  pkgconfig(json-glib-1.0)
+BuildRequires:  pkgconfig(libcurl)
+BuildRequires:  pkgconfig(capi-network-connection)
 
 %description
 Car application
diff --git a/src/cloud/car_info.c b/src/cloud/car_info.c
new file mode 100644 (file)
index 0000000..7c977d7
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+#include <string.h>
+#include <regex.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include "cloud/car_info.h"
+#include "log.h"
+
+#define MAX_LENGTH 256
+#define MAX_LENGTH_IP 16
+#define MAX_LENGTH_MAC 18
+#define MAX_LENGTH_SSID 33
+
+#define SAFE_STR_CPY(src, obj, size)\
+do {\
+    if(obj)\
+        src = strndup(obj, size);\
+} while(0)
+
+static int validate_ip_address(const char *ip_address);
+static int validate_mac_address(const char *mac_address);
+
+struct car_info
+{
+    char *id;
+    char *name;
+    char *ip;
+    char *ap_mac;
+    char *ap_ssid;
+};
+
+car_info_t *car_info_create()
+{
+    struct car_info *car_info = g_new0(struct car_info, 1);
+    retvm_if(!car_info, NULL, "Could not allocate memory!");
+
+    return car_info;
+}
+
+car_info_t *car_info_copy(const car_info_t *car_info)
+{
+    retv_if(!car_info, NULL);
+
+    struct car_info *car_info_cpy = g_new0(struct car_info, 1);
+    SAFE_STR_CPY(car_info_cpy->id, car_info->id, MAX_LENGTH);
+    SAFE_STR_CPY(car_info_cpy->name, car_info->name, MAX_LENGTH);
+    SAFE_STR_CPY(car_info_cpy->ip, car_info->ip, MAX_LENGTH_IP);
+    SAFE_STR_CPY(car_info_cpy->ap_mac, car_info->ap_mac, MAX_LENGTH_MAC);
+    SAFE_STR_CPY(car_info_cpy->ap_ssid, car_info->ap_ssid, MAX_LENGTH_SSID);
+
+    return car_info_cpy;
+}
+
+void car_info_destroy(car_info_t *car_info)
+{
+    ret_if(!car_info);
+
+    free(car_info->id);
+    free(car_info->name);
+    free(car_info->ip);
+    free(car_info->ap_mac);
+    free(car_info->ap_ssid);
+
+    g_free(car_info);
+}
+
+bool car_info_is_valid(car_info_t *car_info)
+{
+    return (car_info->id && car_info->ip && car_info->ap_mac && car_info->ap_ssid);
+}
+
+const char *car_info_get_car_id(car_info_t *car_info)
+{
+    retv_if(!car_info, NULL);
+
+    return car_info->id;
+}
+
+int car_info_set_car_id(car_info_t *car_info, const char *car_id)
+{
+    retv_if(!car_info, -1);
+    retv_if(car_id == NULL, -1);
+    retv_if(strlen(car_id) >= MAX_LENGTH, -1);
+
+    if (!car_info->id)
+        car_info->id = (char *)g_malloc(MAX_LENGTH * sizeof(char));
+
+    snprintf(car_info->id, MAX_LENGTH, "%s", car_id);
+    return 0;
+}
+
+const char *car_info_get_car_name(car_info_t *car_info)
+{
+    retv_if(!car_info, NULL);
+    return car_info->name;
+}
+
+int car_info_set_car_name(car_info_t *car_info, const char *car_name)
+{
+    retv_if(!car_info, -1);
+    retv_if(car_name == NULL, -1);
+    retv_if(strlen(car_name) >= MAX_LENGTH, -1);
+
+    if (!car_info->name)
+        car_info->name = (char *)g_malloc(MAX_LENGTH * sizeof(char));
+
+    snprintf(car_info->name, MAX_LENGTH, "%s", car_name);
+    return 0;
+}
+
+const char *car_info_get_car_ip(car_info_t *car_info)
+{
+    retv_if(!car_info, NULL);
+    return car_info->ip;
+}
+
+int car_info_set_car_ip(car_info_t *car_info, const char *car_ip)
+{
+    retv_if(!car_info, -1);
+    retv_if(car_ip == NULL, -1);
+    retv_if(strlen(car_ip) >= MAX_LENGTH_IP, -1);
+    retv_if(validate_ip_address(car_ip) != 0, -1);
+
+    if (!car_info->ip)
+        car_info->ip = (char *)g_malloc(MAX_LENGTH_IP * sizeof(char));
+
+    snprintf(car_info->ip, MAX_LENGTH_IP, "%s", car_ip);
+    return 0;
+}
+
+const char *car_info_get_ap_mac(car_info_t *car_info)
+{
+    retv_if(!car_info, NULL);
+    return car_info->ap_mac ? car_info->ap_mac : "NULL";
+}
+
+int car_info_set_car_ap_mac(car_info_t *car_info, const char *ap_mac)
+{
+    retv_if(!car_info, -1);
+    retv_if(ap_mac == NULL, -1);
+    retv_if(strlen(ap_mac) >= MAX_LENGTH_MAC, -1);
+    retv_if(validate_mac_address(ap_mac) != 0, -1);
+
+    if (!car_info->ap_mac)
+        car_info->ap_mac = (char *)g_malloc(MAX_LENGTH_MAC * sizeof(char));
+
+    snprintf(car_info->ap_mac, MAX_LENGTH_MAC, "%s", ap_mac);
+    return 0;
+}
+
+const char *car_info_get_ap_ssid(car_info_t *car_info)
+{
+    retv_if(!car_info, NULL);
+    return car_info->ap_ssid;
+}
+
+int car_info_set_ap_ssid(car_info_t *car_info, const char *ap_ssid)
+{
+    retv_if(!car_info, -1);
+    retv_if(ap_ssid == NULL, -1);
+    retv_if(strlen(ap_ssid) >= MAX_LENGTH_SSID, -1);
+
+    if (!car_info->ap_ssid)
+        car_info->ap_ssid = (char *)g_malloc(MAX_LENGTH_SSID * sizeof(char));
+
+    snprintf(car_info->ap_ssid, MAX_LENGTH_SSID, "%s", ap_ssid);
+    return 0;
+}
+
+static int validate_ip_address(const char *ip_address)
+{
+    struct sockaddr_in sa;
+    int result = inet_pton(AF_INET, ip_address, &(sa.sin_addr));
+    return result == 1 ? 0 : -1;
+}
+
+static int validate_mac_address(const char *mac_address)
+{
+    int i = 0;
+    int segments = 1;
+
+    while(*mac_address)
+    {
+        if (isxdigit(*mac_address))
+        {
+            i++;
+            if (i > 2) return -1;
+        }
+        else if (*mac_address == ':')
+        {
+            if (i != 2) return -1;
+            i = 0;
+            segments++;
+        }
+        else {
+            return -1;
+        }
+        mac_address++;
+    }
+
+    return (segments == 6 && i == 2) ? 0 : -1;
+}
diff --git a/src/cloud/car_info_serializer.c b/src/cloud/car_info_serializer.c
new file mode 100644 (file)
index 0000000..3ee74f6
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cloud/car_info_serializer.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <json-glib/json-glib.h>
+#include "log.h"
+#include "cloud/car_info.h"
+#include "string.h"
+
+#define JSON_SCHEMA_CAR_ID "carId"
+#define JSON_SCHEMA_RESPONSE_CAR_ID "id"
+#define JSON_SCHEMA_CAR_NAME "carName"
+#define JSON_SCHEMA_CAR_IP "carIp"
+#define JSON_SCHEMA_AP_MAC "apMac"
+#define JSON_SCHEMA_AP_SSID "apSsid"
+
+static JsonNode *parse_string(JsonParser *parser, const char *config_json);
+static void car_info_array_iterate_cb(JsonArray *array, guint index, JsonNode *element, gpointer user_data);
+
+char *car_info_serializer_serialize(car_info_t *car_info)
+{
+    JsonGenerator *generator = json_generator_new();
+    JsonBuilder *builder = json_builder_new();
+    json_builder_begin_object(builder);
+
+    json_builder_set_member_name(builder, JSON_SCHEMA_CAR_ID);
+    json_builder_add_string_value(builder, car_info_get_car_id(car_info));
+
+    json_builder_set_member_name(builder, JSON_SCHEMA_CAR_NAME);
+    json_builder_add_string_value(builder, car_info_get_car_name(car_info));
+
+    json_builder_set_member_name(builder, JSON_SCHEMA_CAR_IP);
+    json_builder_add_string_value(builder, car_info_get_car_ip(car_info));
+
+    json_builder_set_member_name(builder, JSON_SCHEMA_AP_MAC);
+    json_builder_add_string_value(builder, car_info_get_ap_mac(car_info));
+
+    json_builder_set_member_name(builder, JSON_SCHEMA_AP_SSID);
+    json_builder_add_string_value(builder, car_info_get_ap_ssid(car_info));
+
+    json_builder_end_object(builder);
+
+    JsonNode *root = json_builder_get_root(builder);
+    json_generator_set_root(generator, root);
+
+    char *json_data = json_generator_to_data(generator, NULL);
+
+    g_object_unref(builder);
+    g_object_unref(generator);
+    return json_data;
+}
+
+car_info_t **car_info_serializer_deserialize_array(const char *json_data, int *size)
+{
+    JsonParser *parser = json_parser_new();
+
+    JsonNode *root = parse_string(parser, json_data);
+    JsonArray *array = json_node_get_array(root);
+    if (!array)
+    {
+        _E("Json is invalid!");
+        g_object_unref(parser);
+        return NULL;
+    }
+
+    *size = json_array_get_length(array);
+
+    if (*size == 0) {
+        return NULL;
+    }
+
+    car_info_t **car_info_array = g_malloc(*size * sizeof(car_info_t *));
+
+    json_array_foreach_element(array, car_info_array_iterate_cb, car_info_array);
+
+    g_object_unref(parser);
+    return car_info_array;
+}
+
+static JsonNode *parse_string(JsonParser *parser, const char *json)
+{
+    GError *err = NULL;
+
+    if (!json_parser_load_from_data(parser, json, -1, &err))
+    {
+        _E("Function \"json_parser_load_from_data()\" failed with message: %s", err->message);
+        g_error_free(err);
+        return NULL;
+    }
+
+    return json_parser_get_root(parser);
+}
+
+static void car_info_array_iterate_cb(JsonArray *array, guint index, JsonNode *element, gpointer user_data)
+{
+    car_info_t **car_info_array = (car_info_t **)user_data;
+
+    JsonObject *entry = json_node_get_object(element);
+
+    car_info_array[index] = car_info_create();
+
+    if (json_object_has_member(entry, JSON_SCHEMA_RESPONSE_CAR_ID) &&
+        car_info_set_car_id(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_RESPONSE_CAR_ID)) != 0)
+    {
+        _E("Couldn't set car id!");
+    }
+
+    if (json_object_has_member(entry, JSON_SCHEMA_CAR_NAME) &&
+        car_info_set_car_name(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_CAR_NAME)) != 0)
+    {
+        _E("Couldn't set car name!");
+    }
+
+    if (json_object_has_member(entry, JSON_SCHEMA_CAR_IP) &&
+        car_info_set_car_ip(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_CAR_IP)) != 0)
+    {
+        _E("Couldn't set car ip!");
+    }
+
+    if (json_object_has_member(entry, JSON_SCHEMA_AP_MAC) &&
+        car_info_set_car_ap_mac(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_AP_MAC)) != 0)
+    {
+        _E("Couldn't set access point MAC address!");
+    }
+
+    if (json_object_has_member(entry, JSON_SCHEMA_AP_SSID) &&
+        car_info_set_ap_ssid(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_AP_SSID)) != 0)
+    {
+        _E("Couldn't set access point SSID!");
+    }
+}
diff --git a/src/cloud/cloud_request.c b/src/cloud/cloud_request.c
new file mode 100644 (file)
index 0000000..1a46fa1
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cloud/cloud_request.h"
+#include "cloud/car_info_serializer.h"
+#include "cloud/http_request.h"
+#include <glib.h>
+#include <gio/gio.h>
+#include <string.h>
+#include "log.h"
+
+#define BASE_URL "https://son.tizen.online"
+#define PATH_API_RACING "/api/racing"
+
+typedef struct {
+    char *ap_mac;
+    cloud_request_car_list_data_cb cb;
+    void *user_data;
+} car_api_get_request_context_t;
+
+typedef struct {
+    car_info_t *car;
+    cloud_request_car_post_finish_cb cb;
+    void *user_data;
+} car_api_post_request_context_t;
+
+typedef struct {
+    long response_code;
+    char *response_msg;
+} car_api_post_request_response_t;
+
+typedef struct {
+    long response_code;
+    car_info_t **cars;
+    int size;
+} car_api_get_request_response_t;
+
+GQuark g_spawn_error_quark();
+static void car_api_post_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable);
+static void car_api_post_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data);
+static void car_api_post_task_context_free(car_api_post_request_context_t *context);
+static void car_api_post_request_response_free(car_api_post_request_response_t *response);
+
+static void car_api_get_task_context_free(car_api_get_request_context_t *context);
+static void car_api_get_request_response_free(car_api_get_request_response_t *response);
+static void car_api_get_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable);
+static void car_api_get_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data);
+
+#define G_ERROR_DOMAIN g_spawn_error_quark()
+
+GCancellable *cloud_request_api_racing_get(const char *ap_mac, cloud_request_car_list_data_cb cb, void *user_data)
+{
+    GCancellable *cancellable = g_cancellable_new();
+
+    GTask *task = g_task_new(NULL, cancellable, car_api_get_task_ready_cb, NULL);
+    g_task_set_source_tag(task, cloud_request_api_racing_get);
+    g_task_set_return_on_cancel(task, FALSE);
+
+    car_api_get_request_context_t *context = g_new0(car_api_get_request_context_t, 1);
+    context->ap_mac = strndup(ap_mac, strlen(ap_mac));
+    context->cb = cb;
+    context->user_data = user_data;
+
+    g_task_set_task_data(task, context, (GDestroyNotify)car_api_get_task_context_free);
+    g_task_run_in_thread(task, car_api_get_task_thread_cb);
+
+    g_object_unref(task);
+
+    return cancellable;
+}
+
+GCancellable *cloud_request_api_racing_post(const car_info_t *car_info, cloud_request_car_post_finish_cb cb, void *user_data)
+{
+    GCancellable *cancellable = g_cancellable_new();
+
+    GTask *task = g_task_new(NULL, cancellable, car_api_post_task_ready_cb, NULL);
+    g_task_set_source_tag(task, cloud_request_api_racing_post);
+    g_task_set_return_on_cancel(task, FALSE);
+
+    car_api_post_request_context_t *context = g_new0(car_api_post_request_context_t, 1);
+    context->car = car_info_copy(car_info);
+    context->cb = cb;
+    context->user_data = user_data;
+
+    g_task_set_task_data(task, context, (GDestroyNotify)car_api_post_task_context_free);
+    g_task_run_in_thread(task, car_api_post_task_thread_cb);
+
+    g_object_unref(task);
+
+    return cancellable;
+}
+
+GQuark g_spawn_error_quark()
+{
+  return g_quark_from_static_string("cloud_request");
+}
+
+static void car_api_post_task_context_free(car_api_post_request_context_t *context)
+{
+    ret_if(!context);
+
+    car_info_destroy(context->car);
+    g_free(context);
+}
+
+static void car_api_post_request_response_free(car_api_post_request_response_t *response)
+{
+    ret_if(!response);
+
+    g_free(response->response_msg);
+    g_free(response);
+}
+
+static void car_api_post_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
+{
+    car_api_post_request_context_t *context = (car_api_post_request_context_t *)task_data;
+
+    if (g_task_return_error_if_cancelled(task)) {
+        return;
+    }
+
+    car_api_post_request_response_t *response = g_new0(car_api_post_request_response_t, 1);
+
+    char *json = car_info_serializer_serialize(context->car);
+    int retval = http_request_post(BASE_URL""PATH_API_RACING, json, &(response->response_msg), &(response->response_code));
+    g_free(json);
+
+    if (retval != 0) {
+        GError *err = g_error_new(G_ERROR_DOMAIN, retval, "http_request_post failed!");
+        g_task_return_error(task, err);
+    }
+
+    g_task_return_pointer(task, response, (GDestroyNotify)car_api_post_request_response_free);
+}
+
+static void car_api_post_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+    GTask *task = G_TASK(res);
+    GError *error = NULL;
+
+    //If no error occurred g_task_propagate_pointer transfers ownership, so later response have to be freed.
+    car_api_post_request_response_t *response = g_task_propagate_pointer(task, &error);
+    if (error != NULL) {
+        _E("POST async task failed with msg: %s", error->message);
+        g_error_free(error);
+        return;
+    }
+    car_api_post_request_context_t *context = g_task_get_task_data(task);
+
+    request_result_e result = (response->response_code == 200 && (strcmp(response->response_msg, "Success") == 0)) ?
+        SUCCESS :
+        FAILURE;
+
+    if (context->cb) {
+        context->cb(result, context->user_data);
+    }
+
+    car_api_post_request_response_free(response);
+}
+
+static void car_api_get_task_context_free(car_api_get_request_context_t *context)
+{
+    ret_if(!context);
+
+    g_free(context->ap_mac);
+    g_free(context);
+}
+
+static void car_api_get_request_response_free(car_api_get_request_response_t *response)
+{
+    ret_if(!response);
+    ret_if(response->size <= 0);
+
+    for (int i = 0; i < response->size; i++)
+    {
+        car_info_destroy(response->cars[i]);
+    }
+    g_free(response->cars);
+    g_free(response);
+}
+
+static void car_api_get_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
+{
+    car_api_get_request_context_t *context = (car_api_get_request_context_t *)task_data;
+
+    if (g_task_return_error_if_cancelled(task)) {
+        return;
+    }
+
+    car_api_get_request_response_t *response = g_new0(car_api_get_request_response_t, 1);
+    char *response_json = NULL;
+
+    GString *url = g_string_new(BASE_URL""PATH_API_RACING"?apMac=");
+    g_string_append(url, context->ap_mac);
+
+    int retval = http_request_get(url->str, &response_json, &(response->response_code));
+    g_string_free(url, TRUE);
+
+    if (retval != 0) {
+        GError *err = g_error_new(G_ERROR_DOMAIN, retval, "http_request_get failed!");
+        g_task_return_error(task, err);
+    }
+    else {
+        response->cars = car_info_serializer_deserialize_array(response_json, &(response->size));
+    }
+
+    g_free(response_json);
+    g_task_return_pointer(task, response, (GDestroyNotify)car_api_get_request_response_free);
+}
+
+static void car_api_get_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+    GTask *task = G_TASK(res);
+    GError *error = NULL;
+
+    //If no error occurred g_task_propagate_pointer transfers ownership, so later response have to be freed.
+    car_api_get_request_response_t *response = g_task_propagate_pointer(task, &error);
+    if (error != NULL) {
+        _E("GET async task failed with msg: %s", error->message);
+        g_error_free(error);
+        return;
+    }
+    car_api_get_request_context_t *context = g_task_get_task_data(task);
+
+    request_result_e result = (response->response_code == 200) ? SUCCESS : FAILURE;
+
+    if (context->cb) {
+        context->cb(result, response->cars, response->size, context->user_data);
+    }
+
+    car_api_get_request_response_free(response);
+}
\ No newline at end of file
diff --git a/src/cloud/http_request.c b/src/cloud/http_request.c
new file mode 100644 (file)
index 0000000..b094227
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cloud/http_request.h"
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <curl/curl.h>
+#include "log.h"
+
+static size_t _response_write(void *ptr, size_t size, size_t nmemb, void *data);
+
+int http_request_get(const char *url, char **response, long *response_code)
+{
+    retvm_if(!url, -1, "GET request URL is NULL!");
+    retvm_if(!response, -1, "GET request response is null");
+
+    CURL *curl = NULL;
+    CURLcode res = CURLE_OK;
+
+    curl = curl_easy_init();
+    retvm_if(!curl, -1, "Failed to initialize curl!");
+
+    curl_easy_setopt(curl, CURLOPT_URL, url);
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _response_write);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response);
+
+    res = curl_easy_perform(curl);
+    if (res != CURLE_OK) {
+        _E("curl_easy_perform() failed: %s", curl_easy_strerror(res));
+        curl_easy_cleanup(curl);
+        return -1;
+    }
+
+    long _response_code;
+    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_response_code);
+    if (_response_code) {
+        *response_code = _response_code;
+    }
+
+    curl_easy_cleanup(curl);
+
+    return 0;
+}
+
+int http_request_post(const char *url, const char *json, char **response, long *response_code)
+{
+    retvm_if(!url, -1, "POST request URL is NULL!");
+    retvm_if(!json, -1, "POST request JSON message is NULL!");
+
+    CURL *curl = curl_easy_init();
+    retvm_if(!curl, -1, "Failed to initialize curl!");
+
+    char *_response = NULL;
+    struct curl_slist *headers = curl_slist_append(NULL, "Content-Type: application/json");
+
+    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+    curl_easy_setopt(curl, CURLOPT_URL, url);
+    curl_easy_setopt(curl, CURLOPT_POST, 1L);
+    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json);
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _response_write);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&_response);
+
+    CURLcode res = CURLE_OK;
+    res = curl_easy_perform(curl);
+    if (res != CURLE_OK) {
+        _E("curl_easy_perform() failed: %s", curl_easy_strerror(res));
+        curl_slist_free_all(headers);
+        curl_easy_cleanup(curl);
+        return -1;
+    }
+
+    long _response_code;
+    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &_response_code);
+    if (response_code) {
+        *response_code = _response_code;
+    }
+
+    if (response) {
+        *response = _response;
+    }
+    else {
+        g_free(_response);
+    }
+
+    curl_slist_free_all(headers);
+    curl_easy_cleanup(curl);
+
+    return 0;
+}
+
+static size_t _response_write(void *ptr, size_t size, size_t nmemb, void *data)
+{
+    char **received = (char **)data;
+    size_t real_size = size * nmemb;
+    const char *response_msg = (const char *)ptr;
+
+    if (received && real_size > 0) {
+        if (*received) {
+            char *temp = strndup(response_msg, real_size);
+            size_t length = strlen(*received) + strlen(temp);
+            char *new = g_malloc(length * sizeof(char));
+            snprintf(new, length, "%s%s", *received, temp);
+            g_free(temp);
+            g_free(*received);
+            *received = new;
+        }
+        else {
+            *received = g_strndup((const char *)ptr, real_size);
+        }
+    }
+    else {
+        _E("Failed to get response, response size : %lu", real_size);
+    }
+
+    return real_size;
+}
\ No newline at end of file