From 29f06f1c6d641c17fa54f0458d91f7f158fee8f9 Mon Sep 17 00:00:00 2001 From: Preeja Raveendran Date: Tue, 17 Feb 2026 15:15:13 +0530 Subject: [PATCH 1/7] upgrade --- server/CMakeLists.txt | 51 +- server/gdial1p6-rest.c | 1376 ++++++++++++++++++++++++++++++++++++++ server/gdial1p6-shield.c | 148 ++++ server/gdial1p6-ssdp.c | 391 +++++++++++ server/gdialservice.cpp | 23 + 5 files changed, 1975 insertions(+), 14 deletions(-) create mode 100644 server/gdial1p6-rest.c create mode 100644 server/gdial1p6-shield.c create mode 100644 server/gdial1p6-ssdp.c diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index c506e0d7..39f4804e 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -26,14 +26,24 @@ set (GDIAL_VERSION_MINOR 0) find_package (PkgConfig REQUIRED) pkg_search_module (GLIB REQUIRED glib-2.0) pkg_search_module (GIO REQUIRED gio-2.0) -pkg_search_module (GSSDP12 gssdp-1.2) pkg_search_module (LIBSOUP3 libsoup-3.0) -if (GSSDP12_FOUND) - pkg_search_module (GSSDP REQUIRED gssdp-1.2) - add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) - message("Using gssdp-1.2") +if (LIBSOUP3_FOUND) + add_definitions(-DHAVE_LIBSOUP_VERSION_3) + pkg_search_module (GSSDP16 gssdp-1.6) + if (GSSDP16_FOUND) + pkg_search_module (GSSDP REQUIRED gssdp-1.6) + add_definitions(-DHAVE_GSSDP_VERSION_1_6_OR_NEWER) + message("Using gssdp-1.6") + endif() else() - pkg_search_module (GSSDP REQUIRED gssdp-1.0) + pkg_search_module (GSSDP12 gssdp-1.2) + if (GSSDP12_FOUND) + pkg_search_module (GSSDP REQUIRED gssdp-1.2) + add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) + message("Using gssdp-1.2") + else() + pkg_search_module (GSSDP REQUIRED gssdp-1.0) + endif() endif() if (LIBSOUP3_FOUND) pkg_search_module (SOUP REQUIRED libsoup-3.0) @@ -73,14 +83,27 @@ include_directories ( ${PROJECT_GLIB_INCLUDE_DIRS} ) -set (GDIAL_EXEC_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/gdial-util.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial-app.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial-rest.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial-ssdp.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial-shield.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdialservice.cpp -) +if (LIBSOUP3_FOUND) + set (GDIAL_EXEC_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/gdial-util.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdial-app.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdial1p6-rest.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdial1p6-ssdp.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdial1p6-shield.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdialservice.cpp + ) + message("Using libsoup-3.0 compatible source files (gdial1p6-*.c)") +else() + set (GDIAL_EXEC_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/gdial-util.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdial-app.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdial-rest.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdial-ssdp.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdial-shield.c + ${CMAKE_CURRENT_SOURCE_DIR}/gdialservice.cpp + ) + message("Using libsoup-2.4 compatible source files (gdial-*.c)") +endif() link_directories ( ${GLIB_LIBRARY_DIRS} diff --git a/server/gdial1p6-rest.c b/server/gdial1p6-rest.c new file mode 100644 index 00000000..98b49467 --- /dev/null +++ b/server/gdial1p6-rest.c @@ -0,0 +1,1376 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "gdial-rest.h" +#include "gdial-util.h" +#include "gdial-debug.h" + +#include "gdial-plat-util.h" +#include "gdial-plat-dev.h" +#include "gdial-plat-app.h" +#include "gdial-rest-builder.h" +#include "gdialservicelogging.h" + +typedef struct _GDialRestServerPrivate { + GList *registered_apps; + SoupServer *soup_instance; + SoupServer *local_soup_instance; +} GDialRestServerPrivate; + +enum { + PROP_0, + PROP_SOUP_INSTANCE, + PROP_LOCAL_SOUP_INSTANCE, + PROP_ENABLE, + N_PROPERTIES +}; + +enum { + SIGNAL_INVALID_URI, + SIGNAL_GMAINLOOP_QUIT, + SIGNAL_REST_ENABLE, + N_SIGNALS +}; + +#define GDIAL_MERGE_URL_AND_BODY_QUERY 0 + +static guint gdial_rest_server_signals[N_SIGNALS] = {0}; +static gchar *GDIAL_REST_HTTP_APPS_URI = NULL; + +G_DEFINE_TYPE_WITH_PRIVATE(GDialRestServer, gdial_rest_server, G_TYPE_OBJECT) + +#define GDIAL_SOUP_MESSAGE_PATH_HAS_ROOT + +static gboolean gdial_soup_message_security_check(SoupServerMessage *msg) { + g_return_val_if_fail(msg != NULL, FALSE); + GUri *uri = soup_server_message_get_uri(msg); + if (uri == NULL) return FALSE; + const char *scheme = g_uri_get_scheme(uri); + g_return_val_if_fail(scheme != NULL && (g_strcmp0(scheme, "http") == 0 || g_strcmp0(scheme, "https") == 0), FALSE); + + return TRUE; +} + +static void gdial_soup_message_set_http_error(SoupServerMessage *msg, guint state_code) { + GDIAL_LOGERROR("uri[%s] state_code[%d]", g_uri_get_path(soup_server_message_get_uri(msg)), state_code); + soup_message_headers_replace(soup_server_message_get_response_headers(msg), "Connection", "close"); + soup_server_message_set_status(msg, state_code, NULL); + return; +} + +#define gdial_rest_server_http_print_and_return_if_fail(expr, msg_param, state, fmt, merr) \ +{\ + if (!(expr)) {\ + g_warn_msg_if_fail(expr, fmt, merr);\ + gdial_soup_message_set_http_error(msg_param, state);\ + return;\ + }\ +} + +#define gdial_rest_server_http_check_if_fail(expr, msg, state, fail_flag) \ +{\ + if (!(expr)) {\ + g_warn_if_fail(expr);\ + gdial_soup_message_set_http_error(msg, state);\ + fail_flag = TRUE;\ + } else {\ + fail_flag = FALSE;\ + }\ +} + +#define gdial_rest_server_http_return_if_fail(expr, msg, state) \ +{\ + if (!(expr)) {\ + g_warn_if_fail(expr);\ + gdial_soup_message_set_http_error(msg, state);\ + return;\ + }\ +} + +#define gdial_rest_server_http_return_if(expr, msg, state) \ +{\ + if ((expr)) {\ + g_warn_if_fail(!(expr));\ + gdial_soup_message_set_http_error(msg, state);\ + return;\ + }\ +} + +#define gdial_soup_message_headers_set_Allow_Origin(msg, allowed) \ +{\ + const gchar *header_origin = soup_message_headers_get_one(soup_server_message_get_request_headers(msg), "Origin");\ + if (allowed && header_origin && strlen(header_origin)) {\ + soup_message_headers_replace(soup_server_message_get_response_headers(msg), "Access-Control-Allow-Origin", header_origin);\ + }\ + else {\ + soup_message_headers_remove(soup_server_message_get_response_headers(msg), "Access-Control-Allow-Origin");\ + }\ +} + +#define gdial_soup_message_headers_replace_va(hdrs, name, format, ...) \ +{\ + gchar *value = g_strdup_printf(format, __VA_ARGS__); \ + soup_message_headers_replace(hdrs, name, value); \ + g_free(value);\ +} + +#define gdial_soup_message_headers_replace_va2(hdrs, name, format, ...) \ +{\ + GString *value_buf = g_string_new("");\ + g_string_printf(value_buf, format, __VA_ARGS__);\ + gchar *value = g_string_free(value_buf, FALSE); \ + soup_message_headers_replace(hdrs, name, value); \ + g_free(value);\ +} + +#define gdial_soup_message_set_response_va(msg, ctype, format, ...) \ +{\ + GString *value_buf = g_string_new("");\ + g_string_printf(value_buf, format, __VA_ARGS__);\ + soup_server_message_set_response(msg, ctype, SOUP_MEMORY_TAKE, value_buf->str, value_buf->len); \ + g_string_free(value_buf, FALSE); \ +} + +static GList *gdial_rest_server_registered_apps_clear(GDialRestServer *self, GList *registered_apps, GList *found) { + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + GDialAppRegistry *app_registry = (GDialAppRegistry *)found->data; + registered_apps = g_list_remove_link(registered_apps, found); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler Removed for App[%s]instance[%p]", app_registry->name,(void*)priv->local_soup_instance); + soup_server_remove_handler(priv->local_soup_instance, app_registry->app_uri); + gdial_app_regstry_dispose (app_registry); + g_list_free(found); + return registered_apps; +} + +GDIAL_STATIC gboolean gdial_rest_server_should_relaunch_app(GDialApp *app, const gchar *payload) { + /* + *@TODO: connect state callback to plat-app. Here add workaround + * by checking the state first. If app is stopped, then relaunch. + */ + g_return_val_if_fail(app != NULL && app->name != NULL, TRUE); + if (gdial_app_state(app) != GDIAL_APP_ERROR_NONE || GDIAL_APP_GET_STATE(app) == GDIAL_APP_STATE_STOPPED) { + GDIAL_LOGINFO("app [%s] state is stopped, relaunch required", app->name); + return TRUE; + } + + gchar *cached_payload = gdial_app_get_launch_payload(app); + if ((cached_payload == NULL) && (payload == NULL)) { + return FALSE; + } + + if ((cached_payload != NULL) && (payload != NULL)) { + int changed = g_strcmp0(cached_payload, payload); + if (changed) { + GDIAL_LOGINFO("relaunch requred due to payload change [%s] vs [%s]", cached_payload, payload); + } + return changed; + } + + return TRUE; +} + +static gint GCompareFunc_match_registry_app_name(gconstpointer a, gconstpointer b) { + GDialAppRegistry *app_registry = (GDialAppRegistry *)a; + int is_matched = 0; + /* match by prefix first */ + GList *app_prefixes = app_registry->app_prefixes; + while (app_prefixes) { + gchar *app_prefix = (gchar *)app_prefixes->data; + if (GDIAL_STR_STARTS_WITH(b, app_prefix)) { + is_matched = 1; + break; + } + app_prefixes = app_prefixes->next; + } + /* match by exact name */ + if (!is_matched) is_matched = (g_strcmp0(app_registry->name, b) == 0); + return is_matched ? 0 : 1; +} + +static gint GCompareFunc_match_registry_app_uuid(gconstpointer a, gconstpointer b) { + GDialAppRegistry *app_registry = (GDialAppRegistry *)a; + int is_matched = 0; + /* match by exact uuid */ + is_matched = (g_strcmp0(&app_registry->app_uri[1], b) == 0); + return is_matched ? 0 : 1; +} + +GDIAL_STATIC GDialAppRegistry *gdial_rest_server_find_app_registry(GDialRestServer *self, const gchar *app_name) { + g_return_val_if_fail(self != NULL && app_name != NULL, FALSE); + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + GList *found = g_list_find_custom(priv->registered_apps, app_name, GCompareFunc_match_registry_app_name); + if (found) { + return (GDialAppRegistry *)found->data; + } + return NULL; +} + +GDIAL_STATIC GDialAppRegistry *gdial_rest_server_find_app_registry_by_uuid(GDialRestServer *self, const gchar *app_uuid) { + g_return_val_if_fail(self != NULL && app_uuid != NULL, FALSE); + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + GList *found = g_list_find_custom(priv->registered_apps, app_uuid, GCompareFunc_match_registry_app_uuid); + if (found) { + return (GDialAppRegistry *)found->data; + } + return NULL; + } + +GDIAL_STATIC gboolean gdial_rest_server_is_allowed_youtube_origin(GDialRestServer *self, const gchar *header_origin, const gchar *app_name) { + if (self == NULL) return FALSE; + + gboolean is_allowed = FALSE; + /* YouTube DIAL requirements overwrite the standard DIAL requirements for cors validation + * 7.10 The target device MUST support CORS as outlined in Section 6.6 of the DIAL 2.2.1 protocol specification, + * with the additional requirement that the target device MUST reject all requests to the DIAL server where the + * ORIGIN header is not present. + * 7.10.1 The ORIGIN header MUST match https://${ANY}.youtube.com or package + */ + GUri *origin_uri = g_uri_parse(header_origin, G_URI_FLAGS_NONE, NULL); + const gchar *uri_scheme = origin_uri ? g_uri_get_scheme(origin_uri) : NULL; + + if (origin_uri && uri_scheme && + (!g_strcmp0(uri_scheme, "https"))) { + GDialAppRegistry *app_registry = gdial_rest_server_find_app_registry(self, app_name); + if (app_registry) { + is_allowed = gdial_app_registry_is_allowed_origin (app_registry, header_origin); + } + else { + } + } + else { + if(!g_strcmp0(uri_scheme,"package")) { + is_allowed = TRUE; + } + else { + is_allowed = FALSE; + } + } + if (origin_uri) g_uri_unref(origin_uri); + + return is_allowed; +} + +GDIAL_STATIC gboolean gdial_rest_server_is_allowed_origin(GDialRestServer *self, const gchar *header_origin, const gchar *app_name) { + if (self == NULL) return FALSE; + if (g_str_has_prefix(app_name,"YouTube")) return gdial_rest_server_is_allowed_youtube_origin(self,header_origin,app_name); + if (header_origin == NULL) return TRUE; + if (!g_strcmp0(header_origin, "")) return TRUE; + + gboolean is_allowed = FALSE; + + GUri *origin_uri = g_uri_parse(header_origin, G_URI_FLAGS_NONE, NULL); + const gchar *uri_scheme = origin_uri ? g_uri_get_scheme(origin_uri) : NULL; + + if (origin_uri && uri_scheme && + (!g_strcmp0(uri_scheme, "package") || !g_strcmp0(uri_scheme, "https"))) { + GDialAppRegistry *app_registry = gdial_rest_server_find_app_registry(self, app_name); + if (app_registry) { + is_allowed = gdial_app_registry_is_allowed_origin (app_registry, header_origin); + } + else { + } + } + else { + is_allowed = TRUE; + } + if (origin_uri) g_uri_unref(origin_uri); + + return is_allowed; +} + +GDIAL_STATIC gchar *gdial_rest_server_new_additional_data_url(guint listening_port, const gchar *app_name, gboolean encode , const gchar* app_uri) { + /* + * The specifciation of additionalDataUrl in form of /apps//dial_data + * thus the instance data must be included in the query or payload, not the path. + * + * [SPEC] + * [[The additionalDataUrl value MUST be URL-encoded using the rules defined in [5] + * for MIME type application/x-www-form-urlencoded.]] + */ + GString *url_buf = g_string_new(""); + g_string_printf(url_buf, "http://%s:%d%s%s", "localhost", listening_port, app_uri, GDIAL_REST_HTTP_DIAL_DATA_URI); + gchar *unencoded = g_string_free(url_buf, FALSE); + if (encode) { + gchar *encoded = g_uri_escape_string(unencoded, NULL, FALSE); + g_free(unencoded); + return encoded; + } + else { + return unencoded; + } +} + +static void gdial_rest_app_state_changed_cb(GDialApp *app, gpointer signal_param_user_data, gpointer user_data) { + GDialRestServer *gdial_rest_server = (GDIAL_REST_SERVER(user_data)); + g_return_if_fail(gdial_rest_server_is_app_registered(gdial_rest_server, app->name)); + GDIAL_LOGINFO("gdial_rest_app_state_changed_cb : [%s].state = %d data[%p]", app->name, app->state, signal_param_user_data); +} + +static void gdial_rest_server_handle_OPTIONS(SoupServerMessage *msg, const gchar *allow_methods) { + soup_message_headers_replace(soup_server_message_get_response_headers(msg), "Access-Control-Allow-Methods", allow_methods); + soup_message_headers_replace(soup_server_message_get_response_headers(msg), "Access-Control-Max-Age", "86400"); + gdial_soup_message_headers_set_Allow_Origin(msg, TRUE); + soup_server_message_set_status(msg, SOUP_STATUS_NO_CONTENT, NULL); +} + +static gboolean gdial_rest_server_is_bad_payload(const gchar *data, goffset length) { + return (gdial_util_is_ascii_printable(data, length) == FALSE); +} + +static GDialApp *gdial_rest_server_check_instance(GDialApp *app, const gchar *instance) { + /* + * instance in URL should be "run" or an actual instance_id (e.g. pid_t) + * @TODO: match exact instance sent in LOCATION URL. + */ + g_return_val_if_fail(app && instance, NULL); + + GDialApp *app_by_instance = NULL; + if (app && g_strcmp0(instance, (const char*)&(GDIAL_REST_HTTP_RUN_URI[1])) != 0) { + gchar *endptr = NULL; + gint instance_id = (gint)g_ascii_strtoll(instance, &endptr, 10); + if (strlen(endptr) == 0) { + app_by_instance = gdial_app_find_instance_by_instance_id(instance_id); + g_return_val_if_fail(app == app_by_instance, app); + } + else { + GDIAL_LOGERROR("invalid instance %s %d", instance, instance_id); + } + } + else { + app_by_instance = app; + } + + return app_by_instance; +} + +static void gdial_rest_server_handle_POST_hide(SoupServerMessage *msg, GDialApp *app) { + gdial_rest_server_http_return_if_fail((gdial_app_state(app) == GDIAL_APP_ERROR_NONE), msg, SOUP_STATUS_NOT_FOUND); + gdial_rest_server_http_return_if_fail((GDIAL_APP_GET_STATE(app) == GDIAL_APP_STATE_RUNNING) || (GDIAL_APP_GET_STATE(app) == GDIAL_APP_STATE_HIDE), msg, SOUP_STATUS_NOT_FOUND); + + GDialAppError app_error = GDIAL_APP_ERROR_NONE; + GDialAppState current_state = GDIAL_APP_GET_STATE(app); + + if ( current_state == GDIAL_APP_STATE_HIDE) { + // Do not call gdial_app_hide if current app state is GDIAL_APP_STATE_HIDE + } + else if ( (app_error = gdial_app_hide(app)) == GDIAL_APP_ERROR_NONE) { + g_warn_if_fail(gdial_app_state(app) == GDIAL_APP_ERROR_NONE && GDIAL_APP_GET_STATE(app) == GDIAL_APP_STATE_HIDE); + } + else if (app_error == GDIAL_APP_ERROR_NOT_IMPLEMENTED) { + gdial_rest_server_http_return_if_fail(FALSE, msg, SOUP_STATUS_NOT_IMPLEMENTED); + } + else { + GDIAL_LOGERROR("gdial_app_hide(%s) failed", app->name); + gdial_rest_server_http_return_if_fail(FALSE, msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + } + if(current_state == GDIAL_APP_STATE_STOPPED) { + soup_server_message_set_status(msg, SOUP_STATUS_CREATED, NULL); + } + else { + soup_server_message_set_status(msg, SOUP_STATUS_OK, NULL); + } + soup_message_headers_replace(soup_server_message_get_response_headers(msg), "Content-Type", "text/plain; charset=utf-8"); + gdial_soup_message_headers_set_Allow_Origin(msg, TRUE); +} + +static void gdial_rest_server_handle_DELETE(SoupServerMessage *msg, GHashTable *query, GDialApp *app) { + gdial_rest_server_http_return_if_fail(g_strcmp0(app->name, "system") != 0, msg, SOUP_STATUS_FORBIDDEN); + gdial_rest_server_http_return_if_fail((gdial_app_state(app) == GDIAL_APP_ERROR_NONE), msg, SOUP_STATUS_NOT_FOUND); + gdial_rest_server_http_return_if_fail((GDIAL_APP_GET_STATE(app) == GDIAL_APP_STATE_RUNNING) || (GDIAL_APP_GET_STATE(app) == GDIAL_APP_STATE_HIDE), msg, SOUP_STATUS_NOT_FOUND); + + if (gdial_app_stop(app) == GDIAL_APP_ERROR_NONE) { + g_warn_if_fail(gdial_app_state(app) == GDIAL_APP_ERROR_NONE && GDIAL_APP_GET_STATE(app) == GDIAL_APP_STATE_STOPPED); + } + else { + GDIAL_LOGERROR("gdial_app_stop(%s) query(%p) failed, force shutdown", app->name,query); + gdial_app_force_shutdown(app); + } + + soup_message_headers_replace(soup_server_message_get_response_headers(msg), "Content-Type", "text/plain; charset=utf-8"); + gdial_soup_message_headers_set_Allow_Origin(msg, TRUE); + soup_server_message_set_status(msg, SOUP_STATUS_OK, NULL); + g_object_unref(app); +} + +static void gdial_rest_server_handle_POST(GDialRestServer *gdial_rest_server, SoupServerMessage* msg, GHashTable *query, const gchar *app_name) { + GDIAL_LOGTRACE("Entering ..."); + GDialAppRegistry *app_registry = gdial_rest_server_find_app_registry(gdial_rest_server, app_name); + SoupMessageBody *request_body = soup_server_message_get_request_body(msg); + gdial_rest_server_http_return_if_fail(app_registry, msg, SOUP_STATUS_NOT_FOUND); + if (request_body && request_body->data && request_body->length) { + gdial_rest_server_http_return_if_fail(request_body->length <= GDIAL_REST_HTTP_MAX_PAYLOAD, msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE); + gdial_rest_server_http_return_if_fail(!gdial_rest_server_is_bad_payload(request_body->data, request_body->length), msg, SOUP_STATUS_BAD_REQUEST); + } + GSocketAddress *socket_addr = soup_server_message_get_local_address(msg); + guint listening_port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(socket_addr)); + gdial_rest_server_http_return_if_fail(listening_port != 0, msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + + GDIAL_LOGERROR("Starting the app with payload %.*s", (int)request_body->length, request_body->data); + GDialApp *app = gdial_app_find_instance_by_name(app_registry->name); + gboolean new_app_instance = FALSE; + gboolean first_instance_created = FALSE; + GDialAppState current_state = GDIAL_APP_STATE_STOPPED; + + if (app != NULL && app_registry->is_singleton) { + /* + * Reuse app instance as is, but do not update refcnt + * per DIAL 2.1 recommendation, push relaunch decision to application platform, + */ + GDIAL_LOGERROR("POST request received for running app [%s]", app->name); + new_app_instance = TRUE; + first_instance_created = FALSE; + current_state = GDIAL_APP_GET_STATE(app); + } + else { + app = gdial_app_new(app_registry->name); + new_app_instance = TRUE; + first_instance_created = TRUE; + } + + GDialAppError start_error = GDIAL_APP_ERROR_NONE; + + if (new_app_instance) { + gchar *additional_data_url = NULL; + if (app_registry->use_additional_data) { + additional_data_url = gdial_rest_server_new_additional_data_url(listening_port, app_registry->name, FALSE, app_registry->app_uri ); + } + gchar *additional_data_url_safe = g_uri_escape_string(additional_data_url, NULL, FALSE); + GDIAL_LOGINFO("additionalDataUrl = %s, %s", additional_data_url, additional_data_url_safe); + g_signal_connect_object(app, "state-changed", G_CALLBACK(gdial_rest_app_state_changed_cb), gdial_rest_server, 0); + const gchar *query_str = g_uri_get_query(soup_server_message_get_uri(msg)); + gchar *query_str_safe = NULL; + const int use_query_directly_from_soup = 1; + if(query_str && strlen(query_str)) { + GDIAL_LOGINFO("query = %s", query_str); + if (!use_query_directly_from_soup) { + char *tmp = g_uri_escape_string(query_str, NULL, FALSE); + // note that we later g_free(query_str_safe) which doesn't necessarily work with malloc'ed memory (seems to depend on glib version) + query_str_safe = g_strdup(tmp); + free(tmp); + } + else { + query_str_safe = g_strdup(query_str); + } + } + const gchar *payload = request_body->data; + gchar *payload_safe = NULL; + if (payload && strlen(payload)) { + if (g_str_has_prefix(app->name, "YouTube")) { + /* temporary disabling encoding payload for YouTube till cloud side changed*/ + payload_safe = g_strdup(payload); + } + else { + char *tmp = g_uri_escape_string(payload, "=&", FALSE); + // note that we later g_free(payload_safe) which doesn't necessarily work with malloc'ed memory (seems to depend on glib version) + payload_safe = g_strdup(tmp); + free(tmp); + } + } + start_error = gdial_app_start(app, payload_safe, query_str_safe, additional_data_url_safe, gdial_rest_server); + if (query_str_safe) g_free(query_str_safe); + if (payload_safe) g_free(payload_safe); + free(additional_data_url_safe); + g_free(additional_data_url); + } + else { + /* + * start_error = NONE; + * app exist, and could be in hidden state, so resume; + */ + start_error = gdial_app_start(app, NULL, NULL, NULL, gdial_rest_server); + } + + /* + * The app start could be asyn, thus app->state may not have changed + * to RUNNING yet. so started == TRUE, there will be followed by a + * RUNNING callback. if started == FAUSE, then the instance is not + * created; + */ + if (start_error == GDIAL_APP_ERROR_NONE) { + soup_message_headers_replace(soup_server_message_get_response_headers(msg), "Content-Type", "text/plain; charset=utf-8"); + gdial_soup_message_headers_replace_va(soup_server_message_get_response_headers(msg), "Location", "http://%s:%d%s/%s/run", + g_uri_get_host(soup_server_message_get_uri(msg)), listening_port, GDIAL_REST_HTTP_APPS_URI, app->name); + gdial_soup_message_headers_set_Allow_Origin(msg, TRUE); + if (new_app_instance) { + if (g_strcmp0(app->name, "system") != 0 && (first_instance_created || current_state == GDIAL_APP_STATE_HIDE)) { + soup_server_message_set_status(msg, SOUP_STATUS_CREATED, NULL); + } + else { + soup_server_message_set_status(msg, SOUP_STATUS_OK, NULL); + } + /* + *@TODO request_body may not need to be cached app->payload as it is + * only used by shouldRelaunch(), which is not used and we don't support + * relaunch anyway; + * + * If relaunch is needed it is better to leave it to the app. + */ + if(request_body && request_body->data) { + GDIAL_LOGINFO("POST request payload = [%s]", request_body->data); + gdial_app_set_launch_payload(app, request_body->data); + } + } + else { + soup_server_message_set_status(msg, SOUP_STATUS_OK, NULL); + } + } + else { + g_object_unref(app); + // FIX(Copilot): Set app to NULL after unref to prevent use-after-free + app = NULL; + gdial_rest_server_http_return_if(start_error == GDIAL_APP_ERROR_FORBIDDEN, msg, SOUP_STATUS_FORBIDDEN); + gdial_rest_server_http_return_if(start_error == GDIAL_APP_ERROR_UNAUTH, msg, SOUP_STATUS_UNAUTHORIZED); + gdial_rest_server_http_return_if(TRUE, msg, SOUP_STATUS_SERVICE_UNAVAILABLE); + } + GDIAL_LOGTRACE("Exiting ..."); +} + +static void gdial_rest_server_handle_GET_app(GDialRestServer *gdial_rest_server, SoupServerMessage *msg, GHashTable *query, const gchar *app_name, gint instance_id) { + gchar *client_dial_version_str = NULL; + GDIAL_LOGTRACE("Entering ..."); + if (query) { + client_dial_version_str = g_hash_table_lookup(query, "clientDialVer"); + } + + GDialAppRegistry *app_registry = gdial_rest_server_find_app_registry(gdial_rest_server, app_name); + gdial_rest_server_http_return_if_fail(app_registry, msg, SOUP_STATUS_NOT_FOUND); + + GDialApp *app = gdial_app_find_instance_by_name(app_name); + GDialAppState app_state = GDIAL_APP_STATE_MAX; + + if (app != NULL) { + /* + * Get the app state + */ + gdial_app_state(app); + app_state = app->state; + /* + * if app has stopped, destroy the app instance + */ + } + else { + /* + * There is no app instance, but app may have started through + * other means. ask platform for app state + */ + app = gdial_app_new(app_name); + gdial_app_state(app); + app_state = app->state; + if (app_state != GDIAL_APP_STATE_STOPPED) { + g_signal_connect_object(app, "state-changed", G_CALLBACK(gdial_rest_app_state_changed_cb), gdial_rest_server, 0); + GDIAL_LOGINFO("creating app instance from state %d ", app_state); + } + } + + gdial_soup_message_headers_set_Allow_Origin(msg, TRUE); + soup_server_message_set_status(msg, SOUP_STATUS_OK, NULL); + #if 0 + void *builder = GET_APP_response_builder_new(app_name); + GET_APP_response_builder_set_option(builder, "allowStop", "true"); + GET_APP_response_builder_set_state(builder, app_state); + GET_APP_response_builder_set_additionalData(builder, " "); + gsize response_len = 0; + gchar *response_str = GET_APP_response_builder_build(builder, &response_len); + //g_printf("############ response_str ##########%s", response_str); + soup_server_message_set_response(msg, "text/xml", SOUP_MEMORY_TAKE, response_str, response_len); + GET_APP_response_builder_destroy(builder); + #else + int response_len = 0; + gchar *allow_stop = NULL; + + if (app_registry->properties) + { + allow_stop = (gchar*)g_hash_table_lookup(app_registry->properties,"allowStop"); + } + if(allow_stop == NULL) { + allow_stop = "false"; + } + GDIAL_LOGINFO("server_register_application allowStop:%s",allow_stop); + gchar *response_str = gdial_app_state_response_new(app, GDIAL_PROTOCOL_VERSION_STR, client_dial_version_str, GDIAL_PROTOCOL_XMLNS_SCHEMA, &response_len); + #endif + soup_server_message_set_response(msg, "text/xml; charset=utf-8", SOUP_MEMORY_TAKE, response_str, response_len); + if (app_state == GDIAL_APP_STATE_STOPPED) { + GDIAL_LOGINFO("deleting app instance from state %d ", app_state); + g_object_unref(app); + } + GDIAL_LOGTRACE("Exiting ..."); +} + +static void gdial_rest_server_handle_POST_dial_data(GDialRestServer *gdial_rest_server, SoupServerMessage* msg, GHashTable *query, const gchar *app_name) { + /* + * All instances of same app shares same additonalDataUrl + */ + GDIAL_LOGTRACE("Entering ..."); + SoupMessageBody *request_body = soup_server_message_get_request_body(msg); + if(request_body && request_body->data && request_body->length) { + gdial_rest_server_http_return_if_fail(request_body->length < GDIAL_APP_DIAL_DATA_MAX_LEN, msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE); + gdial_rest_server_http_return_if_fail(!gdial_rest_server_is_bad_payload(request_body->data, request_body->length), msg, SOUP_STATUS_BAD_REQUEST); + } + /* + * Cache dial_data so as to use on future queries. + */ + GDialApp *app = gdial_app_find_instance_by_name(app_name); + if(app == NULL) + { + GDIAL_LOGINFO("gdial_rest_server_handle_POST_dial_data creating app instance [%p] ",gdial_rest_server); + app = gdial_app_new(app_name); + } + gdial_rest_server_http_return_if_fail(app, msg, SOUP_STATUS_NOT_FOUND); + /* + * Give priority to body (body overrites query + */ + if (GDIAL_MERGE_URL_AND_BODY_QUERY && query && !request_body) { + gdial_app_set_additional_dial_data(app, query); + } + else if ((request_body && request_body->data && request_body->length)) { + /* according to SoupMessage doc, request_body c string, with the nul byte at data[length] */ + gdial_rest_server_http_return_if_fail(request_body->data[request_body->length] == '\0', msg, SOUP_STATUS_BAD_REQUEST); + GHashTable *body_query = soup_form_decode(request_body->data); + if (body_query) { + #if GDIAL_MERGE_URL_AND_BODY_QUERY + /*append url query to body_query */ + if (query) { + GHashTable *dupQuery = gdial_util_str_str_hashtable_dup(query); + body_query = query ? gdial_util_str_str_hashtable_merge(body_query, dupQuery) : body_query; + // FIX(Copilot): Correct typo g_hash_table_destory -> g_hash_table_destroy + g_hash_table_destroy(dupQuery); + } + #endif + gdial_app_set_additional_dial_data(app, body_query); + g_hash_table_destroy(body_query); + } + } + else { + GDIAL_LOGINFO("clear [%s] dial_data", app_name); + GHashTable* empty = g_hash_table_new(NULL, NULL); + gdial_app_set_additional_dial_data(app, empty); + g_hash_table_destroy(empty); + } + + gdial_soup_message_headers_set_Allow_Origin(msg, TRUE); + soup_server_message_set_status(msg, SOUP_STATUS_OK, NULL); + GDIAL_LOGTRACE("Exiting ..."); +} + +inline static void gdial_rest_http_server_system_callback(SoupServer *server, + SoupServerMessage *msg, const gchar *path, GHashTable *query, + gpointer user_data) { + + GDialRestServer *gdial_rest_server = (GDIAL_REST_SERVER(user_data)); + + if (soup_server_message_get_method(msg) == SOUP_METHOD_DELETE) { + /* + * Stop Server + */ + g_signal_emit(gdial_rest_server, gdial_rest_server_signals[SIGNAL_GMAINLOOP_QUIT], 0, "stop rest http gmainloop"); + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_PUT) { + gchar *value = g_hash_table_lookup(query,"rest_enable"); + g_print_with_timestamp("gdial_rest_http_server_system_callback emit SIGNAL_REST_ENABLE value:%s ",(gchar *)value); + g_signal_emit(gdial_rest_server, gdial_rest_server_signals[SIGNAL_REST_ENABLE], 0,value); + } + soup_server_message_set_status(msg, SOUP_STATUS_OK, NULL); +} + +static void gdial_local_rest_http_server_callback(SoupServer *server, + SoupServerMessage *msg, const gchar *path, GHashTable *query, + gpointer user_data) { + gchar *remote_address_str = g_inet_address_to_string(g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(soup_server_message_get_remote_address(msg)))); + GDIAL_LOGINFO("method[%s] path[%s] recv from [%s], in thread %lx", soup_server_message_get_method(msg), path, remote_address_str, pthread_self()); + g_free(remote_address_str); + GDialRestServer *gdial_rest_server = (GDIAL_REST_SERVER(user_data)); + gchar **elements = g_strsplit(&path[1], "/", 3); + gdial_rest_server_http_return_if_fail(elements != NULL, msg, SOUP_STATUS_NOT_IMPLEMENTED); + gchar base[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; + gchar instance[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; + gchar last_elem[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; + int i = 0; + int j = 0; + for (i = 0; elements[i] != NULL; i++) { + /* do not allow any element to be empty, stop on first one */ + gsize ret; + if ((strlen(elements[i])) == 0) { + GDIAL_LOGWARNING("Warn: empty elements in URI path"); + continue; + } + if (j == 0) { + ret = g_strlcpy(base, elements[i], sizeof(base)); + if (ret >= sizeof(base)) { + GDIAL_LOGERROR("Warn: base too long"); + } + } + else if (j == 1) { + ret = g_strlcpy(instance, elements[i], sizeof(instance)); + if (ret >= sizeof(instance)) { + GDIAL_LOGERROR("Warn: instance too long"); + } + } + ret = g_strlcpy(last_elem, elements[i], sizeof(last_elem)); + if (ret >= sizeof(last_elem)) { + GDIAL_LOGERROR("Warn: last_elem too long"); + } + GDIAL_LOGINFO("last_elem[%s]", last_elem); + j++; + } + g_strfreev(elements); + const int element_num = j; + GDIAL_LOGINFO("there are %d non-empty elems", element_num); + if(element_num == 2 && g_strcmp0(instance,"dial_data") == 0) + { + GDialAppRegistry *app_registry = gdial_rest_server_find_app_registry_by_uuid(gdial_rest_server, base); + + gdial_rest_server_http_return_if_fail(app_registry, msg, SOUP_STATUS_NOT_FOUND); + if (soup_server_message_get_method(msg) == SOUP_METHOD_POST) { + gdial_rest_server_handle_POST_dial_data(gdial_rest_server, msg, query, app_registry->name); + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_GET) { + gdial_rest_server_handle_GET_app(gdial_rest_server, msg, query, app_registry->name, GDIAL_APP_INSTANCE_NULL); + } + else { + gdial_rest_server_http_return_if_fail(soup_server_message_get_method(msg) == SOUP_METHOD_POST, msg, SOUP_STATUS_NOT_IMPLEMENTED); + } + } + else { + gdial_soup_message_set_http_error(msg,SOUP_STATUS_NOT_IMPLEMENTED); + } +} + +static void gdial_rest_http_server_apps_callback(SoupServer *server, + SoupServerMessage *msg, const gchar *path, GHashTable *query, + gpointer user_data) { + gchar *remote_address_str = g_inet_address_to_string(g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(soup_server_message_get_remote_address(msg)))); + g_print_with_timestamp("gdial_rest_http_server_apps_callback() %s path=%s recv from [%s], in thread %lx", soup_server_message_get_method(msg), path, remote_address_str, pthread_self()); + g_free(remote_address_str); + + gdial_rest_server_http_return_if_fail(server && msg && path && user_data, msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + GDialRestServer *gdial_rest_server = (GDIAL_REST_SERVER(user_data)); + + /* + * Valid http paths (DIAL 2.1) + * + * Minimum URI must start with "/apps" -- ensured by soup + * Minimum URI must be larger than "/apps/" + * URI must not end with '/' + * + * Default is "run", but this is not guarnteed + * + * POST http://:/apps/Netflix -- launch app + * GET http://:/apps/Netflix -- get app state, and instance URL + * GET http://:/apps/Netflix/ -- get instance state + * DELETE http://:/apps/Netflix/ -- stop instance + * POST http://:/apps/Netflix//hide -- hide instance + * POST http://:/apps/Netflix/dial_data + */ + gdial_rest_server_http_return_if_fail( + g_socket_address_get_family(soup_server_message_get_remote_address(msg)) == G_SOCKET_FAMILY_IPV4, msg, SOUP_STATUS_NOT_IMPLEMENTED); + gdial_rest_server_http_return_if_fail(gdial_soup_message_security_check(msg), msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + gdial_rest_server_http_return_if_fail(path != NULL, msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + + size_t path_len = strlen(path); + gdial_rest_server_http_return_if_fail(path_len < GDIAL_REST_HTTP_MAX_URI_LEN, msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + gdial_rest_server_http_return_if_fail(path_len > (GDIAL_STR_SIZEOF(GDIAL_REST_HTTP_APPS_URI) + GDIAL_STR_SIZEOF("/")), msg, SOUP_STATUS_NOT_IMPLEMENTED); + gdial_rest_server_http_return_if_fail(strncmp(path, GDIAL_REST_HTTP_APPS_URI, GDIAL_STR_SIZEOF(GDIAL_REST_HTTP_APPS_URI)) == 0, msg, SOUP_STATUS_NOT_IMPLEMENTED); + + const gchar *header_host = soup_message_headers_get_one(soup_server_message_get_request_headers(msg), "Host"); + gdial_rest_server_http_return_if_fail(header_host, msg, SOUP_STATUS_FORBIDDEN); + + /* + * @TODO remote consecutive slashes. + */ + gchar **elements = g_strsplit(&path[1], "/", 4); + gdial_rest_server_http_return_if_fail(elements != NULL, msg, SOUP_STATUS_NOT_IMPLEMENTED); + + gchar base[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; + gchar app_name[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; + gchar instance[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; + gchar last_elem[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; + + gboolean invalid_uri = FALSE; + + int i = 0; + int j = 0; + for (i = 0; elements[i] != NULL; i++) { + gsize ret; + /* do not allow any element to be empty, stop on first one */ + if (j == 0) { + ret = g_strlcpy(base, elements[i], sizeof(base)); + if (ret >= sizeof(base)) { + GDIAL_LOGWARNING("Warn: base too long"); + } + } + else if (j == 1) { + ret = g_strlcpy(app_name, elements[i], sizeof(app_name)); + if (ret >= sizeof(app_name)) { + GDIAL_LOGWARNING("Warn: app_name too long"); + } + } + else if (j == 2) { + ret = g_strlcpy(instance, elements[i], sizeof(instance)); + if (ret >= sizeof(instance)) { + GDIAL_LOGWARNING("Warn: instance too long"); + } + } + ret = g_strlcpy(last_elem, elements[i], sizeof(last_elem)); + if (ret >= sizeof(last_elem)) { + GDIAL_LOGWARNING("Warn: last_elem too long"); + } + j++; + } + + const int element_num = j; + GDIAL_LOGINFO("there are %d non-empty elems", element_num); + /* + * Make sure path remains same as given + */ + const gchar *copied_str[] = {base, app_name, instance, last_elem}; + i = 0; j = 0; + while (i < element_num && (unsigned int)i < sizeof(copied_str)/sizeof(copied_str[0])) { + bool flag = false; + if (strlen(elements[j]) == 0) { + j++; + continue; + } + invalid_uri = invalid_uri || g_strcmp0(copied_str[i], elements[j]); + gdial_rest_server_http_check_if_fail(!invalid_uri, msg, SOUP_STATUS_NOT_IMPLEMENTED, flag); + if(flag){ + g_strfreev(elements); + return; + } + j++;i++; + } + + g_strfreev(elements); + + invalid_uri = invalid_uri || (i > 4 || i < 2); + invalid_uri = invalid_uri || (g_strcmp0(base, &GDIAL_REST_HTTP_APPS_URI[1]) != 0); + invalid_uri = invalid_uri || (strlen(app_name) == 0); + + gdial_rest_server_http_return_if_fail(!invalid_uri, msg, SOUP_STATUS_NOT_IMPLEMENTED); + + if(!gdial_rest_server_is_app_registered(gdial_rest_server, app_name)) { + /* + * Any request only respond to app name that is among registered apps + */ + g_signal_emit(gdial_rest_server, gdial_rest_server_signals[SIGNAL_INVALID_URI], 0, "URI containes unregistered app name"); + gdial_rest_server_http_return_if_fail(FALSE, msg, SOUP_STATUS_NOT_FOUND); + } + + const gchar *header_origin = soup_message_headers_get_one(soup_server_message_get_request_headers(msg), "Origin"); + GDIAL_LOGERROR("Origin %s, Host: %s, Method: %s", header_origin, header_host, soup_server_message_get_method(msg)); + if (!gdial_rest_server_is_allowed_origin(gdial_rest_server, header_origin, app_name)) { + gdial_rest_server_http_print_and_return_if_fail(FALSE, msg, SOUP_STATUS_FORBIDDEN, "origin %s is not allowed", header_origin); + } + /* + * element_num == 2: + * apps/Netflix + * + * element_num == 3: + * apps/Netflix/run + * apps/Netflix/12345 + * apps/Netflix/dial_data + * + * element_num == 4: + * apps/Netflix/run/hide + * apps/Netflix/12345/hide + */ + if (element_num == 2) { + // URL ends with app name + GDIAL_LOGINFO("app_name is %s", app_name); + if (!header_host || !gdial_rest_server_is_allowed_origin(gdial_rest_server, header_origin, app_name)) { + gdial_rest_server_http_return_if_fail(FALSE, msg, SOUP_STATUS_FORBIDDEN); + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_OPTIONS) { + gdial_rest_server_handle_OPTIONS(msg, "GET, POST, OPTIONS"); + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_POST) { + gdial_rest_server_handle_POST(gdial_rest_server, msg, query, app_name); + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_GET) { + /* + * GET_app will get app state...there is no instance_id in URL + */ + gdial_rest_server_handle_GET_app(gdial_rest_server, msg, query, app_name, GDIAL_APP_INSTANCE_NULL); + } + else { + gdial_rest_server_http_return_if_fail(FALSE, msg, SOUP_STATUS_NOT_IMPLEMENTED); + } + } + else if (element_num == 3) { + if (g_strcmp0(last_elem, &GDIAL_REST_HTTP_DIAL_DATA_URI[1]) ==0) { + // URL ends with dial_data, only accepted when originating from localhost + GDIAL_LOGINFO("for [%s] app_name is %s", last_elem, app_name); + GSocketAddress *remote_address = soup_server_message_get_remote_address(msg); + GError *error = NULL; + struct sockaddr_in saddr; + gdial_rest_server_http_return_if_fail(remote_address && g_socket_address_to_native(remote_address, &saddr, sizeof(saddr), &error) && !error, msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + gdial_rest_server_http_return_if_fail(saddr.sin_addr.s_addr == htonl(INADDR_LOOPBACK), msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + + if (soup_server_message_get_method(msg) == SOUP_METHOD_OPTIONS) { + gdial_rest_server_handle_OPTIONS(msg, "POST, OPTIONS"); + } + else { + gdial_rest_server_http_return_if_fail(soup_server_message_get_method(msg) == SOUP_METHOD_POST, msg, SOUP_STATUS_NOT_IMPLEMENTED); + } + } + else { + // URL ends with .../run or some app specific instance Id .../ + GDIAL_LOGINFO("for instance [%s] app_name is %s", last_elem, app_name); + if (!header_host || !gdial_rest_server_is_allowed_origin(gdial_rest_server, header_origin, app_name)) { + gdial_rest_server_http_return_if_fail(FALSE, msg, SOUP_STATUS_FORBIDDEN); + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_OPTIONS) { + gdial_rest_server_handle_OPTIONS(msg, "DELETE, OPTIONS"); + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_DELETE) { + GDialApp *app = gdial_app_find_instance_by_name(app_name); + GDialApp *app_by_instance = gdial_rest_server_check_instance(app, instance); + if (app_by_instance) { + gdial_rest_server_handle_DELETE(msg, query, app); + } + else { + GDIAL_LOGWARNING("app to delete is not found"); + gdial_soup_message_set_http_error(msg, SOUP_STATUS_NOT_FOUND); + } + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_POST) { + gdial_soup_message_set_http_error(msg, SOUP_STATUS_NOT_FOUND); + } + else { + gdial_rest_server_http_return_if_fail(FALSE, msg, SOUP_STATUS_NOT_FOUND); + } + } + } + else if (element_num == 4) { + if (g_strcmp0(last_elem, &GDIAL_REST_HTTP_HIDE_URI[1]) == 0) { + // URL ends with hide + GDIAL_LOGINFO("for [%s] app_name is %s, instance is %s", last_elem, app_name, instance); + if (soup_server_message_get_method(msg) == SOUP_METHOD_OPTIONS) { + gdial_rest_server_handle_OPTIONS(msg, "POST, OPTIONS"); + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_POST) { + + GDialApp *app = gdial_app_find_instance_by_name(app_name); + GDialApp *app_by_instance = gdial_rest_server_check_instance(app, instance); + if (app_by_instance) { + gdial_rest_server_handle_POST_hide(msg, app); + } + else { + GDIAL_LOGWARNING("app to hide is not found"); + gdial_rest_server_http_return_if_fail(FALSE, msg, SOUP_STATUS_NOT_FOUND); + } + } + else if (soup_server_message_get_method(msg) == SOUP_METHOD_DELETE) { + gdial_rest_server_http_return_if_fail(soup_server_message_get_method(msg) == SOUP_METHOD_POST, msg, SOUP_STATUS_NOT_FOUND); + } + else { + gdial_rest_server_http_return_if_fail(soup_server_message_get_method(msg) == SOUP_METHOD_POST, msg, SOUP_STATUS_NOT_IMPLEMENTED); + } + } + else { + invalid_uri = TRUE; + } + } + + gdial_rest_server_http_return_if_fail(!invalid_uri, msg, SOUP_STATUS_NOT_IMPLEMENTED); +} + +static void gdial_rest_server_dispose(GObject *object) { + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(GDIAL_REST_SERVER(object)); + soup_server_remove_handler(priv->soup_instance, GDIAL_REST_HTTP_APPS_URI); + if(GDIAL_REST_HTTP_APPS_URI) { + g_free(GDIAL_REST_HTTP_APPS_URI); + GDIAL_REST_HTTP_APPS_URI = NULL; + } + g_object_unref(priv->soup_instance); + g_object_unref(priv->local_soup_instance); + while (priv->registered_apps) { + priv->registered_apps = gdial_rest_server_registered_apps_clear(object, priv->registered_apps, priv->registered_apps); + } + G_OBJECT_CLASS (gdial_rest_server_parent_class)->dispose (object); +} + +static void gdial_rest_server_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(GDIAL_REST_SERVER(object)); + + switch (property_id) { + case PROP_SOUP_INSTANCE: + g_value_set_object(value, priv->soup_instance); + break; + case PROP_LOCAL_SOUP_INSTANCE: + g_value_set_object(value, priv->local_soup_instance); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void gdial_rest_server_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { + GDialRestServer *self = GDIAL_REST_SERVER(object); + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + + switch (property_id) { + case PROP_SOUP_INSTANCE: + priv->soup_instance = g_value_get_object(value); + break; + case PROP_LOCAL_SOUP_INSTANCE: + priv->local_soup_instance = g_value_get_object(value); + break; + case PROP_ENABLE: + if(priv->soup_instance) + { + if(g_value_get_boolean(value)) + { + GDIAL_LOGINFO("gdial_rest_server_set_property add handler"); + soup_server_add_handler(priv->soup_instance, GDIAL_REST_HTTP_APPS_URI, gdial_rest_http_server_apps_callback, object, NULL); + } + else + { + GDIAL_LOGINFO("gdial_rest_server_set_property remove handler"); + soup_server_remove_handler(priv->soup_instance, GDIAL_REST_HTTP_APPS_URI); + } + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void gdial_rest_server_class_init(GDialRestServerClass *klass) { + + GObjectClass *gobject_class = (GObjectClass *)klass; + + gobject_class->dispose = gdial_rest_server_dispose; + gobject_class->get_property = gdial_rest_server_get_property; + gobject_class->set_property = gdial_rest_server_set_property; + + g_object_class_install_property (gobject_class, PROP_SOUP_INSTANCE, + g_param_spec_object("soup_instance", NULL, "Http Server for DIAL Rest Service", + SOUP_TYPE_SERVER, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_LOCAL_SOUP_INSTANCE, + g_param_spec_object("local_soup_instance", NULL, "Local Http Server for DIAL Rest Service", + SOUP_TYPE_SERVER, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_ENABLE, + g_param_spec_boolean("enable", NULL, "Enable REST Server", + FALSE, + G_PARAM_WRITABLE)); + + gdial_rest_server_signals[SIGNAL_INVALID_URI] = + g_signal_new ("invalid-uri", + G_OBJECT_CLASS_TYPE (gobject_class), + 0, /* flag */ + 0, /* class offset */ + NULL, NULL, /* accumulator, accu_data */ + NULL, /* c_marshaller, use default */ + G_TYPE_NONE, 1, /* return type, arg num */ + G_TYPE_STRING); /* arg types */ + + gdial_rest_server_signals[SIGNAL_GMAINLOOP_QUIT] = + g_signal_new ("gmainloop-quit", + G_OBJECT_CLASS_TYPE (gobject_class), + 0, /* flag */ + 0, /* class offset */ + NULL, NULL, /* accumulator, accu_data */ + NULL, /* c_marshaller, use default */ + G_TYPE_NONE, 1, /* return type, arg num */ + G_TYPE_STRING); /* arg types */ + + gdial_rest_server_signals[SIGNAL_REST_ENABLE] = + g_signal_new ("rest-enable", + G_OBJECT_CLASS_TYPE (gobject_class), + 0, /* flag */ + 0, /* class offset */ + NULL, NULL, /* accumulator, accu_data */ + NULL, /* c_marshaller, use default */ + G_TYPE_NONE, 1, /* return type, arg num */ + G_TYPE_STRING); /* arg types */ +} + +static void gdial_rest_server_init(GDialRestServer *self) { + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + priv->registered_apps = NULL; +} +GDialRestServer *gdial_rest_server_new(SoupServer *rest_http_server,SoupServer * local_rest_http_server, gchar *random_id) { + g_return_val_if_fail(rest_http_server != NULL, NULL); + g_return_val_if_fail(local_rest_http_server != NULL, NULL); + g_object_ref(local_rest_http_server); + g_object_ref(rest_http_server); + gpointer object = g_object_new(GDIAL_TYPE_REST_SERVER, GDIAL_REST_SERVER_SOUP_INSTANCE, rest_http_server,GDIAL_LOCAL_REST_SERVER_SOUP_INSTANCE,local_rest_http_server, NULL); +#ifdef GDIAL_BUILD_TEST + /* + * For Testing Purpose only + */ + soup_server_add_handler(rest_http_server, "/apps/system", gdial_rest_http_server_system_callback, object, NULL); +#endif + GDIAL_REST_HTTP_APPS_URI = g_strdup_printf("/%s", random_id); + return object; +} + +gboolean gdial_rest_server_register_app(GDialRestServer *self, const gchar *app_name, const GList *app_prefixes, GHashTable *properties, gboolean is_singleton, gboolean use_additional_data, const GList *allowed_origins) { + + g_return_val_if_fail(self != NULL && app_name != NULL, FALSE); + GDIAL_LOGTRACE("Entering ..."); + /* + *@TODO: support multiple app instances. + */ + g_return_val_if_fail(is_singleton, FALSE); + + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + if (g_list_find_custom(priv->registered_apps, app_name, GCompareFunc_match_registry_app_name) != NULL) { + /* + * Do not support duplicate registration with different param + * + *@TODO: check params. If identical to previous registraiton, return TRUE; + */ + GDIAL_LOGTRACE("Exiting ..."); + return FALSE; + } + + GDialAppRegistry *app_registry = gdial_app_registry_new (g_strdup(app_name), + app_prefixes, + properties, + is_singleton, + use_additional_data, + allowed_origins); + priv->registered_apps = g_list_prepend(priv->registered_apps, app_registry); + + /* + * when an app is registered, we also check if it is already running + * @TODO + */ + + if(strcmp(app_name,"system") == 0){ + gdial_app_new(app_registry->name); + } + g_return_val_if_fail(priv->registered_apps != NULL, FALSE); + g_return_val_if_fail(gdial_rest_server_is_app_registered(self, app_name), FALSE); + + if( 0 != strcmp(app_name,"system")) + { + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%p]",app_name,app_registry->app_uri,(void*)priv->local_soup_instance); + soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); + } + GDIAL_LOGTRACE("Exiting ..."); + return TRUE; +} + +gboolean gdial_rest_server_register_app_registry(GDialRestServer *self, GDialAppRegistry *app_registry) { + + g_return_val_if_fail(self != NULL && app_registry != NULL, FALSE); + + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + if (g_list_find_custom(priv->registered_apps, app_registry->name, GCompareFunc_match_registry_app_name) != NULL) { + /* + * Do not support duplicate registration with different param + * + *@TODO: check params. If identical to previous registraiton, return TRUE; + */ + return FALSE; + } + + priv->registered_apps = g_list_prepend(priv->registered_apps, app_registry); + + /* + * when an app is registered, we also check if it is already running + * @TODO + */ + + g_return_val_if_fail(priv->registered_apps != NULL, FALSE); + g_return_val_if_fail(gdial_rest_server_is_app_registered(self, app_registry->name), FALSE); + if( 0 != strcmp(app_registry->name,"system")) + { + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%p]",app_registry->name,app_registry->app_uri,(void*)priv->local_soup_instance); + soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); + } + return TRUE; +} + +gboolean gdial_rest_server_unregister_all_apps(GDialRestServer *self) { + g_return_val_if_fail(self != NULL, FALSE); + + GDIAL_LOGTRACE("Entering ..."); + + GDIAL_LOGINFO("Inside gdial_rest_server_unregister_all_apps"); + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + GList *registered_apps_head = priv->registered_apps; + /*Stopping all registread Apps*/ + while (priv->registered_apps) { + GDialAppRegistry *app_registry = priv->registered_apps->data; + GDialApp *app = gdial_app_find_instance_by_name(app_registry->name); + if (app) { + if (gdial_app_stop(app) == GDIAL_APP_ERROR_NONE) { + g_warn_if_fail(gdial_app_state(app) == GDIAL_APP_ERROR_NONE && GDIAL_APP_GET_STATE(app) == GDIAL_APP_STATE_STOPPED); + } + else { + GDIAL_LOGERROR("gdial_app_stop(%s) failed, force shutdown", app->name); + gdial_app_force_shutdown(app); + } + g_object_unref(app); + } + priv->registered_apps = priv->registered_apps->next; + } + priv->registered_apps = registered_apps_head; + /*Remove all registered apps before*/ + while (priv->registered_apps) { + priv->registered_apps = gdial_rest_server_registered_apps_clear(self, priv->registered_apps, priv->registered_apps); + } + GDIAL_LOGTRACE("Exiting ..."); + return TRUE; +} + +gboolean gdial_rest_server_is_app_registered(GDialRestServer *self, const gchar *app_name) { + return gdial_rest_server_find_app_registry(self, app_name) != NULL; +} + +gboolean gdial_rest_server_unregister_app(GDialRestServer *self, const gchar *app_name) { + g_return_val_if_fail(self != NULL && app_name != NULL, FALSE); + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + GList *found = g_list_find_custom(priv->registered_apps, app_name, GCompareFunc_match_registry_app_name); + if (found == NULL) return FALSE; + priv->registered_apps = gdial_rest_server_registered_apps_clear(self, priv->registered_apps, found); + return TRUE; +} + +typedef struct { + gchar *app_name; + gchar *dialVer; + GHashTable *options; + GDialAppState state; + gchar *installable; + gchar *link_href; + gchar *additionalData; +} GDialServerResponseBuilderGetApp; + + +GDIAL_STATIC_INLINE void *GET_APP_response_builder_new(const gchar *app_name) { + GDialServerResponseBuilderGetApp *rbuilder = (GDialServerResponseBuilderGetApp *) malloc(sizeof(*rbuilder)); + memset(rbuilder, 0, sizeof(*rbuilder)); + memset(rbuilder, 0, sizeof(*rbuilder)); + rbuilder->app_name = g_strdup(app_name); + rbuilder->options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + rbuilder->dialVer = g_strdup(GDIAL_PROTOCOL_VERSION_STR); + rbuilder->link_href = g_strdup("run"); + return rbuilder; +} + +/* + * options: + * [@allowStop, ...] + */ +GDIAL_STATIC_INLINE void *GET_APP_response_builder_set_option(void *builder, const gchar *option_name, const gchar *option_value) { + GDialServerResponseBuilderGetApp * rbuilder = (GDialServerResponseBuilderGetApp *)builder; + /* + * Simple check only... + */ + if (option_name && option_value) { + // note that this will leak if option_name key is already in the table + g_hash_table_insert(rbuilder->options, g_strdup(option_name), g_strdup(option_value)); + } + return builder; +} + +GDIAL_STATIC_INLINE void *GET_APP_response_builder_set_state(void *builder, GDialAppState state) { + ((GDialServerResponseBuilderGetApp *)builder)->state= state; + return builder; +} + +GDIAL_STATIC_INLINE void *GET_APP_response_builder_set_installable(void *builder, const gchar *encoded_url) { + ((GDialServerResponseBuilderGetApp *)builder)->installable= g_uri_escape_string(encoded_url, NULL, FALSE); + return builder; +} + +GDIAL_STATIC_INLINE void *GET_APP_response_builder_set_link_href(void *builder, const gchar *encoded_href) { + GDialServerResponseBuilderGetApp * rbuilder = (GDialServerResponseBuilderGetApp *)builder; + g_free(rbuilder->link_href); + if (encoded_href) { + char *tmp = g_uri_escape_string(encoded_href, NULL, FALSE); + rbuilder->link_href= g_strdup(tmp); + free(tmp); + } + else { + rbuilder->link_href = g_strdup("run"); + } + return builder; +} + +GDIAL_STATIC_INLINE void *GET_APP_response_builder_set_additionalData(void *builder, const gchar *additionalData) { + if (additionalData) { + ((GDialServerResponseBuilderGetApp *)builder)->additionalData = g_strdup(additionalData); + } + return builder; +} + +GDIAL_STATIC_INLINE gchar *GET_APP_response_builder_build(void *builder, gsize *length) { + GDialServerResponseBuilderGetApp * rbuilder = (GDialServerResponseBuilderGetApp *)builder; + GString *rbuf = g_string_new_len('\0', 128); + if(rbuf == NULL){ + return NULL; + } + gsize options_length = 0; + gchar *options_str = gdial_util_str_str_hashtable_to_xml_string(rbuilder->options, &options_length); + + g_string_append_printf(rbuf, "\r\n"); + g_string_append_printf(rbuf, "\r\n", GDIAL_PROTOCOL_XMLNS_SCHEMA, rbuilder->dialVer); + g_string_append_printf(rbuf, " %s\r\n", rbuilder->app_name); + if (options_str) { + g_string_append_printf(rbuf, " \r\n", options_str); + g_free(options_str); + } + g_string_append_printf(rbuf, " %s\r\n", gdial_app_state_to_string(rbuilder->state)); + if (rbuilder->state != GDIAL_APP_STATE_STOPPED) { + g_string_append_printf(rbuf, " \r\n", rbuilder->link_href); + } + if (rbuilder->additionalData) { + g_string_append_printf(rbuf, " %s\r\n", ""); + } + g_string_append_printf(rbuf, "\r\n"); + + if (length) { + *length = rbuf->len; + } + + return g_string_free(rbuf, FALSE); +} + +GDIAL_STATIC_INLINE void GET_APP_response_builder_destroy(void *builder) { + GDialServerResponseBuilderGetApp * rbuilder = (GDialServerResponseBuilderGetApp *)builder; + g_free(rbuilder->app_name); + g_free(rbuilder->dialVer); + g_hash_table_destroy(rbuilder->options); + g_free(rbuilder->link_href); + g_free(rbuilder->installable); + g_free(rbuilder->additionalData); + + g_free(builder); +} diff --git a/server/gdial1p6-shield.c b/server/gdial1p6-shield.c new file mode 100644 index 00000000..8da7ab6b --- /dev/null +++ b/server/gdial1p6-shield.c @@ -0,0 +1,148 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 +#include +#include +#include + +#include "gdial-config.h" +#include "gdial-debug.h" + +typedef struct DialShieldConnectionContext { + GSocket *read_gsocket; + guint read_timeout_source; +} DialShieldConnectionContext; + +static GHashTable *active_conns_ = NULL; + +static void soup_message_weak_ref_callback(gpointer user_data, GObject *obj); +static void server_request_remove_callback (SoupMessage *msg) { + GDIAL_LOGTRACE("Entering ..."); + DialShieldConnectionContext * conn_context = (DialShieldConnectionContext *) g_hash_table_lookup(active_conns_, msg); + if (conn_context) { + if (conn_context->read_timeout_source != 0) { + g_print_with_timestamp("server_request_remove_callback tid=[%lx] msg=%p timeout source %d removed", + pthread_self(), msg, conn_context->read_timeout_source); + g_source_remove(conn_context->read_timeout_source); + } + g_hash_table_remove(active_conns_, msg); + g_free(conn_context); + conn_context = NULL; + } + GDIAL_LOGTRACE("Exiting ..."); +} + +static gboolean soup_message_read_timeout_callback(gpointer user_data) { + SoupMessage *msg = (SoupMessage*)user_data; + GDIAL_LOGTRACE("Entering ..."); + DialShieldConnectionContext * conn_context = (DialShieldConnectionContext *)g_hash_table_lookup(active_conns_, msg); + g_print_with_timestamp("soup_message_read_timeout_callback tid=[%lx] msg=%p", pthread_self(), msg); + if (conn_context) { + conn_context->read_timeout_source = 0; + g_socket_close(conn_context->read_gsocket, NULL);//this will trigger abort callback + } + GDIAL_LOGTRACE("Exiting ..."); + return G_SOURCE_REMOVE;; +} + + +static void server_request_read_callback (SoupServer *server, SoupServerMessage *msg, + gpointer data) { + GDIAL_LOGTRACE("Entering ..."); + g_print_with_timestamp("server_request_read_callback tid=[%lx] msg=%p", pthread_self(), msg); + g_object_weak_unref(G_OBJECT(msg), (GWeakNotify)soup_message_weak_ref_callback, msg); + server_request_remove_callback(msg); + GDIAL_LOGTRACE("Exiting ..."); +} + +static void server_request_finished_callback (SoupServer *server, SoupServerMessage *msg, + gpointer data) { + GDIAL_LOGTRACE("!!!"); +} + +static void server_request_aborted_callback (SoupServer *server, SoupServerMessage *msg, + gpointer data) { + GDIAL_LOGTRACE("Entering ..."); + g_print_with_timestamp("server_request_aborted_callback tid=[%lx] msg=%p", pthread_self(), msg); + + g_object_weak_unref(G_OBJECT(msg), (GWeakNotify)soup_message_weak_ref_callback, msg); + server_request_remove_callback(msg); + GDIAL_LOGTRACE("Exiting ..."); +} + +static void soup_message_weak_ref_callback(gpointer user_data, GObject *obj) { + SoupMessage *msg0=(SoupMessage*)obj; + SoupMessage *msg=(SoupMessage*)user_data; + GDIAL_LOGTRACE("Entering ..."); + assert(msg0==msg); + g_print_with_timestamp("soup_message_weak_ref_callback tid=[%lx] msg=%p", pthread_self(), msg); + server_request_remove_callback(msg); + GDIAL_LOGTRACE("Exiting ..."); +} + +static void server_request_started_callback (SoupServer *server, SoupServerMessage *msg, + gpointer data) { + + static const int throttle = GDIAL_THROTTLE_DELAY_US; + GDIAL_LOGTRACE("Entering ..."); + DialShieldConnectionContext *conn_context = g_new(DialShieldConnectionContext, 1); + guint read_timeout_source = g_timeout_add(2000, (GSourceFunc)soup_message_read_timeout_callback, msg); + conn_context->read_gsocket = soup_server_message_get_socket(msg); + conn_context->read_timeout_source = read_timeout_source; + g_print_with_timestamp("server_request_started_callback tid=[%lx] msg=%p timeout source %d added with socket fd = %d", + pthread_self(), msg, read_timeout_source, g_socket_get_fd(conn_context->read_gsocket)); + g_hash_table_insert(active_conns_, msg, conn_context); + g_object_weak_ref(G_OBJECT(msg), (GWeakNotify)soup_message_weak_ref_callback, msg); + usleep(throttle); + GDIAL_LOGTRACE("Exiting ..."); +} + +void gdial_shield_init(void) { + GDIAL_LOGTRACE("Entering ..."); + active_conns_ = g_hash_table_new(g_direct_hash, g_direct_equal); + GDIAL_LOGTRACE("Exiting ..."); +} + +void gdial_shield_server(SoupServer *server) { + g_return_if_fail(server != NULL); + GDIAL_LOGTRACE("Entering ..."); + g_signal_connect(server, "request_started", G_CALLBACK(server_request_started_callback), NULL); + g_signal_connect(server, "request_read", G_CALLBACK(server_request_read_callback), NULL); + g_signal_connect(server, "request_finished",G_CALLBACK(server_request_finished_callback), NULL); + g_signal_connect(server, "request_aborted", G_CALLBACK(server_request_aborted_callback), NULL); + GDIAL_LOGTRACE("Exiting ..."); +} + +void gdial_shield_term(void) { + GHashTableIter iter; + gpointer key, value; + GDIAL_LOGTRACE("Entering ..."); + g_return_if_fail(active_conns_ != NULL); + GDIAL_LOGINFO("gdial_shield_term: hash_table_size start= %d", g_hash_table_size(active_conns_)); + g_hash_table_iter_init(&iter, (GHashTable *)active_conns_); + while (g_hash_table_iter_next(&iter, &key, &value)) { + SoupMessage *msg = (SoupMessage *)key; + server_request_remove_callback(msg); + } + GDIAL_LOGINFO("gdial_shield_term: hash_table_size end= %d", g_hash_table_size(active_conns_)); + g_hash_table_unref(active_conns_); + active_conns_ = NULL; + GDIAL_LOGTRACE("Exiting ..."); +} diff --git a/server/gdial1p6-ssdp.c b/server/gdial1p6-ssdp.c new file mode 100644 index 00000000..b5b36094 --- /dev/null +++ b/server/gdial1p6-ssdp.c @@ -0,0 +1,391 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2019 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "gdial-config.h" +#include "gdial-plat-util.h" +#include "gdial-plat-dev.h" +#include "gdial-ssdp.h" +#include "gdialservicelogging.h" + +#define MAX_POWERON_TIME 10 +static SoupServer *ssdp_http_server_ = NULL; +static GDialOptions *gdial_options_ = NULL; +static GSSDPClient *ssdp_client_ = NULL; +static GSSDPResourceGroup *ssdp_resource_group_ = NULL; +static int ssdp_resource_id_ = 0; +/* + * ssdp settings + */ +static const char *dial_ssdp_ST_target = "urn:dial-multiscreen-org:service:dial:1"; +#define DIAL_SSDP_USN_FMT "uuid:%s::urn:dial-multiscreen-org:service:dial:1" +#define DIAL_SSDP_LOCATION_FMT "http://%s:%d/%s/dd.xml" +#define DIAL_SSDP_WAKEUP_FMT "MAC=%s;Timeout=%d" + + +/* + * server cmdline options + */ +#define GDIAL_SSDP_DEVICE_UUID_DEFAULT "12345678-abcd-abcd-1234-123456789abc" +#define GDIAL_SSDP_FRIENDLY_DEFAULT "DialClient" +#define GDIAL_SSDP_MANUFACTURER_DEFAULT "OEM" +#define GDIAL_SSDP_MODELNAME_DEFAULT "Device" + +static const char *iface_ipv4_address = NULL; +/* + * Copyright (c) 2014 Netflix, Inc. + * Licensed under the BSD-2 license + */ +static const char ssdp_device_xml_template[] = "" + "" + "" + " 1 0 " + " " + " urn:schemas-upnp-org:device:tvdevice:1" + " %s" + " %s" + " %s" + " uuid:%s" + " " + ""; + +static gchar *dd_xml_response_str_ = NULL; +static gchar *app_friendly_name = NULL; +static gchar *app_random_uuid = NULL; +static gchar *app_manufacturer_name = NULL; +static gchar *app_model_name = NULL; +static pthread_mutex_t ssdpServerEventSync = PTHREAD_MUTEX_INITIALIZER; + +static void ssdp_http_server_callback(SoupServer *server, SoupServerMessage *msg, const char *path, GHashTable *query, gpointer user_data) { + /* + * /dd.xml only supports GET + */ + if (!msg || !soup_server_message_get_method(msg) || soup_server_message_get_method(msg) != SOUP_METHOD_GET) { + soup_server_message_set_status(msg, SOUP_STATUS_BAD_REQUEST, NULL); + GDIAL_CHECK("GET_method_only"); + GDIAL_DEBUG("warning: SSDP HTTP Method is not GET"); + return; + } + + pthread_mutex_lock(&ssdpServerEventSync); + /* + * there is no variant here, so we can cache the response. + */ + static size_t dd_xml_response_str_len = 0; + + if (!dd_xml_response_str_) { + const gchar *manufacturer= app_manufacturer_name; + const gchar *model = app_model_name; + + if (manufacturer == NULL) { + manufacturer = gdial_options_->manufacturer; + } + + if (model == NULL) { + model = gdial_options_->model_name; + } + + if(gdial_options_->feature_friendlyname && app_friendly_name && strlen(app_friendly_name)) + { + dd_xml_response_str_ = g_strdup_printf(ssdp_device_xml_template, app_friendly_name, manufacturer, model, gdial_options_->uuid); + } + else + { + dd_xml_response_str_ = g_strdup_printf(ssdp_device_xml_template, gdial_options_->friendly_name, manufacturer, model, gdial_options_->uuid); + } + + if ( dd_xml_response_str_ ) { + dd_xml_response_str_len = strlen(dd_xml_response_str_); + } + else { + GDIAL_LOGERROR("Failed to allocate memory for dd.xml response"); + soup_server_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); + } + } + + if ( dd_xml_response_str_ ) { + gchar *application_url_str = g_strdup_printf("http://%s:%d/%s/", iface_ipv4_address, GDIAL_REST_HTTP_PORT,app_random_uuid); + + if ( application_url_str ) + { + soup_message_headers_replace (soup_server_message_get_response_headers(msg), "Application-URL", application_url_str); + g_free(application_url_str); + application_url_str = NULL; + + soup_server_message_set_response(msg, "text/xml; charset=utf-8", SOUP_MEMORY_STATIC, dd_xml_response_str_, dd_xml_response_str_len); + soup_server_message_set_status(msg, SOUP_STATUS_OK, NULL); + + GDIAL_CHECK("Content-Type:text/xml"); + GDIAL_CHECK("Application-URL: exist"); + } + else + { + GDIAL_LOGERROR("Failed to allocate memory for response_headers"); + soup_server_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL); + } + } + pthread_mutex_unlock(&ssdpServerEventSync); +} + +void gdial_ssdp_networkstandbymode_handler(const bool nwstandby) +{ + if(ssdp_client_){ + if(nwstandby){ + GDIAL_LOGINFO("gdial_ssdp_networkstandbymode_handler add WAKEUP header "); + gchar *dial_ssdp_WAKEUP = g_strdup_printf(DIAL_SSDP_WAKEUP_FMT,gdial_plat_util_get_iface_mac_addr(gdial_options_->iface_name),MAX_POWERON_TIME); + gssdp_client_append_header(ssdp_client_, "WAKEUP", dial_ssdp_WAKEUP); + g_free(dial_ssdp_WAKEUP); + dial_ssdp_WAKEUP = NULL; + } + else{ + GDIAL_LOGINFO("gdial_ssdp_networkstandbymode_handler remove WAKEUP header "); + gssdp_client_remove_header(ssdp_client_, "WAKEUP"); + } + } + return; +} + +int gdial_ssdp_new(SoupServer *ssdp_http_server, GDialOptions *options, const gchar *random_uuid) { + + g_return_val_if_fail(ssdp_http_server != NULL, -1); + g_return_val_if_fail(options != NULL, -1); + g_return_val_if_fail(options->iface_name != NULL, -1); + + if (0 != pthread_mutex_init(&ssdpServerEventSync, NULL)) + { + GDIAL_LOGERROR("Failed to initializing mutex"); + return EXIT_FAILURE; + } + + gdial_options_ = options; + GDIAL_LOGINFO("gdial_options_->friendly_name[%p]",gdial_options_->friendly_name); + + if (gdial_options_->friendly_name == NULL) gdial_options_->friendly_name = g_strdup(GDIAL_SSDP_FRIENDLY_DEFAULT); + if (gdial_options_->manufacturer== NULL) gdial_options_->manufacturer = g_strdup(GDIAL_SSDP_MANUFACTURER_DEFAULT); + if (gdial_options_->model_name== NULL) gdial_options_->model_name = g_strdup(GDIAL_SSDP_MODELNAME_DEFAULT); + if (gdial_options_->uuid == NULL) gdial_options_->uuid = g_strdup(GDIAL_SSDP_DEVICE_UUID_DEFAULT); + if (gdial_options_->iface_name == NULL) gdial_options_->iface_name = g_strdup(GDIAL_IFACE_NAME_DEFAULT); + + GError *error = NULL; + + iface_ipv4_address = gdial_plat_util_get_iface_ipv4_addr(gdial_options_->iface_name); + if (!iface_ipv4_address) { + return EXIT_FAILURE; + } + + GSSDPClient *ssdp_client = gssdp_client_new( +#ifndef HAVE_GSSDP_VERSION_1_6_OR_NEWER + NULL, +#endif + gdial_options_->iface_name, &error); + + if (!ssdp_client || error) { + GDIAL_LOGERROR("%s", error->message); + g_error_free(error); + return EXIT_FAILURE; + } + + gdail_plat_dev_register_nwstandbymode_cb(gdial_ssdp_networkstandbymode_handler); + /* + * setup configurable headers. + * header "SERVER" is populated by gssdp. + * header "EXT" is mandatory, set by gssdp + * header "CACHE-CONTROL" is mandatory, set by gssdp, default 1800 + */ + gssdp_client_append_header(ssdp_client, "BOOTID.UPNP.ORG", "1"); + + /* feature_wolwake should be handled gdialservice users*/ + if(gdial_options_->feature_wolwake) { + GDIAL_LOGINFO("WOL Wake feature is enabled"); + gchar *dial_ssdp_WAKEUP = g_strdup_printf(DIAL_SSDP_WAKEUP_FMT,gdial_plat_util_get_iface_mac_addr(gdial_options_->iface_name),MAX_POWERON_TIME); + gssdp_client_append_header(ssdp_client, "WAKEUP", dial_ssdp_WAKEUP); + g_free(dial_ssdp_WAKEUP); + dial_ssdp_WAKEUP = NULL; + } + else { + GDIAL_LOGINFO("WOL Wake feature is disabled"); + } + GDIAL_CHECK("EXT"); + GDIAL_CHECK("CACHE-CONTROL"); + GDIAL_CHECK("BOOTID.UPNP.ORG"); + + GSSDPResourceGroup *ssdp_resource_group = gssdp_resource_group_new(ssdp_client); + gchar *dial_ssdp_USN = g_strdup_printf(DIAL_SSDP_USN_FMT, gdial_options_->uuid); + gchar *dial_ssdp_LOCATION = g_strdup_printf(DIAL_SSDP_LOCATION_FMT, iface_ipv4_address, GDIAL_SSDP_HTTP_PORT,random_uuid); + ssdp_resource_id_ = + gssdp_resource_group_add_resource_simple (ssdp_resource_group, dial_ssdp_ST_target, dial_ssdp_USN, dial_ssdp_LOCATION); + gssdp_resource_group_set_available (ssdp_resource_group, FALSE); + g_free(dial_ssdp_USN); + dial_ssdp_USN = NULL; + g_free(dial_ssdp_LOCATION); + dial_ssdp_LOCATION = NULL; + + ssdp_resource_group_ = ssdp_resource_group; + + g_object_ref(ssdp_http_server); + ssdp_http_server_ = ssdp_http_server; + app_random_uuid = g_strdup(random_uuid); + gchar *dail_ssdp_handler = g_strdup_printf("/%s/%s", random_uuid,"dd.xml"); + soup_server_add_handler(ssdp_http_server_, dail_ssdp_handler, ssdp_http_server_callback, NULL, NULL); + ssdp_client_ = ssdp_client; + + return 0; +} + +int gdial_ssdp_destroy() { + GDIAL_LOGTRACE("Entering ..."); + pthread_mutex_lock(&ssdpServerEventSync); + if (ssdp_http_server_) + { + soup_server_remove_handler(ssdp_http_server_, "/dd.xml"); + } + + if (ssdp_resource_group_) + { + gssdp_resource_group_remove_resource(ssdp_resource_group_, ssdp_resource_id_); + ssdp_resource_id_ = 0; + } + + if (dd_xml_response_str_) + { + g_free(dd_xml_response_str_); + dd_xml_response_str_ = NULL; + } + if (gdial_options_) + { + if (gdial_options_->friendly_name != NULL) + { + g_free(gdial_options_->friendly_name); + gdial_options_->friendly_name = NULL; + } + if (gdial_options_->uuid != NULL) + { + g_free(gdial_options_->uuid); + gdial_options_->uuid = NULL; + } + if (gdial_options_->iface_name != NULL) + { + g_free(gdial_options_->iface_name); + gdial_options_->iface_name = NULL; + } + gdial_options_ = NULL; + } + if (app_random_uuid != NULL) { + g_free(app_random_uuid); + app_random_uuid = NULL; + } + if (ssdp_client_) + { + gssdp_client_clear_headers(ssdp_client_); + } + if (ssdp_http_server_) + { + g_object_unref(ssdp_http_server_); + ssdp_http_server_ = NULL; + } + if (ssdp_resource_group_) + { + g_object_unref(ssdp_resource_group_); + ssdp_resource_group_ = NULL; + } + if (ssdp_client_) + { + g_object_unref(ssdp_client_); + ssdp_client_ = NULL; + } + pthread_mutex_unlock(&ssdpServerEventSync); + pthread_mutex_destroy(&ssdpServerEventSync); + GDIAL_LOGTRACE("Exiting ..."); + return 0; +} + +int gdial_ssdp_set_available(bool activation_status, const gchar *friendlyname) +{ + GDIAL_LOGINFO("gdial_ssdp_set_available activation_status :%d ",activation_status); + gdial_ssdp_set_friendlyname(friendlyname); + if(ssdp_resource_group_) gssdp_resource_group_set_available (ssdp_resource_group_, activation_status); + return 0; +} + +int gdial_ssdp_set_friendlyname(const gchar *friendlyname) +{ + pthread_mutex_lock(&ssdpServerEventSync); + if(gdial_options_ && gdial_options_->feature_friendlyname && friendlyname) + { + if (app_friendly_name != NULL) { + g_free(app_friendly_name); + } + app_friendly_name = g_strdup(friendlyname); + + GDIAL_LOGINFO("gdial_ssdp_set_friendlyname app_friendly_name :%s ",app_friendly_name); + if (dd_xml_response_str_!= NULL){ + g_free(dd_xml_response_str_); + dd_xml_response_str_ = NULL; + } + } + pthread_mutex_unlock(&ssdpServerEventSync); + return 0; +} + +int gdial_ssdp_set_manufacturername(const gchar *manufacturer_name) +{ + pthread_mutex_lock(&ssdpServerEventSync); + if(manufacturer_name) + { + if (app_manufacturer_name != NULL) { + g_free(app_manufacturer_name); + } + app_manufacturer_name = g_strdup(manufacturer_name); + + GDIAL_LOGINFO("app_manufacturer_name :%s ",app_manufacturer_name); + if (dd_xml_response_str_!= NULL){ + g_free(dd_xml_response_str_); + dd_xml_response_str_ = NULL; + } + } + pthread_mutex_unlock(&ssdpServerEventSync); + return 0; +} + +int gdial_ssdp_set_modelname(const gchar *model_name) +{ + pthread_mutex_lock(&ssdpServerEventSync); + if(model_name) + { + if (app_model_name != NULL) { + g_free(app_model_name); + } + app_model_name = g_strdup(model_name); + + GDIAL_LOGINFO("app_model_name :%s ",app_model_name); + if (dd_xml_response_str_!= NULL){ + g_free(dd_xml_response_str_); + dd_xml_response_str_ = NULL; + } + } + pthread_mutex_unlock(&ssdpServerEventSync); + return 0; +} diff --git a/server/gdialservice.cpp b/server/gdialservice.cpp index fdd64ed9..8200088b 100644 --- a/server/gdialservice.cpp +++ b/server/gdialservice.cpp @@ -199,12 +199,22 @@ static void signal_handler_rest_server_rest_enable(GDialRestServer *dial_rest_se } static void gdial_http_server_throttle_callback(SoupServer *server, +#ifdef HAVE_LIBSOUP_VERSION_3 + SoupServerMessage *msg, const gchar *path, GHashTable *query, + gpointer user_data) +#else SoupMessage *msg, const gchar *path, GHashTable *query, SoupClientContext *client, gpointer user_data) +#endif { GDIAL_LOGINFO("gdial_http_server_throttle_callback "); +#ifdef HAVE_LIBSOUP_VERSION_3 + soup_message_headers_replace(soup_server_message_get_response_headers(msg), "Connection", "close"); + soup_server_message_set_status(msg, SOUP_STATUS_NOT_FOUND, NULL); +#else soup_message_headers_replace(msg->response_headers, "Connection", "close"); soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND); +#endif } static void gdial_quit_thread(int signum) @@ -490,6 +500,18 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) GSList *uris = soup_server_get_uris(m_servers[i]); for (GSList *uri = uris; uri != NULL; uri = uri->next) { +#ifdef HAVE_LIBSOUP_VERSION_3 + GUri *origin_uri = (GUri *)uri->data; + if (!origin_uri) + { + GDIAL_LOGWARNING("Failed to get GUri from SoupServer at index [%d]", i); + continue; + } + char *uri_string = g_uri_to_string(origin_uri); + GDIAL_LOGINFO("Listening on %s", uri_string); + g_free(uri_string); + g_uri_unref(origin_uri); +#else SoupURI *origin_uri = (SoupURI *)uri->data; if (!origin_uri) { @@ -500,6 +522,7 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) GDIAL_LOGINFO("Listening on %s", uri_string); g_free(uri_string); soup_uri_free(origin_uri); +#endif } g_slist_free(uris); } From ed2715c3eb8673bbece282b2af4087b321188adc Mon Sep 17 00:00:00 2001 From: Preeja Raveendran Date: Tue, 17 Feb 2026 19:22:29 +0530 Subject: [PATCH 2/7] cmake --- server/CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 39f4804e..af0ba756 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -34,6 +34,17 @@ if (LIBSOUP3_FOUND) pkg_search_module (GSSDP REQUIRED gssdp-1.6) add_definitions(-DHAVE_GSSDP_VERSION_1_6_OR_NEWER) message("Using gssdp-1.6") + else() + # Fallback to gssdp-1.2 if gssdp-1.6 not found + pkg_search_module (GSSDP12 gssdp-1.2) + if (GSSDP12_FOUND) + pkg_search_module (GSSDP REQUIRED gssdp-1.2) + add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) + message("Using gssdp-1.2") + else() + pkg_search_module (GSSDP REQUIRED gssdp-1.0) + message("Using gssdp-1.0") + endif() endif() else() pkg_search_module (GSSDP12 gssdp-1.2) From a6f97f10af0765ba756eb6ebf5d9b5f4343eeaf6 Mon Sep 17 00:00:00 2001 From: Preeja Raveendran Date: Tue, 17 Feb 2026 21:44:43 +0530 Subject: [PATCH 3/7] gssdp --- server/CMakeLists.txt | 27 ++++++------------- server/{ => libsoup-2p4}/gdial-rest.c | 0 server/{ => libsoup-2p4}/gdial-shield.c | 0 server/{ => libsoup-2p4}/gdial-ssdp.c | 0 .../gdial-rest.c} | 0 .../gdial-shield.c} | 0 .../gdial-ssdp.c} | 0 7 files changed, 8 insertions(+), 19 deletions(-) rename server/{ => libsoup-2p4}/gdial-rest.c (100%) rename server/{ => libsoup-2p4}/gdial-shield.c (100%) rename server/{ => libsoup-2p4}/gdial-ssdp.c (100%) rename server/{gdial1p6-rest.c => libsoup-3p0/gdial-rest.c} (100%) rename server/{gdial1p6-shield.c => libsoup-3p0/gdial-shield.c} (100%) rename server/{gdial1p6-ssdp.c => libsoup-3p0/gdial-ssdp.c} (100%) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index af0ba756..fdbdd62a 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -34,17 +34,6 @@ if (LIBSOUP3_FOUND) pkg_search_module (GSSDP REQUIRED gssdp-1.6) add_definitions(-DHAVE_GSSDP_VERSION_1_6_OR_NEWER) message("Using gssdp-1.6") - else() - # Fallback to gssdp-1.2 if gssdp-1.6 not found - pkg_search_module (GSSDP12 gssdp-1.2) - if (GSSDP12_FOUND) - pkg_search_module (GSSDP REQUIRED gssdp-1.2) - add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) - message("Using gssdp-1.2") - else() - pkg_search_module (GSSDP REQUIRED gssdp-1.0) - message("Using gssdp-1.0") - endif() endif() else() pkg_search_module (GSSDP12 gssdp-1.2) @@ -98,22 +87,22 @@ if (LIBSOUP3_FOUND) set (GDIAL_EXEC_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/gdial-util.c ${CMAKE_CURRENT_SOURCE_DIR}/gdial-app.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial1p6-rest.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial1p6-ssdp.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial1p6-shield.c + ${CMAKE_CURRENT_SOURCE_DIR}/libsoup-3p0/gdial-rest.c + ${CMAKE_CURRENT_SOURCE_DIR}/libsoup-3p0/gdial-ssdp.c + ${CMAKE_CURRENT_SOURCE_DIR}/libsoup-3p0/gdial-shield.c ${CMAKE_CURRENT_SOURCE_DIR}/gdialservice.cpp ) - message("Using libsoup-3.0 compatible source files (gdial1p6-*.c)") + message("Using libsoup-3.0 compatible source files from libsoup-3p0/") else() set (GDIAL_EXEC_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/gdial-util.c ${CMAKE_CURRENT_SOURCE_DIR}/gdial-app.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial-rest.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial-ssdp.c - ${CMAKE_CURRENT_SOURCE_DIR}/gdial-shield.c + ${CMAKE_CURRENT_SOURCE_DIR}/libsoup-2p4/gdial-rest.c + ${CMAKE_CURRENT_SOURCE_DIR}/libsoup-2p4/gdial-ssdp.c + ${CMAKE_CURRENT_SOURCE_DIR}/libsoup-2p4/gdial-shield.c ${CMAKE_CURRENT_SOURCE_DIR}/gdialservice.cpp ) - message("Using libsoup-2.4 compatible source files (gdial-*.c)") + message("Using libsoup-2.4 compatible source files from libsoup-2p4/") endif() link_directories ( diff --git a/server/gdial-rest.c b/server/libsoup-2p4/gdial-rest.c similarity index 100% rename from server/gdial-rest.c rename to server/libsoup-2p4/gdial-rest.c diff --git a/server/gdial-shield.c b/server/libsoup-2p4/gdial-shield.c similarity index 100% rename from server/gdial-shield.c rename to server/libsoup-2p4/gdial-shield.c diff --git a/server/gdial-ssdp.c b/server/libsoup-2p4/gdial-ssdp.c similarity index 100% rename from server/gdial-ssdp.c rename to server/libsoup-2p4/gdial-ssdp.c diff --git a/server/gdial1p6-rest.c b/server/libsoup-3p0/gdial-rest.c similarity index 100% rename from server/gdial1p6-rest.c rename to server/libsoup-3p0/gdial-rest.c diff --git a/server/gdial1p6-shield.c b/server/libsoup-3p0/gdial-shield.c similarity index 100% rename from server/gdial1p6-shield.c rename to server/libsoup-3p0/gdial-shield.c diff --git a/server/gdial1p6-ssdp.c b/server/libsoup-3p0/gdial-ssdp.c similarity index 100% rename from server/gdial1p6-ssdp.c rename to server/libsoup-3p0/gdial-ssdp.c From c49f311ae0809abba569abba8b28fd17bbe5ff5c Mon Sep 17 00:00:00 2001 From: Preeja Raveendran Date: Tue, 17 Feb 2026 22:23:36 +0530 Subject: [PATCH 4/7] cmake --- server/CMakeLists.txt | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index fdbdd62a..07655a26 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -27,30 +27,28 @@ find_package (PkgConfig REQUIRED) pkg_search_module (GLIB REQUIRED glib-2.0) pkg_search_module (GIO REQUIRED gio-2.0) pkg_search_module (LIBSOUP3 libsoup-3.0) +pkg_search_module (GSSDP16 gssdp-1.6) +pkg_search_module (GSSDP12 gssdp-1.2) + if (LIBSOUP3_FOUND) add_definitions(-DHAVE_LIBSOUP_VERSION_3) - pkg_search_module (GSSDP16 gssdp-1.6) - if (GSSDP16_FOUND) - pkg_search_module (GSSDP REQUIRED gssdp-1.6) - add_definitions(-DHAVE_GSSDP_VERSION_1_6_OR_NEWER) - message("Using gssdp-1.6") - endif() -else() - pkg_search_module (GSSDP12 gssdp-1.2) - if (GSSDP12_FOUND) - pkg_search_module (GSSDP REQUIRED gssdp-1.2) - add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) - message("Using gssdp-1.2") - else() - pkg_search_module (GSSDP REQUIRED gssdp-1.0) - endif() endif() -if (LIBSOUP3_FOUND) + +if (LIBSOUP3_FOUND AND GSSDP16_FOUND) pkg_search_module (SOUP REQUIRED libsoup-3.0) message("Using libsoup-3.0") -else() + pkg_search_module (GSSDP REQUIRED gssdp-1.6) + add_definitions(-DHAVE_GSSDP_VERSION_1_6_OR_NEWER) + message("Using gssdp-1.6") +else if(GSSDP12_FOUND) pkg_search_module (SOUP REQUIRED libsoup-2.4) -endif() + pkg_search_module (GSSDP REQUIRED gssdp-1.2) + add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) + message("Using gssdp-1.2") +else () + pkg_search_module (SOUP REQUIRED libsoup-2.4) + pkg_search_module (GSSDP REQUIRED gssdp-1.0) +endif () pkg_search_module (XML2 REQUIRED libxml-2.0) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -g") From 04c560c91ce5a1db8cf62be1918e411609ee54cf Mon Sep 17 00:00:00 2001 From: preeja33 Date: Wed, 18 Feb 2026 12:30:55 +0530 Subject: [PATCH 5/7] Update CMakeLists.txt --- server/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 07655a26..f5fa12c9 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -40,7 +40,7 @@ if (LIBSOUP3_FOUND AND GSSDP16_FOUND) pkg_search_module (GSSDP REQUIRED gssdp-1.6) add_definitions(-DHAVE_GSSDP_VERSION_1_6_OR_NEWER) message("Using gssdp-1.6") -else if(GSSDP12_FOUND) +elseif(GSSDP12_FOUND) pkg_search_module (SOUP REQUIRED libsoup-2.4) pkg_search_module (GSSDP REQUIRED gssdp-1.2) add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) From d65be8a78992f2aca469b8940c380a74e06ea83a Mon Sep 17 00:00:00 2001 From: preeja33 Date: Wed, 18 Feb 2026 13:30:39 +0530 Subject: [PATCH 6/7] Update CMakeLists.txt --- server/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index f5fa12c9..f01fc705 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -81,7 +81,7 @@ include_directories ( ${PROJECT_GLIB_INCLUDE_DIRS} ) -if (LIBSOUP3_FOUND) +if (LIBSOUP3_FOUND AND GSSDP16_FOUND) set (GDIAL_EXEC_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/gdial-util.c ${CMAKE_CURRENT_SOURCE_DIR}/gdial-app.c From c9de5ebc80f20e21591fb876a065eff3a702549d Mon Sep 17 00:00:00 2001 From: preeja33 Date: Wed, 18 Feb 2026 13:58:17 +0530 Subject: [PATCH 7/7] Update CMakeLists.txt --- server/CMakeLists.txt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index f01fc705..3d86e7dd 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -30,24 +30,23 @@ pkg_search_module (LIBSOUP3 libsoup-3.0) pkg_search_module (GSSDP16 gssdp-1.6) pkg_search_module (GSSDP12 gssdp-1.2) -if (LIBSOUP3_FOUND) - add_definitions(-DHAVE_LIBSOUP_VERSION_3) -endif() - if (LIBSOUP3_FOUND AND GSSDP16_FOUND) pkg_search_module (SOUP REQUIRED libsoup-3.0) + add_definitions(-DHAVE_LIBSOUP_VERSION_3) message("Using libsoup-3.0") pkg_search_module (GSSDP REQUIRED gssdp-1.6) add_definitions(-DHAVE_GSSDP_VERSION_1_6_OR_NEWER) message("Using gssdp-1.6") -elseif(GSSDP12_FOUND) +else () pkg_search_module (SOUP REQUIRED libsoup-2.4) - pkg_search_module (GSSDP REQUIRED gssdp-1.2) - add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) - message("Using gssdp-1.2") -else () - pkg_search_module (SOUP REQUIRED libsoup-2.4) + if(GSSDP12_FOUND) + pkg_search_module (GSSDP REQUIRED gssdp-1.2) + add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) + message("Using gssdp-1.2") + else () pkg_search_module (GSSDP REQUIRED gssdp-1.0) + message("Using gssdp-1.0") + endif () endif () pkg_search_module (XML2 REQUIRED libxml-2.0)