+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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.
+ */
+
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <linux/limits.h>
+#include <glib.h>
+#include <bundle_internal.h>
+#include <pkgmgr-info.h>
+#include <aul_svc.h>
+#include <aul_svc_internal.h>
+
+#include "appcore_base.h"
+#include "appcore_base_private.h"
+#include "appcore_base_control.h"
+
+struct appcore_base_control_s {
+ char *id;
+ appcore_base_control_cb callback;
+ void *user_data;
+};
+
+struct control_info_s {
+ char *id;
+ char *operation;
+ char *uri;
+ char *uri_scheme;
+ char *uri_host;
+ char *mime;
+ char *mime_type;
+ char *mime_subtype;
+};
+
+static GList *__controls;
+static GList *__control_infos;
+static bool __control_info_initialized;
+
+static void __destroy_control_info(gpointer data)
+{
+ struct control_info_s *info = (struct control_info_s *)data;
+
+ if (!info)
+ return;
+
+ if (info->mime_subtype)
+ free(info->mime_subtype);
+ if (info->mime_type)
+ free(info->mime_type);
+ if (info->mime)
+ free(info->mime);
+ if (info->uri_host)
+ free(info->uri_host);
+ if (info->uri_scheme)
+ free(info->uri_scheme);
+ if (info->uri)
+ free(info->uri);
+ if (info->operation)
+ free(info->operation);
+ if (info->id)
+ free(info->id);
+ free(info);
+}
+
+static struct control_info_s *__create_control_info(const char *id,
+ const char *operation, const char *mime,
+ const char *uri)
+{
+ struct control_info_s *info;
+
+ info = calloc(1, sizeof(struct control_info_s));
+ if (!info) {
+ _ERR("Out of memory");
+ return NULL;
+ }
+
+ info->id = strdup(id);
+ if (!info->id) {
+ _ERR("Failed to duplicate app-control ID");
+ free(info);
+ return NULL;
+ }
+
+ info->operation = strdup(operation);
+ if (!info->operation) {
+ _ERR("Failed to duplicate app-control operation");
+ __destroy_control_info(info);
+ return NULL;
+ }
+
+ info->mime = strdup(mime ? mime : "NULL");
+ if (!info->mime) {
+ _ERR("Failed to duplicate app-control MIME-type");
+ __destroy_control_info(info);
+ return NULL;
+ }
+
+ info->uri = strdup(uri ? uri : "NULL");
+ if (!info->uri) {
+ _ERR("Failed to duplicate app-control URI");
+ __destroy_control_info(info);
+ return NULL;
+ }
+
+ return info;
+}
+
+static struct control_info_s *__create_control_info_from_bundle(bundle *b)
+{
+ struct control_info_s *info;
+ aul_svc_info_h svc_info;
+ int r;
+
+ r = aul_svc_info_create(b, &svc_info);
+ if (r != AUL_SVC_RET_OK)
+ return NULL;
+
+ info = calloc(1, sizeof(struct control_info_s));
+ if (!info) {
+ _ERR("Out of memory");
+ goto err;
+ }
+
+ r = aul_svc_info_get_operation(svc_info, &info->operation);
+ if (r != AUL_SVC_RET_OK)
+ goto err;
+
+ r = aul_svc_info_get_uri(svc_info, &info->uri);
+ if (r != AUL_SVC_RET_OK)
+ goto err;
+
+ r = aul_svc_info_get_uri_scheme(svc_info, &info->uri_scheme);
+ if (r != AUL_SVC_RET_OK)
+ goto err;
+
+ r = aul_svc_info_get_uri_host(svc_info, &info->uri_host);
+ if (r != AUL_SVC_RET_OK)
+ goto err;
+
+ r = aul_svc_info_get_mime(svc_info, &info->mime);
+ if (r != AUL_SVC_RET_OK)
+ goto err;
+
+ r = aul_svc_info_get_mime_type(svc_info, &info->mime_type);
+ if (r != AUL_SVC_RET_OK)
+ goto err;
+
+ r = aul_svc_info_get_mime_subtype(svc_info, &info->mime_subtype);
+ if (r != AUL_SVC_RET_OK)
+ goto err;
+
+ aul_svc_info_destroy(svc_info);
+
+ return info;
+err:
+ __destroy_control_info(info);
+ aul_svc_info_destroy(svc_info);
+
+ return NULL;
+}
+
+static gint __compare_control_infos(gconstpointer a, gconstpointer b)
+{
+ struct control_info_s *a_info = (struct control_info_s *)a;
+ struct control_info_s *b_info = (struct control_info_s *)b;
+ char mime[256];
+
+ if (strcmp(a_info->operation, b_info->operation) != 0)
+ return -1;
+
+ if (!strcmp(a_info->uri, b_info->uri) &&
+ !strcmp(a_info->mime, b_info->mime))
+ return 0;
+
+ if (!strcmp(a_info->uri, b_info->uri)) {
+ if (!strcmp(b_info->mime, "NULL") &&
+ !strcmp(b_info->mime_subtype, "%")) {
+ snprintf(mime, sizeof(mime), "%s/*",
+ b_info->mime_type);
+ if (!strcmp(a_info->mime, mime))
+ return 0;
+ }
+
+ if (!strcmp(b_info->mime, "NULL") &&
+ !strcmp(b_info->mime_type, "%")) {
+ if (!strcmp(a_info->mime, "*/*"))
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static struct control_info_s *__find_control_info(bundle *b)
+{
+ struct control_info_s *info;
+ struct control_info_s b_info;
+ char uri[256];
+ GList *found;
+
+ info = __create_control_info_from_bundle(b);
+ if (!info)
+ return NULL;
+
+ b_info = *info;
+
+ /* Step 1 */
+ found = g_list_find_custom(__control_infos, &b_info,
+ __compare_control_infos);
+ if (found) {
+ __destroy_control_info(info);
+ return found->data;
+ }
+
+ /* Step 2 */
+ if (strcmp(b_info.uri_scheme, "NULL") != 0 &&
+ strcmp(b_info.uri_host, "NULL") != 0) {
+ snprintf(uri, sizeof(uri), "%s://%s",
+ b_info.uri_scheme, b_info.uri_host);
+ b_info.uri = uri;
+ found = g_list_find_custom(__control_infos, &b_info,
+ __compare_control_infos);
+ if (found) {
+ __destroy_control_info(info);
+ return found->data;
+ }
+ }
+
+ /* Step 3 */
+ b_info.uri = info->uri_scheme;
+ found = g_list_find_custom(__control_infos, &b_info,
+ __compare_control_infos);
+ if (found) {
+ __destroy_control_info(info);
+ return found->data;
+ }
+
+ /* Step 4 */
+ b_info.uri = "*";
+ found = g_list_find_custom(__control_infos, &b_info,
+ __compare_control_infos);
+ if (found) {
+ __destroy_control_info(info);
+ return found->data;
+ }
+
+ /* Step 5 */
+ if (!strcmp(b_info.uri_scheme, "file") &&
+ strcmp(b_info.mime, "NULL") != 0) {
+ b_info.uri = "NULL";
+ found = g_list_find_custom(__control_infos, &b_info,
+ __compare_control_infos);
+ if (found) {
+ __destroy_control_info(info);
+ return found->data;
+ }
+ }
+
+ __destroy_control_info(info);
+
+ return NULL;
+}
+
+static int __foreach_app_control_cb(const char *operation,
+ const char *uri, const char *mime, const char *id,
+ void *user_data)
+{
+ struct control_info_s *info;
+
+ info = __create_control_info(id, operation, mime, uri);
+ if (!info)
+ return -1;
+
+ __control_infos = g_list_append(__control_infos, info);
+
+ return 0;
+}
+
+int appcore_base_control_init(void)
+{
+ pkgmgrinfo_appinfo_h handle;
+ char appid[512];
+ int r;
+
+ if (__control_info_initialized)
+ return APPCORE_BASE_ERROR_NONE;
+
+ r = aul_app_get_appid_bypid(getpid(), appid, sizeof(appid));
+ if (r != AUL_R_OK) {
+ _ERR("Failed to get application ID. result(%x)", r);
+ return APPCORE_BASE_ERROR_IO_ERROR;
+ }
+
+ r = pkgmgrinfo_appinfo_get_appinfo(appid, &handle);
+ if (r != PMINFO_R_OK) {
+ _ERR("Failed to get app info. result(%x)", r);
+ return APPCORE_BASE_ERROR_IO_ERROR;
+ }
+
+ r = pkgmgrinfo_appinfo_foreach_appcontrol_v2(handle,
+ __foreach_app_control_cb, NULL);
+ if (r != PMINFO_R_OK) {
+ _ERR("Failed to retrieve app-control. result(%x)", r);
+ pkgmgrinfo_appinfo_destroy_appinfo(handle);
+ return APPCORE_BASE_ERROR_IO_ERROR;
+ }
+
+ pkgmgrinfo_appinfo_destroy_appinfo(handle);
+
+ __control_info_initialized = true;
+
+ return APPCORE_BASE_ERROR_NONE;
+}
+
+void appcore_base_control_fini(void)
+{
+ if (__controls) {
+ g_list_free_full(__controls,
+ (GDestroyNotify)appcore_base_control_remove);
+ __controls = NULL;
+ }
+
+ if (__control_infos) {
+ g_list_free_full(__control_infos, __destroy_control_info);
+ __control_infos = NULL;
+ }
+
+ __control_info_initialized = false;
+}
+
+int appcore_base_control_invoke(bundle *b)
+{
+ struct appcore_base_control_s *ctrl;
+ struct control_info_s *info;
+ GList *iter;
+
+ if (!b) {
+ _ERR("Invalid parameter");
+ return APPCORE_BASE_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!__controls)
+ return APPCORE_BASE_ERROR_NONE;
+
+ info = __find_control_info(b);
+ if (!info)
+ return APPCORE_BASE_ERROR_NONE;
+
+ iter = __controls;
+ while (iter) {
+ ctrl = (struct appcore_base_control_s *)iter->data;
+ if (!strcmp(ctrl->id, info->id))
+ ctrl->callback(b, ctrl->user_data);
+
+ iter = g_list_next(iter);
+ }
+
+ _DBG("[app-control] id(%s)", info->id);
+ return APPCORE_BASE_ERROR_NONE;
+}
+
+static bool __control_info_exists(const char *id)
+{
+ struct control_info_s *info;
+ GList *iter;
+
+ iter = __control_infos;
+ while (iter) {
+ info = (struct control_info_s *)iter->data;
+ if (!strcmp(info->id, id))
+ return true;
+
+ iter = g_list_next(iter);
+ }
+
+ return false;
+}
+
+EXPORT_API int appcore_base_control_add(const char *id,
+ appcore_base_control_cb callback, void *user_data,
+ appcore_base_control_h *h)
+{
+ struct appcore_base_control_s *ctrl;
+ int r;
+
+ if (!id || !callback || !h) {
+ _ERR("Invalid parameter");
+ return APPCORE_BASE_ERROR_INVALID_PARAMETER;
+ }
+
+ r = appcore_base_control_init();
+ if (r < 0) {
+ _ERR("Failed to initialize app-control info");
+ return r;
+ }
+
+ if (!__control_info_exists(id)) {
+ _ERR("Failed to find control info(%s)", id);
+ return APPCORE_BASE_ERROR_KEY_NOT_FOUND;
+ }
+
+ ctrl = calloc(1, sizeof(struct appcore_base_control_s));
+ if (!ctrl) {
+ _ERR("Out of memory");
+ return APPCORE_BASE_ERROR_OUT_OF_MEMORY;
+ }
+
+ ctrl->id = strdup(id);
+ if (!ctrl->id) {
+ _ERR("Failed to duplicate app-control ID");
+ free(ctrl);
+ return APPCORE_BASE_ERROR_OUT_OF_MEMORY;
+ }
+
+ ctrl->callback = callback;
+ ctrl->user_data = user_data;
+
+ __controls = g_list_append(__controls, ctrl);
+
+ *h = ctrl;
+
+ return APPCORE_BASE_ERROR_NONE;
+}
+
+EXPORT_API int appcore_base_control_remove(appcore_base_control_h h)
+{
+ struct appcore_base_control_s *ctrl;
+
+ if (!h || !g_list_find(__controls, h)) {
+ _ERR("Invalid parameter");
+ return APPCORE_BASE_ERROR_INVALID_PARAMETER;
+ }
+
+ ctrl = (struct appcore_base_control_s *)h;
+ __controls = g_list_remove(__controls, ctrl);
+
+ if (ctrl->id)
+ free(ctrl->id);
+ free(ctrl);
+
+ return APPCORE_BASE_ERROR_NONE;
+}