ENDIF(DEFINED MAX_CONCURRENT_DOWNLOADS)
ADD_DEFINITIONS(-DLIB_AGENT_PATH=\"${LIB_AGENT_PATH}\")
+ADD_DEFINITIONS(-DLIB_CACHE_AGENT_PATH=\"${LIB_CACHE_AGENT_PATH}\")
IF(BUILD_GCOV)
ADD_DEFINITIONS(-DBUILD_GCOV)
ENDIF(BUILD_GCOV)
# BUILD
ADD_SUBDIRECTORY(agent)
+ADD_SUBDIRECTORY(cache)
ADD_SUBDIRECTORY(provider-interface)
ADD_SUBDIRECTORY(provider)
# i18n
CONFIGURE_FILE(download-provider.pc.in download-provider.pc @ONLY)
CONFIGURE_FILE(download-agent.pc.in download-agent.pc @ONLY)
+CONFIGURE_FILE(cache-agent.pc.in cache-agent.pc @ONLY)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/download-provider.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/download-agent.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/cache-agent.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/systemd/download-provider.service DESTINATION /lib/systemd/system)
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/systemd/download-provider.socket DESTINATION /lib/systemd/system)
DA_LOGE("http_msg_response is NULL");
if (file_info->file_path)
info->saved_path = strdup(file_info->file_path);
+
+ if (file_info->pure_file_name) {
+ if (file_info->extension) {
+ char file_name[DA_MAX_FILE_NAME_LEN] = { 0, };
+ snprintf(file_name, DA_MAX_FILE_NAME_LEN, "%s.%s", file_info->pure_file_name, file_info->extension);
+ info->ori_file = strdup(file_name);
+ } else {
+ info->ori_file = strdup(file_info->pure_file_name);
+ }
+ }
+
if (http_info->etag_from_header)
info->etag = strdup(http_info->etag_from_header);
+ if (http_info->cache_control_from_header)
+ info->cache_control = strdup(http_info->cache_control_from_header);
+ if (http_info->last_modified_from_header)
+ info->last_modified = strdup(http_info->last_modified_from_header);
+
info->err = err;
da_info->cb_info.finished_cb(info,
req_info->user_req_data, req_info->user_client_data);
req_info->network_bonding = ext_data->network_bonding;
req_info->disable_verify_host = ext_data->disable_verify_host;
+ req_info->cache = ext_data->cache;
da_info->req_info = req_info;
}
NULL_CHECK_AND_FREE(http_info->location_url);
NULL_CHECK_AND_FREE(http_info->content_type_from_header);
NULL_CHECK_AND_FREE(http_info->etag_from_header);
+ NULL_CHECK_AND_FREE(http_info->cache_control_from_header);
+ NULL_CHECK_AND_FREE(http_info->last_modified_from_header);
NULL_CHECK_AND_FREE(http_info->file_name_from_header);
if (http_info->proxy_info) {
__destroy_proxy_info(http_info->proxy_info);
DA_LOGV("HTTP Status is %d - 204 server got the request, \
but no content to reply back, \
304 means not modified!", http_status);
- ret = DA_ERR_SERVER_RESPOND_BUT_SEND_NO_CONTENT;
+ if (!req_info->cache)
+ ret = DA_ERR_SERVER_RESPOND_BUT_SEND_NO_CONTENT;
break;
case 416: // Requested range not satisfiable
da_size_t size = 0;
char *mime_type = DA_NULL;
char *etag = DA_NULL;
+ char *cache_control = DA_NULL;
+ char *last_modified = DA_NULL;
char *file_name = DA_NULL;
NULL_CHECK_RET(da_info);
file_info->mime_type = strdup(mime_type);
http_msg_response_get_ETag(http_msg_response, &etag);
http_info->etag_from_header = etag;
+
+ http_msg_response_get_cache_control(http_msg_response, &cache_control);
+ http_info->cache_control_from_header = cache_control;
+ http_msg_response_get_last_modified(http_msg_response, &last_modified);
+ http_info->last_modified_from_header = last_modified;
+
http_msg_response_get_content_disposition(
http_msg_response, http_msg, DA_NULL, &file_name);
http_info->file_name_from_header = file_name;
}
}
-da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response,
- char **out_value)
+da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response, char **out_value)
{
da_bool_t b_ret = DA_FALSE;
http_header_t *header = NULL;
return DA_TRUE;
}
+da_bool_t http_msg_response_get_cache_control(http_msg_response_t *http_msg_response, char **out_value)
+{
+ da_bool_t b_ret = DA_FALSE;
+ http_header_t *header = NULL;
+
+ if (!out_value) {
+ DA_LOGE("[NULL CHECK] out_value");
+ return DA_FALSE;
+ }
+
+ b_ret = __get_http_header_for_field(http_msg_response, HTTP_FIELD_CACHE_CONTROL,
+ &header);
+ if (!b_ret) {
+ DA_LOGV("no cache-control");
+ return DA_FALSE;
+ }
+
+ if (header->value)
+ *out_value = strdup(header->value);
+ else
+ return DA_FALSE;
+
+ return DA_TRUE;
+}
+
+da_bool_t http_msg_response_get_last_modified(http_msg_response_t *http_msg_response, char **out_value)
+{
+ da_bool_t b_ret = DA_FALSE;
+ http_header_t *header = NULL;
+
+ if (!out_value) {
+ DA_LOGE("[NULL CHECK] out_value");
+ return DA_FALSE;
+ }
+
+ b_ret = __get_http_header_for_field(http_msg_response, HTTP_FIELD_LAST_MODIFIED,
+ &header);
+ if (!b_ret) {
+ DA_LOGV("no last-modified");
+ return DA_FALSE;
+ }
+ if (header->value)
+ *out_value = strdup(header->value);
+ else
+ return DA_FALSE;
+
+ return DA_TRUE;
+}
+
#ifdef _RAF_SUPPORT
da_bool_t http_msg_response_get_RAF_mode(http_msg_response_t *http_msg_response,
char **out_value)
DA_SECURE_LOGI("pkg_name[%s]", ext_data->pkg_name);
if (ext_data->network_bonding)
DA_LOGD("network bonding option[%d]", ext_data->network_bonding);
+ if (ext_data->cache)
+ DA_LOGD("cache option[%d]", ext_data->cache);
if (ext_data->user_req_data)
DA_LOGI("user_req_data[%p]", ext_data->user_req_data);
if (ext_data->user_client_data)
char *pkg_name;
int network_bonding;
int disable_verify_host;
+ int cache;
void *user_req_data;
void *user_client_data;
} req_info_t;
char *file_name_from_header;
da_size_t content_len_from_header;
char *etag_from_header;
+ char *cache_control_from_header;
+ char *last_modified_from_header;
int error_code; // for error value for http abort.
da_size_t total_size;
#ifdef _RAF_SUPPORT
#define HTTP_FIELD_LOCATION "Location"
#define HTTP_FIELD_DATA "Date"
#define HTTP_FIELD_ETAG "ETag"
+#define HTTP_FIELD_CACHE_CONTROL "cache-control"
+#define HTTP_FIELD_LAST_MODIFIED "last-modified"
#ifdef _RAF_SUPPORT
#define HTTP_FIELD_RAF_MODE "x-direct-write"
#endif
da_bool_t http_msg_response_get_content_length(http_msg_response_t *http_msg_response, da_size_t *out_length);
da_bool_t http_msg_response_get_content_disposition(http_msg_response_t *http_msg_response, http_msg_t *http_msg, char **out_disposition, char **out_file_name);
da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response, char **out_value);
+da_bool_t http_msg_response_get_cache_control(http_msg_response_t *http_msg_response, char **out_value);
+da_bool_t http_msg_response_get_last_modified(http_msg_response_t *http_msg_response, char **out_value);
da_bool_t http_msg_response_get_date(http_msg_response_t *http_msg_response, char **out_value);
da_bool_t http_msg_response_get_location(http_msg_response_t *http_msg_response, char **out_value);
da_bool_t http_msg_response_get_transfer_encoding(http_msg_response_t *http_msg_response, char **out_value);
typedef struct {
int download_id;
char *saved_path;
+ char *ori_file;
char *etag;
+ char *cache_control;
+ char *last_modified;
int err;
int http_status;
} finished_info_t;
const char *pkg_name;
int network_bonding;
int disable_verify_host;
+ int cache;
void *user_req_data;
void *user_client_data;
} req_data_t;
--- /dev/null
+# Package Information
+
+Name: @PROJECT_NAME@
+Description: @PACKAGE_DESCRIPTION@
+Version: @VERSION@
+Cflags: -I/usr/include/cacheagent
--- /dev/null
+PROJECT(cacheagent C)
+
+IF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+ SET(CMAKE_BUILD_TYPE "Release")
+ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "")
+MESSAGE("Build type: ${CMAKE_BUILD_TYPE}")
+
+SET(VERSION "0.1.0")
+
+FIND_PROGRAM(UNAME NAMES uname)
+EXEC_PROGRAM("${UNAME}" ARGS "-m" OUTPUT_VARIABLE "ARCH")
+IF("${ARCH}" MATCHES "^arm.*")
+ ADD_DEFINITIONS("-D_TARGET")
+ SET(CMAKE_C_FLAGS_RELEASE "-mabi=aapcs-linux -msoft-float -O2")
+ENDIF("${ARCH}" MATCHES "^arm.*")
+
+#CA Engine Include Directory
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/provider/include)
+
+SET(SRCS_PATH ".")
+SET(SRCS_CA
+ ${SRCS_PATH}/cache-agent-interface.c
+ ${SRCS_PATH}/cache-agent-storage.c
+ ${SRCS_PATH}/cache-agent-config.c
+)
+
+SET(HEADERS
+ include/cache-agent-defs.h
+ include/cache-agent-interface.h
+)
+
+INCLUDE(FindPkgConfig)
+
+pkg_check_modules(subpkgs REQUIRED
+ xdgmime
+ vconf
+ dlog
+ storage
+)
+
+IF(SUPPORT_LARGE_FILE)
+ MESSAGE("Large file:On")
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -D_FILE_OFFSET_BITS=64")
+ENDIF(SUPPORT_LARGE_FILE)
+
+FOREACH(flag ${subpkgs_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+IF (SUPPORT_OMA_DRM)
+ FOREACH(flag ${drmpkgs_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ ENDFOREACH(flag)
+ #This is request of DRM Team.
+ ADD_DEFINITIONS("-D_FILE_OFFSET_BITS=64")
+ENDIF (SUPPORT_OMA_DRM)
+
+IF (USE_SSL_THREAD_LOCKING)
+ MESSAGE("USE_SSL_THREAD_LOCKING(openssl<=1.0):On")
+ ADD_DEFINITIONS("-DUSE_SSL_THREAD_LOCKING")
+ENDIF (USE_SSL_THREAD_LOCKING)
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fpic -Wall -Werror -Werror-implicit-function-declaration")
+IF (BUILD_GTESTS)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fvisibility=default")
+ELSE (BUILD_GTESTS)
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fvisibility=hidden")
+ENDIF (BUILD_GTESTS)
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -fPIE")
+SET(CMAKE_C_FLAGS_RELEASE "-O2 -fPIE")
+
+ADD_DEFINITIONS("-D_ENABLE_DLOG")
+#This should be removed when release a target
+
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS_CA})
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${subpkgs_LDFLAGS} ${drmpkgs_LDFLAGS})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION})
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR} COMPONENT RuntimeLibraries)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/cache-agent-interface.h DESTINATION ${INCLUDE_INSTALL_DIR}/${PKG_NAME})
--- /dev/null
+/*
+ * Copyright (c) 2023 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 "include/cache-agent-config.h"
+
+static pthread_mutex_t mutex_ca_config_info = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_ca_config_capa_cb = PTHREAD_MUTEX_INITIALIZER;
+static cache_config_info_t config_info = { 0, 0, CA_NULL, 0, 0, CA_NULL };
+
+
+static GKeyFile *__ca_config_keyfile_load(const char *pathname)
+{
+ GKeyFile *keyfile = NULL;
+ GError *error = NULL;
+
+ keyfile = g_key_file_new();
+ if (g_key_file_load_from_file(keyfile, pathname, 0, &error) != TRUE) {
+ CA_LOGE("Unable to open %s, error %s", pathname, error->message);
+ g_error_free(error);
+
+ g_key_file_free(keyfile);
+ keyfile = NULL;
+ }
+
+ CA_LOGD("loaded keyfile %s", pathname);
+ return keyfile;
+}
+
+static int __ca_config_keyfile_save(GKeyFile *keyfile, char *pathname)
+{
+ int ret = CA_RESULT_OK;
+ gchar *data = NULL;
+ gsize length = 0;
+ GError *error = NULL;
+
+ data = g_key_file_to_data(keyfile, &length, NULL);
+
+ if (!g_file_set_contents(pathname, data, length, &error)) {
+ CA_LOGE("Failed to store configurations: %s", error->message);
+ g_error_free(error);
+ ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+ }
+
+ g_free(data);
+ return ret;
+}
+
+static int __load_configurations(void)
+{
+ int ret = CA_RESULT_OK;
+ GKeyFile *keyfile = NULL;
+ char *str;
+ int value;
+ GError *error = NULL;
+
+ config_info.max_cache_size = CA_MAX_CACHE_SIZE;
+ config_info.max_lifecycle = CA_MAX_LIFECYCLE;
+
+ keyfile = __ca_config_keyfile_load(CA_CONFIG_FILE_PATH);
+ if (keyfile == NULL) {
+ CA_LOGE("keyfile[%s] is NULL", CA_CONFIG_FILE_PATH);
+ ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+ goto done;
+ }
+
+ str = g_key_file_get_string(keyfile, CA_SETTING_GROUP_NAME, CA_STORAGE_PATH_KEY, &error);
+ if (!str || *str == '\0') {
+ CA_LOGE("Failed to get string value from config file: %s, use default value: %s",
+ error->message, CA_STORAGE_PATH);
+ free(str);
+ g_error_free(error);
+ error = NULL;
+
+ config_info.cache_storage_path = strdup(CA_STORAGE_PATH);
+ } else {
+ g_strchomp(str);
+ CA_LOGD("Cache path: %s", str);
+ config_info.cache_storage_path = str;
+ }
+
+ if (!config_info.cache_storage_path) {
+ ret = CA_ERR_FAIL_TO_MEMALLOC;
+ goto done;
+ }
+
+ value = g_key_file_get_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_CACHE_SIZE_KEY, &error);
+ if (error) {
+ CA_LOGE("Failed to get max cache size from config file: %s, use default value: %d",
+ error->message, CA_MAX_CACHE_SIZE);
+ g_error_free(error);
+ error = NULL;
+ } else {
+ CA_LOGD("Max cache size: %d", value);
+ config_info.max_cache_size = value;
+ }
+
+ value = g_key_file_get_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_LIFECYCLE_KEY, &error);
+ if (error) {
+ CA_LOGE("Failed to get max lifecycle from config file: %s, use default value: %d",
+ error->message, CA_MAX_LIFECYCLE);
+ g_error_free(error);
+ } else {
+ CA_LOGD("Max lifecycle: %d", value);
+ config_info.max_lifecycle = value;
+ }
+
+done:
+ if (keyfile)
+ g_key_file_free(keyfile);
+
+ return ret;
+}
+
+static int __save_configurations(void)
+{
+ int ret = CA_RESULT_OK;
+ GKeyFile *keyfile = NULL;
+
+ keyfile = __ca_config_keyfile_load(CA_CONFIG_FILE_PATH);
+ if (keyfile == NULL) {
+ CA_LOGE("keyfile[%s] is NULL, create new file", CA_CONFIG_FILE_PATH);
+ keyfile = g_key_file_new();
+ }
+
+ if (config_info.cache_storage_path)
+ g_key_file_set_string(keyfile, CA_SETTING_GROUP_NAME, CA_STORAGE_PATH_KEY, config_info.cache_storage_path);
+ else
+ g_key_file_set_string(keyfile, CA_SETTING_GROUP_NAME, CA_STORAGE_PATH_KEY, CA_STORAGE_PATH);
+
+ if (config_info.max_cache_size > 0)
+ g_key_file_set_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_CACHE_SIZE_KEY, config_info.max_cache_size);
+ else
+ g_key_file_set_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_CACHE_SIZE_KEY, CA_MAX_CACHE_SIZE);
+
+ if (config_info.max_lifecycle > 0)
+ g_key_file_set_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_LIFECYCLE_KEY, config_info.max_lifecycle);
+ else
+ g_key_file_set_integer(keyfile, CA_SETTING_GROUP_NAME, CA_MAX_LIFECYCLE_KEY, CA_MAX_LIFECYCLE);
+
+ __ca_config_keyfile_save(keyfile, CA_CONFIG_FILE_PATH);
+
+ if (keyfile)
+ g_key_file_free(keyfile);
+
+ return ret;
+}
+
+int ca_config_init()
+{
+ int ret = CA_RESULT_OK;
+
+ CA_LOGI("");
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ ret = __load_configurations();
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_deinit()
+{
+ int ret = CA_RESULT_OK;
+
+ CA_LOGI("");
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ free(config_info.cache_storage_path);
+ config_info.cache_storage_path = CA_NULL;
+ config_info.max_cache_size = 0;
+ config_info.max_lifecycle = 0;
+ config_info.cache_size = 0;
+ config_info.oldest_time = 0;
+ config_info.capa_cb = CA_NULL;
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_set_cache_path(const char *cache_path)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ if (config_info.cache_storage_path)
+ free(config_info.cache_storage_path);
+
+ config_info.cache_storage_path = strdup(cache_path);
+ if (!config_info.cache_storage_path)
+ ret = CA_ERR_FAIL_TO_MEMALLOC;
+ else
+ __save_configurations();
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+char *ca_config_get_cache_path(void)
+{
+ char *cache_path;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ if (config_info.cache_storage_path)
+ cache_path = strdup(config_info.cache_storage_path);
+ else
+ cache_path = strdup(CA_STORAGE_PATH);
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return cache_path;
+}
+
+int ca_config_set_max_cache_size(unsigned int max_cache_size)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ config_info.max_cache_size = max_cache_size;
+ __save_configurations();
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_get_max_cache_size(unsigned int *max_cache_size)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ *max_cache_size = (config_info.max_cache_size > 0) ? config_info.max_cache_size : CA_MAX_CACHE_SIZE;
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_set_max_lifecycle(unsigned int max_lifecycle)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ config_info.max_lifecycle = max_lifecycle;
+ __save_configurations();
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_get_max_lifecycle(unsigned int *max_lifecycle)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ *max_lifecycle = (config_info.max_lifecycle > 0) ? config_info.max_lifecycle : CA_MAX_LIFECYCLE;
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_set_oldest_time(ca_time_t oldest_time)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ config_info.oldest_time = oldest_time;
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_get_oldest_time(ca_time_t *oldest_time)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ *oldest_time = (config_info.oldest_time > 0) ? config_info.oldest_time : 0;
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_set_cache_size(ca_size_t cache_size)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ config_info.cache_size = cache_size;
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_get_cache_size(ca_size_t *cache_size)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_info));
+
+ *cache_size = (config_info.cache_size > 0) ? config_info.cache_size : 0;
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_info));
+ return ret;
+}
+
+int ca_config_set_capacity_cb(ca_capacity_event_cb capa_cb)
+{
+ int ret = CA_RESULT_OK;
+ CA_MUTEX_LOCK(&(mutex_ca_config_capa_cb));
+
+ config_info.capa_cb = capa_cb;
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_capa_cb));
+ return ret;
+}
+
+GSList *ca_config_call_capacity_cb(ca_capacity_event_type type)
+{
+ GSList *file_list = CA_NULL;
+
+ CA_MUTEX_LOCK(&(mutex_ca_config_capa_cb));
+
+ if (config_info.capa_cb)
+ file_list = config_info.capa_cb(type);
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_config_capa_cb));
+
+ return file_list;
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#include "include/cache-agent-interface.h"
+
+static pthread_mutex_t mutex_ca_block_request = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_ca_capa_handling = PTHREAD_MUTEX_INITIALIZER;
+static ca_bool_t block_request = CA_TRUE;
+static pthread_t g_capa_tid = 0;
+
+static void __set_block_request(ca_bool_t update)
+{
+ CA_MUTEX_LOCK(&mutex_ca_block_request);
+ block_request = update;
+ CA_MUTEX_UNLOCK(&mutex_ca_block_request);
+}
+
+static int __cancel_cache_download(int req_id, ca_bool_t update)
+{
+ int ret = CA_RESULT_OK;
+ ca_info_t *ca_info = CA_NULL;
+
+ CA_LOGV("cache_id[%d]", req_id);
+ ret = ca_get_info_with_ca_id(req_id, &ca_info);
+ if (ret != CA_RESULT_OK)
+ goto done;
+
+ ca_info->is_cb_update = update;
+
+ ret = ca_request_to_cancel_cache_download(ca_info);
+ if (ret != CA_RESULT_OK)
+ goto done;
+
+ CA_LOGI("Cache-download is canceled for cache id[%d]", req_id);
+
+done:
+ CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
+ return ret;
+}
+
+static int __suspend_cache_download(int req_id, ca_bool_t update)
+{
+ int ret = CA_RESULT_OK;
+ ca_info_t *ca_info = CA_NULL;
+
+ CA_LOGV("cache_id[%d]", req_id);
+ ret = ca_get_info_with_ca_id(req_id, &ca_info);
+ if (ret != CA_RESULT_OK)
+ goto done;
+
+ ca_info->is_cb_update = update;
+
+ ret = ca_request_to_suspend_cache_download(ca_info);
+ if (ret != CA_RESULT_OK)
+ goto done;
+
+ CA_LOGI("Cache-download is paused for cache id[%d]", req_id);
+
+done:
+ CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
+ return ret;
+}
+
+static int __check_capacity_exceeded(const char *file_path, ca_bool_t *is_allowed)
+{
+ int ret = CA_RESULT_OK;
+ ca_size_t cache_size = 0;
+ unsigned int max_cache_size = 0;
+ ca_size_t src_file_size = 0;
+ *is_allowed = CA_FALSE;
+
+ if (file_path)
+ ca_storage_get_file_size(file_path, &src_file_size);
+
+ if (src_file_size < 0) {
+ CA_LOGE("Unable to get the file size!");
+ return CA_ERR_FAIL_TO_ACCESS_FILE;
+ }
+
+ ca_config_get_cache_size(&cache_size);
+ ca_config_get_max_cache_size(&max_cache_size);
+ cache_size /= CA_MB_BYTE;
+ src_file_size /= CA_MB_BYTE;
+
+ if (cache_size + src_file_size >= max_cache_size * CA_CAPA_THRESHOLD_RATIO) {
+ CA_LOGD("Capacity is exceeded!, cache_size: %lld, src_file_size: %lld, max_cache_size * %f: %f",
+ cache_size, src_file_size, CA_CAPA_THRESHOLD_RATIO,
+ max_cache_size * CA_CAPA_THRESHOLD_RATIO);
+ ret = CA_ERR_DISK_FULL;
+ }
+
+ if (cache_size + src_file_size < max_cache_size)
+ *is_allowed = CA_TRUE;
+
+ return ret;
+}
+
+static ca_bool_t __check_lifecycle_exceeded(void)
+{
+ ca_time_t oldest_time;
+ unsigned int max_lifecycle;
+
+ ca_config_get_oldest_time(&oldest_time);
+ if (oldest_time == 0) {
+ ca_config_set_oldest_time(time(CA_NULL));
+ return CA_FALSE;
+ }
+
+ ca_config_get_max_lifecycle(&max_lifecycle);
+
+ if (difftime(time(CA_NULL), oldest_time) >= max_lifecycle * CA_CAPA_THRESHOLD_RATIO) {
+ CA_LOGD("Lifecycle expired!, oldest_time: %ld, max_lifecycle: %u, difftime: %f, max_lifecycle * %f: %f",
+ oldest_time, max_lifecycle, difftime(time(CA_NULL), oldest_time),
+ CA_CAPA_THRESHOLD_RATIO, max_lifecycle * CA_CAPA_THRESHOLD_RATIO);
+ return CA_TRUE;
+ }
+
+ return CA_FALSE;
+}
+
+static char **__convert_gslist_to_array(GSList *file_list, unsigned int *file_count)
+{
+ char **file_array = CA_NULL;
+ int index = 0;
+
+ *file_count = g_slist_length(file_list);
+ if (*file_count == 0)
+ return CA_NULL;
+
+ file_array = (char **)calloc(*file_count, sizeof(char *));
+ if (!file_array)
+ return CA_NULL;
+
+ for (GSList* node = file_list; node != NULL; node = node->next) {
+ file_array[index] = (char *)node->data;
+ index++;
+ }
+
+ return file_array;
+}
+
+static void *__thread_clear_exceeded_files(void *data)
+{
+ GSList *file_list = CA_NULL;
+ char **file_array = CA_NULL;
+ unsigned int file_count = 0;
+ ca_capacity_event_type type = (ca_capacity_event_type)data;
+
+ if (type != CA_CAPACITY_MAX_SIZE && type != CA_CAPACITY_LIFECYCLE)
+ goto done;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, CA_NULL);
+
+ __set_block_request(CA_TRUE);
+
+ file_list = ca_config_call_capacity_cb(type);
+ if (!file_list)
+ goto done;
+
+ file_array = __convert_gslist_to_array(file_list, &file_count);
+ if (!file_array) {
+ g_slist_free_full(file_list, g_free);
+ goto done;
+ }
+
+ ca_storage_remove_files((int)file_count, (const char **) file_array);
+
+ g_slist_free_full(file_list, g_free);
+ free(file_array);
+
+done:
+ __set_block_request(CA_FALSE);
+
+ CA_LOGI("=====EXIT thread : ca_id[%lu]=====", g_capa_tid);
+
+ CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
+ g_capa_tid = 0;
+ CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+
+ pthread_exit((void *)CA_NULL);
+ return CA_NULL;
+}
+
+static void __create_capacity_thread(ca_capacity_event_type type)
+{
+ pthread_attr_t thread_attr;
+
+ if (pthread_attr_init(&thread_attr) != 0)
+ return;
+
+ if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0)
+ return;
+
+ if (pthread_create(&g_capa_tid, &thread_attr, __thread_clear_exceeded_files, (void *)type) != 0) {
+ CA_LOGE("Fail to make thread capacity managing: type[%d]", type);
+ return;
+ }
+
+ CA_LOGI("Thread is created:thread id[%lu]", g_capa_tid);
+}
+
+int ca_init(ca_capacity_event_cb capa_cb)
+{
+ int ret = CA_RESULT_OK;
+ CA_LOGI("");
+
+ ret = ca_storage_init();
+ if (ret != CA_RESULT_OK)
+ return ret;
+
+ ret = ca_config_init();
+ if (ret != CA_RESULT_OK) {
+ ca_storage_deinit();
+ return ret;
+ }
+
+ ca_config_set_capacity_cb(capa_cb);
+ __set_block_request(CA_FALSE);
+
+ return CA_RESULT_OK;
+}
+
+int ca_deinit()
+{
+ int ret = CA_RESULT_OK;
+ CA_LOGI("====== ca_deint EXIT =====");
+
+ __set_block_request(CA_TRUE);
+
+ ca_storage_deinit();
+ ca_config_deinit();
+
+ return ret;
+}
+
+char *ca_store_file(const char *file_path)
+{
+ int ret = CA_RESULT_OK;
+ ca_bool_t is_allowed = CA_TRUE;
+
+ REQ_BLOCK_RET_PTR(&mutex_ca_block_request, block_request);
+
+ if (!file_path)
+ return CA_NULL;
+
+ CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
+
+ if (g_capa_tid != 0) {
+ CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+ return CA_NULL;
+ }
+
+ ret = __check_capacity_exceeded(file_path, &is_allowed);
+ if (ret == CA_ERR_DISK_FULL)
+ __create_capacity_thread(CA_CAPACITY_MAX_SIZE);
+
+ if (!is_allowed) {
+ CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+ return CA_NULL;
+ }
+
+ if (g_capa_tid == 0 && __check_lifecycle_exceeded())
+ __create_capacity_thread(CA_CAPACITY_LIFECYCLE);
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+
+ return ca_storage_store_file(file_path);
+}
+
+int ca_remove_files(int file_count, const char *cache_files[])
+{
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ if (file_count <= 0)
+ return CA_ERR_INVALID_ARGUMENT;
+
+ return ca_storage_remove_files(file_count, cache_files);
+}
+
+int ca_is_file_available(const char *cache_file)
+{
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ if (!cache_file)
+ return CA_ERR_INVALID_ARGUMENT;
+
+ return ca_storage_is_file_available(cache_file);
+}
+
+int ca_get_used_cache_size(ca_size_t *size)
+{
+ if (!size)
+ return CA_ERR_INVALID_ARGUMENT;
+
+ return ca_storage_get_used_cache_size(size);
+}
+
+int ca_start_cache_download(const char *cache_file,
+ ca_req_data_t *ext_data, ca_cb_t *ca_cb_data, int *cache_id)
+{
+ int ret = CA_RESULT_OK;
+ int ca_id = CA_INVALID_ID;
+ ca_info_t *ca_info = CA_NULL;
+
+ if (!cache_file || !ext_data || !ca_cb_data || !cache_id) {
+ CA_LOGE("Null Check: cache_file, ext_data, ca_cb_data, cache_id");
+ return CA_ERR_INVALID_ARGUMENT;
+ }
+
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ ret = ca_get_available_ca_id(&ca_id);
+ if (ret != CA_RESULT_OK) {
+ CA_LOGE("No available cache id");
+ return CA_ERR_ALREADY_MAX_COPY;
+ }
+
+ ca_info = ca_get_ca_info(ca_id);
+ if (ca_info == CA_NULL) {
+ CA_LOGE("No available cache info, ca_id[%d]", ca_id);
+ return CA_ERR_INVALID_STATE;
+ }
+
+ ca_info->ca_id = ca_id;
+
+ if (ext_data->install_path)
+ CA_SECURE_LOGI("install path[%s]", ext_data->install_path);
+ if (ext_data->file_name)
+ CA_SECURE_LOGI("file_name[%s]", ext_data->file_name);
+ if (ext_data->pkg_name)
+ CA_SECURE_LOGI("pkg_name[%s]", ext_data->pkg_name);
+ if (ext_data->user_req_data)
+ CA_LOGI("user_req_data[%p]", ext_data->user_req_data);
+ if (ext_data->user_client_data)
+ CA_LOGI("user_client_data[%p]", ext_data->user_client_data);
+
+ ret = ca_init_cache_download_data(ca_info, cache_file, ext_data);
+ if (ret != CA_RESULT_OK) {
+ ca_destroy_ca_info(ca_info->ca_id);
+ CA_LOGE("Unable to init cache download info");
+ return CA_ERR_INVALID_STATE;
+ }
+
+ ca_info->is_cb_update = CA_TRUE;
+ memcpy(&(ca_info->cb_info), ca_cb_data, sizeof(ca_cb_t));
+
+ ret = ca_storage_copy_file(ca_info);
+
+ if (ret != CA_RESULT_OK) {
+ if (ca_info->dst_fp && ca_info->dst_file) {
+ fclose(ca_info->dst_fp);
+ unlink(ca_info->dst_file);
+ ca_info->dst_fp = CA_NULL;
+ }
+
+ ca_destroy_ca_info(ca_info->ca_id);
+ }
+
+ *cache_id = ca_id;
+ CA_LOGI("Return:id[%d], ret[%d]", *cache_id, ret);
+
+ return ret;
+}
+
+int ca_pause_cache_download(int req_id)
+{
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+ return __suspend_cache_download(req_id, CA_TRUE);
+}
+
+int ca_resume_cache_download(int req_id)
+{
+ int ret = CA_RESULT_OK;
+ ca_info_t *ca_info = CA_NULL;
+
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ CA_LOGV("cache_id[%d]", req_id);
+ ret = ca_get_info_with_ca_id(req_id, &ca_info);
+ if (ret != CA_RESULT_OK)
+ goto done;
+
+ ca_info->is_cb_update = CA_TRUE;
+
+ ret = ca_request_to_resume_cache_download(ca_info);
+ if (ret != CA_RESULT_OK)
+ goto done;
+
+ CA_LOGI("Cache-download is resumed for cache id[%d]", req_id);
+
+done:
+ CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
+ return ret;
+}
+
+int ca_cancel_cache_download(int req_id)
+{
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+ return __cancel_cache_download(req_id, CA_TRUE);
+}
+
+int ca_pause_cache_download_without_update(int req_id)
+{
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+ return __suspend_cache_download(req_id, CA_FALSE);
+}
+
+int ca_cancel_cache_download_without_update(int req_id)
+{
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+ return __cancel_cache_download(req_id, CA_FALSE);
+}
+
+int ca_is_alive_cache_download(int req_id)
+{
+ int ret = CA_RESULT_OK;
+ ca_info_t *ca_info = CA_NULL;
+
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ ca_info = ca_get_ca_info(req_id);
+ if (ca_info == CA_NULL) {
+ CA_LOGE("No available cache info, ca_id[%d]", req_id);
+ ret = CA_ERR_INVALID_ARGUMENT;
+ goto done;
+ }
+
+ if (CA_RESULT_OK != ca_check_ca_id(ca_info, req_id)) {
+ CA_LOGE("Invalid ca_id[%d]", ca_info->ca_id);
+ ret = CA_ERR_INVALID_ARGUMENT;
+ }
+
+done:
+ CA_LOGI("Return:id[%d], ret[%d]", req_id, ret);
+ return ret;
+}
+
+int ca_get_max_cache_size(unsigned int *size)
+{
+ return ca_config_get_max_cache_size(size);
+}
+
+int ca_set_max_cache_size(unsigned int size)
+{
+ int ret = CA_RESULT_OK;
+ ca_bool_t is_allowed;
+
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ ret = ca_config_set_max_cache_size(size);
+ if (ret != CA_RESULT_OK)
+ return ret;
+
+ CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
+
+ if (g_capa_tid != 0) {
+ CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+ return ret;
+ }
+
+ ret = __check_capacity_exceeded(CA_NULL, &is_allowed);
+ if (ret == CA_ERR_DISK_FULL)
+ __create_capacity_thread(CA_CAPACITY_MAX_SIZE);
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+
+ return CA_RESULT_OK;
+}
+
+int ca_get_cache_path(char **path)
+{
+ int ret = CA_RESULT_OK;
+
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ *path = (char *)ca_config_get_cache_path();
+ if (!*path)
+ ret = CA_ERR_INVALID_STATE;
+
+ return ret;
+}
+
+int ca_set_cache_path(const char *path)
+{
+ int ret = CA_RESULT_OK;
+
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ __set_block_request(CA_TRUE);
+
+ ca_cancel_all_cache_operations();
+
+ ret = ca_clear_all_cache_files();
+ if (ret != CA_RESULT_OK) {
+ __set_block_request(CA_FALSE);
+ return ret;
+ }
+
+ ca_config_set_cache_path(path);
+
+ __set_block_request(CA_FALSE);
+
+ return ret;
+}
+
+int ca_get_cache_lifecycle(unsigned int *time)
+{
+ return ca_config_get_max_lifecycle(time);
+}
+
+int ca_set_cache_lifecycle(unsigned int time)
+{
+ int ret = CA_RESULT_OK;
+
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ ret = ca_config_set_max_lifecycle(time);
+ if (ret != CA_RESULT_OK)
+ return ret;
+
+ CA_MUTEX_LOCK(&(mutex_ca_capa_handling));
+
+ if (g_capa_tid == 0 && __check_lifecycle_exceeded())
+ __create_capacity_thread(CA_CAPACITY_LIFECYCLE);
+
+ CA_MUTEX_UNLOCK(&(mutex_ca_capa_handling));
+
+ return ret;
+}
+
+int ca_clear_all_files(void)
+{
+ int ret = CA_RESULT_OK;
+
+ REQ_BLOCK_RET(&mutex_ca_block_request, block_request);
+
+ __set_block_request(CA_TRUE);
+
+ ca_cancel_all_cache_operations();
+ ret = ca_clear_all_cache_files();
+
+ ca_config_set_cache_size(0);
+ ca_config_set_oldest_time(time(CA_NULL));
+
+ __set_block_request(CA_FALSE);
+
+ return ret;
+}
+
+int ca_set_oldest_file_time(ca_time_t oldest_time)
+{
+ return ca_config_set_oldest_time(oldest_time);
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#include <pthread.h>
+#include <ftw.h>
+#include "include/cache-agent-interface.h"
+
+static pthread_mutex_t mutex_ca_info_list = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t mutex_ca_file_io = PTHREAD_MUTEX_INITIALIZER;
+static ca_info_t *ca_info_list[CA_MAX_ID];
+static ca_size_t cache_dir_size;
+
+static char *__get_file_name(const char *file_path)
+{
+ char *file_name = CA_NULL;
+
+ file_name = strrchr(file_path, '/');
+ if (file_name)
+ ++file_name;
+ else
+ return strdup(file_path);
+
+ return strdup(file_name);
+}
+
+static void __ca_progress_cb(int cache_id,
+ unsigned long long copied_size, void *user_req_data, void *user_client_data)
+{
+ CA_LOGI("__ca_progress_cb id: %d, copied size: %llu", cache_id, copied_size);
+}
+
+static int __sum(const char *fpath, const struct stat *sb, int typeflag) {
+ cache_dir_size += sb->st_size;
+ return 0;
+}
+
+static void __destroy_ca_req_info(ca_req_info_t *req_info)
+{
+ if (req_info) {
+ if (req_info->install_path)
+ free(req_info->install_path);
+ if (req_info->file_name)
+ free(req_info->file_name);
+ if (req_info->pkg_name)
+ free(req_info->pkg_name);
+
+ req_info->user_req_data = CA_NULL;
+ req_info->user_client_data = CA_NULL;
+ }
+}
+
+static void __init_ca_info(int id)
+{
+ ca_info_t *ca_info = CA_NULL;
+ ca_req_info_t *req_info = CA_NULL;
+
+ ca_info = (ca_info_t *)calloc(1, sizeof(ca_info_t));
+ if (!ca_info) {
+ CA_LOGE("Fail to calloc. id[%d]", id);
+ ca_info_list[id] = CA_NULL;
+ return;
+ }
+
+ req_info = (ca_req_info_t *)calloc(1, sizeof(ca_req_info_t));
+ if (!req_info) {
+ CA_LOGE("Fail to calloc. id[%d]", id);
+ free(ca_info);
+ return;
+ }
+
+ CA_MUTEX_INIT(&(ca_info->mutex_state), CA_NULL);
+ CA_COND_INIT(&(ca_info->cond), CA_NULL);
+
+ ca_info->state = CA_STATE_NONE;
+ ca_info->ca_id = CA_INVALID_ID;
+ ca_info->req_info = req_info;
+ ca_info_list[id] = ca_info;
+}
+
+static int __remove_func(const char *filename,
+ const struct stat *statptr, int fileflags)
+{
+ int ret = 0;
+ char *cache_path = ca_config_get_cache_path();
+
+ if (strlen(filename) == strlen(cache_path)) {
+ free(cache_path);
+ return ret;
+ }
+
+ ret = unlink(filename);
+ if (ret < 0) {
+ char err_buf[CA_MAX_ERROR_STR_LEN] = { 0, };
+
+ strerror_r(errno, err_buf, CA_MAX_ERROR_STR_LEN);
+ CA_LOGE("Unable to remove the file: %s, error [%d/%s]", filename, errno, err_buf);
+ }
+
+ free(cache_path);
+ return ret;
+}
+
+void ca_destroy_ca_info(int id)
+{
+ ca_info_t *ca_info = CA_NULL;
+
+ CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+ ca_info = ca_info_list[id];
+ if (ca_info) {
+ if (ca_info->dst_fp)
+ fclose(ca_info->dst_fp);
+
+ if (ca_info->src_file)
+ free(ca_info->src_file);
+
+ if (ca_info->dst_file)
+ free(ca_info->dst_file);
+
+ if (ca_info->file_name)
+ free(ca_info->file_name);
+
+ if (ca_info->req_info) {
+ __destroy_ca_req_info(ca_info->req_info);
+ free(ca_info->req_info);
+ }
+
+ CA_MUTEX_DESTROY(&(ca_info->mutex_state));
+ CA_COND_DESTROY(&(ca_info->cond));
+
+ free(ca_info);
+ ca_info_list[id] = CA_NULL;
+ }
+
+ CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+}
+
+ca_info_t *ca_get_ca_info(int ca_id)
+{
+ CA_MUTEX_LOCK(&mutex_ca_info_list);
+ ca_info_t *ca_info = ca_info_list[ca_id];
+ CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+
+ return ca_info;
+}
+
+int ca_check_ca_id(ca_info_t *ca_info, int ca_id)
+{
+ int ret = CA_ERR_INVALID_ARGUMENT;
+
+ CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+ if (ca_info && ca_info->ca_id == ca_id)
+ ret = CA_RESULT_OK;
+
+ CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+
+ return ret;
+}
+
+int ca_get_info_with_ca_id(int id, ca_info_t **out_info)
+{
+ int ret = CA_ERR_INVALID_ARGUMENT;
+
+ CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+ for (int i = 0; i < CA_MAX_ID; i++) {
+ if (CA_NULL != ca_info_list[i] && ca_info_list[i]->ca_id == id) {
+ *out_info = ca_info_list[i];
+ ret = CA_RESULT_OK;
+ break;
+ }
+ }
+
+ CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+
+ return ret;
+}
+
+void ca_cancel_all_cache_operations(void)
+{
+ CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+ for (int i = 0; i < CA_MAX_ID; i++) {
+ if (CA_NULL != ca_info_list[i])
+ ca_request_to_cancel_cache_download(ca_info_list[i]);
+ }
+
+ CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+}
+
+int ca_clear_all_cache_files(void)
+{
+ int ret = CA_RESULT_OK;
+ char *cache_path = ca_config_get_cache_path();
+
+ if (ftw(cache_path, __remove_func, 1) == -1)
+ ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+
+ free(cache_path);
+ return ret;
+}
+
+int ca_request_to_cancel_cache_download(ca_info_t *ca_info)
+{
+ int ret = CA_RESULT_OK;
+
+ CA_MUTEX_LOCK(&(ca_info->mutex_state));
+ CA_LOGD("state [%d]", ca_info->state);
+
+ switch (ca_info->state) {
+ case CA_STATE_PAUSED:
+ CA_COND_SIGNAL(&(ca_info->cond));
+ ca_info->state = CA_STATE_CANCELED;
+ break;
+ case CA_STATE_COPYING:
+ ca_info->state = CA_STATE_CANCELED;
+ break;
+ case CA_STATE_CANCELED:
+ ret = CA_ERR_ALREADY_CANCELED;
+ break;
+ case CA_STATE_NONE:
+ case CA_STATE_COMPLETED:
+ case CA_STATE_FAILED:
+ ret = CA_ERR_INVALID_STATE;
+ break;
+ default:
+ ret = CA_ERR_INVALID_STATE;
+ break;
+ }
+
+ CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+ return ret;
+}
+
+int ca_request_to_suspend_cache_download(ca_info_t *ca_info)
+{
+ int ret = CA_RESULT_OK;
+
+ CA_MUTEX_LOCK(&(ca_info->mutex_state));
+ CA_LOGD("state [%d]", ca_info->state);
+
+ switch (ca_info->state) {
+ case CA_STATE_PAUSED:
+ ret = CA_ERR_ALREADY_SUSPENDED;
+ break;
+ case CA_STATE_COPYING:
+ ca_info->state = CA_STATE_PAUSED;
+ break;
+ case CA_STATE_NONE:
+ case CA_STATE_COMPLETED:
+ case CA_STATE_CANCELED:
+ case CA_STATE_FAILED:
+ ret = CA_ERR_INVALID_STATE;
+ break;
+ default:
+ ret = CA_ERR_INVALID_STATE;
+ break;
+ }
+
+ CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+ return ret;
+}
+
+int ca_request_to_resume_cache_download(ca_info_t *ca_info)
+{
+ int ret = CA_RESULT_OK;
+
+ CA_MUTEX_LOCK(&(ca_info->mutex_state));
+ CA_LOGD("state [%d]", ca_info->state);
+
+ switch (ca_info->state) {
+ case CA_STATE_PAUSED:
+ CA_COND_SIGNAL(&(ca_info->cond));
+ ca_info->state = CA_STATE_COPYING;
+ break;
+ case CA_STATE_COPYING:
+ case CA_STATE_NONE:
+ case CA_STATE_COMPLETED:
+ case CA_STATE_CANCELED:
+ case CA_STATE_FAILED:
+ ret = CA_ERR_INVALID_STATE;
+ break;
+ default:
+ ret = CA_ERR_INVALID_STATE;
+ break;
+ }
+
+ CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+ return ret;
+}
+
+int ca_get_available_ca_id(int *available_id)
+{
+ int ret = CA_ERR_ALREADY_MAX_COPY;
+ int i = 0;
+
+ CA_MUTEX_LOCK(&mutex_ca_info_list);
+
+ for (i = 0; i < CA_MAX_ID; i++) {
+ if (ca_info_list[i] == CA_NULL) {
+ *available_id = i;
+ CA_LOGV("available cache id[%d]", *available_id);
+
+ __init_ca_info(i);
+ ret = CA_RESULT_OK;
+
+ break;
+ }
+ }
+
+ CA_MUTEX_UNLOCK(&mutex_ca_info_list);
+
+ return ret;
+}
+
+void ca_storage_get_file_size(const char *file_path, ca_size_t *file_size)
+{
+ struct stat dir_state;
+ int stat_ret;
+
+ *file_size = -1;
+
+ if (!file_path) {
+ CA_LOGE("NULL CHECK!: file path");
+ return;
+ }
+
+ CA_MUTEX_LOCK(&mutex_ca_file_io);
+ stat_ret = stat(file_path, &dir_state);
+ CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+ if (stat_ret != 0)
+ return;
+
+ if (dir_state.st_mode & S_IFREG) {
+ CA_LOGV("file size = %lu", dir_state.st_size);
+ *file_size = dir_state.st_size;
+ }
+}
+
+static ca_file_state_e __get_file_state(const char *file_path)
+{
+ struct stat dir_state;
+ int stat_ret;
+
+ if (file_path == CA_NULL) {
+ CA_LOGE("file path is CA_NULL");
+ return CA_NOT_EXIST_FILE;
+ }
+
+ CA_MUTEX_LOCK(&mutex_ca_file_io);
+ stat_ret = stat(file_path, &dir_state);
+ CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+ if (stat_ret == 0) {
+ if (dir_state.st_mode & S_IFREG)
+ return CA_REGULAR_FILE;
+
+ return CA_OTHER_FILE;
+ }
+ return CA_NOT_EXIST_FILE;
+}
+
+static int __extract_file_name_and_extension(const char *in_file_name, char **pure_file_name, char **extension)
+{
+ char *file_name = CA_NULL;
+ char *tmp_ptr = CA_NULL;
+ char temp_file[CA_MAX_FILE_NAME_LEN + 1] = {0,};
+ char tmp_ext[CA_MAX_FILE_NAME_LEN] = {0,};
+ int len = 0;
+
+ if (!in_file_name || !pure_file_name)
+ return CA_ERR_INVALID_ARGUMENT;
+
+ file_name = (char *)in_file_name;
+ tmp_ptr = strrchr(file_name, '.');
+
+ if (!tmp_ptr || tmp_ptr == file_name)
+ tmp_ptr = CA_NULL;
+ else
+ tmp_ptr++;
+
+ if (tmp_ptr && extension) {
+ strncpy((char*)tmp_ext, tmp_ptr, sizeof(tmp_ext) - 1);
+ *extension = strdup((const char *)tmp_ext);
+ CA_LOGD("extension [%s]", *extension);
+ }
+
+ if (tmp_ptr)
+ len = tmp_ptr - file_name - 1;
+ else
+ len = strlen(file_name);
+
+ if (len >= CA_MAX_FILE_NAME_LEN) {
+ strncpy((char*)temp_file, file_name, CA_MAX_FILE_NAME_LEN);
+ temp_file[CA_MAX_FILE_NAME_LEN] = '\0';
+ } else {
+ strncpy((char*) temp_file, file_name, len);
+ temp_file[len] = '\0';
+ }
+
+ if (strlen(temp_file) >= 1)
+ *pure_file_name = strdup((const char*)temp_file);
+
+ CA_LOGD("pure file name [%s]", *pure_file_name ? *pure_file_name : "NoName");
+ return CA_RESULT_OK;
+}
+
+static char *__generate_cache_file_name(char *dst_path, char *file_name)
+{
+ int i;
+ char *cache_path;
+
+ CA_MUTEX_LOCK(&mutex_ca_file_io);
+
+ cache_path = ca_config_get_cache_path();
+
+ for (i = 1; i <= CA_MAX_DUP_FILE_COUNT; i++) {
+ if (access(dst_path, F_OK) != 0)
+ break;
+
+ snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s.%d", cache_path, file_name, i);
+ }
+
+ CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+ free(cache_path);
+
+ if (i > CA_MAX_DUP_FILE_COUNT)
+ return CA_NULL;
+
+ return __get_file_name(dst_path);
+}
+
+static char *__generate_dst_file_name(char *dst_path, const char *dir_path, const char *file_name)
+{
+ int i;
+ char *pure_file_name = CA_NULL;
+ char *extension = CA_NULL;
+
+ __extract_file_name_and_extension(file_name, &pure_file_name, &extension);
+ if (!pure_file_name)
+ return CA_NULL;
+
+ CA_MUTEX_LOCK(&mutex_ca_file_io);
+
+ for (i = 1; i <= CA_MAX_DUP_FILE_COUNT; i++) {
+ if (access(dst_path, F_OK) != 0)
+ break;
+
+ if (extension)
+ snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s(%d).%s", dir_path, pure_file_name, i, extension);
+ else
+ snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s(%d)", dir_path, pure_file_name, i);
+ }
+
+ CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+ free(pure_file_name);
+ free(extension);
+
+ if (i > CA_MAX_DUP_FILE_COUNT)
+ return CA_NULL;
+
+ return __get_file_name(dst_path);
+}
+
+static int __is_realpath(const char *file_path)
+{
+ char *resolved_path = CA_NULL;
+
+ if (!file_path)
+ return CA_ERR_INVALID_ARGUMENT;
+
+ CA_MUTEX_LOCK(&mutex_ca_file_io);
+ resolved_path = realpath(file_path, NULL);
+ CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+ if (resolved_path) {
+ /* Check if actual_file_path is symbolic file or not */
+ if (strcmp(resolved_path, file_path) != 0) {
+ free(resolved_path);
+ return CA_ERR_INVALID_ARGUMENT;
+ }
+ } else if (errno != ENOENT) {
+ return CA_ERR_INVALID_ARGUMENT;
+ }
+
+ free(resolved_path);
+ return CA_RESULT_OK;
+}
+
+static void *__open_dst_file(const char *dst_file)
+{
+ void *fp = CA_NULL;
+
+ if (!dst_file) {
+ CA_LOGE("NULL CHECK!: dst_file");
+ return CA_NULL;
+ }
+
+ CA_MUTEX_LOCK(&mutex_ca_file_io);
+ fp = fopen(dst_file, "wb");
+ CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+ if (fp == CA_NULL) {
+ char err_buf[CA_MAX_ERROR_STR_LEN] = { 0, };
+
+ strerror_r(errno, err_buf, CA_MAX_ERROR_STR_LEN);
+ CA_LOGE("File open failed [%d/%s]", errno, err_buf);
+
+ if (errno == ENOSPC)
+ CA_LOGE("Disk full!");
+ else
+ CA_LOGE("Fail to access file!");
+ }
+
+ return fp;
+}
+
+static void *__open_src_file(const char *src_file)
+{
+ void *fp = CA_NULL;
+
+ if (!src_file) {
+ CA_LOGE("NULL CHECK!: src_file");
+ return CA_NULL;
+ }
+
+ CA_MUTEX_LOCK(&mutex_ca_file_io);
+ fp = fopen(src_file, "rb");
+ CA_MUTEX_UNLOCK(&mutex_ca_file_io);
+
+ if (fp == CA_NULL) {
+ char err_buf[CA_MAX_ERROR_STR_LEN] = { 0, };
+
+ strerror_r(errno, err_buf, CA_MAX_ERROR_STR_LEN);
+ CA_LOGE("File open failed [%d/%s]", errno, err_buf);
+ }
+
+ return fp;
+}
+
+static void __call_started_cb(int error, ca_info_t *ca_info, ca_size_t src_file_size)
+{
+ cache_info_t cache_info = { 0, CA_RESULT_OK, 0, CA_NULL, CA_NULL };
+
+ if (!ca_info)
+ return;
+
+ cache_info.cache_id = ca_info->ca_id;
+ cache_info.error = error;
+ cache_info.file_size = src_file_size;
+ cache_info.content_name = ca_info->file_name;
+ cache_info.saved_path = ca_info->dst_file;
+
+ if (ca_info->cb_info.cache_info_cb)
+ ca_info->cb_info.cache_info_cb(&cache_info,
+ ca_info->req_info->user_req_data, ca_info->req_info->user_client_data);
+}
+
+
+static void __call_finidhed_cb(int error, int http_status, ca_info_t *ca_info, ca_size_t src_file_size)
+{
+ ca_finished_info_t finished_info = { 0, CA_RESULT_OK, 0, CA_NULL, 0};
+
+ if (!ca_info || !ca_info->is_cb_update)
+ return;
+
+ finished_info.cache_id = ca_info->ca_id;
+ finished_info.error = error;
+ finished_info.file_size = src_file_size;
+
+ if (error == CA_RESULT_OK)
+ finished_info.saved_path = ca_info->dst_file;
+
+ finished_info.http_status = http_status;
+
+ if (ca_info->cb_info.finished_cb)
+ ca_info->cb_info.finished_cb(&finished_info,
+ ca_info->req_info->user_req_data, ca_info->req_info->user_client_data);
+}
+
+static void __call_paused_cb(ca_info_t *ca_info)
+{
+ if (!ca_info || !ca_info->is_cb_update)
+ return;
+
+ if (ca_info->cb_info.paused_cb)
+ ca_info->cb_info.paused_cb(ca_info->ca_id,
+ ca_info->req_info->user_req_data, ca_info->req_info->user_client_data);
+}
+
+static void __set_cache_state(int state, ca_info_t *ca_info)
+{
+ CA_MUTEX_LOCK(&(ca_info->mutex_state));
+
+ ca_info->state = state;
+
+ CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+}
+
+static ca_bool_t __check_cancel_state(ca_info_t *ca_info)
+{
+ ca_bool_t ret = CA_TRUE;
+
+ CA_MUTEX_LOCK(&(ca_info->mutex_state));
+ if (ca_info->state != CA_STATE_CANCELED)
+ ret = CA_FALSE;
+ CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+
+ return ret;
+}
+
+static void __handle_suspend_state(ca_info_t *ca_info)
+{
+ CA_MUTEX_LOCK(&(ca_info->mutex_state));
+ if (ca_info->state != CA_STATE_PAUSED) {
+ CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+ return;
+ }
+ CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+
+ __call_paused_cb(ca_info);
+
+ CA_MUTEX_LOCK(&(ca_info->mutex_state));
+ CA_COND_WAIT(&(ca_info->cond), &(ca_info->mutex_state));
+ CA_MUTEX_UNLOCK(&(ca_info->mutex_state));
+}
+
+static int __copy_content(ca_info_t *ca_info)
+{
+ int ret = CA_RESULT_OK;
+ FILE *src_fp = CA_NULL;
+ FILE *dst_fp = CA_NULL;
+ void *file_buff = CA_NULL;
+ size_t r_size = 0;
+ size_t w_size = 0;
+ ca_size_t src_file_size = 0;
+ ca_size_t dst_file_size = 0;
+ struct timespec curr_time = {0};
+ __time_t prev_time = 0;
+
+ if (!ca_info || !ca_info->src_file || !ca_info->dst_file) {
+ CA_LOGE("NULL CHECK!: ca_info, src_file, dst_file");
+ ret = CA_ERR_INVALID_ARGUMENT;
+ goto done;
+ }
+
+ CA_LOGI("=====START thread : ca_id[%d]=====", ca_info->ca_id);
+
+ file_buff = calloc(CA_MAX_FILE_BUFFER_SIZE, sizeof(char));
+ if (!file_buff) {
+ CA_LOGE("Out of memory");
+ ret = CA_ERR_FAIL_TO_MEMALLOC;
+ goto done;
+ }
+
+ ca_storage_get_file_size(ca_info->src_file, &src_file_size);
+ if (src_file_size < 0) {
+ CA_LOGE("Unable to get dst file size!");
+ ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+ goto done;
+ }
+
+ if (ca_info->dst_fp) {
+ dst_fp = ca_info->dst_fp;
+ } else {
+ dst_fp = __open_dst_file(ca_info->dst_file);
+ if (!dst_fp) {
+ CA_LOGE("Unable to open dst file!");
+ ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+ goto done;
+ }
+ ca_info->dst_fp = dst_fp;
+ }
+
+ src_fp = (FILE *)__open_src_file(ca_info->src_file);
+ if (!src_fp) {
+ CA_LOGE("Unable to open src file!");
+ ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+ goto done;
+ }
+
+ if (ca_info->is_cb_update)
+ __call_started_cb(CA_RESULT_OK, ca_info, src_file_size);
+
+ while (0 < (r_size = fread(file_buff, 1, CA_MAX_FILE_BUFFER_SIZE, src_fp)) ) {
+ w_size = fwrite(file_buff, 1, r_size, dst_fp);
+ if (w_size != r_size)
+ break;
+
+ dst_file_size += w_size;
+
+ __handle_suspend_state(ca_info);
+
+ if (__check_cancel_state(ca_info)) {
+ dst_file_size = src_file_size;
+ ret = CA_RESULT_USER_CANCELED;
+ break;
+ }
+
+ if (!ca_info->is_cb_update)
+ continue;
+
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_time);
+ if (curr_time.tv_sec - prev_time >= 1) {
+ if (ca_info->cb_info.progress_cb)
+ ca_info->cb_info.progress_cb(ca_info->ca_id, dst_file_size,
+ ca_info->req_info->user_req_data, ca_info->req_info->user_client_data);
+
+ prev_time = curr_time.tv_sec;
+ }
+ }
+
+ if (dst_file_size != src_file_size) {
+ ret = CA_ERR_FAIL_TO_ACCESS_FILE;
+ CA_LOGE("The file sizes of the src and dst do not match!");
+ }
+
+ fclose(src_fp);
+ fclose(dst_fp);
+ ca_info->dst_fp = CA_NULL;
+
+done:
+ if (file_buff)
+ free(file_buff);
+
+ if (ret == CA_RESULT_OK)
+ __call_finidhed_cb(CA_RESULT_OK, 200, ca_info, src_file_size);
+ else
+ __call_finidhed_cb(ret, 0, ca_info, src_file_size);
+
+ if (ca_info && ca_info->is_cache_adding && ret == CA_RESULT_OK) {
+ ca_size_t cache_size;
+
+ ca_config_get_cache_size(&cache_size);
+ cache_size += dst_file_size;
+ ca_config_set_cache_size(cache_size);
+ }
+
+ return ret;
+}
+
+static void *__thread_start_copy(void *data)
+{
+ ca_info_t *ca_info = CA_NULL;
+ int ca_id = CA_INVALID_ID;
+ int ret = CA_RESULT_OK;
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, CA_NULL);
+
+ ca_info = (ca_info_t *)data;
+ if (!ca_info) {
+ CA_LOGE("[NULL Check] ca_info");
+ goto done;
+ }
+
+ __set_cache_state(CA_STATE_COPYING, ca_info);
+
+ ca_id = ca_info->ca_id;
+
+ ret = __copy_content(ca_info);
+ if (ret != CA_RESULT_OK) {
+ CA_LOGE("Failed to copy file");
+
+ if (ca_info->dst_file)
+ unlink(ca_info->dst_file);
+ }
+
+ ca_destroy_ca_info(ca_id);
+done:
+ CA_LOGI("=====EXIT thread : ca_id[%d]=====", ca_id);
+ pthread_exit((void *)CA_NULL);
+
+ return CA_NULL;
+}
+
+static void __reduce_cache_size(ca_size_t total_file_size)
+{
+ ca_size_t cache_size;
+
+ if (total_file_size <= 0)
+ return;
+
+ ca_config_get_cache_size(&cache_size);
+ cache_size -= total_file_size;
+ ca_config_set_cache_size(cache_size);
+}
+
+int ca_storage_init()
+{
+ ca_size_t size;
+ CA_LOGI("");
+
+ if (CA_RESULT_OK != ca_storage_get_used_cache_size(&size))
+ return CA_ERR_FAIL_TO_ACCESS_FILE;
+
+ return ca_config_set_cache_size(size);
+}
+
+int ca_storage_deinit()
+{
+ int ret = CA_RESULT_OK;
+ CA_LOGI("");
+ ca_cancel_all_cache_operations();
+
+ return ret;
+}
+
+char *ca_storage_store_file(const char *file_path)
+{
+ int ret = CA_RESULT_OK;
+ int ca_id = CA_INVALID_ID;
+ char dst_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+ char *file_name = CA_NULL;
+ char *dst_file_name = CA_NULL;
+ ca_info_t *ca_info = CA_NULL;
+ void *dst_fp = CA_NULL;
+ char *cache_path = ca_config_get_cache_path();
+
+ if (__get_file_state(file_path) != CA_REGULAR_FILE) {
+ CA_LOGE("Not a regular file!");
+ free(cache_path);
+ return CA_NULL;
+ }
+
+ if (__is_realpath(file_path) != CA_RESULT_OK) {
+ CA_LOGE("Not a real file!");
+ free(cache_path);
+ return CA_NULL;
+ }
+
+ ret = ca_get_available_ca_id(&ca_id);
+ if (ret != CA_RESULT_OK) {
+ CA_LOGD("No available cache id");
+ free(cache_path);
+ return CA_NULL;
+ }
+
+ ca_cb_t ca_cb = {
+ CA_NULL,
+ __ca_progress_cb,
+ CA_NULL,
+ CA_NULL
+ };
+
+ ca_info = ca_info_list[ca_id];
+ ca_info->ca_id = ca_id;
+
+ file_name = __get_file_name(file_path);
+ snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s", cache_path, file_name);
+ dst_file_name = __generate_cache_file_name(dst_path, file_name);
+ free(cache_path);
+ free(file_name);
+
+ if (dst_file_name == CA_NULL) {
+ CA_LOGE("Fail to generate dst file name!");
+ ca_destroy_ca_info(ca_info->ca_id);
+ return CA_NULL;
+ }
+
+ dst_fp = __open_dst_file(dst_path);
+ if (dst_fp == CA_NULL) {
+ CA_LOGE("Unable to open dst file!");
+ free(dst_file_name);
+ ca_destroy_ca_info(ca_info->ca_id);
+ return CA_NULL;
+ }
+
+ ca_info->dst_fp = (FILE *)dst_fp;
+ ca_info->src_file = strdup(file_path);
+ ca_info->dst_file = strdup(dst_path);
+ ca_info->file_name = strdup(dst_file_name);
+
+ ca_info->is_cb_update = CA_TRUE;
+ ca_info->is_cache_adding = CA_TRUE;
+ memcpy(&(ca_info->cb_info), &ca_cb, sizeof(ca_cb_t));
+
+ ret = ca_storage_copy_file(ca_info);
+
+ if (ret != CA_RESULT_OK) {
+ free(dst_file_name);
+ dst_file_name = CA_NULL;
+
+ if (ca_info->dst_fp) {
+ fclose(ca_info->dst_fp);
+ ca_info->dst_fp = CA_NULL;
+ unlink(dst_path);
+ }
+
+ ca_destroy_ca_info(ca_info->ca_id);
+ }
+
+ CA_LOGI("file name: %s, id: %d, ret: %d", dst_file_name, ca_id, ret);
+
+ return dst_file_name;
+}
+
+int ca_storage_remove_files(int file_count, const char *cache_files[])
+{
+ int ret = CA_RESULT_OK;
+ char dst_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+ char *cache_path = ca_config_get_cache_path();
+ ca_size_t total_file_size = 0;
+ ca_size_t file_size = 0;
+
+ for (int i = 0; i < file_count; i++) {
+ if (!cache_files[i]) {
+ CA_LOGE("[NULL-CHECK] cache_files[%d]", i);
+
+ ret = CA_ERR_INVALID_ARGUMENT;
+ break;
+ }
+
+ snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s", cache_path, cache_files[i]);
+ ca_storage_get_file_size(dst_path, &file_size);
+
+ if (unlink(dst_path) < 0) {
+ char err_buf[CA_MAX_ERROR_STR_LEN] = { 0, };
+
+ strerror_r(errno, err_buf, CA_MAX_ERROR_STR_LEN);
+ CA_LOGE("Unable to remove the file: %s, error [%d/%s]", dst_path, errno, err_buf);
+
+ ret = CA_ERR_INVALID_PATH;
+ break;
+ }
+
+ if (file_size > 0)
+ total_file_size += file_size;
+ }
+
+ free(cache_path);
+ __reduce_cache_size(total_file_size);
+
+ return ret;
+}
+
+int ca_storage_is_file_available(const char *cache_file)
+{
+ int ret = CA_RESULT_OK;
+ char dst_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+ char *cache_path = ca_config_get_cache_path();
+
+ snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s", cache_path, cache_file);
+
+ if (__get_file_state(dst_path) != CA_REGULAR_FILE) {
+ CA_LOGE("Not a regular file!");
+ ret = CA_ERR_INVALID_ARGUMENT;
+ goto done;
+ }
+
+ if (__is_realpath(dst_path) != CA_RESULT_OK) {
+ CA_LOGE("Not a real file!");
+ ret = CA_ERR_INVALID_ARGUMENT;
+ }
+
+done:
+ free(cache_path);
+ return ret;
+}
+
+int ca_storage_get_used_cache_size(ca_size_t *size)
+{
+ cache_dir_size = 0;
+ char *cache_path = ca_config_get_cache_path();
+
+ if (ftw(cache_path, __sum, 1) != 0) {
+ CA_LOGE("Failed to get cache directory size!");
+ free(cache_path);
+ return CA_ERR_INVALID_ARGUMENT;
+ }
+
+ *size = cache_dir_size;
+
+ CA_LOGD("size(Bytes): %lld", *size);
+ free(cache_path);
+
+ return CA_RESULT_OK;
+}
+
+int ca_storage_copy_file(ca_info_t *ca_info)
+{
+ pthread_attr_t thread_attr;
+ pthread_t tid;
+
+ if (pthread_attr_init(&thread_attr) != 0)
+ return CA_ERR_FAIL_TO_CREATE_THREAD;
+
+ if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0)
+ return CA_ERR_FAIL_TO_CREATE_THREAD;
+
+ if (pthread_create(&(tid), &thread_attr, __thread_start_copy, ca_info) != 0) {
+ CA_LOGE("Fail to make thread:id[%d]", ca_info->ca_id);
+ return CA_ERR_FAIL_TO_CREATE_THREAD;
+ }
+
+ ca_info->thread_id = tid;
+ CA_LOGI("Thread is created:thread id[%lu]", ca_info->thread_id);
+
+ return CA_RESULT_OK;
+}
+
+int ca_init_cache_download_data(ca_info_t *ca_info,
+ const char *cache_file, ca_req_data_t *ext_data)
+{
+ char src_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+ char dst_path[CA_MAX_FILE_PATH_LEN] = { 0, };
+ char *gen_dst_file_name = CA_NULL;
+ void *dst_fp = CA_NULL;
+ const char *dst_file_name = ext_data->file_name;
+ const char *dst_file_path = ext_data->install_path;
+ char *cache_path = ca_config_get_cache_path();
+
+ if (!dst_file_name || !dst_file_path) {
+ CA_LOGE("Invalid dst file path or dst file name!");
+ free(cache_path);
+ return CA_ERR_INVALID_ARGUMENT;
+ }
+
+ snprintf(src_path, CA_MAX_FILE_PATH_LEN, "%s/%s", cache_path, cache_file);
+ snprintf(dst_path, CA_MAX_FILE_PATH_LEN, "%s/%s", dst_file_path, dst_file_name);
+
+ free(cache_path);
+
+ if (__get_file_state(src_path) != CA_REGULAR_FILE) {
+ CA_LOGE("No src file exist!");
+ return CA_ERR_INVALID_PATH;
+ }
+
+ gen_dst_file_name = __generate_dst_file_name(dst_path, dst_file_path, dst_file_name);
+
+ if (gen_dst_file_name == CA_NULL) {
+ CA_LOGE("Fail to generate dst file name!");
+ return CA_ERR_INVALID_STATE;
+ }
+
+ dst_fp = __open_dst_file(dst_path);
+ if (dst_fp == CA_NULL) {
+ CA_LOGE("Unable to open dst file!");
+ free(gen_dst_file_name);
+ return CA_ERR_FAIL_TO_ACCESS_FILE;
+ }
+
+ ca_info->dst_fp = (FILE *)dst_fp;
+ ca_info->src_file = strdup(src_path);
+ ca_info->dst_file = strdup(dst_path);
+ ca_info->file_name = gen_dst_file_name;
+
+ ca_req_info_t *req_info = ca_info->req_info;
+
+ if (ext_data->install_path)
+ req_info->install_path = strdup(ext_data->install_path);
+ if (ext_data->file_name)
+ req_info->file_name = strdup(ext_data->file_name);
+ if (ext_data->pkg_name)
+ req_info->pkg_name = strdup(ext_data->pkg_name);
+ if (ext_data->user_req_data)
+ req_info->user_req_data = ext_data->user_req_data;
+ if (ext_data->user_client_data)
+ req_info->user_client_data = ext_data->user_client_data;
+
+ return CA_RESULT_OK;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef _CACHE_AGENT_CONFIG_H
+#define _CACHE_AGENT_CONFIG_H
+
+#include "cache-agent-defs.h"
+#include "cache-agent-debug.h"
+#include "cache-agent-info.h"
+
+#define CA_CONFIG_FILE_PATH "/var/lib/download-provider/settings"
+#define CA_STORAGE_PATH "/opt/usr/data/download_cache"
+#define CA_MAX_CACHE_SIZE 1000 /* 1000 MByte */
+#define CA_MAX_LIFECYCLE 172800 /* Seconds, 48 hours */
+
+#define CA_SETTING_GROUP_NAME "CACHE"
+#define CA_STORAGE_PATH_KEY "CACHE_PATH"
+#define CA_MAX_CACHE_SIZE_KEY "CACHE_SIZE"
+#define CA_MAX_LIFECYCLE_KEY "CACHE_LIFECYCLE"
+
+typedef struct {
+ int max_cache_size;
+ int max_lifecycle;
+ char *cache_storage_path;
+ ca_time_t oldest_time;
+ ca_size_t cache_size;
+ ca_capacity_event_cb capa_cb;
+} cache_config_info_t;
+
+int ca_config_init();
+int ca_config_deinit();
+int ca_config_set_cache_path(const char *cache_path);
+char *ca_config_get_cache_path(void);
+int ca_config_set_max_cache_size(unsigned int max_cache_size);
+int ca_config_get_max_cache_size(unsigned int *max_cache_size);
+int ca_config_set_max_lifecycle(unsigned int max_lifecycle);
+int ca_config_get_max_lifecycle(unsigned int *max_lifecycle);
+int ca_config_set_oldest_time(ca_time_t oldest_time);
+int ca_config_get_oldest_time(ca_time_t *oldest_time);
+int ca_config_set_cache_size(ca_size_t cache_size);
+int ca_config_get_cache_size(ca_size_t *cache_size);
+int ca_config_set_capacity_cb(ca_capacity_event_cb capa_cb);
+GSList *ca_config_call_capacity_cb(ca_capacity_event_type type);
+
+#endif //_CACHE_AGENT_CONFIG_H
+
+
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef _CACHE_AGENT_DEBUG_H
+#define _CACHE_AGENT_DEBUG_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <pthread.h>
+
+// ansi color
+#define COLOR_RED "\033[0;31m"
+#define COLOR_GREEN "\033[0;32m"
+#define COLOR_BROWN "\033[0;33m"
+#define COLOR_LIGHTBLUE "\033[0;37m"
+#define COLOR_END "\033[0;m"
+
+#ifdef _ENABLE_DLOG
+#include <unistd.h>
+#include <syscall.h>
+#include <dlog.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif /* LOG_TAG */
+
+#define LOG_TAG "DP_CA"
+#define CA_LOGV(format, ...) ((void)0)
+#define CA_LOGD(format, ...) LOGD(COLOR_LIGHTBLUE "[%ld]:"format COLOR_END, syscall(__NR_gettid), ##__VA_ARGS__)
+#define CA_LOGI(format, ...) LOGI(COLOR_BROWN "[%ld]:"format COLOR_END, syscall(__NR_gettid), ##__VA_ARGS__)
+#define CA_LOGE(format, ...) LOGE(COLOR_RED "[%ld]:"format COLOR_END, syscall(__NR_gettid), ##__VA_ARGS__)
+#define CA_SECURE_LOGD(format, ...) SECURE_LOGD(COLOR_GREEN format COLOR_END, ##__VA_ARGS__)
+#define CA_SECURE_LOGI(format, ...) SECURE_LOGI(COLOR_GREEN format COLOR_END, ##__VA_ARGS__)
+#define CA_SECURE_LOGE(format, ...) SECURE_LOGE(COLOR_GREEN format COLOR_END, ##__VA_ARGS__)
+#else
+
+#include <unistd.h>
+#include <syscall.h>
+
+#define CA_LOGD(format, ...) do {\
+ fprintf(stderr, "[CA][%ld][%s():%d] "format"\n", syscall(__NR_gettid), __FUNCTION__, __LINE__, ##__VA_ARGS__);\
+} while (0)
+#define CA_LOGE(format, ...) do {\
+ fprintf(stderr, "[CA][%ld][ERR][%s():%d]\n", syscall(__NR_gettid), __FUNCTION__, __LINE__, ##__VA_ARGS__);\
+} while (0)
+#define CA_LOGV CA_LOGD
+#define CA_LOGI CA_LOGD
+#define CA_SECURE_LOGD(format, ...) ((void)0)
+#define CA_SECURE_LOGI(format, ...) ((void)0)
+#define CA_SECURE_LOGE(format, ...) ((void)0)
+#endif /* _ENABLE_DLOG */
+
+#endif /* _CACHE_AGENT_DEBUG_H */
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef _CACHE_AGENT_DEFS_H
+#define _CACHE_AGENT_DEFS_H
+
+#include <pthread.h>
+#include <time.h>
+
+typedef long long ca_size_t;
+typedef time_t ca_time_t;
+typedef int ca_bool_t;
+
+#define CA_DEFAULT_INSTALL_PATH "/opt/usr/home/owner/media/Downloads"
+
+// Maximum number of copy operations
+#define CA_MAX_ID 32
+
+#define CA_RESULT_OK 0
+
+#define CA_TRUE 1
+#define CA_FALSE 0
+#define CA_NULL (void *)0
+#define CA_INVALID_ID -1
+
+#define CA_RESULT_USER_CANCELED -10
+
+// InputError Input error (-100 ~ -199)
+// Client passed wrong parameter
+#define CA_ERR_INVALID_ARGUMENT -100
+#define CA_ERR_INVALID_URL -101
+#define CA_ERR_INVALID_PATH -102
+#define CA_ERR_INVALID_MIME_TYPE -103
+
+// Client passed correct parameter, but Cache Agent rejects the request because of internal policy.
+#define CA_ERR_ALREADY_CANCELED -160
+#define CA_ERR_ALREADY_SUSPENDED -161
+#define CA_ERR_ALREADY_RESUMED -162
+#define CA_ERR_CANNOT_SUSPEND -170
+#define CA_ERR_CANNOT_RESUME -171
+#define CA_ERR_INVALID_STATE -190
+#define CA_ERR_ALREADY_MAX_COPY -191
+#define CA_ERR_UNSUPPORTED_PROTOCAL -192
+
+// System error (-200 ~ -299)
+#define CA_ERR_FAIL_TO_MEMALLOC -200
+#define CA_ERR_FAIL_TO_CREATE_THREAD -210
+#define CA_ERR_FAIL_TO_ACCESS_FILE -230
+#define CA_ERR_DISK_FULL -240
+
+// DRM error - not conforming with DRM spec (-600 ~ -699)
+#define CA_ERR_DRM_FAIL -600
+
+#define CA_CAPA_THRESHOLD_RATIO 0.9
+
+#define NULL_CHECK_RET_OPT(DATA, RET_DATA) {\
+ if (!DATA) {\
+ CA_LOGE("NULL CHECK!:%s", (#DATA));\
+ return RET_DATA;\
+ } \
+}
+
+#define CA_MUTEX_INIT(mutex_add, attr) {\
+ int ret = 0;\
+ do {\
+ ret = pthread_mutex_init(mutex_add, attr);\
+ if (0 == ret) {\
+ break;\
+ } else if (EINVAL == ret) {\
+ CA_LOGE("pthread_mutex_init FAIL with EINVAL.");\
+ break;\
+ } else if (ENOMEM == ret) {\
+ CA_LOGE("pthread_mutex_init FAIL with ENOMEM.");\
+ break;\
+ } else {\
+ CA_LOGE("pthread_mutex_init FAIL with %d.", ret);\
+ break;\
+ } \
+ } while (1);\
+}
+
+#define CA_MUTEX_LOCK(mutex_add) {\
+ int ret = 0;\
+ do {\
+ ret = pthread_mutex_lock(mutex_add);\
+ if (0 == ret) {\
+ break;\
+ } else if (EINVAL == ret) {\
+ CA_LOGE("pthread_mutex_lock FAIL with EINVAL.");\
+ break;\
+ } else if (EDEADLK == ret) {\
+ CA_LOGE("pthread_mutex_lock FAIL with EDEADLK.");\
+ break;\
+ } else {\
+ CA_LOGE("pthread_mutex_lock FAIL with %d.", ret);\
+ break;\
+ } \
+ } while (1);\
+}
+
+#define CA_MUTEX_UNLOCK(mutex_add) {\
+ int ret = 0;\
+ do {\
+ ret = pthread_mutex_unlock(mutex_add);\
+ if (0 == ret) {\
+ break;\
+ } else if (EINVAL == ret) {\
+ CA_LOGE("pthread_mutex_unlock FAIL with EINVAL.");\
+ break;\
+ } else if (EPERM == ret) {\
+ CA_LOGE("pthread_mutex_unlock FAIL with EPERM.");\
+ break;\
+ } else {\
+ CA_LOGE("pthread_mutex_unlock FAIL with %d.", ret);\
+ break;\
+ } \
+ } while (1);\
+}
+
+#define CA_MUTEX_DESTROY(mutex_add) {\
+ int ret = 0;\
+ do {\
+ ret = pthread_mutex_destroy(mutex_add);\
+ if (0 == ret) {\
+ break;\
+ } else if (EINVAL == ret) {\
+ CA_LOGE("pthread_mutex_destroy FAIL with EINVAL.");\
+ break;\
+ } else if (EBUSY == ret) {\
+ CA_LOGE("pthread_mutex_destroy FAIL with EBUSY.");\
+ break;\
+ } else {\
+ CA_LOGE("pthread_mutex_destroy FAIL with %d.", ret);\
+ break;\
+ } \
+ } while (1);\
+}
+
+#define CA_COND_INIT(cond_add, attr) do {\
+ if (0 != pthread_cond_init(cond_add, attr)) {\
+ CA_LOGE("pthread_cond_init FAIL");\
+ } \
+ } while (0)
+
+#define CA_COND_SIGNAL(cond_add) do {\
+ if (0 != pthread_cond_signal(cond_add)) {\
+ CA_LOGE("pthread_cond_signal FAIL");\
+ } \
+ } while (0)
+
+#define CA_COND_WAIT(cond_add, mutex_add) do {\
+ if (0 != pthread_cond_wait(cond_add, mutex_add)) {\
+ CA_LOGE("pthread_cond_wait FAIL");\
+ } \
+ } while (0)
+
+#define CA_COND_TIMED_WAIT(cond_add, mutex_add, time) do {\
+ if (0 != pthread_cond_timedwait(cond_add, mutex_add, time)) {\
+ CA_LOGE("pthread_cond_wait FAIL");\
+ } \
+ } while (0)
+
+#define CA_COND_DESTROY(cond_add) do {\
+ if (0 != pthread_cond_destroy(cond_add)) {\
+ CA_LOGE("pthread_cond_destroy FAIL");\
+ } \
+ } while (0)
+
+#endif
+
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef _CACHE_AGENT_INFO_H
+#define _CACHE_AGENT_INFO_H
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glib.h>
+
+typedef enum {
+ CA_STATE_NONE,
+ CA_STATE_COPYING,
+ CA_STATE_PAUSED,
+ CA_STATE_COMPLETED,
+ CA_STATE_CANCELED,
+ CA_STATE_FAILED,
+} ca_state_type;
+
+typedef enum {
+ CA_CAPACITY_MAX_SIZE,
+ CA_CAPACITY_LIFECYCLE,
+} ca_capacity_event_type;
+
+typedef struct {
+ int cache_id;
+ int error;
+ long long file_size;
+ char *saved_path;
+ char *content_name;
+} cache_info_t;
+
+typedef struct {
+ int cache_id;
+ int error;
+ long long file_size;
+ char *saved_path;
+ int http_status;
+} ca_finished_info_t;
+
+typedef struct {
+ char *install_path;
+ char *file_name;
+ char *pkg_name;
+ void *user_req_data;
+ void *user_client_data;
+} ca_req_info_t;
+
+typedef GSList* (*ca_capacity_event_cb) (ca_capacity_event_type capa_type);
+
+typedef void (*ca_paused_cb) (int cache_id,
+ void *user_param1, void *user_param2);
+typedef void (*ca_progress_cb) (int cache_id,
+ unsigned long long received_size,
+ void *user_param1, void *user_param2);
+typedef void (*ca_started_cb) (cache_info_t *cache_info,
+ void *user_param1, void *user_param2);
+typedef void (*ca_finished_cb) (ca_finished_info_t *finished_info,
+ void *user_param1, void *user_param2);
+
+typedef struct {
+ ca_started_cb cache_info_cb;
+ ca_progress_cb progress_cb;
+ ca_finished_cb finished_cb;
+ ca_paused_cb paused_cb;
+} ca_cb_t;
+
+typedef struct {
+ int ca_id;
+ pthread_t thread_id;
+ pthread_mutex_t mutex_state;
+ pthread_cond_t cond;
+ ca_state_type state;
+ char *src_file;
+ char *dst_file;
+ char *file_name;
+ FILE *dst_fp;
+ ca_req_info_t *req_info;
+ ca_cb_t cb_info;
+ ca_bool_t is_cb_update;
+ int update_time;
+ ca_bool_t is_cache_adding;
+} ca_info_t;
+
+typedef struct {
+ const char *install_path;
+ const char *file_name;
+ const char *pkg_name;
+ void *user_req_data;
+ void *user_client_data;
+} ca_req_data_t;
+
+#endif //_CACHE_AGENT_INFO_H
+
+
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef _CACHE_AGENT_INTERFACE_H
+#define _CACHE_AGENT_INTERFACE_H
+
+#ifndef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "cache-agent-storage.h"
+
+#define REQ_BLOCK_RET(mutex_add, DATA) do {\
+ pthread_mutex_lock(mutex_add);\
+ if (DATA) {\
+ pthread_mutex_unlock(mutex_add);\
+ CA_LOGD("Request is blocked!");\
+ return CA_ERR_INVALID_STATE;\
+ } \
+ pthread_mutex_unlock(mutex_add);\
+ } while (0)
+
+#define REQ_BLOCK_RET_PTR(mutex_add, DATA) do {\
+ pthread_mutex_lock(mutex_add);\
+ if (DATA) {\
+ pthread_mutex_unlock(mutex_add);\
+ CA_LOGD("Request is blocked!");\
+ return NULL;\
+ } \
+ pthread_mutex_unlock(mutex_add);\
+ } while (0)
+
+EXPORT_API int ca_init(ca_capacity_event_cb capa_cb);
+EXPORT_API int ca_deinit();
+
+EXPORT_API char *ca_store_file(const char *file_path);
+EXPORT_API int ca_remove_files(int file_count, const char *cache_files[]);
+EXPORT_API int ca_is_file_available(const char *cache_file);
+EXPORT_API int ca_get_used_cache_size(ca_size_t *size);
+
+EXPORT_API int ca_start_cache_download(const char *cache_file,
+ ca_req_data_t *ext_data, ca_cb_t *ca_cb_data, int *cache_id);
+EXPORT_API int ca_pause_cache_download(int req_id);
+EXPORT_API int ca_resume_cache_download(int req_id);
+EXPORT_API int ca_cancel_cache_download(int req_id);
+EXPORT_API int ca_pause_cache_download_without_update(int req_id);
+EXPORT_API int ca_cancel_cache_download_without_update(int req_id);
+EXPORT_API int ca_is_alive_cache_download(int req_id);
+
+EXPORT_API int ca_get_max_cache_size(unsigned int *size);
+EXPORT_API int ca_set_max_cache_size(unsigned int size);
+EXPORT_API int ca_get_cache_path(char **path);
+EXPORT_API int ca_set_cache_path(const char *path);
+EXPORT_API int ca_get_cache_lifecycle(unsigned int *time);
+EXPORT_API int ca_set_cache_lifecycle(unsigned int time);
+EXPORT_API int ca_clear_all_files(void);
+EXPORT_API int ca_set_oldest_file_time(ca_time_t oldest_time);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_CACHE_AGENT_INTERFACE_H
+
+
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef _CACHE_AGENT_STORAGE_H
+#define _CACHE_AGENT_STORAGE_H
+
+#include <linux/limits.h>
+#include "cache-agent-defs.h"
+#include "cache-agent-debug.h"
+#include "cache-agent-info.h"
+#include "cache-agent-config.h"
+
+#define CA_MAX_FILE_PATH_LEN PATH_MAX
+#define CA_MAX_FILE_NAME_LEN NAME_MAX
+#define CA_MAX_ERROR_STR_LEN 256
+#define CA_MAX_DUP_FILE_COUNT 2048
+#define CA_MAX_FILE_BUFFER_SIZE 65536
+#define CA_MB_BYTE 1048576
+
+typedef enum {
+ CA_NOT_EXIST_FILE,
+ CA_REGULAR_FILE,
+ CA_OTHER_FILE
+} ca_file_state_e;
+
+ca_info_t *ca_get_ca_info(int ca_id);
+int ca_check_ca_id(ca_info_t *ca_info, int ca_id);
+int ca_get_info_with_ca_id(int id, ca_info_t **out_info);
+void ca_cancel_all_cache_operations(void);
+int ca_clear_all_cache_files(void);
+int ca_request_to_cancel_cache_download(ca_info_t *ca_info);
+int ca_request_to_suspend_cache_download(ca_info_t *ca_info);
+int ca_request_to_resume_cache_download(ca_info_t *ca_info);
+void ca_destroy_ca_info(int id);
+int ca_get_available_ca_id(int *available_id);
+void ca_storage_get_file_size(const char *file_path, ca_size_t *file_size);
+
+int ca_storage_init();
+int ca_storage_deinit();
+
+char *ca_storage_store_file(const char *file_path);
+int ca_storage_remove_files(int file_count, const char *cache_files[]);
+int ca_storage_is_file_available(const char *cache_file);
+int ca_storage_get_used_cache_size(ca_size_t *size);
+
+int ca_storage_copy_file(ca_info_t *ca_info);
+int ca_init_cache_download_data(ca_info_t *ca_info,
+ const char *cache_file, ca_req_data_t *ext_data);
+
+#endif //_CACHE_AGENT_STORAGE_H
+
+
Name: download-provider
Summary: Download the contents in background
-Version: 2.3.10
+Version: 2.3.11
Release: 0
Group: Development/Libraries
License: Apache-2.0
-DBIN_INSTALL_DIR:PATH=%{_bindir} \
-DLIB_INSTALL_DIR:PATH=%{_libdir} \
-DLIB_AGENT_PATH="/usr/%{?_lib}/libdownloadagent2.so.0.1.0" \
+ -DLIB_CACHE_AGENT_PATH="/usr/%{?_lib}/libcacheagent.so.0.1.0" \
-DINCLUDE_INSTALL_DIR:PATH=%{_includedir} \
-DPKG_NAME=%{name} \
-DPKG_VERSION=%{version} \
install -m 644 packaging/download-provider.notstrip %{buildroot}/etc/notstrip/download-provider.notstrip
%endif
+mkdir -p %{buildroot}/var/lib/download-provider/
+install -m 600 res/var/lib/download-provider/settings %{buildroot}/var/lib/download-provider/settings
+
## container_enable
mkdir -p %{buildroot}/etc/vasum/vsmzone.resource/
mv %{buildroot}/usr/share/download-provider/download-provider.res %{buildroot}/etc/vasum/vsmzone.resource/
chsmack -a 'System' %{_database_client_dir}
chown -R web_fw:web_fw %{_database_client_dir}
chown -R web_fw:web_fw %{_data_install_path}
+chown -R web_fw:web_fw /var/lib/download-provider/
+mkdir -p /opt/usr/data/download_cache
+chown -R web_fw:web_fw /opt/usr/data/download_cache
%files
%defattr(-,root,root,-)
%{_imagedir}/*.png
%{_localedir}/*/*/download-provider.mo
%{_libdir}/*.so.*
+%attr(0600,web_fw,web_fw) /var/lib/download-provider/settings
/lib/systemd/system/download-provider.service
/lib/systemd/system/download-provider.socket
/lib/systemd/system/sockets.target.wants/download-provider.socket
%{_libdir}/pkgconfig/download-provider.pc
%{_libdir}/pkgconfig/download-provider-interface.pc
%{_libdir}/pkgconfig/download-agent.pc
+%{_libdir}/pkgconfig/cache-agent.pc
%{_includedir}/download-provider/download-provider.h
%{_includedir}/download-provider/download-provider-interface.h
%{_includedir}/download-provider/download-agent-interface.h
+%{_includedir}/download-provider/cache-agent-interface.h
%if 0%{?gtests:1}
%{_bindir}/gtest*
TRACE_ERROR("check permission");
return -DP_ERROR_PERMISSION_DENIED;
}
+
+ char err_buf[DOWNLOAD_FILENAME_MAX] = { 0, };
+ strerror_r(errno, err_buf, DOWNLOAD_FILENAME_MAX);
+ TRACE_ERROR("DP Connection error [%d/%s]", errno, err_buf);
return -1;
}
TRACE_DEBUG("sockfd [%d]", sockfd);
return __dp_ipc_set_int(id, DP_SEC_SET, DP_PROP_VERIFY_HOST,
enable, __FUNCTION__);
}
+
+int dp_interface_set_cache(const int id, int enable)
+{
+ return __dp_ipc_set_int(id, DP_SEC_SET, DP_PROP_CACHE,
+ enable, __FUNCTION__);
+}
+
+int dp_interface_get_cache(const int id, int *enable)
+{
+ if (enable == NULL) {
+ TRACE_ERROR("check buffer");
+ return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+ }
+ return __dp_ipc_get_int(id, DP_PROP_CACHE, enable, __FUNCTION__);
+}
+
+int dp_interface_reset_cache(const int id)
+{
+ return __dp_ipc_echo(id, DP_SEC_CONTROL, DP_PROP_RESET_CACHE, __FUNCTION__);
+}
+
+int dp_interface_set_cache_max_size(const int id, const unsigned int size)
+{
+ return __dp_ipc_set_int(id, DP_SEC_SET, DP_PROP_SET_CACHE_MAX_SIZE,
+ size, __FUNCTION__);
+}
+
+int dp_interface_get_cache_max_size(const int id, unsigned int *size)
+{
+ if (size == NULL) {
+ TRACE_ERROR("check buffer");
+ return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+ }
+ return __dp_ipc_get_int(id, DP_PROP_GET_CACHE_MAX_SIZE, (int *)size, __FUNCTION__);
+}
+
+int dp_interface_reset_all_cache(const int id)
+{
+ return __dp_ipc_echo(id, DP_SEC_CONTROL, DP_PROP_RESET_ALL_CACHE, __FUNCTION__);
+}
+
+int dp_interface_set_cache_path(const int id, const char *path)
+{
+ if (path == NULL) {
+ TRACE_ERROR("check buffer");
+ return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+ }
+ return __dp_ipc_set_string(id, DP_SEC_SET, DP_PROP_SET_CACHE_PATH, path, __FUNCTION__);
+}
+
+int dp_interface_get_cache_path(const int id, char **path)
+{
+ if (path == NULL) {
+ TRACE_ERROR("check buffer");
+ return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+ }
+ return __dp_ipc_get_string(id, DP_PROP_GET_CACHE_PATH, path, __FUNCTION__);
+}
+
+int dp_interface_set_cache_lifecycle(const int id, const unsigned int time)
+{
+ return __dp_ipc_set_int(id, DP_SEC_SET, DP_PROP_SET_CACHE_LIFECYCLE,
+ time, __FUNCTION__);
+}
+
+int dp_interface_get_cache_lifecycle(const int id, unsigned int *time)
+{
+ if (time == NULL) {
+ TRACE_ERROR("check buffer");
+ return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;
+ }
+ return __dp_ipc_get_int(id, DP_PROP_GET_CACHE_LIFECYCLE, (int *)time, __FUNCTION__);
+}
EXPORT_API int dp_interface_get_notification_type(const int id, int *type);
EXPORT_API int dp_interface_set_verify_host(const int id, int enable);
+
+EXPORT_API int dp_interface_set_cache(const int id, int enable);
+EXPORT_API int dp_interface_get_cache(const int id, int *enable);
+EXPORT_API int dp_interface_reset_cache(const int id);
+
+EXPORT_API int dp_interface_set_cache_max_size(const int id, const unsigned int size);
+EXPORT_API int dp_interface_get_cache_max_size(const int id, unsigned int *size);
+EXPORT_API int dp_interface_reset_all_cache(const int id);
+EXPORT_API int dp_interface_set_cache_path(const int id, const char *path);
+EXPORT_API int dp_interface_get_cache_path(const int id, char **path);
+EXPORT_API int dp_interface_set_cache_lifecycle(const int id, const unsigned int time);
+EXPORT_API int dp_interface_get_cache_lifecycle(const int id, unsigned int *time);
+
#ifdef __cplusplus
}
#endif
ENDIF(SUPPORT_LARGE_FILE)
## INCLUDES
-INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/agent/include)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${CMAKE_SOURCE_DIR}/cache/include
+ ${CMAKE_SOURCE_DIR}/agent/include)
set(DP2_LINK_LIBRARIES ${GLIB-2_LIBRARIES}
${GOBJECT-2_LIBRARIES}
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-network.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-db.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-plugin-download-agent.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-plugin-cache-agent.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-queue.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-queue-manager.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-client.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-client-manager.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-notification.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-notification-manager.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-cache.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-cache-manager.c
${CMAKE_CURRENT_SOURCE_DIR}/download-provider-main.c )
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${dp2_pkgs_LDFLAGS} ${DP2_LINK_LIBRARIES} ${dp2_noti_pkgs_LDFLAGS} ${dp2_companion_pkgs_LDFLAGS} ${dp2_wifi_direct_pkgs_LDFLAGS} ${dp2_security_privilege_pkgs_LDFLAGS} -ldl -pie)
INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BIN_INSTALL_DIR})
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#include <stdlib.h>
+#include <download-provider-cache.h>
+#include <download-provider-cache-manager.h>
+#include <download-provider-client-manager.h>
+#include <download-provider-ipc.h>
+#include <download-provider-log.h>
+
+static char *__dp_cache_print_request_type(unsigned req_type)
+{
+ switch (req_type) {
+ case DP_CACHE_REQUEST_RESET_CACHE:
+ return "RESET_CACHE";
+ case DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE:
+ return "SET_CACHE_MAX_SIZE";
+ case DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE:
+ return "GET_CACHE_MAX_SIZE";
+ case DP_CACHE_REQUEST_SET_CACHE_PATH:
+ return "SET_CACHE_PATH";
+ case DP_CACHE_REQUEST_GET_CACHE_PATH:
+ return "GET_CACHE_PATH";
+ case DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE:
+ return "SET_CACHE_LIFECYCLE";
+ case DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE:
+ return "GET_CACHE_LIFECYCLE";
+ case DP_CACHE_REQUEST_RESET_ALL_CACHE:
+ return "RESET_ALL_CACHE";
+ default:
+ break;
+ }
+ return "UNKNOWN";
+}
+
+static int __dp_cache_request_feedback_int(int sock, dp_ipc_fmt *ipc_info, void *value, int errorvalue, size_t extra_size)
+{
+ int errorcode = DP_ERROR_NONE;
+ if (errorvalue != DP_ERROR_NONE)
+ extra_size = 0;
+ if (dp_ipc_query(sock, ipc_info->id, ipc_info->section, ipc_info->property, errorvalue, extra_size) < 0) {
+ errorcode = DP_ERROR_IO_ERROR;
+ TRACE_ERROR("sock:%d check ipc length:%zd", sock, extra_size);
+ }
+ if (errorvalue == DP_ERROR_NONE && errorcode == DP_ERROR_NONE) {
+ if (dp_ipc_write(sock, value, extra_size) < 0) {
+ errorcode = DP_ERROR_IO_ERROR;
+ TRACE_ERROR("sock:%d check ipc length:%zd", sock, extra_size);
+ }
+ }
+ return errorcode;
+}
+
+static int __dp_cache_request_feedback_string(int sock, dp_ipc_fmt *ipc_info, void *string, size_t length, int errorvalue)
+{
+ int errorcode = DP_ERROR_NONE;
+
+ if (length == 0 && errorvalue == DP_ERROR_NONE)
+ errorvalue = DP_ERROR_NO_DATA;
+
+ if (dp_ipc_query(sock, ipc_info->id, ipc_info->section, ipc_info->property, errorvalue, length * sizeof(char)) < 0) {
+ errorcode = DP_ERROR_IO_ERROR;
+ TRACE_ERROR("sock:%d check ipc length:%zd", sock, length);
+ }
+ if (errorvalue == DP_ERROR_NONE && errorcode == DP_ERROR_NONE) {
+ if (dp_ipc_write(sock, string, sizeof(char) * length) < 0) {
+ errorcode = DP_ERROR_IO_ERROR;
+ TRACE_ERROR("sock:%d check ipc length:%zd", sock, length);
+ }
+ }
+ return errorcode;
+}
+
+static int __dp_cache_request_read_int(int sock, dp_ipc_fmt *ipc_info, int *value)
+{
+ int errorcode = DP_ERROR_NONE;
+ if (ipc_info->size == sizeof(int)) {
+ if (dp_ipc_read(sock, value, ipc_info->size, __FUNCTION__) < 0) {
+ TRACE_ERROR("sock:%d check ipc length:%zd", sock, ipc_info->size);
+ errorcode = DP_ERROR_IO_ERROR;
+ }
+ } else
+ errorcode = DP_ERROR_IO_ERROR;
+
+ return errorcode;
+}
+
+static int __dp_cache_request_read_string(int sock, dp_ipc_fmt *ipc_info, char **string)
+{
+ int errorcode = DP_ERROR_NONE;
+ if (ipc_info->size > 0) {
+ char *recv_str = (char *)calloc((ipc_info->size + (size_t)1), sizeof(char));
+ if (recv_str == NULL) {
+ TRACE_ERROR("sock:%d check memory length:%zd", sock, ipc_info->size);
+ errorcode = DP_ERROR_OUT_OF_MEMORY;
+ } else {
+ if (dp_ipc_read(sock, recv_str, ipc_info->size, __FUNCTION__) <= 0) {
+ TRACE_ERROR("sock:%d check ipc length:%zd", sock, ipc_info->size);
+ errorcode = DP_ERROR_IO_ERROR;
+ free(recv_str);
+ } else {
+ recv_str[ipc_info->size] = '\0';
+ TRACE_DEBUG("sock:%d length:%zd string:%s", sock, ipc_info->size, recv_str);
+ *string = recv_str;
+ }
+ }
+ } else
+ errorcode = DP_ERROR_IO_ERROR;
+
+ return errorcode;
+}
+
+int dp_cache_manager_handle_request(void *req_slot, void *req_ipc_info, const int req_type)
+{
+ if (req_type < 0) {
+ TRACE_ERROR("check req_type");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (req_slot == NULL) {
+ TRACE_ERROR("check req_slot");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ dp_client_slots_fmt *slot = req_slot;
+ dp_client_fmt *client = &slot->client;
+ dp_ipc_fmt *ipc_info = req_ipc_info;
+ if (client == NULL || ipc_info == NULL) {
+ TRACE_ERROR("check client or ipc_info");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ int errorcode = DP_ERROR_NONE;
+ switch (req_type) {
+ case DP_CACHE_REQUEST_RESET_CACHE:
+ {
+ if (dp_cache_reset_cache(slot->pkgname, &errorcode) < 0)
+ TRACE_ERROR("failed to control %s", __dp_cache_print_request_type(req_type));
+ // feedback
+ if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_CONTROL,
+ ipc_info->property, errorcode, 0) < 0)
+ TRACE_ERROR("check ipc sock:%d", client->channel);
+ break;
+ }
+ case DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE:
+ {
+ int recv_int = -1;
+ errorcode = __dp_cache_request_read_int(client->channel, ipc_info, &recv_int);
+ if (errorcode == DP_ERROR_NONE) {
+ if (recv_int < 0)
+ errorcode = DP_ERROR_INVALID_PARAMETER;
+ else {
+ if (dp_cache_set_cache_max_size((unsigned int)recv_int, &errorcode) < 0)
+ TRACE_ERROR("failed to set %s", __dp_cache_print_request_type(req_type));
+ }
+ }
+ // feedback
+ if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET,
+ ipc_info->property, errorcode, 0) < 0)
+ TRACE_ERROR("check ipc sock:%d", client->channel);
+ break;
+ }
+ case DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE:
+ {
+ int errorcode = DP_ERROR_NONE;
+ size_t size = sizeof(int);
+ unsigned int value = 0;
+ if (dp_cache_get_cache_max_size(&value, &errorcode) < 0)
+ TRACE_ERROR("failed to get %s", __dp_cache_print_request_type(req_type));
+
+ // feedback
+ if (__dp_cache_request_feedback_int(client->channel, ipc_info,
+ (void *)&value, errorcode, size) == DP_ERROR_IO_ERROR) {
+ errorcode = DP_ERROR_IO_ERROR;
+ TRACE_ERROR("check ipc sock:%d", client->channel);
+ }
+ break;
+ }
+ case DP_CACHE_REQUEST_RESET_ALL_CACHE:
+ {
+ if (dp_cache_reset_all_cache(&errorcode) < 0)
+ TRACE_ERROR("failed to control %s", __dp_cache_print_request_type(req_type));
+ // feedback
+ if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_CONTROL,
+ ipc_info->property, errorcode, 0) < 0)
+ TRACE_ERROR("check ipc sock:%d", client->channel);
+ break;
+ }
+ case DP_CACHE_REQUEST_SET_CACHE_PATH:
+ {
+ char *recv_str = NULL;
+ errorcode = __dp_cache_request_read_string(client->channel, ipc_info, &recv_str);
+ if (errorcode == DP_ERROR_NONE) {
+ if (recv_str == NULL) {
+ errorcode = DP_ERROR_INVALID_PARAMETER;
+ } else {
+ if (dp_cache_set_cache_path((const char *)recv_str, &errorcode) < 0)
+ TRACE_ERROR("failed to set %s", __dp_cache_print_request_type(req_type));
+ free(recv_str);
+ }
+ }
+ // feedback
+ if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET,
+ ipc_info->property, errorcode, 0) < 0)
+ TRACE_ERROR("check ipc sock:%d", client->channel);
+ break;
+ }
+ case DP_CACHE_REQUEST_GET_CACHE_PATH:
+ {
+ int errorcode = DP_ERROR_NONE;
+ char *string = NULL;
+ unsigned length = 0;
+ if (dp_cache_get_cache_path(&string, &errorcode) < 0)
+ TRACE_ERROR("failed to get %s", __dp_cache_print_request_type(req_type));
+ if (string)
+ length = strlen(string);
+
+ // feedback
+ if (__dp_cache_request_feedback_string(client->channel, ipc_info,
+ string, length, errorcode) == DP_ERROR_IO_ERROR) {
+ errorcode = DP_ERROR_IO_ERROR;
+ TRACE_ERROR("check ipc sock:%d", client->channel);
+ }
+
+ free(string);
+ break;
+ }
+ case DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE:
+ {
+ int recv_int = -1;
+ errorcode = __dp_cache_request_read_int(client->channel, ipc_info, &recv_int);
+ if (errorcode == DP_ERROR_NONE) {
+ if (recv_int < 0)
+ errorcode = DP_ERROR_INVALID_PARAMETER;
+ else {
+ if (dp_cache_set_cache_lifecycle((unsigned int)recv_int, &errorcode) < 0)
+ TRACE_ERROR("failed to set %s", __dp_cache_print_request_type(req_type));
+ }
+ }
+ // feedback
+ if (dp_ipc_query(client->channel, ipc_info->id, DP_SEC_SET,
+ ipc_info->property, errorcode, 0) < 0)
+ TRACE_ERROR("check ipc sock:%d", client->channel);
+ break;
+ }
+ case DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE:
+ {
+ int errorcode = DP_ERROR_NONE;
+ size_t size = sizeof(int);
+ unsigned int value = 0;
+ if (dp_cache_get_cache_lifecycle(&value, &errorcode) < 0)
+ TRACE_ERROR("failed to get %s", __dp_cache_print_request_type(req_type));
+
+ // feedback
+ if (__dp_cache_request_feedback_int(client->channel, ipc_info,
+ (void *)&value, errorcode, size) == DP_ERROR_IO_ERROR) {
+ errorcode = DP_ERROR_IO_ERROR;
+ TRACE_ERROR("check ipc sock:%d", client->channel);
+ }
+ break;
+ }
+ default:
+ errorcode = DP_ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ return errorcode;
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+
+#include <stdio.h>
+#include <download-provider.h>
+#include <download-provider-cache.h>
+#include <download-provider-db.h>
+#include <download-provider-db-defs.h>
+#include <download-provider-log.h>
+#include <download-provider-plugin-cache-agent.h>
+
+extern void *g_db_handle;
+/*
+void __print_data(gpointer data, gpointer user_data)
+{
+ TRACE_DEBUG("%s", (char *)data);
+}
+*/
+static char **__convert_list_to_array(const int file_count, GSList *file_list)
+{
+ char **file_array = (char **)malloc(sizeof(char *) * file_count);
+ if (!file_array)
+ return NULL;
+
+ int index = 0;
+ for (GSList* node = file_list; node != NULL; node = node->next) {
+ file_array[index] = (char *)node->data;
+ index++;
+ }
+
+ return file_array;
+}
+
+static void __clear_file_array( const int file_count, char **file_array)
+{
+ if (file_array != NULL) {
+ for (int i = 0; i < file_count; i++)
+ free((void *)(file_array[i]));
+ }
+ free(file_array);
+}
+
+static int __verify_directory(const char *dir)
+{
+ int ret = DP_ERROR_NONE;
+
+ if (!dir) {
+ TRACE_ERROR("NULL CHECK!: directory path");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (access(dir, R_OK | W_OK | X_OK) != 0) {
+ TRACE_ERROR("Invalid path [%s]", dir);
+ ret = DP_ERROR_INVALID_PARAMETER;
+ }
+
+ return ret;
+}
+
+int dp_cache_delete_all_from_db(int *error)
+{
+ if (dp_db_delete_all(g_db_handle, DP_TABLE_CACHE, error) < 0) {
+ TRACE_ERROR("failed to delete all from cache table");
+ return -1;
+ }
+
+ if (dp_db_delete_all(g_db_handle, DP_TABLE_CACHE_FILE, error) < 0) {
+ TRACE_ERROR("failed to delete all from cache_file table");
+ return -1;
+ }
+
+ return 0;
+}
+
+int dp_cache_reset_cache(const char *package, int *error)
+{
+ int ret = 0;
+ GSList *file_list = NULL;
+
+ int file_count = dp_db_clear_app_cache(g_db_handle, package, &file_list, error);
+ if (file_count < 0) {
+ TRACE_ERROR("failed to delete app cache. package:%s", package);
+ return -1;
+ }
+ if (file_count == 0 || file_list == NULL)
+ return 0;
+
+ // g_slist_foreach(file_list, __print_data, NULL);
+
+ char **file_array = __convert_list_to_array(file_count, file_list);
+ if (!file_array) {
+ TRACE_ERROR("failed to allocate the file_array");
+ *error = DP_ERROR_OUT_OF_MEMORY;
+ g_slist_free_full(file_list, g_free);
+ return -1;
+ }
+
+ ret = dp_remove_files(file_count, (const char **)file_array);
+
+ __clear_file_array(file_count, file_array);
+ g_slist_free(file_list);
+
+ return ret;
+}
+
+int dp_cache_set_cache_max_size(unsigned int size, int *error)
+{
+ *error = dp_set_max_cache_size(size);
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+int dp_cache_get_cache_max_size(unsigned int *size, int *error)
+{
+ *error = dp_get_max_cache_size(size);
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+int dp_cache_set_cache_path(const char *path, int *error)
+{
+ if (__verify_directory(path) != DP_ERROR_NONE)
+ return -1;
+
+ if (dp_cache_delete_all_from_db(error) < 0)
+ return -1;
+
+ *error = dp_set_cache_path(path);
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+int dp_cache_get_cache_path(char **path, int *error)
+{
+ *error = dp_get_cache_path(path);
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+int dp_cache_set_cache_lifecycle(unsigned int time, int *error)
+{
+ *error = dp_set_cache_lifecycle(time);
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+int dp_cache_get_cache_lifecycle(unsigned int *time, int *error)
+{
+ *error = dp_get_cache_lifecycle(time);
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+int dp_cache_reset_all_cache(int *error)
+{
+ if (dp_cache_delete_all_from_db(error) < 0)
+ return -1;
+
+ // delete from cache.
+ if (dp_clear_all_cache_files() != DP_ERROR_NONE) {
+ TRACE_ERROR("failed to remove files");
+ *error = DP_ERROR_INVALID_PARAMETER;
+ return -1;
+ }
+
+ return 0;
+}
+
+GSList *dp_cache_clear_exceeded_files(long long max_val, int type)
+{
+ GSList *file_list = NULL;
+ int error;
+
+ dp_db_clear_exceeded_cache(g_db_handle, &file_list, max_val, type, &error);
+
+ for (GSList* node = file_list; node != NULL; node = node->next) {
+ const char *file_name = (char *)node->data;
+ dp_db_clear_app_cache_by_filename(g_db_handle, file_name, &error);
+ }
+
+ return file_list;
+}
#include <download-provider-queue-manager.h>
#include <download-provider-client-manager.h>
#include <download-provider-plugin-download-agent.h>
+#include <download-provider-plugin-cache-agent.h>
#include <download-provider-network.h>
static int g_dp_sock = -1;
static dp_client_slots_fmt *g_dp_client_slots = NULL;
-static void *g_db_handle = 0;
-static pthread_mutex_t g_db_mutex = PTHREAD_MUTEX_INITIALIZER;
+void *g_db_handle = 0;
+pthread_mutex_t g_db_mutex = PTHREAD_MUTEX_INITIALIZER;
extern pthread_t g_client_manager_tid;
void dp_terminate(int signo)
FD_SET(g_dp_sock, &listen_fdset);
FD_SET(g_dp_sock, &except_fdset);
+ if (dp_init_cache_agent() != DP_ERROR_NONE)
+ TRACE_ERROR("failed to init cache agent");
+
int maxfd = g_dp_sock;
while (g_dp_sock >= 0) {
// initialize timeout structure for calling timeout exactly
free(g_dp_client_slots);
// free all resources
+ dp_deinit_cache_agent();
TRACE_INFO("client-manager's working is done");
ERR:
#include <download-provider-notification-manager.h>
#include <download-provider-queue-manager.h>
#include <download-provider-client-manager.h>
+#include <download-provider-cache-manager.h>
#include <download-provider-db-defs.h>
#include <download-provider-db.h>
#include <download-provider-plugin-download-agent.h>
+#include <download-provider-plugin-cache-agent.h>
#include <download-provider-security.h>
#ifndef SIZE_MAX
[DP_PROP_NOTIFICATION_SUBJECT] = DP_DB_COL_NOTI_SUBJECT,
[DP_PROP_NOTIFICATION_DESCRIPTION] = DP_DB_COL_NOTI_DESCRIPTION,
[DP_PROP_NOTIFICATION_TYPE] = DP_DB_COL_NOTI_TYPE,
+ [DP_PROP_CACHE] = DP_DB_COL_CACHE,
[DP_PROP_CREATE] = NULL,
[DP_PROP_START] = NULL,
[DP_PROP_PAUSE] = NULL,
return "HTTP_HEADER";
case DP_PROP_VERIFY_HOST:
return "VERIFY_HOST";
+ case DP_PROP_CACHE:
+ return "CACHE";
+ case DP_PROP_RESET_CACHE:
+ return "RESET_CACHE";
+ case DP_PROP_SET_CACHE_MAX_SIZE:
+ return "SET_CACHE_MAX_SIZE";
+ case DP_PROP_GET_CACHE_MAX_SIZE:
+ return "GET_CACHE_MAX_SIZE";
+ case DP_PROP_SET_CACHE_PATH:
+ return "SET_CACHE_PATH";
+ case DP_PROP_GET_CACHE_PATH:
+ return "GET_CACHE_PATH";
+ case DP_PROP_SET_CACHE_LIFECYCLE:
+ return "SET_CACHE_LIFECYCLE";
+ case DP_PROP_GET_CACHE_LIFECYCLE:
+ return "GET_CACHE_LIFECYCLE";
+ case DP_PROP_RESET_ALL_CACHE:
+ return "RESET_ALL_CACHE";
default:
break;
}
case DP_PROP_PROGRESS_CALLBACK:
case DP_PROP_NETWORK_TYPE:
case DP_PROP_NETWORK_BONDING:
+ case DP_PROP_CACHE:
return DP_TABLE_REQUEST;
case DP_PROP_AUTO_DOWNLOAD:
case DP_PROP_STATE:
return NULL;
}
+static const int __dp_get_cache_req_type(unsigned property)
+{
+ switch (property) {
+ case DP_PROP_RESET_CACHE:
+ return DP_CACHE_REQUEST_RESET_CACHE;
+ case DP_PROP_SET_CACHE_MAX_SIZE:
+ return DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE;
+ case DP_PROP_GET_CACHE_MAX_SIZE:
+ return DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE;
+ case DP_PROP_RESET_ALL_CACHE:
+ return DP_CACHE_REQUEST_RESET_ALL_CACHE;
+ case DP_PROP_SET_CACHE_PATH:
+ return DP_CACHE_REQUEST_SET_CACHE_PATH;
+ case DP_PROP_GET_CACHE_PATH:
+ return DP_CACHE_REQUEST_GET_CACHE_PATH;
+ case DP_PROP_SET_CACHE_LIFECYCLE:
+ return DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE;
+ case DP_PROP_GET_CACHE_LIFECYCLE:
+ return DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE;
+ default:
+ break;
+ }
+ return -1;
+}
+
static int __dp_get_download_id(dp_client_fmt *client)
{
int download_id = -1;
request->id = download_id;
request->agent_id = -1;
+ request->cache_agent_id = -1;
request->state = DP_STATE_READY;
request->error = DP_ERROR_NONE;
request->network_type = DP_NETWORK_ALL;
request->content_type = DP_CONTENT_UNKNOWN;
request->file_size = 0;
request->noti_priv_id = -1;
+ request->file_name = NULL;
dp_request_create(client, request);
ipc_info->id = download_id;
{
// free notification handle here
TRACE_DEBUG("destory id:%d", request->id);
+ if (request->file_name) {
+ free(request->file_name);
+ request->file_name = NULL;
+ }
+
free(request);
}
return 1;
}
- if (request->state == DP_STATE_PAUSED &&
- dp_is_alive_download(request->agent_id) == 0) {
- // paused & agent_id not exist.... unload from memory.
- TRACE_ERROR("id:%d hanged as paused (%d/%d)",request->id, request->access_time, now_time);
- return 1;
+ if (request->state == DP_STATE_PAUSED) {
+ int ret;
+ if (request->cache_agent_id >= 0)
+ ret = dp_is_alive_cache_download(request->cache_agent_id);
+ else
+ ret = dp_is_alive_download(request->agent_id);
+ if (ret == 0) {
+ // paused & agent_id not exist.... unload from memory.
+ TRACE_ERROR("id:%d hanged as paused (%d/%d)",request->id, request->access_time, now_time);
+ return 1;
+ }
}
return 0;
if (is_zombie) {
TRACE_ERROR("id:%d is zombie request.", tailp->id);
- if (dp_cancel_agent_download_without_update(tailp->agent_id) < 0)
- TRACE_ERROR("failed to cancel download(%d) id:%d", tailp->agent_id, tailp->id);
+ int ret;
+ if (tailp->cache_agent_id >= 0) {
+ ret = dp_cancel_cache_download_without_update(tailp->cache_agent_id);
+ } else {
+ ret = dp_cancel_agent_download_without_update(tailp->agent_id);
+ }
+ if (ret < 0) {
+ TRACE_ERROR("failed to cancel download for id(%d). agent-id:%d cache-id:%d", tailp->id,
+ tailp->agent_id, tailp->cache_agent_id);
+ }
tailp->state = DP_STATE_FAILED;
tailp->error = DP_ERROR_CONNECTION_TIMED_OUT;
if (tailp->noti_type == DP_NOTIFICATION_TYPE_COMPLETE_ONLY ||
case DP_PROP_NETWORK_BONDING:
case DP_PROP_AUTO_DOWNLOAD:
case DP_PROP_HTTP_STATUS:
+ case DP_PROP_CACHE:
errorcode = __dp_request_get_info_int_from_db(client, ipc_info);
break;
case DP_PROP_HTTP_HEADERS:
if (requestp->state == DP_STATE_CONNECTING ||
requestp->state == DP_STATE_DOWNLOADING) {
// pause & push queue
- if (dp_pause_agent_download_without_update(requestp->agent_id) < 0) {
- TRACE_ERROR("failed to pause download(%d) id:%d", requestp->agent_id, ipc_info->id);
+ int ret;
+ if (requestp->cache_agent_id >= 0) {
+ ret = dp_pause_cache_download_without_update(requestp->cache_agent_id);
} else {
- requestp->state = DP_STATE_PAUSED;
- requestp->error = DP_ERROR_NONE;
- if (dp_queue_manager_push_queue(slot, requestp) < 0) {
- if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_FAILED, DP_ERROR_QUEUE_FULL, &errorcode) < 0)
- TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id);
- requestp->state = DP_STATE_FAILED;
- requestp->error = DP_ERROR_QUEUE_FULL;
- errorcode = DP_ERROR_QUEUE_FULL;
- }
+ ret = dp_pause_agent_download_without_update(requestp->agent_id);
+ }
+ if (ret < 0) {
+ TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", ipc_info->id,
+ requestp->agent_id, requestp->cache_agent_id);
+ } else {
+ requestp->state = DP_STATE_PAUSED;
+ requestp->error = DP_ERROR_NONE;
+ if (dp_queue_manager_push_queue(slot, requestp) < 0) {
+ if (dp_db_update_logging(client->dbhandle, ipc_info->id, DP_STATE_FAILED, DP_ERROR_QUEUE_FULL, &errorcode) < 0)
+ TRACE_ERROR("update log sock:%d download-id:%d", client->channel, ipc_info->id);
+ requestp->state = DP_STATE_FAILED;
+ requestp->error = DP_ERROR_QUEUE_FULL;
+ errorcode = DP_ERROR_QUEUE_FULL;
+ }
}
}
}
}
break;
}
+ case DP_PROP_CACHE:
+ {
+ int recv_int = -1;
+ errorcode = __dp_request_read_int(client->channel, ipc_info, &recv_int);
+ if (errorcode == DP_ERROR_NONE) {
+ if (dp_db_replace_property(client->dbhandle, ipc_info->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (void *)&recv_int, ipc_info->size, 0, &errorcode) < 0) {
+ TRACE_ERROR("failed to set %s", dp_print_property(ipc_info->property));
+ errorcode = DP_ERROR_DISK_BUSY;
+ }
+ }
+ break;
+ }
default:
errorcode = DP_ERROR_INVALID_PARAMETER;
break;
{
int ret = -1;
if (request != NULL) {
- if (request->agent_id >= 0) {
- TRACE_INFO("cancel download(%d) id: %d state:%s", request->agent_id,
- request->id, dp_print_state(request->state));
+ if (request->cache_agent_id >= 0) {
+ TRACE_INFO("cancel download for id(%d). cache-id:%d state:%s", request->id,
+ request->cache_agent_id, dp_print_state(request->state));
+ if (dp_cancel_cache_download_without_update(request->cache_agent_id) == 0)
+ ret = 0;
+ }
+ else if (request->agent_id >= 0) {
+ TRACE_INFO("cancel download for id(%d). agent-id:%d state:%s", request->id,
+ request->agent_id, dp_print_state(request->state));
if (dp_cancel_agent_download_without_update(request->agent_id) == 0)
ret = 0;
} else {
- TRACE_ERROR("invalid agent-id:%d id:%d", request->agent_id, request->id);
+ TRACE_ERROR("invalid agent-id:%d cache-id:%d id:%d", request->agent_id,
+ request->cache_agent_id, request->id);
}
}
return ret;
dp_queue_manager_clear_queue(requestp);
} else if (requestp->state == DP_STATE_CONNECTING ||
requestp->state == DP_STATE_DOWNLOADING) {
- if (dp_pause_agent_download_without_update(requestp->agent_id) < 0)
- TRACE_ERROR("failed to pause download(%d) id:%d", requestp->agent_id, ipc_info->id);
+ int ret;
+ if (requestp->cache_agent_id >= 0) {
+ ret = dp_pause_cache_download_without_update(requestp->cache_agent_id);
+ } else {
+ ret = dp_pause_agent_download_without_update(requestp->agent_id);
+ }
+ if (ret < 0) {
+ TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", ipc_info->id,
+ requestp->agent_id, requestp->cache_agent_id);
+ }
}
requestp->state = DP_STATE_PAUSED;
requestp->error = DP_ERROR_NONE;
if (__dp_call_cancel_agent(requestp) < 0)
TRACE_ERROR("failed to cancel download(%d) id:%d", requestp->agent_id, ipc_info->id);
}
+
requestp->agent_id = -1;
requestp->state = DP_STATE_CANCELED;
requestp->error = DP_ERROR_NONE;
return errorcode;
}
+ if (ipc_info->property >= DP_PROP_RESET_CACHE && ipc_info->property <= DP_PROP_RESET_ALL_CACHE)
+ return dp_cache_manager_handle_request(slot, ipc_info, __dp_get_cache_req_type(ipc_info->property));
+
switch (ipc_info->section) {
case DP_SEC_CONTROL:
errorcode = __dp_request_controls(slot, ipc_info, requestp);
TRACE_DEBUG("request %d stop id:%d state:%s", i, tailp->id, dp_print_state(tailp->state));
int state = tailp->state;
if (state == DP_STATE_CONNECTING) {
- if (dp_cancel_agent_download_without_update(tailp->agent_id) < 0)
- TRACE_ERROR("failed to cancel download(%d) id:%d", tailp->agent_id, tailp->id);
+ int ret;
+ if (tailp->cache_agent_id >= 0) {
+ ret = dp_cancel_cache_download_without_update(tailp->cache_agent_id);
+ } else {
+ ret = dp_cancel_agent_download_without_update(tailp->agent_id);
+ }
+ if (ret < 0) {
+ TRACE_ERROR("failed to cancel download for id(%d). agent-id:%d cache-id:%d", tailp->id,
+ tailp->agent_id, tailp->cache_agent_id);
+ }
} else if (state == DP_STATE_DOWNLOADING) {
- if (dp_pause_agent_download(tailp->agent_id) < 0)
- TRACE_ERROR("failed to pause download(%d) id:%d", tailp->agent_id, tailp->id);
+ int ret;
+ if (tailp->cache_agent_id >= 0) {
+ ret = dp_pause_cache_download(tailp->cache_agent_id);
+ } else {
+ ret = dp_pause_agent_download(tailp->agent_id);
+ }
+ if (ret < 0) {
+ TRACE_ERROR("failed to pause download for id(%d). agent-id:%d cache-id:%d", tailp->id,
+ tailp->agent_id, tailp->cache_agent_id);
+ }
}
if (state == DP_STATE_DOWNLOADING || state == DP_STATE_CONNECTING) {
tailp->state = DP_STATE_QUEUED;
sigaddset(&newmask, SIGUSR1);
act.sa_handler = dp_client_sig_handler;
sigaction(SIGUSR1, &act, NULL);
-
fd_set imask, emask;
int errorcode = DP_ERROR_NONE;
dp_client_fmt *client = &slot->client;
int client_sock = client->channel;
struct timeval timeout; // for timeout of select
-
CLIENT_MUTEX_UNLOCK(&slot->mutex);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
-
while (slot != NULL && client_sock >= 0 &&
client_sock == slot->client.channel) {
memset(&timeout, 0x00, sizeof(struct timeval));
#include <stdio.h>
#include <stdlib.h> // alloc
//#include <unistd.h> // unlink
+#include <string.h>
#include <sqlite3.h>
#include "download-provider-db.h"
#include "download-provider-log.h"
#include "download-provider-utils.h"
+#include "download-provider-cache.h"
+#include "download-provider-plugin-cache-agent.h"
+#define DP_DB_PARAM_NULL_CHECK do {\
+ if (handle == 0) {\
+ TRACE_ERROR("check connection handle");\
+ return -1;\
+ } \
+} while (0)
+
+#define DP_DB_BUFFER_NULL_CHECK(buffer) do {\
+ if (buffer == NULL) {\
+ TRACE_ERROR("check available memory");\
+ return -1;\
+ } \
+} while (0)
+
+#define DP_DB_BASIC_EXCEPTION_CHECK do {\
+ if (errorcode != SQLITE_OK) {\
+ if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)\
+ *error = DP_ERROR_INVALID_PARAMETER;\
+ __dp_finalize(stmt);\
+ return -1;\
+ } \
+} while (0)
+
+#define DP_DB_WRITE_STEP_EXCEPTION_CHECK do {\
+ errorcode = sqlite3_step(stmt);\
+ __dp_finalize(stmt);\
+ if (errorcode != SQLITE_DONE) {\
+ if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)\
+ *error = DP_ERROR_INVALID_PARAMETER;\
+ return -1;\
+ } \
+} while (0)
static void __basic_property(sqlite3 *handle)
{
return 0;
}
+static bool __check_cache_directory_empty(void)
+{
+ int entry_count = 0;
+ bool ret = false;
+ char *cache_path = NULL;
+ struct dirent *entry;
+ DIR *dir;
+
+ if (dp_get_cache_path(&cache_path) != DP_ERROR_NONE)
+ return ret;
+
+ if (!cache_path)
+ return ret;
+
+ dir = opendir(cache_path);
+ if (!dir) {
+ free(cache_path);
+ return ret;
+ }
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (++entry_count > 2)
+ break;
+ }
+
+ if (entry_count <= 2) {
+ TRACE_DEBUG("Cache directory is empty: %s, entry count: %d",
+ cache_path, entry_count);
+ ret = true;
+ }
+
+ free(cache_path);
+ closedir(dir);
+
+ return ret;
+}
+
+static time_t __dp_get_oldest_cache_file_time_from_db(void *handle, int *error)
+{
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+ time_t result = 0;
+ *error = DP_ERROR_INVALID_PARAMETER;
+
+ DP_DB_PARAM_NULL_CHECK;
+
+ char *query = sqlite3_mprintf("SELECT MIN(%s) FROM %s",
+ DP_DB_COL_CACHE_LAST_UPDATE, DP_TABLE_CACHE_FILE);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+
+ errorcode = sqlite3_step(stmt);
+ if (errorcode == SQLITE_ROW) {
+ int data_type = sqlite3_column_type(stmt, 0);
+ if (data_type == SQLITE_INTEGER) {
+ long long last_update;
+ last_update = sqlite3_column_int64(stmt, 0);
+ if (last_update < 0)
+ result = 0;
+ else
+ result = (time_t)last_update;
+ } else {
+ TRACE_ERROR("check column type:%d", data_type);
+ *error = DP_ERROR_NO_DATA;
+ }
+ } else if (errorcode == SQLITE_DONE) {
+ TRACE_DEBUG("no data");
+ *error = DP_ERROR_NO_DATA;
+ } else {
+ if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)
+ *error = DP_ERROR_ID_NOT_FOUND;
+ }
+
+ __dp_finalize(stmt);
+
+ return result;
+}
+
static int __rebuild_client_manager_tables(sqlite3 *handle)
{
int ret = SQLITE_OK;
+ int errorcode = DP_ERROR_NONE;
+ static int cache_path_initial_check = DP_ERROR_UNKNOWN;
+
if (__check_table(handle, DP_TABLE_CLIENTS) < 0)
ret = sqlite3_exec(handle, DP_SCHEMA_CLIENTS, 0, 0, 0);
if (ret != SQLITE_OK) {
- TRACE_ERROR("create tables:%d error:%s", ret, sqlite3_errmsg(handle));
+ TRACE_ERROR("create clients table:%d error:%s", ret, sqlite3_errmsg(handle));
+ return -1;
+ }
+ if (__check_table(handle, DP_TABLE_CACHE) < 0)
+ ret = sqlite3_exec(handle, DP_SCHEMA_CACHE, 0, 0, 0);
+ if (ret != SQLITE_OK) {
+ TRACE_ERROR("create cache table:%d error:%s", ret, sqlite3_errmsg(handle));
return -1;
}
+ if (__check_table(handle, DP_TABLE_CACHE_FILE) < 0)
+ ret = sqlite3_exec(handle, DP_SCHEMA_CACHE_FILE, 0, 0, 0);
+ if (ret != SQLITE_OK) {
+ TRACE_ERROR("create cache_file table:%d error:%s", ret, sqlite3_errmsg(handle));
+ return -1;
+ }
+
+ if (cache_path_initial_check == DP_ERROR_UNKNOWN) {
+ if (__check_cache_directory_empty()) {
+ dp_cache_delete_all_from_db(&errorcode);
+ } else {
+ time_t oldest_time = 0;
+ int error;
+
+ oldest_time = __dp_get_oldest_cache_file_time_from_db(handle, &error);
+ dp_set_oldest_cache_file_time(oldest_time);
+ }
+ cache_path_initial_check = DP_ERROR_ALREADY_COMPLETED;
+ }
+
return 0;
}
return DP_ERROR_NONE;
}
-
-#define DP_DB_PARAM_NULL_CHECK do {\
- if (handle == 0) {\
- TRACE_ERROR("check connection handle");\
- return -1;\
- } \
-} while (0)
-
-#define DP_DB_BUFFER_NULL_CHECK(buffer) do {\
- if (buffer == NULL) {\
- TRACE_ERROR("check available memory");\
- return -1;\
- } \
-} while (0)
-
-#define DP_DB_BASIC_EXCEPTION_CHECK do {\
- if (errorcode != SQLITE_OK) {\
- if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)\
- *error = DP_ERROR_INVALID_PARAMETER;\
- __dp_finalize(stmt);\
- return -1;\
- } \
-} while (0)
-
-#define DP_DB_WRITE_STEP_EXCEPTION_CHECK do {\
- errorcode = sqlite3_step(stmt);\
- __dp_finalize(stmt);\
- if (errorcode != SQLITE_DONE) {\
- if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)\
- *error = DP_ERROR_INVALID_PARAMETER;\
- return -1;\
- } \
-} while (0)
-
int dp_db_get_ids(void *handle, const char *table, char *idcolumn, int *ids, const char *where, const int limit, char *ordercolumn, char *ordering, int *error)
{
*error = DP_ERROR_INVALID_PARAMETER;
return 0;
}
+int dp_db_delete_all(void *handle, const char *table, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ DP_DB_PARAM_NULL_CHECK;
+ if (table == NULL) {
+ TRACE_ERROR("check materials for query");
+ return -1;
+ }
+
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+ char *query = sqlite3_mprintf("DELETE FROM %s", table);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+ return 0;
+}
+
int dp_db_new_header(void *handle, const int id, const char *field, const char *value, int *error)
{
*error = DP_ERROR_INVALID_PARAMETER;
return 0;
}
+static int __dp_db_increase_file_refcount(void *handle, const int id, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (id <= 0) {
+ TRACE_ERROR("check id:%d", id);
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "UPDATE %s SET %s=%s+1 WHERE %s IS ?",
+ DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_ID);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int(stmt, 1, id);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+ int refcount = 0;
+ if (dp_db_get_property_int(handle, id, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_REFCOUNT, &refcount, &errorcode) < 0) {
+ TRACE_ERROR("failed to get cache refcount");
+ *error = DP_ERROR_NO_DATA;
+ return -1;
+ }
+
+ return refcount;
+}
+
+static int __dp_db_decrease_file_refcount(void *handle, const int id, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (id <= 0) {
+ TRACE_ERROR("check id:%d", id);
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "UPDATE %s SET %s=%s-1 WHERE %s IS ?",
+ DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_ID);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int(stmt, 1, id);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+ int refcount = -1;
+ if (dp_db_get_property_int(handle, id, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_REFCOUNT, &refcount, error) < 0
+ || refcount < 0) {
+ TRACE_ERROR("failed to get cache refcount");
+ return -1;
+ }
+
+ if (refcount == 0) {
+ if (dp_db_delete(handle, id, DP_TABLE_CACHE_FILE, error) < 0) {
+ TRACE_ERROR("failed to delete cache file");
+ return -1;
+ }
+ }
+
+ return refcount;
+}
+
+static int __dp_db_add_app_cache(void *handle, const char *pkgname, const char *url, const char *file_name, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!pkgname || !url || !file_name) {
+ TRACE_ERROR("NULL CHECK!: pkgname, url or file_name");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "INSERT INTO %s (%s, %s, %s, %s) VALUES (?, ?, ?, ?)",
+ DP_TABLE_CACHE, DP_DB_COL_CACHE_KEY, DP_DB_COL_URL, DP_DB_COL_CACHE_FILE_NAME, DP_DB_COL_PACKAGE);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ unsigned long key = dp_get_hash_key(url);
+ errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 2, url, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 3, file_name, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 4, pkgname, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+ return 0;
+}
+
+int dp_db_new_app_cache(void *handle, const char *pkgname, const char *url, const char *file_name, bool new_file, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ DP_DB_PARAM_NULL_CHECK;
+ if (!pkgname || !url || !file_name) {
+ TRACE_ERROR("NULL CHECK!: pkgname, url or file_name");
+ return -1;
+ }
+
+ if (__dp_db_add_app_cache(handle, pkgname, url, file_name, error) < 0) {
+ TRACE_ERROR("failed to add new cache data");
+ return -1;
+ }
+
+ if (!new_file) {
+ int id = dp_db_get_cache_file_id(handle, file_name, error);
+ if (id < 0) {
+ TRACE_ERROR("failed to get cache file id");
+ return -1;
+ }
+ if (__dp_db_increase_file_refcount(handle, id, error) < 0) {
+ TRACE_ERROR("failed to increase file refcount");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int dp_db_new_file_cache(void *handle, const char *file_name, const char *content_name, const char *etag,
+ const long max_age, const unsigned long long file_size, const long now, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!file_name || !content_name) {
+ TRACE_ERROR("NULL CHECK!: file_name or content_name");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s) VALUES (?, ?, ?, ?, ?, 1, ?, ?)",
+ DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_KEY, DP_DB_COL_CACHE_FILE_NAME, DP_DB_COL_FILENAME, DP_DB_COL_ETAG,
+ DP_DB_COL_CACHE_FILE_SIZE, DP_DB_COL_CACHE_REFCOUNT, DP_DB_COL_CACHE_MAX_AGE, DP_DB_COL_CACHE_LAST_UPDATE);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ unsigned long key = dp_get_hash_key(file_name);
+ errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 2, file_name, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 3, content_name, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 4, etag, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int64(stmt, 5, (sqlite_int64)file_size);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int64(stmt, 6, (sqlite_int64)max_age);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int64(stmt, 7, (sqlite_int64)now);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+ return 0;
+}
+
+int dp_db_clear_app_cache(void *handle, const char *pkgname, GSList **files, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+ GSList *file_list = NULL;
+ int file_count = 0;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!pkgname || !files) {
+ TRACE_ERROR("NULL CHECK!: pkgname or files");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "SELECT %s, %s FROM %s WHERE %s IS ?",
+ DP_DB_COL_ID, DP_DB_COL_CACHE_FILE_NAME, DP_TABLE_CACHE, DP_DB_COL_PACKAGE);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ errorcode = sqlite3_bind_text(stmt, 1, (char *)pkgname, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) {
+ int get_id = sqlite3_column_int(stmt, 0);
+ const char *get_file = (const char *)sqlite3_column_text(stmt, 1);
+ if (dp_db_delete(handle, get_id, DP_TABLE_CACHE, &errorcode) < 0) {
+ TRACE_DEBUG("failed to delete app cache from cache table");
+ *error = DP_ERROR_NONE;
+ continue;
+ }
+ int id = dp_db_get_cache_file_id(handle, get_file, error);
+ if (id < 0) {
+ TRACE_DEBUG("failed to get cache file id");
+ *error = DP_ERROR_NONE;
+ continue;
+ }
+ int refcount = __dp_db_decrease_file_refcount(handle, id, error);
+ if (refcount < 0) {
+ TRACE_DEBUG("failed to decrease file refcount");
+ *error = DP_ERROR_NONE;
+ continue;
+ }
+ if (refcount == 0) {
+ int get_byte = sqlite3_column_bytes(stmt, 1);
+ if (get_byte > 0) {
+ char *get_str = (char *)g_try_malloc0((get_byte + 1) * sizeof(char));
+ if (!get_str) {
+ TRACE_ERROR("check available system memory");
+ *error = DP_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ memcpy(get_str, get_file, get_byte * sizeof(char));
+ get_str[get_byte] = '\0';
+ file_list = g_slist_append(file_list, get_str);
+ file_count++;
+ }
+ }
+ }
+ __dp_finalize(stmt);
+
+ if (*error != DP_ERROR_NONE) {
+ if (file_list)
+ g_slist_free_full(file_list, g_free);
+ return -1;
+ }
+ *files = file_list;
+ return file_count;
+}
+
+static int __dp_db_update_cache_file_name(void *handle, const int id, const char *file_name, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (id <= 0) {
+ TRACE_ERROR("check id:%d", id);
+ return -1;
+ }
+ if (!file_name) {
+ TRACE_ERROR("NULL CHECK!: file_name");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf("UPDATE %s SET %s = ? WHERE %s IS ?",
+ DP_TABLE_CACHE, DP_DB_COL_CACHE_FILE_NAME, DP_DB_COL_ID);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ errorcode = sqlite3_bind_text(stmt, 1, (char *)file_name, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int(stmt, 2, id);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+ return 0;
+}
+
+int dp_db_update_app_cache(void *handle, const char *url, const char *file_name, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!url || !file_name) {
+ TRACE_ERROR("NULL CHECK!: url or file_name");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "SELECT %s FROM %s WHERE %s IS ? AND %s IS ?",
+ DP_DB_COL_ID, DP_TABLE_CACHE, DP_DB_COL_CACHE_KEY, DP_DB_COL_URL);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ unsigned long key = dp_get_hash_key(url);
+ errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 2, (char *)url, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) {
+ int get_id = sqlite3_column_int(stmt, 0);
+ if (__dp_db_update_cache_file_name(handle, get_id, file_name, error) < 0) {
+ TRACE_ERROR("failed to update app cache file");
+ break;
+ }
+ }
+
+ __dp_finalize(stmt);
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+int dp_db_update_file_cache(void *handle, const int id, const char *file_name, const char *etag,
+ const long max_age, const unsigned long long file_size, const long now, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (id <= 0) {
+ TRACE_ERROR("check id:%d", id);
+ return -1;
+ }
+ if (!file_name) {
+ TRACE_ERROR("NULL CHECK!: file_name");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf("UPDATE %s SET %s = ?, %s = ?, %s = ?, %s = ?, %s = ?, %s = ? WHERE %s IS ?",
+ DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_FILE_NAME, DP_DB_COL_CACHE_KEY, DP_DB_COL_ETAG,
+ DP_DB_COL_CACHE_MAX_AGE, DP_DB_COL_CACHE_FILE_SIZE, DP_DB_COL_CACHE_LAST_UPDATE, DP_DB_COL_ID);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ errorcode = sqlite3_bind_text(stmt, 1, (char *)file_name, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ unsigned long key = dp_get_hash_key(file_name);
+ errorcode = sqlite3_bind_int64(stmt, 2, (sqlite_int64)key);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 3, (char *)etag, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int64(stmt, 4, (sqlite_int64)max_age);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int64(stmt, 5, (sqlite_int64)file_size);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int64(stmt, 6, (sqlite_int64)now);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_int(stmt, 7, id);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+ return 0;
+}
+
+// type 1:max_age or last_update 2:etag
+int dp_db_update_file_cache_validator(void *handle, const int id, const char *column, const int type, void *value, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (id <= 0) {
+ TRACE_ERROR("check id:%d", id);
+ return -1;
+ }
+ if (type != 1 && type != 2) {
+ TRACE_ERROR("check type:%d", type);
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf("UPDATE %s SET %s = ? WHERE %s IS ?", DP_TABLE_CACHE_FILE, column, DP_DB_COL_ID);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ if (type == 1) {
+ long *val_ptr = (long *)value;
+ errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)*val_ptr);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ } else if (type == 2) {
+ char *val_ptr = (char *)value;
+ errorcode = sqlite3_bind_text(stmt, 1, (char *)val_ptr, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ }
+
+ errorcode = sqlite3_bind_int(stmt, 2, id);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+ return 0;
+}
+
+int dp_db_get_file_cache_string(void *handle, const char *file_name, const char *column, unsigned char **string, unsigned *length, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+ int found = 0;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!file_name || !column || !string || !length) {
+ TRACE_ERROR("NULL CHECK!: file_name, column, string or length");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf("SELECT %s FROM %s WHERE %s IS ? AND %s IS ?", column, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_KEY, DP_DB_COL_CACHE_FILE_NAME);
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ unsigned long key = dp_get_hash_key(file_name);
+ errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 2, (char *)file_name, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ errorcode = sqlite3_step(stmt);
+ if (errorcode == SQLITE_ROW) {
+ const char *get_col = (const char *)sqlite3_column_text(stmt, 0);
+ int get_byte = sqlite3_column_bytes(stmt, 0);
+ if (get_byte > 0) {
+ unsigned char *get_str = (unsigned char *)calloc(get_byte + 1, sizeof(unsigned char));
+ if (!get_str) {
+ TRACE_ERROR("check available system memory");
+ *error = DP_ERROR_OUT_OF_MEMORY;
+ __dp_finalize(stmt);
+ return -1;
+ }
+
+ memcpy(get_str, get_col, get_byte * sizeof(unsigned char));
+ get_str[get_byte] = '\0';
+ *string = get_str;
+ *length = get_byte;
+ found = 1;
+ } else {
+ TRACE_DEBUG("no data");
+ *error = DP_ERROR_NO_DATA;
+ }
+ } else if (errorcode == SQLITE_DONE) {
+ TRACE_DEBUG("no data");
+ *error = DP_ERROR_NO_DATA;
+ } else {
+ if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)
+ *error = DP_ERROR_ID_NOT_FOUND;
+ }
+ __dp_finalize(stmt);
+
+ if (*error != DP_ERROR_NO_DATA && *error != DP_ERROR_NONE)
+ return -1;
+
+ return found;
+}
+
+int dp_db_get_cache_file_name(void *handle, const char *pkgname, const char *url, char **file_name, unsigned *length, int *pkg_exist, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+ int found = 0;
+ *pkg_exist = 0;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!pkgname || !url || !file_name || !length) {
+ TRACE_ERROR("NULL CHECK!: pkgname, url, file_name or length");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "SELECT %s, %s FROM %s WHERE %s IS ? AND %s IS ?",
+ DP_DB_COL_PACKAGE, DP_DB_COL_CACHE_FILE_NAME, DP_TABLE_CACHE, DP_DB_COL_CACHE_KEY, DP_DB_COL_URL);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ unsigned long key = dp_get_hash_key(url);
+ errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 2, (char *)url, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) {
+ const char *get_pkg = (const char *)sqlite3_column_text(stmt, 0);
+ const char *get_file = (const char *)sqlite3_column_text(stmt, 1);
+ if (found == 0) {
+ int get_byte = sqlite3_column_bytes(stmt, 1);
+ if (get_byte > 0) {
+ char *get_str = (char *)calloc(get_byte + 1, sizeof(char));
+ if (!get_str) {
+ TRACE_ERROR("check available system memory");
+ *error = DP_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ memcpy(get_str, get_file, get_byte * sizeof(char));
+ get_str[get_byte] = '\0';
+ *file_name = get_str;
+ *length = get_byte;
+ found = 1;
+ }
+ }
+ if (strcmp(get_pkg, pkgname) == 0)
+ *pkg_exist = 1;
+ }
+ __dp_finalize(stmt);
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ if (found == 0)
+ *error = DP_ERROR_NO_DATA;
+
+ return found;
+}
+
+int dp_db_get_cache_file_id(void *handle, const char *file_name, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ int errorcode = SQLITE_OK;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!file_name) {
+ TRACE_ERROR("NULL CHECK!: file_name");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "SELECT %s FROM %s WHERE %s IS ? AND %s IS ?",
+ DP_DB_COL_ID, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_KEY, DP_DB_COL_CACHE_FILE_NAME);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ //TRACE_DEBUG("debug query:%s", query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ unsigned long key = dp_get_hash_key(file_name);
+ errorcode = sqlite3_bind_int64(stmt, 1, (sqlite_int64)key);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 2, (char *)file_name, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ errorcode = sqlite3_step(stmt);
+ if (errorcode == SQLITE_ROW) {
+ int get_id = sqlite3_column_int(stmt, 0);
+ __dp_finalize(stmt);
+ return get_id;
+ }
+
+ if (errorcode == SQLITE_DONE) {
+ TRACE_ERROR("no data");
+ *error = DP_ERROR_NO_DATA;
+ } else {
+ if ((*error = dp_db_get_errorcode(handle)) == DP_ERROR_NONE)
+ *error = DP_ERROR_ID_NOT_FOUND;
+ }
+
+ __dp_finalize(stmt);
+ return -1;
+}
+
+static long long __get_capacity_size_threshold(long long max_size)
+{
+ long long deletion_threshold = 0;
+ long long used_cache_size;
+
+ if (dp_get_used_cache_size(&used_cache_size)) {
+ TRACE_ERROR("dp_get_used_cache_size failed!!");
+ return -1;
+ }
+
+ deletion_threshold = max_size - max_size * CACHE_CONTROL_DELETION_RATIO;
+
+ if (deletion_threshold > used_cache_size) {
+ TRACE_ERROR("No need to clear!!");
+ return -1;
+ }
+
+ deletion_threshold = used_cache_size - deletion_threshold;
+
+ return deletion_threshold;
+}
+
+int dp_db_clear_exceeded_cache(void *handle, GSList **files, long long max_val, int type, int *error)
+{
+ int errorcode = DP_ERROR_NONE;
+ sqlite3_stmt *stmt = NULL;
+ GSList *file_list = NULL;
+ time_t now_time = 0;
+ const char *col_str = NULL;
+ long long deletion_threshold = 0;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!files) {
+ TRACE_ERROR("NULL CHECK!: files");
+ return -1;
+ }
+
+ if (type == DP_CACHE_CAPACITY_MAX_SIZE) {
+ col_str = DP_DB_COL_CACHE_FILE_SIZE;
+
+ deletion_threshold = __get_capacity_size_threshold(max_val);
+ if (deletion_threshold < 0)
+ return -1;
+
+ } else if (type == DP_CACHE_CAPACITY_LIFECYCLE) {
+ now_time = time(NULL);
+ col_str = DP_DB_COL_CACHE_LAST_UPDATE;
+ deletion_threshold = max_val - max_val * CACHE_CONTROL_DELETION_RATIO;
+ } else {
+ TRACE_ERROR("Invalid type: %d", type);
+ return -1;
+ }
+
+ TRACE_DEBUG("max_val : %lld, deletion_threshold: %lld, type: %d",
+ max_val, deletion_threshold, type);
+
+ char *query = sqlite3_mprintf(
+ "SELECT %s, %s, %s FROM %s ORDER BY %s",
+ DP_DB_COL_ID, DP_DB_COL_CACHE_FILE_NAME, col_str,
+ DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_LAST_UPDATE);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ *error = DP_ERROR_NONE;
+ while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) {
+ int get_id = sqlite3_column_int(stmt, 0);
+ const char *get_file = (const char *)sqlite3_column_text(stmt, 1);
+ long long get_value = sqlite3_column_int64(stmt, 2);
+
+ if (get_value < 0)
+ get_value = 0;
+
+ if (type == DP_CACHE_CAPACITY_MAX_SIZE) {
+ deletion_threshold -= get_value;
+ if (0 > deletion_threshold)
+ break;
+ } else {
+ if (difftime(now_time, (time_t)get_value) < deletion_threshold) {
+ dp_set_oldest_cache_file_time((time_t)get_value);
+ break;
+ }
+ }
+
+ if (dp_db_delete(handle, get_id, DP_TABLE_CACHE_FILE, &errorcode) < 0) {
+ TRACE_DEBUG("failed to delete cache data from cache_file table");
+ *error = DP_ERROR_NONE;
+ continue;
+ }
+
+ int get_byte = sqlite3_column_bytes(stmt, 1);
+ if (get_byte > 0) {
+ char *get_str = (char *)g_try_malloc0((get_byte + 1) * sizeof(char));
+ if (!get_str) {
+ TRACE_ERROR("check available system memory");
+ *error = DP_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ memcpy(get_str, get_file, get_byte * sizeof(char));
+ get_str[get_byte] = '\0';
+ file_list = g_slist_append(file_list, get_str);
+ }
+
+ }
+ __dp_finalize(stmt);
+
+ TRACE_DEBUG("file_list count: %u", g_slist_length(file_list));
+
+ if (*error != DP_ERROR_NONE) {
+ if (file_list)
+ g_slist_free_full(file_list, g_free);
+ return -1;
+ }
+
+ *files = file_list;
+ return 0;
+}
+
+int dp_db_clear_app_cache_by_filename(void *handle, const char *file_name, int *error)
+{
+ int errorcode = DP_ERROR_NONE;
+ sqlite3_stmt *stmt = NULL;
+
+ DP_DB_PARAM_NULL_CHECK;
+ if (!file_name) {
+ TRACE_ERROR("NULL CHECK!: file_name");
+ return -1;
+ }
+
+ char *query = sqlite3_mprintf(
+ "DELETE FROM %s WHERE %s IS ?",
+ DP_TABLE_CACHE, DP_DB_COL_CACHE_FILE_NAME);
+
+ DP_DB_BUFFER_NULL_CHECK(query);
+ errorcode = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
+ sqlite3_free(query);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+ errorcode = sqlite3_bind_text(stmt, 1, file_name, -1, SQLITE_STATIC);
+ DP_DB_BASIC_EXCEPTION_CHECK;
+
+ DP_DB_WRITE_STEP_EXCEPTION_CHECK;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+
+#include <download-provider.h>
+#include <download-provider-log.h>
+#include <download-provider-pthread.h>
+#include <download-provider-ipc.h>
+#include <download-provider-db-defs.h>
+#include <download-provider-db.h>
+#include <download-provider-utils.h>
+#include <download-provider-notify.h>
+#include <download-provider-security.h>
+#include <download-provider-client.h>
+#include <download-provider-client-manager.h>
+#include <download-provider-plugin-cache-agent.h>
+#include <download-provider-notification-manager.h>
+#include <download-provider-queue-manager.h>
+#include <download-provider-cache.h>
+
+#include <cache-agent-defs.h>
+#include <cache-agent-interface.h>
+#include <tzplatform_config.h>
+
+#include "xdgmime.h"
+#include "content/mime_type.h"
+
+#define DP_SDCARD_MNT_POINT tzplatform_mkpath(TZ_SYS_STORAGE, "sdcard")
+#define DP_EXTERNAL_STORAGE "/opt/media/"
+#define DP_MAX_FILE_PATH_LEN 256
+#define DP_MAX_MIME_TABLE_NUM 15
+
+static pthread_mutex_t mutex_capacity_max = PTHREAD_MUTEX_INITIALIZER;
+static bool g_capacity_max = false;
+
+static void *g_ca_handle = NULL;
+
+static int (*cache_agent_init)(ca_capacity_event_cb) = NULL;
+static int (*cache_agent_deinit)(void) = NULL;
+
+static char *(*cache_agent_store_file)(const char *) = NULL;
+static int (*cache_agent_remove_files)(int, const char **) = NULL;
+static int (*cache_agent_is_file_available)(const char *) = NULL;
+static int (*cache_agent_get_used_cache_size)(ca_size_t *) = NULL;
+
+static int (*cache_agent_start_download)(const char *, ca_req_data_t *, ca_cb_t *, int *) = NULL;
+static int (*cache_agent_pause_download)(int) = NULL;
+static int (*cache_agent_resume_download)(int) = NULL;
+static int (*cache_agent_cancel_download)(int) = NULL;
+static int (*cache_agent_pause_download_without_update)(int) = NULL;
+static int (*cache_agent_cancel_download_without_update)(int) = NULL;
+static int (*cache_agent_is_alive_download)(int) = NULL;
+
+static int (*cache_agent_get_max_cache_size)(unsigned int *) = NULL;
+static int (*cache_agent_set_max_cache_size)(unsigned int) = NULL;
+static int (*cache_agent_get_cache_path)(char **) = NULL;
+static int (*cache_agent_set_cache_path)(const char *) = NULL;
+static int (*cache_agent_get_cache_lifecycle)(unsigned int *) = NULL;
+static int (*cache_agent_set_cache_lifecycle)(unsigned int) = NULL;
+static int (*cache_agent_clear_all_files)(void) = NULL;
+static int (*cache_agent_set_oldest_file_time)(ca_time_t) = NULL;
+
+static int __ca_change_error(int err)
+{
+ int ret = DP_ERROR_NONE;
+ switch (err) {
+ case CA_RESULT_OK:
+ ret = DP_ERROR_NONE;
+ break;
+ case CA_ERR_INVALID_ARGUMENT:
+ ret = DP_ERROR_INVALID_PARAMETER;
+ break;
+ case CA_ERR_FAIL_TO_MEMALLOC:
+ ret = DP_ERROR_OUT_OF_MEMORY;
+ break;
+ case CA_ERR_DISK_FULL:
+ ret = DP_ERROR_NO_SPACE;
+ break;
+ case CA_ERR_ALREADY_MAX_COPY:
+ ret = DP_ERROR_TOO_MANY_DOWNLOADS;
+ break;
+ case CA_ERR_INVALID_STATE:
+ ret = DP_ERROR_INVALID_STATE;
+ break;
+ case CA_ERR_FAIL_TO_CREATE_THREAD:
+ case CA_ERR_FAIL_TO_ACCESS_FILE:
+ default:
+ ret = DP_ERROR_IO_ERROR;
+ break;
+ }
+ return ret;
+}
+
+static void __remove_trailing_slash(char *dir)
+{
+ int dir_path_len = 0;
+
+ dir_path_len = strlen(dir);
+ if (dir[dir_path_len - 1] == '/')
+ dir[dir_path_len - 1] = '\0';
+}
+
+static int __set_file_permission_to_client(dp_client_slots_fmt *slot, dp_request_fmt *request, char *saved_path)
+{
+ if (slot == NULL || request == NULL || saved_path == NULL) {
+ TRACE_ERROR("slot:%p request:%p id:%d cache agent id:%d saved_path:%s",
+ slot, request, (request == NULL ? 0 : request->id),
+ (request == NULL ? 0 : request->cache_agent_id), saved_path ? saved_path : "");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (strncmp(saved_path, DP_EXTERNAL_STORAGE, strlen(DP_EXTERNAL_STORAGE)) == 0
+ || strncmp(DP_SDCARD_MNT_POINT,
+ saved_path, strlen(DP_SDCARD_MNT_POINT)) == 0) {
+ TRACE_INFO("Do not change permission for %s", saved_path);
+ return DP_ERROR_NONE;
+ }
+
+ struct stat lstat_info;
+ int fd;
+ dp_credential cred = slot->credential;
+
+ if (lstat(saved_path, &lstat_info) == -1) {
+ TRACE_ERROR("Cannot access to %s", saved_path);
+ return DP_ERROR_PERMISSION_DENIED;
+ }
+
+ if ((lstat_info.st_mode & S_IFMT) == S_IFLNK) {
+ TRACE_ERROR("%s is a symbolic link.", saved_path);
+ return DP_ERROR_PERMISSION_DENIED;
+ }
+
+ fd = open(saved_path, O_RDONLY);
+ if (fd == -1) {
+ TRACE_SECURE_ERROR("open failed for file : %s", saved_path);
+ return DP_ERROR_IO_ERROR;
+ }
+
+ // Change the owner to client's uid.
+ if (fchown(fd, cred.uid, lstat_info.st_gid) != 0) {
+ TRACE_ERROR("[ERROR][%d] permission user:%d group:%d",
+ request->id, cred.uid, lstat_info.st_gid);
+ close(fd);
+ return DP_ERROR_PERMISSION_DENIED;
+ }
+
+ TRACE_INFO("file owner has been changed to %d:%d", cred.uid, lstat_info.st_gid);
+
+ close(fd);
+ return DP_ERROR_NONE;
+}
+
+static int __ca_precheck_request(dp_client_slots_fmt* slot, dp_request_fmt *request, int agentid)
+{
+ if (request == NULL) {
+ TRACE_ERROR("null-check req_id:%d", agentid);
+ return -1;
+ }
+
+ dp_client_fmt *client = &slot->client;
+
+ for (dp_request_fmt *req = client->requests; req; req = req->next) {
+ if (req == request) {
+ if (request->id < 0 || (request->cache_agent_id != agentid)) {
+ TRACE_ERROR("id-check request_id:%d cache_id:%d req_id:%d",
+ request->id, request->cache_agent_id, agentid);
+ return -1;
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int __dp_ca_state_feedback(dp_client_slots_fmt *slot, dp_request_fmt *request)
+{
+ if (slot == NULL || request == NULL) {
+ TRACE_ERROR("check address");
+ return -1; // try cancel
+ }
+
+ TRACE_INFO("[INFO][%d] state:%s error:%s", request->id,
+ dp_print_state(request->state), dp_print_errorcode(request->error));
+
+ int errorcode = DP_ERROR_NONE;
+ if (dp_db_update_logging(slot->client.dbhandle, request->id,
+ request->state, request->error, &errorcode) < 0) {
+ TRACE_ERROR("logging failure id:%d error:%d", request->id, errorcode);
+ return -1; // try cancel
+ }
+
+ request->access_time = (int)time(NULL);
+
+ if (request->state_cb == 1) {
+ if (slot->client.notify < 0 ||
+ dp_notify_feedback(slot->client.notify, slot, request->id, request->state, request->error, 0) < 0) {
+ TRACE_ERROR("id:%d disable state callback by IO_ERROR", request->id);
+ request->state_cb = 0;
+ }
+ }
+
+ return 0;
+}
+
+static GSList *__capacity_event_cb(ca_capacity_event_type capa_type)
+{
+ GSList *file_list = NULL;
+ unsigned int ref_val;
+ long long max_cache_size;
+
+ pthread_mutex_lock(&(mutex_capacity_max));
+ g_capacity_max = true;
+ pthread_mutex_unlock(&(mutex_capacity_max));
+
+ if (capa_type == CA_CAPACITY_MAX_SIZE) {
+ if (DP_ERROR_NONE != dp_get_max_cache_size(&ref_val))
+ goto done;
+
+ max_cache_size = (long long)ref_val * CA_MB_BYTE;
+ file_list = dp_cache_clear_exceeded_files(max_cache_size, DP_CACHE_CAPACITY_MAX_SIZE);
+
+ } else if (capa_type == CA_CAPACITY_LIFECYCLE) {
+ if (DP_ERROR_NONE != dp_get_cache_lifecycle(&ref_val))
+ goto done;
+
+ file_list = dp_cache_clear_exceeded_files((long long)ref_val, DP_CACHE_CAPACITY_LIFECYCLE);
+ }
+
+done:
+ pthread_mutex_lock(&(mutex_capacity_max));
+ g_capacity_max = false;
+ pthread_mutex_unlock(&(mutex_capacity_max));
+
+ return file_list;
+}
+
+static void __cache_finished_cb(ca_finished_info_t *info,
+ void *user_req_data, void *user_client_data)
+{
+ if (info == NULL) {
+ TRACE_ERROR("check cache info address");
+ return;
+ }
+
+ dp_client_slots_fmt *slot = user_client_data;
+ dp_request_fmt *request = user_req_data;
+ if (slot == NULL || request == NULL) {
+ TRACE_ERROR("check address slot:%p request:%p id:%d agent id:%d",
+ slot, request, (request == NULL ? 0 : request->id), info->cache_id);
+ return;
+ }
+
+ CLIENT_MUTEX_LOCK(&slot->mutex);
+ if (__ca_precheck_request(slot, request, info->cache_id) < 0) {
+ TRACE_ERROR("error request agent_id:%d", info->cache_id);
+ if (dp_cancel_cache_download(info->cache_id) < 0)
+ TRACE_ERROR("failed to call cancel_cache_download(%d)", info->cache_id);
+
+ CLIENT_MUTEX_UNLOCK(&slot->mutex);
+ return ;
+ }
+
+ int state = DP_STATE_NONE;
+ int errorcode = DP_ERROR_NONE;
+
+ if (info->http_status > 0) {
+ if (dp_db_replace_property(slot->client.dbhandle, request->id,
+ DP_TABLE_DOWNLOAD, DP_DB_COL_HTTP_STATUS, (void *)&info->http_status, 0, 0, &errorcode) < 0)
+ TRACE_ERROR("id:%d failed to set http_status(%d)", request->id, info->http_status);
+ }
+
+ TRACE_SECURE_DEBUG("[FINISH][%d][%s]", request->id, info->saved_path);
+
+ if (info->error == CA_RESULT_OK) {
+ if (info->saved_path != NULL) {
+ errorcode = __set_file_permission_to_client(slot,
+ request, info->saved_path);
+ } else {
+ TRACE_ERROR("[ERROR][%d] No SavedPath", request->id);
+ errorcode = DP_ERROR_INVALID_DESTINATION;
+ }
+ if (errorcode == DP_ERROR_NONE)
+ state = DP_STATE_COMPLETED;
+ else
+ state = DP_STATE_FAILED;
+ } else {
+ if (info->error == CA_RESULT_USER_CANCELED) {
+
+ TRACE_INFO("[CANCELED][%d]", request->id);
+
+ if (request->error == DP_ERROR_IO_EAGAIN) {
+ request->error = DP_ERROR_NONE;
+ } else {
+ state = DP_STATE_CANCELED;
+ errorcode = request->error;
+ }
+ } else {
+ state = DP_STATE_FAILED;
+ errorcode = __ca_change_error(info->error);
+ TRACE_ERROR("[FAILED][%d][%s] agent error:%d", request->id,
+ dp_print_errorcode(errorcode), info->error);
+ }
+ }
+
+ if (errorcode == DP_ERROR_NONE && info->saved_path != NULL) {
+
+ char *content_name = NULL;
+ char *str = NULL;
+ str = strrchr(info->saved_path, '/');
+ if (str != NULL) {
+ str++;
+ content_name = dp_strdup(str);
+ }
+ if (request->file_size == 0) {// missed in download_cb
+ request->file_size = info->file_size;
+ if (dp_db_replace_property(slot->client.dbhandle, request->id,
+ DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_SIZE, (void *)&request->file_size, 0, 1, &errorcode) < 0)
+ TRACE_ERROR("id:%d failed to set content_size(%llu)", request->id, request->file_size);
+ }
+
+ if (content_name != NULL) {
+ if (dp_db_replace_property(slot->client.dbhandle, request->id,
+ DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_NAME, (void *)content_name, 0, 2, &errorcode) < 0)
+ TRACE_ERROR("id:%d failed to set content_name", request->id);
+ }
+
+ if (dp_db_replace_property(slot->client.dbhandle, request->id,
+ DP_TABLE_DOWNLOAD, DP_DB_COL_SAVED_PATH, (void *)info->saved_path, 0, 2, &errorcode) < 0)
+ TRACE_ERROR("id:%d failed to set saved_path", request->id);
+
+ free(content_name);
+
+ /* update the received file size.
+ * The last received file size cannot update
+ * because of reducing update algorithm */
+ request->received_size = info->file_size;
+ if (request->progress_cb == 1) {
+ if (slot->client.notify < 0 ||
+ dp_notify_feedback(slot->client.notify, slot, request->id,
+ DP_STATE_DOWNLOADING, DP_ERROR_NONE, request->received_size) < 0) {
+ TRACE_ERROR("id:%d disable progress callback by IO_ERROR", request->id);
+ request->progress_cb = 0;
+ }
+ }
+ }
+
+ request->state = state;
+ request->error = errorcode;
+
+ if (__dp_ca_state_feedback(slot, request) < 0) {
+ TRACE_ERROR("id:%d check notify channel", request->id);
+ if (dp_cancel_cache_download_without_update(request->cache_agent_id) < 0)
+ TRACE_ERROR("[fail][%d]cancel_agent", request->id);
+ }
+ if (request->noti_type == DP_NOTIFICATION_TYPE_COMPLETE_ONLY ||
+ request->noti_type == DP_NOTIFICATION_TYPE_ALL) {
+ if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION) < 0)
+ TRACE_ERROR("failed to register notification for id:%d", request->id);
+ }
+
+ CLIENT_MUTEX_UNLOCK(&slot->mutex);
+}
+
+static void __cache_paused_cb(int cache_id, void *user_req_data, void *user_client_data)
+{
+ dp_client_slots_fmt *slot = user_client_data;
+ dp_request_fmt *request = user_req_data;
+ if (slot == NULL || request == NULL) {
+ TRACE_ERROR("check address slot:%p request:%p id:%d agent id:%d",
+ slot, request, (request == NULL ? 0 : request->id), cache_id);
+ return ;
+ }
+
+ TRACE_DEBUG("[PAUSED] id:%d agent id:%d", request->id, cache_id);
+ dp_queue_manager_wake_up();
+}
+
+static void __cache_download_info_cb(cache_info_t *info, void *user_req_data, void *user_client_data)
+{
+ if (info == NULL) {
+ TRACE_ERROR("check download info address");
+ return ;
+ }
+
+ dp_client_slots_fmt *slot = user_client_data;
+ dp_request_fmt *request = user_req_data;
+
+ if (slot == NULL || request == NULL) {
+ TRACE_ERROR("check address slot:%p request:%p id:%d agent id:%d",
+ slot, request, (request == NULL ? 0 : request->id), info->cache_id);
+ return ;
+ }
+
+ CLIENT_MUTEX_LOCK(&slot->mutex);
+
+ if (__ca_precheck_request(slot, request, info->cache_id) < 0) {
+ TRACE_ERROR("error request agent_id:%d", info->cache_id);
+
+ if (dp_cancel_cache_download(info->cache_id) < 0)
+ TRACE_ERROR("failed to call cancel_download(%d)", info->cache_id);
+
+ CLIENT_MUTEX_UNLOCK(&slot->mutex);
+ return ;
+ }
+
+ if (info->error != CA_RESULT_OK) {
+ request->error = DP_ERROR_IO_ERROR;
+ } else {
+ int errorcode = DP_ERROR_NONE;
+
+ if (dp_db_replace_property(slot->client.dbhandle, request->id,
+ DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_NAME, (void *)info->content_name, 0, 2, &errorcode) < 0)
+ TRACE_ERROR("id:%d failed to set contentname", request->id);
+
+ if (info->file_size > 0 && dp_db_replace_property(slot->client.dbhandle, request->id,
+ DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_SIZE, (void *)&(info->file_size), 0, 1, &errorcode) < 0)
+ TRACE_ERROR("id:%d failed to set file size", request->id);
+
+ errorcode = __set_file_permission_to_client(slot, request, info->saved_path);
+ request->error = errorcode;
+ }
+
+ if (request->error != DP_ERROR_NONE) {
+ request->state = DP_STATE_FAILED;
+ TRACE_ERROR("id:%d try to cancel(%d)", request->id, info->cache_id);
+
+ if (dp_cancel_cache_download(request->cache_agent_id) < 0)
+ TRACE_ERROR("[fail][%d] cancel_agent:%d", request->id, info->cache_id);
+
+ } else {
+ request->state = DP_STATE_DOWNLOADING;
+ request->file_size = info->file_size;
+ TRACE_DEBUG("[STARTED] id:%d agent id:%d", request->id, info->cache_id);
+ }
+
+ if (__dp_ca_state_feedback(slot, request) < 0) {
+ TRACE_ERROR("id:%d check notify channel", request->id);
+ if (dp_cancel_cache_download(request->cache_agent_id) < 0)
+ TRACE_ERROR("[fail][%d]cancel_agent", request->id);
+ }
+
+ if (request->noti_type == DP_NOTIFICATION_TYPE_ALL) {
+ if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION_ONGOING_UPDATE) < 0)
+ TRACE_ERROR("failed to register notification for id:%d", request->id);
+ }
+
+ CLIENT_MUTEX_UNLOCK(&slot->mutex);
+}
+
+static void __cache_progress_cb(int cache_id, unsigned long long received_size,
+ void *user_req_data, void *user_client_data)
+{
+ dp_client_slots_fmt *slot = user_client_data;
+ dp_request_fmt *request = user_req_data;
+ if (slot == NULL || request == NULL) {
+ TRACE_ERROR("check address slot:%p request:%p id:%d agent id:%d",
+ slot, request, (request == NULL ? 0 : request->id), cache_id);
+ return ;
+ }
+ CLIENT_MUTEX_LOCK(&slot->mutex);
+
+ if (__ca_precheck_request(slot, request, cache_id) < 0) {
+ TRACE_ERROR("error request agent_id:%d", cache_id);
+ if (dp_cancel_cache_download(cache_id) < 0)
+ TRACE_ERROR("failed to call cancel_download(%d)", cache_id);
+ CLIENT_MUTEX_UNLOCK(&slot->mutex);
+ return ;
+ }
+
+ TRACE_DEBUG("state: %d, progress %llu", request->state, received_size);
+
+ // For resume case after pause, it change state from connecting to downloading
+ if (request->state == DP_STATE_CONNECTING) {
+ request->state = DP_STATE_DOWNLOADING;
+ if (__dp_ca_state_feedback(slot, request) < 0) {
+ TRACE_ERROR("id:%d check notify channel", request->id);
+ if (dp_cancel_cache_download(request->cache_agent_id) < 0)
+ TRACE_ERROR("[fail][%d]cancel_agent", request->id);
+ }
+ }
+
+ if (request->state == DP_STATE_DOWNLOADING) {
+ request->received_size = received_size;
+
+ if (request->progress_cb == 1) {
+ if (slot->client.notify < 0 ||
+ dp_notify_feedback(slot->client.notify, slot,
+ request->id, DP_STATE_DOWNLOADING, DP_ERROR_NONE, received_size) < 0) {
+ // failed to read from socket // ignore this status
+ TRACE_ERROR("id:%d disable progress callback by IO_ERROR", request->id);
+ request->progress_cb = 0;
+ }
+ }
+
+ if (request->noti_type == DP_NOTIFICATION_TYPE_ALL) {
+ if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION_ONGOING_PROGRESS) < 0)
+ TRACE_ERROR("failed to register notification for id:%d", request->id);
+ }
+ }
+ CLIENT_MUTEX_UNLOCK(&slot->mutex);
+}
+
+int dp_init_cache_agent()
+{
+ if (g_ca_handle)
+ return DP_ERROR_NONE;
+
+ g_ca_handle = dlopen(LIB_CACHE_AGENT_PATH, RTLD_LAZY | RTLD_GLOBAL);
+
+ if (!g_ca_handle) {
+ TRACE_ERROR("[dlopen] %s", dlerror());
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+ dlerror(); /* Clear any existing error */
+
+ *(void **) (&cache_agent_init) = dlsym(g_ca_handle, "ca_init");
+ if (cache_agent_init == NULL) {
+ TRACE_ERROR("[dlsym] ca_init:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_deinit) = dlsym(g_ca_handle, "ca_deinit");
+ if (cache_agent_deinit == NULL) {
+ TRACE_ERROR("[dlsym] ca_deinit:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_store_file) = dlsym(g_ca_handle, "ca_store_file");
+ if (cache_agent_store_file == NULL) {
+ TRACE_ERROR("[dlsym] ca_store_file:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_remove_files) = dlsym(g_ca_handle, "ca_remove_files");
+ if (cache_agent_remove_files == NULL) {
+ TRACE_ERROR("[dlsym] ca_remove_files:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_is_file_available) = dlsym(g_ca_handle, "ca_is_file_available");
+ if (cache_agent_is_file_available == NULL) {
+ TRACE_ERROR("[dlsym] ca_is_file_available:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_get_used_cache_size) = dlsym(g_ca_handle, "ca_get_used_cache_size");
+ if (cache_agent_get_used_cache_size == NULL) {
+ TRACE_ERROR("[dlsym] ca_get_used_cache_size:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_start_download) = dlsym(g_ca_handle, "ca_start_cache_download");
+ if (cache_agent_start_download == NULL) {
+ TRACE_ERROR("[dlsym] ca_start_cache_download:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_pause_download) = dlsym(g_ca_handle, "ca_pause_cache_download");
+ if (cache_agent_pause_download == NULL) {
+ TRACE_ERROR("[dlsym] ca_pause_cache_download:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_resume_download) = dlsym(g_ca_handle, "ca_resume_cache_download");
+ if (cache_agent_resume_download == NULL) {
+ TRACE_ERROR("[dlsym] ca_resume_cache_download:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_cancel_download) = dlsym(g_ca_handle, "ca_cancel_cache_download");
+ if (cache_agent_cancel_download == NULL) {
+ TRACE_ERROR("[dlsym] ca_cancel_cache_download:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_pause_download_without_update) = dlsym(g_ca_handle, "ca_pause_cache_download_without_update");
+ if (cache_agent_pause_download_without_update == NULL) {
+ TRACE_ERROR("[dlsym] ca_pause_cache_download_without_update:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_cancel_download_without_update) = dlsym(g_ca_handle, "ca_cancel_cache_download_without_update");
+ if (cache_agent_cancel_download_without_update == NULL) {
+ TRACE_ERROR("[dlsym] ca_cancel_cache_download_without_update:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_is_alive_download) = dlsym(g_ca_handle, "ca_is_alive_cache_download");
+ if (cache_agent_is_alive_download == NULL) {
+ TRACE_ERROR("[dlsym] ca_is_alive_cache_download:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_get_max_cache_size) = dlsym(g_ca_handle, "ca_get_max_cache_size");
+ if (cache_agent_get_max_cache_size == NULL) {
+ TRACE_ERROR("[dlsym] ca_get_max_cache_size:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_set_max_cache_size) = dlsym(g_ca_handle, "ca_set_max_cache_size");
+ if (cache_agent_set_max_cache_size == NULL) {
+ TRACE_ERROR("[dlsym] ca_set_max_cache_size:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_get_cache_path) = dlsym(g_ca_handle, "ca_get_cache_path");
+ if (cache_agent_get_cache_path == NULL) {
+ TRACE_ERROR("[dlsym] ca_get_cache_path:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_set_cache_path) = dlsym(g_ca_handle, "ca_set_cache_path");
+ if (cache_agent_set_cache_path == NULL) {
+ TRACE_ERROR("[dlsym] ca_set_cache_path:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_get_cache_lifecycle) = dlsym(g_ca_handle, "ca_get_cache_lifecycle");
+ if (cache_agent_get_cache_lifecycle == NULL) {
+ TRACE_ERROR("[dlsym] ca_get_cache_lifecycle:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_set_cache_lifecycle) = dlsym(g_ca_handle, "ca_set_cache_lifecycle");
+ if (cache_agent_set_cache_lifecycle == NULL) {
+ TRACE_ERROR("[dlsym] ca_set_cache_lifecycle:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_clear_all_files) = dlsym(g_ca_handle, "ca_clear_all_files");
+ if (cache_agent_clear_all_files == NULL) {
+ TRACE_ERROR("[dlsym] ca_clear_all_files:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ *(void **) (&cache_agent_set_oldest_file_time) = dlsym(g_ca_handle, "ca_set_oldest_file_time");
+ if (cache_agent_clear_all_files == NULL) {
+ TRACE_ERROR("[dlsym] ca_set_oldest_file_time:%s", dlerror());
+ dlclose(g_ca_handle);
+ g_ca_handle = NULL;
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ int ca_ret = -1;
+ ca_ret = (*cache_agent_init)(__capacity_event_cb);
+ if (ca_ret != CA_RESULT_OK)
+ return DP_ERROR_OUT_OF_MEMORY;
+ return DP_ERROR_NONE;
+}
+
+void dp_deinit_cache_agent()
+{
+ if (g_ca_handle) {
+ if (cache_agent_deinit) {
+ (*cache_agent_deinit)();
+ g_ca_handle = NULL;
+ // Do not unload a symbol file here.
+ }
+ }
+}
+
+// NULL : failed
+char *dp_store_file(const char *file_path)
+{
+ TRACE_DEBUG("dp_store_file called, file path: %s", file_path);
+
+ if (!file_path) {
+ TRACE_ERROR("[NULL-CHECK] file_path");
+ return NULL;
+ }
+
+ if (cache_agent_store_file)
+ return (*cache_agent_store_file)(file_path);
+
+ return NULL;
+}
+
+// 0 : success
+// -1 : failed
+int dp_remove_files(int file_count, const char *cache_files[])
+{
+ TRACE_DEBUG("dp_remove_files called, file_count: %d", file_count);
+
+ if (file_count <= 0) {
+ TRACE_ERROR("[PARAM-CHECK] file_count");
+ return -1;
+ }
+
+ for (int i = 0; i < file_count; i++) {
+ if (!cache_files[i]) {
+ TRACE_ERROR("[NULL-CHECK] cache_files[%d]", i);
+ return -1;
+ }
+ TRACE_DEBUG("file name[%d]: %s", i, cache_files[i]);
+ }
+
+ if (cache_agent_remove_files) {
+ if ((*cache_agent_remove_files)(file_count, cache_files) == CA_RESULT_OK)
+ return 0;
+ }
+
+ return -1;
+}
+
+// 0 : available
+// -1 : not available
+int dp_is_file_available(const char *cache_file)
+{
+ TRACE_DEBUG("dp_is_file_available called, cache_file: %s", cache_file);
+
+ if (!cache_file) {
+ TRACE_ERROR("[NULL-CHECK] cache_file");
+ return -1;
+ }
+
+ if (cache_agent_is_file_available) {
+ if ((*cache_agent_is_file_available)(cache_file) == CA_RESULT_OK)
+ return 0;
+ }
+
+ return -1;
+}
+
+// 0 : success
+// -1 : failed
+int dp_get_used_cache_size(long long *size)
+{
+ TRACE_DEBUG("dp_get_used_cache_size called");
+
+ if (!size) {
+ TRACE_ERROR("[NULL-CHECK] size");
+ return -1;
+ }
+
+ if (cache_agent_get_used_cache_size) {
+ if ((*cache_agent_get_used_cache_size)(size) == CA_RESULT_OK)
+ return 0;
+ }
+
+ return -1;
+}
+
+int dp_start_cache_download(const char *cache_file, void *slot, void *request)
+{
+ TRACE_DEBUG("dp_start_cache_download called, cache_file: %s", cache_file);
+
+ int ret = -1;
+ int req_cache_id = -1;
+ int errorcode = DP_ERROR_NONE;
+ unsigned length = 0;
+ char *destination = NULL;
+ char *filename = NULL;
+ ca_req_data_t *req_data = NULL;
+
+ dp_client_slots_fmt *base_slot = slot;
+ dp_request_fmt *base_req = request;
+
+ if (cache_file == NULL || slot == NULL || request == NULL) {
+ TRACE_ERROR("check cache_file, slot or request address");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ ca_cb_t ca_cb = {
+ __cache_download_info_cb,
+ __cache_progress_cb,
+ __cache_finished_cb,
+ __cache_paused_cb
+ };
+
+ req_data = (ca_req_data_t *)calloc(1, sizeof(ca_req_data_t));
+ if (req_data == NULL) {
+ TRACE_ERROR("[ERROR] calloc");
+ return DP_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id,
+ DP_TABLE_REQUEST, DP_DB_COL_FILENAME, (unsigned char **)&filename, &length, &errorcode) < 0 ||
+ filename == NULL) {
+ TRACE_DEBUG("filename id:%d NO_DATA, use request->file_name: %s", base_req->id, base_req->file_name);
+ req_data->file_name = base_req->file_name;
+ } else {
+ req_data->file_name = filename;
+ }
+
+ if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id,
+ DP_TABLE_REQUEST, DP_DB_COL_DESTINATION, (unsigned char **)&destination, &length, &errorcode) < 0 ||
+ destination == NULL) {
+ TRACE_DEBUG("destination id:%d NO_DATA, use default install path: %s", base_req->id, CA_DEFAULT_INSTALL_PATH);
+ req_data->install_path = CA_DEFAULT_INSTALL_PATH;
+ } else {
+ __remove_trailing_slash(destination);
+ req_data->install_path = destination;
+ }
+
+ req_data->pkg_name = base_slot->pkgname;
+
+ req_data->user_client_data = (void *)slot;
+ req_data->user_req_data = (void *)request;
+
+ // call start API of cache agent lib
+ if (cache_agent_start_download != NULL)
+ ret = (*cache_agent_start_download)(cache_file, req_data, &ca_cb, &req_cache_id);
+
+ free(destination);
+ free(filename);
+ free(req_data);
+
+ if (ret == CA_RESULT_OK) {
+ TRACE_DEBUG("request id :%d SUCCESS cache_id:%d", base_req->id, req_cache_id);
+ base_req->cache_agent_id = req_cache_id;
+ }
+ return __ca_change_error(ret);
+}
+
+int dp_pause_cache_download(int req_id)
+{
+ TRACE_DEBUG("dp_pause_cache_download called, req_id %d",req_id);
+
+ if (req_id < 0) {
+ TRACE_ERROR("[PARAM-CHECK] req_id");
+ return -1;
+ }
+
+ if (dp_is_alive_cache_download(req_id) == 0) {
+ TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id);
+ return -1;
+ }
+
+ if (cache_agent_pause_download != NULL) {
+ if ((*cache_agent_pause_download)(req_id) == CA_RESULT_OK)
+ return 0;
+ }
+
+ return -1;
+}
+
+int dp_resume_cache_download(int req_id)
+{
+ TRACE_DEBUG("dp_resume_cache_download called, req_id %d",req_id);
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (req_id < 0) {
+ TRACE_ERROR("[PARAM-CHECK] req_id");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (cache_agent_resume_download)
+ ca_ret = (*cache_agent_resume_download)(req_id);
+
+ return __ca_change_error(ca_ret);
+}
+
+int dp_cancel_cache_download(int req_id)
+{
+ TRACE_DEBUG("dp_cancel_cache_download called, req_id %d",req_id);
+
+ if (req_id < 0) {
+ TRACE_ERROR("[NULL-CHECK] req_id");
+ return -1;
+ }
+
+ if (dp_is_alive_cache_download(req_id) == 0) {
+ TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id);
+ return -1;
+ }
+
+ if (cache_agent_cancel_download != NULL) {
+ if ((*cache_agent_cancel_download)(req_id) == CA_RESULT_OK)
+ return 0;
+ }
+
+ return -1;
+}
+
+int dp_pause_cache_download_without_update(int req_id)
+{
+ TRACE_DEBUG("dp_pause_cache_download_without_update called, req_id %d",req_id);
+
+ if (req_id < 0) {
+ TRACE_ERROR("[PARAM-CHECK] req_id");
+ return -1;
+ }
+
+ if (dp_is_alive_cache_download(req_id) == 0) {
+ TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id);
+ return -1;
+ }
+
+ if (cache_agent_pause_download_without_update != NULL) {
+ if ((*cache_agent_pause_download_without_update)(req_id) == CA_RESULT_OK)
+ return 0;
+ }
+
+ return -1;
+}
+
+int dp_cancel_cache_download_without_update(int req_id)
+{
+ TRACE_DEBUG("dp_cancel_cache_download_without_update called, req_id %d",req_id);
+
+ if (req_id < 0) {
+ TRACE_ERROR("[NULL-CHECK] req_id");
+ return -1;
+ }
+
+ if (dp_is_alive_cache_download(req_id) == 0) {
+ TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id);
+ return -1;
+ }
+
+ if (cache_agent_cancel_download_without_update != NULL) {
+ if ((*cache_agent_cancel_download_without_update)(req_id) == CA_RESULT_OK)
+ return 0;
+ }
+
+ return -1;
+}
+
+int dp_is_alive_cache_download(int req_id)
+{
+ TRACE_DEBUG("dp_is_alive_cache_download called, req_id %d",req_id);
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (req_id < 0)
+ return 0;
+
+ if (cache_agent_is_alive_download)
+ ca_ret = (*cache_agent_is_alive_download)(req_id);
+
+ return ca_ret == CA_RESULT_OK ? 1 : 0;
+}
+
+int dp_get_max_cache_size(unsigned int *size)
+{
+ TRACE_DEBUG("dp_get_max_cache_size called");
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (!size) {
+ TRACE_ERROR("[PARAM-CHECK] size");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (cache_agent_get_max_cache_size)
+ ca_ret = (*cache_agent_get_max_cache_size)(size);
+
+ return __ca_change_error(ca_ret);
+}
+
+int dp_set_max_cache_size(unsigned int size)
+{
+ TRACE_DEBUG("dp_set_max_cache_size called, size: %d", size);
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (cache_agent_set_max_cache_size)
+ ca_ret = (*cache_agent_set_max_cache_size)(size);
+
+ return __ca_change_error(ca_ret);
+}
+
+int dp_get_cache_path(char **path)
+{
+ TRACE_DEBUG("dp_get_cache_path called");
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (!path) {
+ TRACE_ERROR("[PARAM-CHECK] path");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (cache_agent_get_cache_path)
+ ca_ret = (*cache_agent_get_cache_path)(path);
+
+ return __ca_change_error(ca_ret);
+}
+
+int dp_set_cache_path(const char *path)
+{
+ TRACE_DEBUG("dp_set_cache_path called, path: %s", path);
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (!path) {
+ TRACE_ERROR("[PARAM-CHECK] path");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (cache_agent_set_cache_path)
+ ca_ret = (*cache_agent_set_cache_path)(path);
+
+ return __ca_change_error(ca_ret);
+}
+
+int dp_get_cache_lifecycle(unsigned int *time)
+{
+ TRACE_DEBUG("dp_get_cache_lifecycle called");
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (!time) {
+ TRACE_ERROR("[PARAM-CHECK] time");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (cache_agent_get_cache_lifecycle)
+ ca_ret = (*cache_agent_get_cache_lifecycle)(time);
+
+ return __ca_change_error(ca_ret);
+}
+
+int dp_set_cache_lifecycle(unsigned int time)
+{
+ TRACE_DEBUG("dp_set_cache_lifecycle called, time: %u", time);
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (cache_agent_set_cache_lifecycle)
+ ca_ret = (*cache_agent_set_cache_lifecycle)(time);
+
+ return __ca_change_error(ca_ret);
+}
+
+int dp_clear_all_cache_files(void)
+{
+ TRACE_DEBUG("dp_clear_all_files called");
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (cache_agent_clear_all_files)
+ ca_ret = (*cache_agent_clear_all_files)();
+
+ return __ca_change_error(ca_ret);
+}
+
+int dp_set_oldest_cache_file_time(time_t time)
+{
+ TRACE_DEBUG("dp_set_oldest_cache_file_time called, time: %ld", time);
+
+ int ca_ret = CA_RESULT_OK;
+
+ if (time < 0) {
+ TRACE_ERROR("[PARAM-CHECK] time");
+ return DP_ERROR_INVALID_PARAMETER;
+ }
+
+ if (cache_agent_set_oldest_file_time)
+ ca_ret = (*cache_agent_set_oldest_file_time)(time);
+
+ return __ca_change_error(ca_ret);
+}
+
+bool dp_is_cache_storage_busy(void)
+{
+ bool ret;
+
+ pthread_mutex_lock(&(mutex_capacity_max));
+ ret = g_capacity_max;
+ pthread_mutex_unlock(&(mutex_capacity_max));
+
+ return ret;
+}
* limitations under the License.
*/
+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <download-provider-security.h>
#include <download-provider-client.h>
#include <download-provider-client-manager.h>
+#include <download-provider-plugin-cache-agent.h>
#include <download-provider-plugin-download-agent.h>
#include <download-provider-notification-manager.h>
#include <download-provider-queue-manager.h>
{"text/calendar", DP_CONTENT_VCAL},
};
+extern void *g_db_handle;
static void *g_da_handle = NULL;
static int (*download_agent_init)(void) = NULL; // int da_init(da_client_cb_t *da_client_callback);
static int (*download_agent_deinit)(void) = NULL; // int da_deinit();
return DP_ERROR_NONE;
}
+static char *__dp_get_cache_directive_string(const char *cache_control, const char *directive)
+{
+ if (!cache_control || !directive)
+ return NULL;
+
+ char *str = strstr(cache_control, directive);
+ return str;
+}
+
+static long __calculate_heuristic_freshness(const char *last_modified)
+{
+ if (!last_modified)
+ return 0;
+
+ struct tm tm = {0};
+ strptime(last_modified, "%a, %d %b %Y %H:%M:%S %Z", &tm);
+ time_t now_time = time(NULL);
+ time_t last_time = timegm(&tm);
+ long freshness_time = (long)difftime(now_time, last_time);
+
+ // calculate the heuristic freshness as 10% of the freshness time.
+ long heuristic_time = freshness_time / 10.0;
+ return heuristic_time;
+}
+
+static long __dp_determine_cache_max_age(const char *cache_control, const char *last_modified)
+{
+ if (cache_control && !__dp_get_cache_directive_string(cache_control, CACHE_CONTROL_NO_CACHE)) {
+ char *str = __dp_get_cache_directive_string(cache_control, CACHE_CONTROL_MAX_AGE);
+ if (str)
+ return strtol(str + strlen(CACHE_CONTROL_MAX_AGE), NULL, 10);
+ }
+
+ return __calculate_heuristic_freshness(last_modified);
+}
+
+static int __dp_store_cache_to_db(const char *package, const char *url, const char *ori_file_name, const char *file_name, const char *etag,
+ const long max_age, const unsigned long long file_size, int *error)
+{
+ *error = DP_ERROR_NONE;
+ if (dp_db_new_app_cache(g_db_handle, package, url, file_name, true, error) < 0) {
+ TRACE_ERROR("failed to add new app cache");
+ return -1;
+ }
+
+ if (dp_db_new_file_cache(g_db_handle, file_name, ori_file_name, etag,
+ max_age, file_size, (const long)time(NULL), error) < 0) {
+ TRACE_ERROR("failed to add new file cache");
+ }
+
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+static int __dp_update_cache_from_db(const char *url, const char *file_name, const char *new_file_name, const char *etag,
+ const long max_age, const unsigned long long file_size, int *error)
+{
+ *error = DP_ERROR_NONE;
+ if (strcmp(file_name, new_file_name) != 0) {
+ if (dp_db_update_app_cache(g_db_handle, url, new_file_name, error) < 0) {
+ TRACE_ERROR("failed to update app cache");
+ return -1;
+ }
+ }
+
+ int id = dp_db_get_cache_file_id(g_db_handle, file_name, error);
+ if (id < 0) {
+ TRACE_ERROR("failed to get cache file id");
+ return -1;
+ }
+
+ if (dp_db_update_file_cache(g_db_handle, id, new_file_name, etag,
+ max_age, file_size, (const long)time(NULL), error) < 0) {
+ TRACE_ERROR("failed to update file cache");
+ }
+
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+static int __dp_remove_old_file(char *file_name)
+{
+ char **file_ptr = (char **)malloc(sizeof(char *));
+ if (!file_ptr) {
+ TRACE_ERROR("failed to allocate the file_array");
+ return -1;
+ }
+ *file_ptr = file_name;
+
+ if (dp_remove_files(1, (const char **)file_ptr) < 0) {
+ TRACE_ERROR("failed to remove cache file");
+ free(file_ptr);
+ return -1;
+ }
+
+ free(file_ptr);
+ return 0;
+}
+
+static int __dp_store_to_cache(const char *package, const char *url, const char *ori_file, const char *cache_control, const char *etag,
+ const char *last_modified, const char *saved_path, const unsigned long long file_size, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ if (!package || !url || !ori_file || !saved_path) {
+ TRACE_ERROR("NULL CHECK!: package, url, ori_file or saved_path");
+ return -1;
+ }
+
+ *error = DP_ERROR_NONE;
+ char *file_name = NULL;
+ unsigned length = 0;
+ int pkg_exist = 0;
+ int found = dp_db_get_cache_file_name(g_db_handle, package, url, &file_name, &length, &pkg_exist, error);
+ if (found < 0) {
+ TRACE_ERROR("failed to get cache_file_name");
+ return -1;
+ }
+ if (found > 0) {
+ if (__dp_remove_old_file(file_name) < 0) {
+ TRACE_ERROR("failed to remove old files");
+ free(file_name);
+ return -1;
+ }
+ }
+
+ char *new_file_name = dp_store_file(saved_path);
+ if (!new_file_name) {
+ TRACE_ERROR("failed to store file");
+ *error = DP_ERROR_DISK_BUSY;
+ free(file_name);
+ return -1;
+ }
+
+ long max_age = __dp_determine_cache_max_age(cache_control, last_modified);
+
+ if (found == 0) {
+ if (__dp_store_cache_to_db(package, url, ori_file, new_file_name, etag, (const long)max_age, file_size, error) < 0)
+ TRACE_ERROR("failed to store new cache data to db");
+ } else {
+ if (__dp_update_cache_from_db(url, file_name, new_file_name, etag, (const long)max_age, file_size, error) < 0)
+ TRACE_ERROR("failed to update cache data from db");
+ }
+
+ free(file_name);
+ free(new_file_name);
+
+ if (*error != DP_ERROR_NONE)
+ return -1;
+
+ return 0;
+}
+
+// return url only when cache feature is enabled.
+static char *__dp_get_cache_key_url(void *client_db_handle, const int req_id, int *error)
+{
+ *error = DP_ERROR_INVALID_PARAMETER;
+ if (!client_db_handle) {
+ TRACE_ERROR("NULL CHECK!: client_db_handle");
+ return NULL;
+ }
+
+ if (req_id < 0) {
+ TRACE_ERROR("check req_id");
+ return NULL;
+ }
+
+ int cache = 0;
+ unsigned length = 0;
+ char *url = NULL;
+
+ *error = DP_ERROR_NONE;
+ if (dp_db_get_property_int(client_db_handle, req_id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&cache, error) < 0 ||
+ cache <= 0) {
+ TRACE_ERROR("failed to get cache value or cache is not enabled for id:%d", req_id);
+ return NULL;
+ }
+
+ if (dp_db_get_property_string(client_db_handle, req_id, DP_TABLE_REQUEST, DP_DB_COL_URL, (unsigned char **)&url, &length, error) < 0 ||
+ !url) {
+ TRACE_ERROR("failed to get url. id:%d", req_id);
+ return NULL;
+ }
+
+ return url;
+}
+
+static char *__get_ori_file_name(const char *file_name)
+{
+ int errorcode = DP_ERROR_INVALID_PARAMETER;
+ if (!file_name) {
+ TRACE_ERROR("NULL CHECK!: file_name");
+ return NULL;
+ }
+
+ char *ori_file = NULL;
+ unsigned len = 0;
+ if (dp_db_get_file_cache_string(g_db_handle, file_name, DP_DB_COL_FILENAME, (unsigned char **)&ori_file, &len, &errorcode) < 0 ||
+ !ori_file) {
+ return NULL;
+ }
+
+ return ori_file;
+}
+
+static int __dp_try_cache_download(void *slot, void *request)
+{
+ int errorcode = DP_ERROR_INVALID_PARAMETER;
+ if (!slot || !request) {
+ TRACE_ERROR("NULL CHECK!: slot or request");
+ return errorcode;
+ }
+
+ dp_client_slots_fmt *req_slot = slot;
+ dp_request_fmt *req = request;
+
+ char *url = __dp_get_cache_key_url(req_slot->client.dbhandle, req->id, &errorcode);
+ if (!url) {
+ TRACE_ERROR("failed to get cache key url");
+ return errorcode;
+ }
+
+ char *file_name = NULL;
+ unsigned length = 0;
+ int pkg_exist = 0;
+ int ret = dp_db_get_cache_file_name(g_db_handle, req_slot->pkgname, url, &file_name, &length, &pkg_exist, &errorcode);
+ if (ret <= 0 || !file_name) {
+ TRACE_ERROR("failed to get cache_file_name");
+ free(url);
+ return errorcode;
+ }
+
+ char *ori_file = __get_ori_file_name(file_name);
+ if (!ori_file) {
+ TRACE_ERROR("failed to get original file name");
+ errorcode = DP_ERROR_NO_DATA;
+ return errorcode;
+ }
+ req->file_name = ori_file;
+
+ errorcode = dp_start_cache_download(file_name, req_slot, req);
+
+ free(url);
+ free(file_name);
+
+ return errorcode;
+}
+
+static int __dp_update_cache_validator(void *slot, void *request, const char *cache_control, const char *etag)
+{
+ int errorcode = DP_ERROR_INVALID_PARAMETER;
+ if (!slot || !request) {
+ TRACE_ERROR("NULL CHECK!: slot or request");
+ return errorcode;
+ }
+
+ dp_client_slots_fmt *req_slot = slot;
+ dp_request_fmt *req = request;
+
+ char *url = __dp_get_cache_key_url(req_slot->client.dbhandle, req->id, &errorcode);
+ if (!url) {
+ TRACE_ERROR("failed to get cache key url for id:%d", req->id);
+ return errorcode;
+ }
+
+ char *file_name = NULL;
+ unsigned length = 0;
+ int pkg_exist = 0;
+ if (dp_db_get_cache_file_name(g_db_handle, req_slot->pkgname, url, &file_name, &length, &pkg_exist, &errorcode) < 0
+ || !file_name) {
+ TRACE_ERROR("failed to get cache file name");
+ free(url);
+ return errorcode;
+ }
+
+ int id = dp_db_get_cache_file_id(g_db_handle, file_name, &errorcode);
+ if (id < 0) {
+ TRACE_ERROR("failed to get cache file id");
+ goto done;
+ }
+
+ long max_age = 0;
+ if (cache_control) {
+ char *str = __dp_get_cache_directive_string(cache_control, CACHE_CONTROL_MAX_AGE);
+ if (str)
+ max_age = strtol(str + strlen(CACHE_CONTROL_MAX_AGE), NULL, 10);
+ }
+
+ if (max_age > 0) {
+ if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_CACHE_MAX_AGE, 1, (void *)&max_age, &errorcode) < 0) {
+ TRACE_ERROR("failed to update max_age");
+ goto done;
+ }
+ }
+
+ if (etag) {
+ if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_ETAG, 2, (void *)etag, &errorcode) < 0) {
+ TRACE_ERROR("failed to update etag");
+ goto done;
+ }
+ }
+
+ long current_time = (long)time(NULL);
+ if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_CACHE_LAST_UPDATE, 1, (void *)¤t_time, &errorcode) < 0) {
+ TRACE_ERROR("failed to update last_update");
+ goto done;
+ }
+
+done:
+ free(url);
+ free(file_name);
+
+ return errorcode;
+}
+
static void __finished_cb(finished_info_t *info, void *user_req_data,
void *user_client_data)
{
if (slot == NULL || request == NULL) {
TRACE_ERROR("check address slot:%p request:%p id:%d agentid:%d", slot, request, (request == NULL ? 0 : request->id), info->download_id);
free(info->etag);
+ free(info->cache_control);
+ free(info->last_modified);
free(info->saved_path);
+ free(info->ori_file);
free(info);
return ;
}
if (dp_cancel_agent_download(info->download_id) < 0)
TRACE_ERROR("failed to call cancel_download(%d)", info->download_id);
free(info->etag);
+ free(info->cache_control);
+ free(info->last_modified);
free(info->saved_path);
+ free(info->ori_file);
free(info);
CLIENT_MUTEX_UNLOCK(&slot->mutex);
return ;
TRACE_ERROR("id:%d failed to set http_status(%d)", request->id, info->http_status);
}
+ TRACE_DEBUG("cache_control: %s, last_modified: %s", info->cache_control, info->last_modified);
TRACE_SECURE_DEBUG("[FINISH][%d][%s]", request->id, info->saved_path);
if (info->err == DA_RESULT_OK) {
+ if (info->http_status == 304) {
+ errorcode = __dp_update_cache_validator(slot, request, info->cache_control, info->etag);
+ if (errorcode != DP_ERROR_NONE) {
+ state = DP_STATE_FAILED;
+ } else {
+ errorcode = __dp_try_cache_download(slot, request);
+ state = (errorcode == DP_ERROR_NONE) ? DP_STATE_COMPLETED : DP_STATE_FAILED;
+ free(info->etag);
+ free(info->cache_control);
+ free(info->last_modified);
+ free(info->saved_path);
+ free(info->ori_file);
+ free(info);
+ CLIENT_MUTEX_UNLOCK(&slot->mutex);
+ return;
+ }
+ }
+
if (info->saved_path != NULL) {
errorcode = __set_file_permission_to_client(slot,
request, info->saved_path);
} else {
- TRACE_ERROR("[ERROR][%d] No SavedPath", request->id);
- errorcode = DP_ERROR_INVALID_DESTINATION;
+ if (state != DP_STATE_COMPLETED) {
+ TRACE_ERROR("[ERROR][%d] No SavedPath", request->id);
+ errorcode = DP_ERROR_INVALID_DESTINATION;
+ }
}
if (errorcode == DP_ERROR_NONE)
state = DP_STATE_COMPLETED;
if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_SAVED_PATH, (void *)info->saved_path, 0, 2, &errorcode) < 0)
TRACE_ERROR("id:%d failed to set saved_path", request->id);
free(content_name);
+
+ // if cache is enabled, store to cache.
+ int cache = 0;
+ if (dp_db_get_property_int(slot->client.dbhandle, request->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&cache, &errorcode) < 0)
+ TRACE_DEBUG("unable to get cache value for id:%d", request->id);
+
+ if (cache && !__dp_get_cache_directive_string(info->cache_control, CACHE_CONTROL_NO_STORE)) {
+ char *url = __dp_get_cache_key_url(slot->client.dbhandle, request->id, &errorcode);
+ if (url) {
+ if (__dp_store_to_cache(slot->pkgname, url, info->ori_file, info->cache_control, info->etag,
+ info->last_modified, info->saved_path, request->file_size, &errorcode) < 0)
+ TRACE_ERROR("failed to store cache for id:%d", request->id);
+ free(url);
+ }
+ }
+
/* update the received file size.
* The last received file size cannot update
* because of reducing update algorithm*/
TRACE_ERROR("failed to register notification for id:%d", request->id);
}
free(info->etag);
+ free(info->cache_control);
+ free(info->last_modified);
free(info->saved_path);
+ free(info->ori_file);
free(info);
CLIENT_MUTEX_UNLOCK(&slot->mutex);
}
return ;
}
+ TRACE_DEBUG("state: %d, progress %llu", request->state, received_size);
+
// For resume case after pause, it change state from connecting to downloading
if (request->state == DP_STATE_CONNECTING) {
request->state = DP_STATE_DOWNLOADING;
char *filename = NULL;
char *etag = NULL;
int user_network_bonding = 0;
+ int user_cache = 0;
if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_URL, (unsigned char **)&url, &length, &errorcode) < 0 ||
url == NULL) {
} else
req_data->network_bonding = user_network_bonding;
+ if (dp_db_get_property_int(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&user_cache, &errorcode) < 0 ||
+ user_cache < 0) {
+ TRACE_DEBUG("unable to get cache value for id:%d", base_req->id);
+ } else {
+ req_data->cache = user_cache;
+ }
+
req_data->disable_verify_host = base_req->disable_verify_host;
req_data->pkg_name = base_slot->pkgname;
return DP_ERROR_INVALID_STATE;
return __change_error(da_ret);
}
-
* limitations under the License.
*/
+#define _GNU_SOURCE
+#include <stdbool.h>
#include <stdlib.h>
+#include <stdio.h>
#include <signal.h> // pthread_kill
#include <errno.h> // ESRCH
+#include <time.h>
#include <sys/time.h>
#include <download-provider-log.h>
#include <download-provider-client.h>
#include <download-provider-client-manager.h>
#include <download-provider-plugin-download-agent.h>
+#include <download-provider-plugin-cache-agent.h>
+
+extern void *g_db_handle;
static pthread_mutex_t g_dp_queue_manager_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_dp_queue_manager_cond = PTHREAD_COND_INITIALIZER;
dp_queue_clear(queue, request);
}
+// return 0:stale 1:fresh -1:error
+static int __dp_queue_manager_check_cache_freshness(const char *file_name)
+{
+ int errorcode = DP_ERROR_NONE;
+ int id = dp_db_get_cache_file_id(g_db_handle, file_name, &errorcode);
+ if (id < 0) {
+ TRACE_ERROR("failed to get cache file id");
+ return -1;
+ }
+
+ long max_age = 0;
+ if (dp_db_get_property_int(g_db_handle, id, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_MAX_AGE, (long *)&max_age, &errorcode) < 0 ||
+ max_age == 0) {
+ TRACE_DEBUG("failed to get max_age");
+ return 0;
+ }
+
+ long last_update = 0;
+ if (dp_db_get_property_int(g_db_handle, id, DP_TABLE_CACHE_FILE, DP_DB_COL_CACHE_LAST_UPDATE, (long *)&last_update, &errorcode) < 0 ||
+ last_update == 0) {
+ TRACE_DEBUG("failed to get last_update");
+ return 0;
+ }
+
+ long current_age = difftime(time(NULL), last_update);
+ if (current_age >= max_age)
+ return 0;
+
+ return 1;
+}
+
+static int __dp_queue_send_request_for_cache_validation(const char *file_name, dp_client_slots_fmt *slot, dp_request_fmt *request)
+{
+ int errorcode = DP_ERROR_INVALID_PARAMETER;
+ if (!file_name || !slot || !request) {
+ TRACE_ERROR("NULL CHECK!: file_name, slot or request");
+ return errorcode;
+ }
+
+ errorcode = DP_ERROR_NONE;
+ char *etag = NULL;
+ unsigned length = 0;
+ if (dp_db_get_file_cache_string(g_db_handle, file_name, DP_DB_COL_ETAG, (unsigned char **)&etag, &length, &errorcode) < 0 ||
+ !etag) {
+ TRACE_DEBUG("failed to get etag");
+ return errorcode;
+ }
+
+ const char *header_field = "If-None-Match";
+ const char *header_value = etag;
+
+ int check_field = dp_db_check_duplicated_string(slot->client.dbhandle, request->id, DP_TABLE_HEADERS, DP_DB_COL_HEADER_FIELD, 0, header_field, &errorcode);
+ if (check_field < 0) {
+ errorcode = DP_ERROR_DISK_BUSY;
+ } else {
+ if (check_field == 0) { // insert
+ if (dp_db_new_header(slot->client.dbhandle, request->id, header_field, header_value, &errorcode) < 0) {
+ TRACE_ERROR("failed to set header");
+ errorcode = DP_ERROR_DISK_BUSY;
+ }
+ } else { // update
+ if (dp_db_update_header(slot->client.dbhandle, request->id, header_field, header_value, &errorcode) < 0) {
+ TRACE_ERROR("failed to set header");
+ errorcode = DP_ERROR_DISK_BUSY;
+
+ }
+ }
+ if (errorcode == DP_ERROR_NONE)
+ errorcode = dp_start_agent_download(slot, request);
+ }
+
+ if (etag)
+ free(etag);
+
+ return errorcode;
+}
+
+static char *__get_ori_file_name(const char *file_name)
+{
+ int errorcode = DP_ERROR_INVALID_PARAMETER;
+ if (!file_name) {
+ TRACE_ERROR("NULL CHECK!: file_name");
+ return NULL;
+ }
+
+ char *ori_file = NULL;
+ unsigned len = 0;
+ if (dp_db_get_file_cache_string(g_db_handle, file_name, DP_DB_COL_FILENAME, (unsigned char **)&ori_file, &len, &errorcode) < 0 ||
+ !ori_file) {
+ return NULL;
+ }
+
+ return ori_file;
+}
+
+static int __dp_queue_manager_try_cache_download(dp_client_slots_fmt *slot, dp_request_fmt *request)
+{
+ int errorcode = DP_ERROR_NONE;
+ char *url = NULL;
+ unsigned length = 0;
+ if (dp_db_get_property_string(slot->client.dbhandle, request->id, DP_TABLE_REQUEST, DP_DB_COL_URL, (unsigned char **)&url, &length, &errorcode) < 0 ||
+ !url) {
+ TRACE_ERROR("faild to get url. id:%d", request->id);
+ return errorcode;
+ }
+
+ char *file_name = NULL;
+ unsigned len = 0;
+ int pkg_exist = 0;
+ int ret = dp_db_get_cache_file_name(g_db_handle, slot->pkgname, url, &file_name, &len, &pkg_exist, &errorcode);
+ if (ret <= 0) {
+ TRACE_DEBUG("no cache file name. id:%d", request->id);
+ free(url);
+ return errorcode;
+ } else {
+ if (pkg_exist == 0) {
+ if (dp_db_new_app_cache(g_db_handle, slot->pkgname, url, (const char *)file_name, false, &errorcode) < 0) {
+ TRACE_ERROR("failed to add new app cache. id:%d", request->id);
+ goto done;
+ }
+ }
+ int fresh = __dp_queue_manager_check_cache_freshness(file_name);
+ if (fresh < 0) {
+ TRACE_ERROR("failed to check cache freshness. id:%d", request->id);
+ errorcode = DP_ERROR_INVALID_PARAMETER;
+ goto done;
+ }
+ if (fresh == 1) {
+ char *ori_file = __get_ori_file_name(file_name);
+ if (!ori_file) {
+ TRACE_ERROR("failed to get pure file name. id:%d", request->id);
+ errorcode = DP_ERROR_NO_DATA;
+ goto done;
+ }
+ request->file_name = ori_file;
+
+ // call cache agent start function
+ errorcode = dp_start_cache_download(file_name, slot, request);
+ } else { // stale
+ errorcode = __dp_queue_send_request_for_cache_validation(file_name, slot, request);
+ }
+ }
+
+done:
+ free(url);
+ free(file_name);
+
+ return errorcode;
+}
+
// if return negative, queue-manager try again.
static int __dp_queue_manager_try_download(dp_client_slots_fmt *slot, dp_request_fmt *request)
{
errorcode = DP_ERROR_NONE;
- if (dp_is_alive_download(request->agent_id) > 0)
+ int cache = 0;
+ if (dp_db_get_property_int(slot->client.dbhandle, request->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&cache, &errorcode) < 0 ||
+ cache < 0) {
+ TRACE_ERROR("unable to get cache value for id:%d", request->id);
+ cache = 0;
+ }
+ if (cache && dp_is_alive_cache_download(request->cache_agent_id) > 0) {
+ errorcode = dp_resume_cache_download(request->cache_agent_id);
+ } else if (dp_is_alive_download(request->agent_id) > 0) {
errorcode = dp_resume_agent_download(request->agent_id);
- else
- // call agent start function
- errorcode = dp_start_agent_download(slot, request);
+ } else {
+ if (cache) {
+ if (__dp_queue_manager_try_cache_download(slot, request) != DP_ERROR_NONE)
+ errorcode = dp_start_agent_download(slot, request);
+ } else {
+ // call agent start function
+ errorcode = dp_start_agent_download(slot, request);
+ }
+ }
if (errorcode == DP_ERROR_NONE) {
request->state = DP_STATE_CONNECTING;
}
return result;
-
}
static int __dp_queue_manager_check_queue(dp_queue_fmt **queue)
{
dp_client_slots_fmt *slot = NULL;
dp_request_fmt *request = NULL;
+
while (dp_queue_pop(queue, (void *)&slot, (void *)&request) == 0) { // pop a request from queue.
TRACE_DEBUG("queue-manager pop a request");
if (slot == NULL || request == NULL) {
#include <sys/time.h>
#include <sys/statfs.h>
#include <unistd.h>
+#include <glib.h>
#include "download-provider-log.h"
}
return -1;
}
+
+unsigned long dp_get_hash_key(const char *str)
+{
+ return g_str_hash(str);
+}
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef DOWNLOAD_PROVIDER_CACHE_MANAGER_H
+#define DOWNLOAD_PROVIDER_CACHE_MANAGER_H
+
+typedef enum {
+ DP_CACHE_REQUEST_RESET_CACHE = 0,
+ DP_CACHE_REQUEST_SET_CACHE_MAX_SIZE,
+ DP_CACHE_REQUEST_GET_CACHE_MAX_SIZE,
+ DP_CACHE_REQUEST_SET_CACHE_PATH,
+ DP_CACHE_REQUEST_GET_CACHE_PATH,
+ DP_CACHE_REQUEST_SET_CACHE_LIFECYCLE,
+ DP_CACHE_REQUEST_GET_CACHE_LIFECYCLE,
+ DP_CACHE_REQUEST_RESET_ALL_CACHE,
+} dp_cache_req_type_defs;
+
+int dp_cache_manager_handle_request(void *req_slot, void *req_ipc_info, const int req_type);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef DOWNLOAD_PROVIDER_CACHE_H
+#define DOWNLOAD_PROVIDER_CACHE_H
+
+#include <glib.h>
+
+int dp_cache_delete_all_from_db(int *error);
+int dp_cache_reset_cache(const char *package, int *error);
+int dp_cache_set_cache_max_size(unsigned int size, int *error);
+int dp_cache_get_cache_max_size(unsigned int *size, int *error);
+int dp_cache_set_cache_path(const char *path, int *error);
+int dp_cache_get_cache_path(char **path, int *error);
+int dp_cache_set_cache_lifecycle(unsigned int time, int *error);
+int dp_cache_get_cache_lifecycle(unsigned int *time, int *error);
+int dp_cache_reset_all_cache(int *error);
+GSList *dp_cache_clear_exceeded_files(long long cache_max, int type);
+
+#endif
int state; // downloading state, to prevent the crash, placed at the head of structure.
int id; // ID created in create request in requests thread.
int agent_id;
+ int cache_agent_id;
int error;
int network_type;
int access_time;
int noti_type;
int noti_priv_id;
int disable_verify_host; // tv only, internal API
+ char *file_name;
void *next;
} dp_request_fmt;
// provider have a groups database file.
#define DP_TABLE_CLIENTS "clients"
+#define DP_TABLE_CACHE "cache"
+#define DP_TABLE_CACHE_FILE "cache_file"
// each client has a database file with below tables. file is named as pkgname.
#define DP_TABLE_LOGGING "logging"
#define DP_TABLE_REQUEST "request"
#define DP_DB_COL_CREATE_TIME "createtime"
#define DP_DB_COL_ACCESS_TIME "accesstime"
+// cache and cache_file table
+#define DP_DB_COL_CACHE_KEY "key"
+#define DP_DB_COL_CACHE_FILE_NAME "cache_file_name"
+#define DP_DB_COL_CACHE_FILE_SIZE "file_size"
+#define DP_DB_COL_CACHE_REFCOUNT "ref_count"
+#define DP_DB_COL_CACHE_MAX_AGE "max_age"
+#define DP_DB_COL_CACHE_LAST_UPDATE "last_update"
+
// clients table
#define DP_DB_COL_SMACK_LABEL "smack_label"
#define DP_DB_COL_PACKAGE "package"
#define DP_DB_COL_NETWORK_TYPE "network_type"
#define DP_DB_COL_NETWORK_BONDING "network_bonding"
#define DP_DB_COL_TEMP_FILE_PATH "temp_file_path"
+#define DP_DB_COL_CACHE "cache"
// download table
#define DP_DB_COL_SAVED_PATH "saved_path"
#define DP_DB_COL_HEADER_DATA "header_data"
-
// when a client is accepted, add
// when disconnected with no request, clear
// if exist, it's possible to be remain some requests
accesstime DATE\
)"
+#define DP_SCHEMA_CACHE "CREATE TABLE IF NOT EXISTS cache(\
+id INTEGER UNIQUE PRIMARY KEY,\
+key UNSIGNED BIG INT DEFAULT 0,\
+url TEXT DEFAULT NULL,\
+cache_file_name TEXT DEFAULT NULL,\
+package TEXT DEFAULT NULL\
+)"
+
+#define DP_SCHEMA_CACHE_FILE "CREATE TABLE IF NOT EXISTS cache_file(\
+id INTEGER UNIQUE PRIMARY KEY,\
+filename TEXT DEFAULT NULL,\
+key UNSIGNED BIG INT DEFAULT 0,\
+cache_file_name TEXT UNIQUE NOT NULL,\
+etag TEXT DEFAULT NULL,\
+file_size UNSIGNED BIG INT DEFAULT 0,\
+ref_count INTEGER DEFAULT 0,\
+max_age BIG INT DEFAULT 0,\
+last_update BIG INT DEFAULT 0\
+)"
+
// limitation : 1000 rows, 48 hours standard by createtime
#define DP_SCHEMA_LOGGING "CREATE TABLE IF NOT EXISTS logging(\
id INTEGER UNIQUE PRIMARY KEY DESC NOT NULL,\
proxy TEXT DEFAULT NULL,\
temp_file_path TEXT DEFAULT NULL,\
network_bonding BOOLEAN DEFAULT 0,\
+cache BOOLEAN DEFAULT 0,\
FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE\
)"
#ifndef DOWNLOAD_PROVIDER_DB_H
#define DOWNLOAD_PROVIDER_DB_H
+#include <glib.h>
+#include <stdbool.h>
+
int dp_db_check_connection(void *handle);
int dp_db_open_client_manager(void **handle, int *errorcode);
int dp_db_open_client(void **handle, char *pkgname, int *errorcode);
int dp_db_get_property_int(void *handle, const int id, const char *table, const char *column, void *value, int *error);
int dp_db_unset_property_string(void *handle, const int id, const char *table, const char *column, int *error);
int dp_db_delete(void *handle, const int id, const char *table, int *error);
+int dp_db_delete_all(void *handle, const char *table, int *error);
int dp_db_new_header(void *handle, const int id, const char *field, const char *value, int *error);
int dp_db_get_http_headers_list(void *handle, int id, char **headers, int *error);
int dp_db_get_max_download_id(void *handle, const char *table, int *pvalue, int *error);
+
+int dp_db_new_app_cache(void *handle, const char *pkgname, const char *url, const char *file_name, bool new_file, int *error);
+int dp_db_new_file_cache(void *handle, const char *file_name, const char *content_name, const char *etag,
+ const long max_age, const unsigned long long file_size, const long now, int *error);
+int dp_db_clear_app_cache(void *handle, const char *pkgname, GSList **files, int *error);
+int dp_db_update_app_cache(void *handle, const char *url, const char *file_name, int *error);
+int dp_db_update_file_cache(void *handle, const int id, const char *file_name, const char *etag,
+ const long max_age, const unsigned long long file_size, const long now, int *error);
+int dp_db_update_file_cache_validator(void *handle, const int id, const char *column, const int type, void *value, int *error);
+int dp_db_get_file_cache_string(void *handle, const char *file_name, const char *column, unsigned char **string, unsigned *length, int *error);
+int dp_db_get_cache_file_name(void *handle, const char *pkgname, const char *url, char **file_name, unsigned *length, int *pkg_exist, int *error);
+int dp_db_get_cache_file_id(void *handle, const char *file_name, int *error);
+int dp_db_clear_exceeded_cache(void *handle, GSList **files, long long cache_max, int type, int *error);
+int dp_db_clear_app_cache_by_filename(void *handle, const char *file_name, int *error);
#endif
--- /dev/null
+/*
+ * Copyright (c) 2023 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.
+ */
+
+#ifndef DOWNLOAD_PROVIDER_PLUGIN_CACHE_AGENT_H
+#define DOWNLOAD_PROVIDER_PLUGIN_CACHE_AGENT_H
+
+#define CACHE_CONTROL_MAX_AGE "max-age="
+#define CACHE_CONTROL_NO_STORE "no-store"
+#define CACHE_CONTROL_NO_CACHE "no-cache"
+#define CACHE_CONTROL_DELETION_RATIO 0.3
+
+typedef enum {
+ DP_CACHE_CAPACITY_MAX_SIZE,
+ DP_CACHE_CAPACITY_LIFECYCLE,
+} dp_cache_capacity_event_type;
+
+int dp_init_cache_agent();
+void dp_deinit_cache_agent();
+
+char *dp_store_file(const char *file_path);
+int dp_remove_files(int file_count, const char *cache_files[]);
+int dp_is_file_available(const char *cache_file);
+int dp_get_used_cache_size(long long *size);
+
+int dp_start_cache_download(const char *cache_file, void *slot, void *request);
+int dp_pause_cache_download(int req_id);
+int dp_resume_cache_download(int req_id);
+int dp_cancel_cache_download(int req_id);
+int dp_pause_cache_download_without_update(int req_id);
+int dp_cancel_cache_download_without_update(int req_id);
+int dp_is_alive_cache_download(int req_id);
+
+int dp_get_max_cache_size(unsigned int *size);
+int dp_set_max_cache_size(unsigned int size);
+int dp_get_cache_path(char **path);
+int dp_set_cache_path(const char *path);
+int dp_get_cache_lifecycle(unsigned int *time);
+int dp_set_cache_lifecycle(unsigned int time);
+int dp_clear_all_cache_files(void);
+int dp_set_oldest_cache_file_time(time_t time);
+bool dp_is_cache_storage_busy(void);
+
+#endif
int dp_is_file_exist(const char *file_path);
long long dp_get_file_modified_time(const char *file_path);
int dp_remove_file(const char *file_path);
+unsigned long dp_get_hash_key(const char *str);
#endif
DP_SEC_CONTROL,
DP_SEC_GET,
DP_SEC_SET,
- DP_SEC_UNSET
+ DP_SEC_UNSET,
} dp_ipc_section_defs;
typedef enum {
DP_PROP_NOTIFICATION_SUBJECT,
DP_PROP_NOTIFICATION_DESCRIPTION,
DP_PROP_NOTIFICATION_TYPE,
+ DP_PROP_CACHE,
+ DP_PROP_RESET_CACHE,
+ DP_PROP_SET_CACHE_MAX_SIZE,
+ DP_PROP_GET_CACHE_MAX_SIZE,
+ DP_PROP_SET_CACHE_PATH,
+ DP_PROP_GET_CACHE_PATH,
+ DP_PROP_SET_CACHE_LIFECYCLE,
+ DP_PROP_GET_CACHE_LIFECYCLE,
+ DP_PROP_RESET_ALL_CACHE,
DP_PROP_CREATE,
DP_PROP_START,
DP_PROP_PAUSE,
DP_PROP_CANCEL,
DP_PROP_DESTROY,
- DP_PROP_VERIFY_HOST
+ DP_PROP_VERIFY_HOST,
} dp_ipc_property_defs;
typedef enum {
DP_CONTENT_VCAL, //13
} dp_content_type;
-
typedef struct {
short section;
unsigned property;