diff options
Diffstat (limited to 'provider')
25 files changed, 9656 insertions, 0 deletions
diff --git a/provider/CMakeLists.txt b/provider/CMakeLists.txt new file mode 100755 index 0000000..fef1222 --- /dev/null +++ b/provider/CMakeLists.txt @@ -0,0 +1,71 @@ + +## PROJECT NAME +PROJECT(${PKG_NAME} C) + +IF("${CMAKE_BUILD_TYPE}" STREQUAL "") + SET(CMAKE_BUILD_TYPE "Debug") +ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "") +MESSAGE("Build type: ${CMAKE_BUILD_TYPE}") + +INCLUDE(FindPkgConfig) + +pkg_check_modules(dp2_pkgs REQUIRED glib-2.0 + gobject-2.0 + sqlite3 + capi-appfw-app-manager + capi-network-connection + wifi-direct + notification + appsvc + bundle + libsmack + libsystemd-daemon + libtzplatform-config + dlog) + +FOREACH(flag ${dp2_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +## INCLUDES +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/agent/include) + +set(DP2_LINK_LIBRARIES ${GLIB-2_LIBRARIES} + ${GOBJECT-2_LIBRARIES} + pthread + capi-appfw-app-manager + ) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -Wall") + +IF(DEFINED DATABASE_SCHEMA_FILE) + ADD_DEFINITIONS(-DDATABASE_SCHEMA_FILE=\"${DATABASE_SCHEMA_FILE}\") +ENDIF(DEFINED DATABASE_SCHEMA_FILE) + +IF(DEFINED IMAGE_DIR) + ADD_DEFINITIONS(-DIMAGE_DIR=\"${IMAGE_DIR}\") +ENDIF(DEFINED IMAGE_DIR) + +IF(DEFINED LOCALE_DIR) + ADD_DEFINITIONS(-DPKG_NAME=\"${PKG_NAME}\") + ADD_DEFINITIONS(-DLOCALE_DIR=\"${LOCALE_DIR}\") +ENDIF(DEFINED LOCALE_DIR) + + +ADD_EXECUTABLE(${PROJECT_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-pid.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-socket.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-slots.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-network.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-db.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-request.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-da-interface.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-thread-request.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-thread-queue.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-notification.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-main.c ) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${dp2_pkgs_LDFLAGS} ${DP2_LINK_LIBRARIES} -ldl) +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BIN_INSTALL_DIR}) + +INSTALL(FILES include/download-provider-defs.h DESTINATION ${INCLUDE_INSTALL_DIR}/${PKG_NAME}) diff --git a/provider/download-provider-da-interface.c b/provider/download-provider-da-interface.c new file mode 100755 index 0000000..87eb375 --- /dev/null +++ b/provider/download-provider-da-interface.c @@ -0,0 +1,903 @@ +/* + * Copyright (c) 2012 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> // dlopen + +#include <sys/smack.h> +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" +#include "download-provider-socket.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-notification.h" +#include "download-provider-request.h" +#include "download-provider-network.h" +#include "download-provider-da-interface.h" + +#include "download-agent-defs.h" +#include "download-agent-interface.h" + +#define DP_SDCARD_MNT_POINT "/opt/storage/sdcard" + +static void *g_da_handle = NULL; +static int (*download_agent_init)(da_client_cb_t *) = NULL; // int da_init(da_client_cb_t *da_client_callback); +static int (*download_agent_deinit)() = NULL; // int da_deinit(); +static int (*download_agent_is_alive)(int) = NULL; // int da_is_valid_download_id(int download_id); +static int (*download_agent_suspend)(int) = NULL; // int da_suspend_download(int download_id); +static int (*download_agent_resume)(int) = NULL; // int da_resume_download(int download_id); +static int (*download_agent_cancel)(int) = NULL; // int da_cancel_download(int download_id); +static int (*download_agent_start)(const char *, extension_data_t *, int *) = NULL; // int da_start_download_with_extension(const char *url, extension_data_t *ext_data, int *download_id); + + +int dp_is_file_exist(const char *file_path) +{ + struct stat file_state; + int stat_ret; + + if (file_path == NULL) { + TRACE_ERROR("[NULL-CHECK] file path is NULL"); + return -1; + } + + stat_ret = stat(file_path, &file_state); + + if (stat_ret == 0) + if (file_state.st_mode & S_IFREG) + return 0; + + return -1; +} + +static int __change_error(int err) +{ + int ret = DP_ERROR_NONE; + switch (err) { + case DA_RESULT_OK: + ret = DP_ERROR_NONE; + break; + case DA_ERR_INVALID_ARGUMENT: + ret = DP_ERROR_INVALID_PARAMETER; + break; + case DA_ERR_FAIL_TO_MEMALLOC: + ret = DP_ERROR_OUT_OF_MEMORY; + break; + case DA_ERR_UNREACHABLE_SERVER: + ret = DP_ERROR_NETWORK_UNREACHABLE; + break; + case DA_ERR_HTTP_TIMEOUT: + ret = DP_ERROR_CONNECTION_TIMED_OUT; + break; + case DA_ERR_DISK_FULL: + ret = DP_ERROR_NO_SPACE; + break; + case DA_ERR_INVALID_STATE: + ret = DP_ERROR_INVALID_STATE; + break; + case DA_ERR_NETWORK_FAIL: + ret = DP_ERROR_CONNECTION_FAILED; + break; + case DA_ERR_INVALID_URL: + ret = DP_ERROR_INVALID_URL; + break; + case DA_ERR_INVALID_INSTALL_PATH: + ret = DP_ERROR_INVALID_DESTINATION; + break; + case DA_ERR_ALREADY_MAX_DOWNLOAD: + ret = DP_ERROR_TOO_MANY_DOWNLOADS; + break; + case DA_ERR_FAIL_TO_CREATE_THREAD: + case DA_ERR_FAIL_TO_OBTAIN_MUTEX: + case DA_ERR_FAIL_TO_ACCESS_FILE: + case DA_ERR_FAIL_TO_GET_CONF_VALUE: + case DA_ERR_FAIL_TO_ACCESS_STORAGE: + default: + ret = DP_ERROR_IO_ERROR; + break; + } + return ret; +} + +static void __download_info_cb(user_download_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + dp_request_slots *request_slot = (dp_request_slots *) user_data; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + return ; + } + CLIENT_MUTEX_LOCK(&request_slot->mutex); + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + dp_request *request = request_slot->request; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + int request_id = request->id; + + // update info before sending event + if (info->tmp_saved_path != NULL) { + + TRACE_SECURE_DEBUG("[STARTED][%d] [%s]", request_id, info->tmp_saved_path); + int conds_count = 6; // id + tmp_saved_path + file_size + content_name + etag + int conds_index = 0; + db_conds_list_fmt conds_p[conds_count]; + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + + conds_p[conds_index].column = DP_DB_COL_TMP_SAVED_PATH; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->tmp_saved_path; + conds_index++; + if (info->file_type != NULL) { + conds_p[conds_index].column = DP_DB_COL_MIMETYPE; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->file_type; + conds_index++; + } + if (info->content_name != NULL) { + conds_p[conds_index].column = DP_DB_COL_CONTENT_NAME; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->content_name; + conds_index++; + } + if (info->etag != NULL) { + conds_p[conds_index].column = DP_DB_COL_ETAG; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->etag; + conds_index++; + } + if (info->file_size > 0) { + request->file_size = info->file_size; + conds_p[conds_index].column = DP_DB_COL_CONTENT_SIZE; + conds_p[conds_index].type = DP_DB_COL_TYPE_INT64; + conds_p[conds_index].value = &info->file_size; + conds_index++; + } + + int check_id = dp_db_get_int_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ID); + if (check_id == request_id) { // update + if (dp_db_update_columns(request_id, DP_DB_TABLE_DOWNLOAD_INFO, conds_index, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + request->error = DP_ERROR_NO_SPACE; + TRACE_ERROR("[SQLITE_FULL][%d]", request_id); + if (dp_cancel_agent_download(request->agent_id) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", request_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + } + } else { // insert + conds_p[conds_index].column = DP_DB_COL_ID; + conds_p[conds_index].type = DP_DB_COL_TYPE_INT; + conds_p[conds_index].value = &request_id; + conds_index++; + if (dp_db_insert_columns(DP_DB_TABLE_DOWNLOAD_INFO, conds_index, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + request->error = DP_ERROR_NO_SPACE; + TRACE_ERROR("[SQLITE_FULL][%d]", request_id); + if (dp_cancel_agent_download(request->agent_id) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", request_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + } + } + + } + + request->ip_changed = 0; + request->state = DP_STATE_DOWNLOADING; + request->error = DP_ERROR_NONE; + dp_request_state_response(request); + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); +} + +static void __progress_cb(user_progress_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + dp_request_slots *request_slot = (dp_request_slots *) user_data; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + return ; + } + CLIENT_MUTEX_LOCK(&request_slot->mutex); + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + dp_request *request = request_slot->request; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + if (request->state == DP_STATE_DOWNLOADING) { + request->received_size = info->received_size; + time_t tt = time(NULL); + struct tm *localTime = localtime(&tt); + // send event every 1 second. + if (request->progress_lasttime != localTime->tm_sec) { + request->progress_lasttime = localTime->tm_sec; + if (request->auto_notification) { + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } else { + int noti_type = dp_db_get_int_column(request->id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if(noti_type == DP_NOTIFICATION_TYPE_ALL) + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } + + if (request->progress_cb && request->group != NULL && + request->group->event_socket >= 0 && + request->received_size > 0) + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, + request->received_size); + } + } + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); +} + +static int __is_transmute_smack(char *path) +{ + char *dir_label = NULL; + int ret = -1; + if (smack_getlabel(path, &dir_label, SMACK_LABEL_TRANSMUTE) == 0 && + dir_label != NULL) { + if (strncmp(dir_label, "TRUE", strlen(dir_label)) == 0) + ret = 0; + } + free(dir_label); + return ret; +} + +static dp_error_type __set_dir_smack_label(char *smack_label, char *dir_path, char *saved_path) +{ + if (smack_label == NULL || dir_path== NULL || saved_path == NULL) + return DP_ERROR_PERMISSION_DENIED; + + int is_setted_dir_label = 0; + dp_error_type errorcode = DP_ERROR_NONE; + + TRACE_SECURE_INFO("[PARSE] dir path [%s]", dir_path); + if (__is_transmute_smack(dir_path) < 0) { + TRACE_DEBUG("[SMACK] no transmute"); + } else { + char *dir_label = NULL; + if (smack_getlabel(dir_path, &dir_label, + SMACK_LABEL_ACCESS) == 0) { + if (smack_have_access(smack_label, dir_label, "t") > 0) { + if (smack_setlabel(saved_path, dir_label, + SMACK_LABEL_ACCESS) != 0) { + TRACE_SECURE_ERROR("[SMACK ERROR] label:%s", + dir_label); + errorcode = DP_ERROR_PERMISSION_DENIED; + } else { + is_setted_dir_label = 1; + } + } else { + TRACE_SECURE_ERROR("[SMACK ERROR] access:%s/%s", + smack_label, dir_label); + errorcode = DP_ERROR_PERMISSION_DENIED; + } + } else { + TRACE_SECURE_ERROR("[SMACK ERROR] no label:", dir_path); + errorcode = DP_ERROR_PERMISSION_DENIED; + } + free(dir_label); + } + if (is_setted_dir_label == 0 && + smack_setlabel(saved_path, smack_label, + SMACK_LABEL_ACCESS) != 0) { + TRACE_SECURE_ERROR("[SMACK ERROR] label:%s", smack_label); + errorcode = DP_ERROR_PERMISSION_DENIED; + // remove file. + if (dp_is_file_exist(saved_path) == 0) + unlink(saved_path); + } + return errorcode; +} + +static dp_error_type __set_file_permission_to_client(dp_request *request, char *saved_path) +{ + dp_error_type errorcode = DP_ERROR_NONE; + char *str = NULL; + char *smack_label = NULL; + str = strrchr(saved_path, '/'); + dp_credential cred; + if (request->group == NULL) { + cred.uid = dp_db_cond_get_int(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_UID, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + cred.gid = dp_db_cond_get_int(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_GID, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + } else { + cred = request->group->credential; + } + TRACE_DEBUG + ("[chown][%d] [%d][%d]", request->id, cred.uid, cred.gid); + if (chown(saved_path, cred.uid, cred.gid) < 0) + TRACE_STRERROR("[ERROR][%d] chown", request->id); + if (chmod(saved_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) + TRACE_STRERROR("[ERROR][%d] chmod", request->id); + if (dp_is_smackfs_mounted() == 1) { + if (request->group == NULL) { + // get smack_label from sql + smack_label = + dp_db_cond_get_text(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_SMACK_LABEL, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + } else { + smack_label = dp_strdup(request->group->smack_label); + } + if (smack_label == NULL) { + TRACE_SECURE_ERROR("[SMACK][%d] no label", request->id); + errorcode = DP_ERROR_PERMISSION_DENIED; + } else { + size_t len = str - (saved_path); + char *dir_path = (char *)calloc(len + 1, sizeof(char)); + if (dir_path != NULL) { + strncpy(dir_path, saved_path, len); + errorcode = + __set_dir_smack_label(smack_label, dir_path, + saved_path); + free(dir_path); + } else { + TRACE_SECURE_ERROR("[ERROR] calloc"); + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + free(smack_label); + } + } + return errorcode; +} + +static void __finished_cb(user_finished_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + dp_request_slots *request_slot = (dp_request_slots *) user_data; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + return ; + } + CLIENT_MUTEX_LOCK(&request_slot->mutex); + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + dp_request *request = request_slot->request; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + int request_id = request->id; + dp_state_type state = DP_STATE_NONE; + dp_error_type errorcode = DP_ERROR_NONE; + + if (info->http_status > 0) + if (dp_db_replace_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_HTTP_STATUS, + DP_DB_COL_TYPE_INT, &info->http_status) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + + if (info->err == DA_RESULT_OK) { + + int conds_count = 5; // id + saved_path + content_name + http_status + file_size + int conds_index = 0; + db_conds_list_fmt conds_p[conds_count]; + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + + char *content_name = NULL; + if (info->saved_path != NULL) { + char *str = NULL; + if(!(strncmp(DP_SDCARD_MNT_POINT, info->saved_path, + strlen(DP_SDCARD_MNT_POINT)) == 0)) { + errorcode = __set_file_permission_to_client(request, + info->saved_path); + } + str = strrchr(info->saved_path, '/'); + if (str != NULL) { + str++; + content_name = dp_strdup(str); + TRACE_SECURE_DEBUG("[PARSE][%d] content_name [%s]", + request_id, content_name); + } + + conds_p[conds_index].column = DP_DB_COL_SAVED_PATH; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->saved_path; + conds_index++; + if (content_name != NULL) { + conds_p[conds_index].column = DP_DB_COL_CONTENT_NAME; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = content_name; + conds_index++; + } + + if (errorcode == DP_ERROR_NONE) { + state = DP_STATE_COMPLETED; + TRACE_SECURE_INFO("[COMPLETED][%d] saved to [%s]", + request_id, info->saved_path); + } else { + state = DP_STATE_FAILED; + TRACE_SECURE_INFO("[FAILED][%d] saved to [%s]", + request_id, info->saved_path); + } + } else { + TRACE_ERROR("Cannot enter here"); + TRACE_ERROR("[ERROR][%d] No SavedPath", request_id); + errorcode = DP_ERROR_INVALID_DESTINATION; + state = DP_STATE_FAILED; + } + if (request->file_size == 0) { + request->file_size = request->received_size; + conds_p[conds_index].column = DP_DB_COL_CONTENT_SIZE; + conds_p[conds_index].type = DP_DB_COL_TYPE_INT64; + conds_p[conds_index].value = &request->file_size; + conds_index++; + } + + int check_id = dp_db_get_int_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ID); + if (check_id == request_id) { // update + if (dp_db_update_columns(request_id, DP_DB_TABLE_DOWNLOAD_INFO, conds_index, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + request->error = DP_ERROR_NO_SPACE; + TRACE_ERROR("[SQLITE_FULL][%d]", request_id); + if (dp_cancel_agent_download(request->agent_id) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", request_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + } + } else { // insert + conds_p[conds_index].column = DP_DB_COL_ID; + conds_p[conds_index].type = DP_DB_COL_TYPE_INT; + conds_p[conds_index].value = &request_id; + conds_index++; + if (dp_db_insert_columns(DP_DB_TABLE_DOWNLOAD_INFO, conds_index, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + request->error = DP_ERROR_NO_SPACE; + TRACE_ERROR("[SQLITE_FULL][%d]", request_id); + if (dp_cancel_agent_download(request->agent_id) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", request_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + } + } + free(content_name); + } else { + char *tmp_saved_path = + dp_request_get_tmpsavedpath(request_id, request, &errorcode); + + if (tmp_saved_path != NULL) { + errorcode = __set_file_permission_to_client(request, tmp_saved_path); + free(tmp_saved_path); + } else { + TRACE_ERROR("Cannot enter here"); + TRACE_ERROR("[ERROR][%d] No SavedPath", request_id); + } + + if (info->err == DA_RESULT_USER_CANCELED) { + state = DP_STATE_CANCELED; + errorcode = request->error; + TRACE_INFO("[CANCELED][%d]", request_id); + } else { + state = DP_STATE_FAILED; + errorcode = __change_error(info->err); + TRACE_ERROR("[FAILED][%d][%s]", request_id, + dp_print_errorcode(errorcode)); + } + } + + request->state = state; + request->error = errorcode; + + // auto resume when failed by ip_changed. + if (state == DP_STATE_FAILED && info->err == DA_ERR_NETWORK_FAIL && + request->network_type != DP_NETWORK_TYPE_WIFI_DIRECT) { + if (request->ip_changed == 1 && + dp_get_network_connection_instant_status() != + DP_NETWORK_TYPE_OFF) { + // resume + TRACE_DEBUG("[RESUME][%d] will be queued", request_id); + request->state = DP_STATE_QUEUED; + request->error = DP_ERROR_NONE; + } else { + TRACE_DEBUG("[CHECK][%d] check again in timeout", request_id); + } + } else { + // stay on memory till called destroy by client or timeout + if (request->group != NULL && + request->group->event_socket >= 0) { + /* update the received file size. + * The last received file size cannot update + * because of reducing update algorithm*/ + if (request->received_size > 0) { + dp_ipc_send_event(request->group->event_socket, + request->id, DP_STATE_DOWNLOADING, request->error, + request->received_size); + } + } + dp_request_state_response(request); + } + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + + dp_thread_queue_manager_wake_up(); +} + +static void __paused_cb(user_paused_info_t *info, void *user_data) +{ + dp_request_slots *request_slot = (dp_request_slots *) user_data; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + return ; + } + CLIENT_MUTEX_LOCK(&request_slot->mutex); + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + dp_request *request = request_slot->request; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + if (request->state != DP_STATE_PAUSE_REQUESTED) { + TRACE_ERROR("[CHECK] now status:%d", request->state); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + int request_id = request->id; + + if (dp_db_update_date + (request_id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + + request->state = DP_STATE_PAUSED; + request->error = DP_ERROR_NONE; + dp_request_state_response(request); + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + + dp_thread_queue_manager_wake_up(); +} + +int dp_init_agent() +{ + + g_da_handle = dlopen("libdownloadagent2.so", RTLD_LAZY | RTLD_GLOBAL); + if (!g_da_handle) { + TRACE_ERROR("[dlopen] %s", dlerror()); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + dlerror(); /* Clear any existing error */ + + *(void **) (&download_agent_init) = dlsym(g_da_handle, "da_init"); + if (download_agent_init == NULL ) { + TRACE_ERROR("[dlsym] da_init:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_deinit) = dlsym(g_da_handle, "da_deinit"); + if (download_agent_deinit == NULL ) { + TRACE_ERROR("[dlsym] da_deinit:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_is_alive) = dlsym(g_da_handle, "da_is_valid_download_id"); + if (download_agent_is_alive == NULL ) { + TRACE_ERROR("[dlsym] da_is_valid_download_id:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_suspend) = dlsym(g_da_handle, "da_suspend_download"); + if (download_agent_suspend == NULL ) { + TRACE_ERROR("[dlsym] da_suspend_download:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_resume) = dlsym(g_da_handle, "da_resume_download"); + if (download_agent_resume == NULL ) { + TRACE_ERROR("[dlsym] da_resume_download:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_cancel) = dlsym(g_da_handle, "da_cancel_download"); + if (download_agent_cancel == NULL ) { + TRACE_ERROR("[dlsym] da_cancel_download:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_start) = dlsym(g_da_handle, "da_start_download_with_extension"); + if (download_agent_start == NULL ) { + TRACE_ERROR("[dlsym] da_start_download_with_extension:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + int da_ret = -1; + da_client_cb_t da_cb = { + __download_info_cb, + __progress_cb, + __finished_cb, + __paused_cb + }; + da_ret = (*download_agent_init)(&da_cb); + if (da_ret != DA_RESULT_OK) { + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +void dp_deinit_agent() +{ + if (g_da_handle != NULL) { + if (download_agent_deinit != NULL) + (*download_agent_deinit)(); + + dlclose(g_da_handle); + g_da_handle = NULL; + } +} + +// 1 : alive +// 0 : not alive +int dp_is_alive_download(int req_id) +{ + int da_ret = 0; + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return 0; + } + if (download_agent_is_alive != NULL) + da_ret = (*download_agent_is_alive)(req_id); + return da_ret; +} + +// 0 : success +// -1 : failed +dp_error_type dp_cancel_agent_download(int req_id) +{ + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return -1; + } + if (dp_is_alive_download(req_id) == 0) { + TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id); + return -1; + } + if (download_agent_cancel != NULL) { + if ((*download_agent_cancel)(req_id) == DA_RESULT_OK) + return 0; + } + return -1; +} + +// 0 : success +// -1 : failed +dp_error_type dp_pause_agent_download(int req_id) +{ + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return -1; + } + if (dp_is_alive_download(req_id) == 0) { + TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id); + return -1; + } + if (download_agent_suspend != NULL) { + if ((*download_agent_suspend)(req_id) == DA_RESULT_OK) + return 0; + } + return -1; +} + + +// 0 : success +// -1 : failed +// -2 : pended +dp_error_type dp_start_agent_download(dp_request_slots *request_slot) +{ + int da_ret = -1; + int req_dl_id = -1; + dp_error_type errorcode = DP_ERROR_NONE; + extension_data_t ext_data = {0,}; + char *etag = NULL; + + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] download_clientinfo_slot"); + return DP_ERROR_INVALID_PARAMETER; + } + + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] download_clientinfo_slot"); + return DP_ERROR_INVALID_PARAMETER; + } + + dp_request *request = request_slot->request; + + char *url = dp_request_get_url(request->id, &errorcode); + if (url == NULL) { + TRACE_ERROR("[ERROR][%d] URL is NULL", request->id); + return DP_ERROR_INVALID_URL; + } + char *destination = + dp_request_get_destination(request->id, request, &errorcode); + if (destination != NULL) + ext_data.install_path = destination; + + char *filename = + dp_request_get_filename(request->id, request, &errorcode); + if (filename != NULL) + ext_data.file_name = filename; + + // call start_download() of download-agent + + char *tmp_saved_path = + dp_request_get_tmpsavedpath(request->id, request, &errorcode); + if (tmp_saved_path) { + etag = dp_request_get_etag(request->id, request, &errorcode); + if (etag) { + TRACE_DEBUG("[RESUME][%d]", request->id); + ext_data.etag = etag; + ext_data.temp_file_path = tmp_saved_path; + } else { + /* FIXME later : It is better to handle the unlink function in download agaent module + * or in upload the request data to memory after the download provider process is restarted */ + TRACE_SECURE_INFO("[RESTART][%d] try to remove tmp file [%s]", + request->id, tmp_saved_path); + if (dp_is_file_exist(tmp_saved_path) == 0) + if (unlink(tmp_saved_path) != 0) + TRACE_STRERROR + ("[ERROR][%d] remove file", request->id); + } + } + char *pkg_name = dp_request_get_pkg_name(request->id, request, &errorcode); + if (pkg_name != NULL) + ext_data.pkg_name = pkg_name; + // get headers list from httpheaders table(DB) + int headers_count = dp_db_get_cond_rows_count + (request->id, DP_DB_TABLE_HTTP_HEADERS, NULL, 0, NULL); + if (headers_count > 0) { + ext_data.request_header = calloc(headers_count, sizeof(char*)); + if (ext_data.request_header != NULL) { + ext_data.request_header_count = dp_db_get_http_headers_list + (request->id, (char**)ext_data.request_header); + } + } + + ext_data.user_data = (void *)request_slot; + + // call start API of agent lib + if (download_agent_start != NULL) + da_ret = (*download_agent_start)(url, &ext_data, &req_dl_id); + if (ext_data.request_header_count > 0) { + int len = 0; + int i = 0; + len = ext_data.request_header_count; + for (i = 0; i < len; i++) { + if (ext_data.request_header[i]) + free((void *)(ext_data.request_header[i])); + } + free(ext_data.request_header); + } + free(url); + if (destination) + free(destination); + if (filename) + free(filename); + if (tmp_saved_path) + free(tmp_saved_path); + if (etag) + free(etag); + if (pkg_name) + free(pkg_name); + + // if start_download() return error cause of maximun download limitation, + // set state to DOWNLOAD_STATE_PENDED. + if (da_ret == DA_ERR_ALREADY_MAX_DOWNLOAD) { + TRACE_DEBUG("[PENDING][%d] DA_ERR_ALREADY_MAX_DOWNLOAD [%d]", + request->id, da_ret); + return DP_ERROR_TOO_MANY_DOWNLOADS; + } else if (da_ret != DA_RESULT_OK) { + TRACE_ERROR("[ERROR][%d] DP_ERROR_CONNECTION_FAILED [%d]", + request->id, da_ret); + return __change_error(da_ret); + } + TRACE_DEBUG("[SUCCESS][%d] agent_id [%d]", request->id, req_dl_id); + request->agent_id = req_dl_id; + return DP_ERROR_NONE; +} + +dp_error_type dp_resume_agent_download(int req_id) +{ + int da_ret = -1; + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return DP_ERROR_INVALID_PARAMETER; + } + if (download_agent_resume != NULL) + da_ret = (*download_agent_resume)(req_id); + if (da_ret == DA_RESULT_OK) + return DP_ERROR_NONE; + else if (da_ret == DA_ERR_INVALID_STATE) + return DP_ERROR_INVALID_STATE; + return __change_error(da_ret); +} + diff --git a/provider/download-provider-db.c b/provider/download-provider-db.c new file mode 100755 index 0000000..6f1d5cf --- /dev/null +++ b/provider/download-provider-db.c @@ -0,0 +1,2352 @@ +/* + * Copyright (c) 2012 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 <string.h> +#include <errno.h> +#include <tzplatform_config.h> + +#include <stdlib.h> +#include <sqlite3.h> + +#include "download-provider-config.h" +#include "download-provider-db.h" +#include "download-provider-slots.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" + +#define DATABASE_FILE tzplatform_mkpath(TZ_SYS_DB,".download-provider.db") + +//BASIC +#define DP_DB_BASIC_GET_QUERY_FORMAT "SELECT %s FROM %s WHERE id = ?" +#define DP_DB_BASIC_SET_QUERY_FORMAT "UPDATE %s SET %s = ? WHERE id = ?" +#define DP_DB_BASIC_INSERT_QUERY_FORMAT "INSERT INTO %s (id, %s) VALUES (?, ?)" +#define DP_DB_BASIC_NOW_DATE_QUERY_FORMAT "UPDATE %s SET %s = DATETIME('now') WHERE id = ?" + +// COND +#define DP_DB_COND_GET_QUERY_FORMAT "SELECT %s FROM %s WHERE id = ? AND %s = ?" +#define DP_DB_COND_SET_QUERY_FORMAT "UPDATE %s SET %s = ? WHERE id = ? AND %s = ?" + +typedef enum { + DP_DB_QUERY_TYPE_GET = 10, + DP_DB_QUERY_TYPE_SET = 20, + DP_DB_QUERY_TYPE_INSERT = 30, + DP_DB_QUERY_TYPE_NOW_DATE = 40 +} db_query_type; + +sqlite3 *g_dp_db_handle = 0; +sqlite3_stmt *g_dp_db_logging_new_stmt = NULL; +sqlite3_stmt *g_dp_db_logging_status_stmt = NULL; +sqlite3_stmt *g_dp_db_logging_get_state_stmt = NULL; + +static void __dp_finalize(sqlite3_stmt *stmt) +{ + if (sqlite3_finalize(stmt) != SQLITE_OK) + TRACE_ERROR("failed sqlite3_finalize [%s]", + sqlite3_errmsg(g_dp_db_handle)); +} + +// called when terminating process +void dp_db_close() +{ + if (g_dp_db_handle) { + if (g_dp_db_logging_new_stmt != NULL) + __dp_finalize(g_dp_db_logging_new_stmt); + if (g_dp_db_logging_status_stmt != NULL) + __dp_finalize(g_dp_db_logging_status_stmt); + sqlite3_exec(g_dp_db_handle, "VACUUM;", 0, 0, 0); // remove empty page of db + sqlite3_close(g_dp_db_handle); + } + g_dp_db_handle = 0; +} + +static void __load_sql_schema() +{ + char *rebuild_query = + sqlite3_mprintf("sqlite3 %s '.read %s'", DATABASE_FILE, DATABASE_SCHEMA_FILE); + if (rebuild_query != NULL) { + TRACE_SECURE_INFO("[QUERY] %s", rebuild_query); + system(rebuild_query); + sqlite3_free(rebuild_query); + } +} + +static int __dp_sql_open() +{ + return dp_db_open(); +} + +static int __check_table(char *table) +{ + //"SELECT name FROM sqlite_master WHERE type='table' AND name='" + table +"'"; + sqlite3_stmt *stmt = NULL; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + char *query = sqlite3_mprintf("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'", table); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + int ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_step(stmt) == SQLITE_ROW) { + __dp_finalize(stmt); + return 1; + } + __dp_finalize(stmt); + return 0; +} + +// called when launching process or in every API +int dp_db_open() +{ + if (g_dp_db_handle == 0) { + TRACE_SECURE_INFO("TRY to open [%s]", DATABASE_FILE); + if (sqlite3_open_v2(DATABASE_FILE, &g_dp_db_handle, + SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) { + TRACE_ERROR("[ERROR][%s][%s]", DATABASE_FILE, + sqlite3_errmsg(g_dp_db_handle)); + int errorcode = sqlite3_errcode(g_dp_db_handle); + dp_db_close(); + if (errorcode == SQLITE_CORRUPT) { + TRACE_SECURE_INFO("unlink [%s]", DATABASE_FILE); + unlink(DATABASE_FILE); + errorcode = SQLITE_CANTOPEN; + } + if (errorcode == SQLITE_CANTOPEN) { + __load_sql_schema(); + return dp_db_open(); + } + return -1; + } + sqlite3_exec(g_dp_db_handle, "PRAGMA journal_mode=PERSIST;", 0, 0, 0); + sqlite3_exec(g_dp_db_handle, "PRAGMA foreign_keys=ON;", 0, 0, 0); + + // if not found group table. load again. (FOTA) + // new table(groups) created by smack_label. 2013.07.09 + if (__check_table(DP_DB_TABLE_GROUPS) == 0) + __load_sql_schema(); + } + return g_dp_db_handle ? 0 : -1; +} + +int dp_db_is_full_error() +{ + if (g_dp_db_handle == 0) { + TRACE_ERROR("HANDLE is null"); + return -1; + } + if (sqlite3_errcode(g_dp_db_handle) == SQLITE_FULL) + return 0; + return -1; +} + +int dp_db_get_count_by_limit_time() +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT count(id) FROM logging \ + WHERE createtime < DATETIME('now','-48 hours')", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_ROW) { + int count = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return count; + } + __dp_finalize(stmt); + return 0; +} + +int dp_db_get_list_by_limit_time(dp_request_slots *requests, int limit) +{ + int errorcode = SQLITE_OK; + int i = 0; + sqlite3_stmt *stmt = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT id, state FROM logging WHERE \ + createtime < DATETIME('now','-48 hours') \ + ORDER BY createtime ASC LIMIT ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW && i < limit) { + // allocation & initialization + requests[i].request = dp_request_new(); + // ID + requests[i].request->id = sqlite3_column_int(stmt, 0); + // state + requests[i].request->state = sqlite3_column_int(stmt, 1); + i++; + } + + __dp_finalize(stmt); + return i; +} + +int dp_db_crashed_list(dp_request_slots *requests, int limit) +{ + int errorcode = SQLITE_OK; + int i = 0; + int buffer_length = 0; + sqlite3_stmt *stmt = NULL; + char *buffer = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT id, state, packagename FROM logging WHERE \ + (state = ? OR state = ? OR state = ?) \ + AND createtime > DATETIME('now','-48 hours') LIMIT ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, DP_STATE_QUEUED) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 2, DP_STATE_DOWNLOADING) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 3, DP_STATE_CONNECTING) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_bind_int(stmt, 4, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + // allocation & initialization + requests[i].request = dp_request_new(); + // ID + requests[i].request->id = sqlite3_column_int(stmt, 0); + // state + requests[i].request->state = sqlite3_column_int(stmt, 1); + // packagename + buffer = (char *)(sqlite3_column_text(stmt, 2)); + requests[i].request->packagename = NULL; + if (buffer) { + buffer_length = strlen(buffer); + if (buffer_length > 1) { + requests[i].request->packagename + = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(requests[i].request->packagename, buffer, + buffer_length * sizeof(char)); + requests[i].request->packagename[buffer_length] = '\0'; + } + } + i++; + } + + __dp_finalize(stmt); + return i; +} + +int dp_db_limit_rows(int limit) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (limit <= 0) { + TRACE_ERROR("[CHECK LIMIT] %d", limit); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // apply "ON DELETE CASCADE" + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "DELETE FROM logging WHERE id NOT IN \ + (SELECT id FROM logging ORDER BY createtime ASC LIMIT ?)", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +dp_request *dp_db_load_logging_request(int id) +{ + int errorcode = SQLITE_OK; + int buffer_length = 0; + sqlite3_stmt *stmt = NULL; + char *buffer = NULL; + dp_request *request = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT state, errorcode, startcount, packagename \ + FROM logging WHERE id = ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + request = dp_request_new(); + if (request == NULL) { + TRACE_ERROR("dp_request_new failed"); + __dp_finalize(stmt); + return NULL; + } + request->id = id; + request->state = sqlite3_column_int(stmt, 0); + request->error = sqlite3_column_int(stmt, 1); + request->startcount = sqlite3_column_int(stmt, 2); + + buffer = (char *)(sqlite3_column_text(stmt, 3)); + if (buffer) { + buffer_length = strlen(buffer); + if (buffer_length > 1) { + request->packagename + = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(request->packagename, buffer, + buffer_length * sizeof(char)); + request->packagename[buffer_length] = '\0'; + } + } + } else { + TRACE_ERROR("sqlite3_step is failed. [%s] errorcode[%d]", + sqlite3_errmsg(g_dp_db_handle), errorcode); + __dp_finalize(stmt); + return NULL; + } + __dp_finalize(stmt); + return request; +} + +int dp_db_remove_all(int id) +{ + #if 0 + dp_db_remove(id, DP_DB_TABLE_REQUEST_INFO); + dp_db_remove(id, DP_DB_TABLE_DOWNLOAD_INFO); + dp_db_remove(id, DP_DB_TABLE_HTTP_HEADERS); + dp_db_remove(id, DP_DB_TABLE_NOTIFICATION); + #endif + // apply "ON DELETE CASCADE" + dp_db_remove(id, DP_DB_TABLE_LOG); + return -1; +} + +int dp_db_remove(int id, char *table) +{ + int errorcode = SQLITE_OK; + int query_len = 0; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query_format = "DELETE FROM %s WHERE id = ? "; + // 2 means the length of one %s + query_len = strlen(query_format) - 2 + strlen(table); + if (query_len < strlen(query_format)) { + TRACE_ERROR("[CHECK QUERY FORMAT] [%s][%s]", + query_format, table); + return -1; + } + + query = (char *)calloc((query_len + 1), sizeof(char)); + if (query == NULL) { + TRACE_STRERROR("[CALLOC]"); + return -1; + } + query[query_len] = '\0'; + + ret = snprintf(query, query_len + 1, query_format, table); + + if (ret < 0) { + TRACE_STRERROR("[CHECK COMBINE] [%s]", query); + free(query); + return -1; + } + + // check error of sqlite3_prepare_v2 + if (sqlite3_prepare_v2 + (g_dp_db_handle, query, -1, &stmt, NULL) != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + free(query); + return -1; + } + free(query); + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +static sqlite3_stmt *__prepare_query(sqlite3 *handle, + db_query_type type, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + int ret = -1; + + if (type == DP_DB_QUERY_TYPE_GET) { + query_format = DP_DB_BASIC_GET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_SET) { + query_format = DP_DB_BASIC_SET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_INSERT) { + query_format = DP_DB_BASIC_INSERT_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_NOW_DATE) { + query_format = DP_DB_BASIC_NOW_DATE_QUERY_FORMAT; + } else { + TRACE_ERROR("[CHECK QUERY TYPE] [%d]", type); + return NULL; + } + + if (type == DP_DB_QUERY_TYPE_GET) + query = sqlite3_mprintf(query_format, column, table); + else + query = sqlite3_mprintf(query_format, table, column); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return NULL; + } + + ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(handle)); + __dp_finalize(stmt); + return NULL; + } + return stmt; +} + +int dp_db_insert_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_INSERT, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // VALUES ( id ) + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_insert_blob_column(int id, char *table, char *column, + void *value, unsigned length) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_INSERT, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + errorcode = sqlite3_bind_blob(stmt, 2, value, (int)length, NULL); + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // VALUES ( id ) + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_SET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 1, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 1, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 2, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_set_blob_column(int id, char *table, char *column, + void *value, unsigned length) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_SET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + errorcode = sqlite3_bind_blob(stmt, 1, value, (int)length, NULL); + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 2, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_replace_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + int check_id = dp_db_get_int_column(id, table, DP_DB_COL_ID); + if (check_id != id) // INSERT + return dp_db_insert_column(id, table, column, datatype, value); + // UPDATE + return dp_db_set_column(id, table, column, datatype, value); +} + +int dp_db_replace_blob_column(int id, char *table, char *column1, + void *value, unsigned length) +{ + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (!column1) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + int check_id = dp_db_get_int_column(id, table, DP_DB_COL_ID); + if (check_id != id) // INSERT + { + return dp_db_insert_blob_column(id, table, column1, value, length); + } + // UPDATE + return dp_db_set_blob_column(id, table, column1, value, length); +} + +// success : 0 +// error : -1 +char *dp_db_get_text_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int buffer_length = 0; + char *columntext = NULL; + char *buffer = (char *)(sqlite3_column_text(stmt, 0)); + if (buffer && (buffer_length = strlen(buffer)) > 1) { + columntext = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(columntext, buffer, buffer_length * sizeof(char)); + columntext[buffer_length] = '\0'; + } + __dp_finalize(stmt); + return columntext; + } + __dp_finalize(stmt); + return NULL; +} + +// success : 0 +// error : -1 +void *dp_db_get_blob_column(int id, char *table, char *column, int *length) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int blob_length = 0; + void *blob_data = NULL; + blob_length = sqlite3_column_bytes(stmt, 0); + if(blob_length > 0){ + blob_data = (void*)calloc(blob_length, sizeof(unsigned char)); + if(blob_data != NULL){ + memcpy(blob_data, sqlite3_column_blob(stmt, 0), + sizeof(unsigned char)*blob_length); + } else { + TRACE_ERROR("[MEM] allocating"); + blob_length = -1; + } + } else { + TRACE_ERROR("NO DATA"); + blob_length = -1; + } + __dp_finalize(stmt); + *length = blob_length; + return blob_data; + } + __dp_finalize(stmt); + return NULL; +} + + +int dp_db_get_int_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int columnvalue = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return columnvalue; + } + __dp_finalize(stmt); + return -1; +} + +long long dp_db_get_int64_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + long long columnvalue = sqlite3_column_int64(stmt, 0); + __dp_finalize(stmt); + return columnvalue; + } + __dp_finalize(stmt); + return -1; +} + +int dp_db_update_date(int id, char *table, char *column) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_NOW_DATE, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("Failed : [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +static sqlite3_stmt *__prepare_cond_query(sqlite3 *handle, + db_query_type type, char *table, + char *column, char *cond_column) +{ + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + int ret = -1; + + if (type == DP_DB_QUERY_TYPE_GET) { + query_format = DP_DB_COND_GET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_SET) { + query_format = DP_DB_COND_SET_QUERY_FORMAT; + } else { + TRACE_ERROR("[CHECK QUERY TYPE] [%d]", type); + return NULL; + } + + if (type == DP_DB_QUERY_TYPE_GET) + query = sqlite3_mprintf(query_format, column, table, cond_column); + else + query = sqlite3_mprintf(query_format, table, column, cond_column); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return NULL; + } + + ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(handle)); + __dp_finalize(stmt); + return NULL; + } + return stmt; + +} + +int dp_db_cond_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value, + char *condcolumn, db_column_data_type condtype, void *condvalue) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (!condcolumn) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_cond_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_SET, table, column, condcolumn); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 1, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 1, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 3, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 3, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 3, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 3, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 2, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +char *dp_db_cond_get_text_column(int id, char *table, char *column, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + stmt = __prepare_cond_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column, condcolumn); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + int errorcode = SQLITE_OK; + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return NULL; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int buffer_length = 0; + char *columntext = NULL; + char *buffer = (char *)(sqlite3_column_text(stmt, 0)); + if (buffer && (buffer_length = strlen(buffer)) > 1) { + columntext = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(columntext, buffer, buffer_length * sizeof(char)); + columntext[buffer_length] = '\0'; + } + __dp_finalize(stmt); + return columntext; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; +} + +int dp_db_cond_remove(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!condcolumn) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query_format = "DELETE FROM %s WHERE id = ? AND %s = ?"; + + query = sqlite3_mprintf(query_format, table, condcolumn); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return -1; + } + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +static int __dp_sql_bind_value(sqlite3_stmt *stmt, + db_column_data_type condtype, void *value, int index) +{ + int errorcode = SQLITE_ERROR; + int *cast_value = 0; + + if (stmt == NULL) + return SQLITE_ERROR; + + switch (condtype) { + case DP_DB_COL_TYPE_INT: + cast_value = value; + errorcode = sqlite3_bind_int(stmt, index, *cast_value); + break; + case DP_DB_COL_TYPE_INT64: +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, index, *cast_value); +#else + cast_value = value; + errorcode = sqlite3_bind_int(stmt, index, *cast_value); +#endif + break; + case DP_DB_COL_TYPE_TEXT: + errorcode = + sqlite3_bind_text(stmt, index, (char *)value, -1, SQLITE_STATIC); + break; + default: + errorcode = SQLITE_ERROR; + break; + } + return errorcode; +} + +char *dp_db_cond_get_text(char *table, char *column, char *condcolumn, + db_column_data_type condtype, void *condvalue) +{ + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int ret = -1; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (column == NULL) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + if (condcolumn == NULL) { + TRACE_ERROR("[CHECK Condition]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + query = sqlite3_mprintf("SELECT %s FROM %s WHERE %s IS ?", + column, table, condcolumn); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return NULL; + } + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (__dp_sql_bind_value(stmt, condtype, condvalue, 1) != SQLITE_OK) { + TRACE_ERROR + ("[BIND][%d][%s]", condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + if (sqlite3_step(stmt) == SQLITE_ROW && + sqlite3_column_type(stmt, 0) == SQLITE_TEXT) { + int getbytes = sqlite3_column_bytes(stmt, 0); + if (getbytes > 0) { + char *getstr = (char *)calloc(getbytes + 1, sizeof(char)); + if (getstr != NULL) { + memcpy(getstr, sqlite3_column_text(stmt, 0), + getbytes * sizeof(char)); + getstr[getbytes] = '\0'; + } + __dp_finalize(stmt); + return getstr; + } + } + __dp_finalize(stmt); + return NULL; +} + +int dp_db_cond_get_int(char *table, char *column, char *condcolumn, + db_column_data_type condtype, void *condvalue) +{ + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int ret = -1; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (column == NULL) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + if (condcolumn == NULL) { + TRACE_ERROR("[CHECK Condition]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query = sqlite3_mprintf("SELECT %s FROM %s WHERE %s IS ?", + column, table, condcolumn); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (__dp_sql_bind_value(stmt, condtype, condvalue, 1) != SQLITE_OK) { + TRACE_ERROR + ("[BIND][%d][%s]", condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_step(stmt) == SQLITE_ROW && + sqlite3_column_type(stmt, 0) == SQLITE_INTEGER) { + int columnvalue = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return columnvalue; + } + __dp_finalize(stmt); + return -1; +} + +int dp_db_get_cond_rows_count(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (condcolumn) + query = + sqlite3_mprintf + ("SELECT count(id) FROM %s WHERE id = ? AND %s = ?", + table, condcolumn); + else + query = + sqlite3_mprintf + ("SELECT count(id) FROM %s WHERE id = ?", table); + + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condcolumn) { + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_ROW) { + int count = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return count; + } + __dp_finalize(stmt); + return 0; +} + +int dp_db_get_http_headers_list(int id, char **headers) +{ + int errorcode = SQLITE_OK; + int i = 0; + int headers_index = 0; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT header_field, header_data FROM httpheaders WHERE id = ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + int buffer_length = 0; + char *header_field = (char *)(sqlite3_column_text(stmt, 0)); + char *header_data = (char *)(sqlite3_column_text(stmt, 1)); + i++; + // REF : http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + buffer_length = strlen(header_field) + strlen(header_data) + 1; + char *headers_buffer = calloc(buffer_length + 1, sizeof(char)); + if (headers_buffer == NULL) { + TRACE_ERROR("[CALLOC] headers_buffer"); + continue; + } + int len = snprintf(headers_buffer, buffer_length + 1, + "%s:%s", header_field, header_data); + if (len <= 0) { + if (headers_buffer) + free(headers_buffer); + continue; + } else { + headers_buffer[len] = '\0'; + } + headers[headers_index++] = headers_buffer; + } + + __dp_finalize(stmt); + return headers_index; +} + +static char *__merge_strings(char *dest, const char *src, char sep) +{ + int dest_length = 0; + int src_length = 0; + char *temp_dest = NULL; + + if (dest == NULL || src == NULL) + return NULL; + + dest_length = strlen(dest); + src_length = strlen(src); + + temp_dest = sqlite3_realloc(dest, dest_length + src_length + 2); + if (temp_dest == NULL) { + free(dest); + return NULL; + } + temp_dest = strncat(temp_dest, &sep, 1); + temp_dest = strncat(temp_dest, src, src_length); + return temp_dest; +} + +static char *__get_conds_query(int count, db_conds_list_fmt *conds, char *op) +{ + char *conditions = NULL; + int i = 0; + + if (count > 0 && conds != NULL) { + conditions = sqlite3_mprintf("WHERE"); + for (i = 0; i < count; i++) { + char *token = + sqlite3_mprintf("%s %s ?", conds[i].column, + (conds[i].is_like == 1 ? "LIKE" : "=")); + if (token != NULL) { + conditions = __merge_strings(conditions, token, ' '); + sqlite3_free(token); + token = NULL; + } + if (i < count - 1 && op) + conditions = __merge_strings(conditions, op, ' '); + } + } + return conditions; +} + +static int __bind_value(sqlite3_stmt *stmt, + db_column_data_type condtype, void *value, int index) +{ + int errorcode = SQLITE_ERROR; + int *cast_value = 0; + + if (stmt == NULL) + return SQLITE_ERROR; + + switch (condtype) { + case DP_DB_COL_TYPE_INT: + cast_value = value; + errorcode = sqlite3_bind_int(stmt, index, *cast_value); + break; + case DP_DB_COL_TYPE_INT64: +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, index, *cast_value); +#else + cast_value = value; + errorcode = sqlite3_bind_int(stmt, index, *cast_value); +#endif + break; + case DP_DB_COL_TYPE_TEXT: + errorcode = + sqlite3_bind_text(stmt, index, (char *)value, -1, SQLITE_STATIC); + break; + default: + errorcode = SQLITE_ERROR; + break; + } + return errorcode; +} + +int dp_db_insert_columns(char *table, int column_count, + db_conds_list_fmt *columns) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int i = 0; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (column_count <= 0) { + TRACE_ERROR("[CHECK db_conds_list_fmt count]"); + return -1; + } + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query = + sqlite3_mprintf("INSERT INTO %s ", table); + query = __merge_strings(query, columns[0].column, '('); + for (i = 1; i < column_count; i++) { + char *column_query = NULL; + column_query = sqlite3_mprintf(", %s", columns[i].column); + query = __merge_strings(query, column_query, ' '); + sqlite3_free(column_query); + } + query = __merge_strings(query, " VALUES ", ')'); + query = __merge_strings(query, "?", '('); + for (i = 1; i < column_count; i++) { + query = __merge_strings(query, ", ?", ' '); + } + query = __merge_strings(query, ")", ' '); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + TRACE_DEBUG("query:%s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + for (i = 0; i < column_count; i++) { + if (__bind_value + (stmt, columns[i].type, columns[i].value, (i + 1)) != + SQLITE_OK) { + TRACE_ERROR("[BIND][%d][%s]", columns[i].type, + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_update_columns(int id, char *table, int column_count, + db_conds_list_fmt *columns) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int i = 0; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (column_count <= 0) { + TRACE_ERROR("[CHECK db_conds_list_fmt count]"); + return -1; + } + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query = + sqlite3_mprintf("UPDATE %s SET %s = ?", table, columns[0].column); + for (i = 1; i < column_count; i++) { + char *column_query = NULL; + column_query = sqlite3_mprintf("%s = ?", columns[i].column); + query = __merge_strings(query, column_query, ','); + sqlite3_free(column_query); + } + query = __merge_strings(query, "WHERE id = ?", ' '); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + TRACE_DEBUG("query:%s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + for (i = 0; i < column_count; i++) { + if (__bind_value + (stmt, columns[i].type, columns[i].value, (i + 1)) != + SQLITE_OK) { + TRACE_ERROR("[BIND][%d][%s]", columns[i].type, + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + if (sqlite3_bind_int(stmt, column_count + 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] ID [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_get_conds_rows_count(char *table, + char *getcolumn, char *op, + int conds_count, db_conds_list_fmt *conds) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int i = 0; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (getcolumn == NULL) { + TRACE_ERROR("[CHECK RESULT COLUMN]"); + return -1; + } + if (op == NULL) { + TRACE_ERROR("[CHECK OPERATOR] AND or OR"); + return -1; + } + if (conds_count <= 0) { + TRACE_ERROR("[CHECK db_conds_list_fmt count]"); + return -1; + } + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query = + sqlite3_mprintf("SELECT count(%s) FROM %s", getcolumn, table); + + char *conditions = __get_conds_query(conds_count, conds, op); + if (conditions != NULL) { + query = __merge_strings(query, conditions, ' '); + sqlite3_free(conditions); + } + + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + for (i = 0; i < conds_count; i++) { + if (__bind_value + (stmt, conds[i].type, conds[i].value, (i + 1)) != + SQLITE_OK) { + TRACE_ERROR("[BIND][%d][%s]", conds[i].type, + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_ROW) { + int count = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return count; + } + __dp_finalize(stmt); + return 0; +} + +int dp_db_get_conds_list(char *table, char *getcolumn, + db_column_data_type gettype, void **list, + int rowslimit, int rowsoffset, + char *ordercolumn, char *ordering, + char *op, int conds_count, + db_conds_list_fmt *conds) +{ + int errorcode = SQLITE_OK; + int rows_count = 0; + sqlite3_stmt *stmt = NULL; + int i = 0; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (op == NULL) { + TRACE_ERROR("[CHECK OPERATOR] AND or OR"); + return -1; + } + if (getcolumn == NULL) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + if (conds_count <= 0) { + TRACE_ERROR("[CHECK db_conds_list_fmt count]"); + return -1; + } + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + char *limit = NULL; + char *order = NULL; + char *query = sqlite3_mprintf("SELECT %s FROM %s", getcolumn, table); + char *conditions = __get_conds_query(conds_count, conds, op); + if (conditions != NULL) { + query = __merge_strings(query, conditions, ' '); + sqlite3_free(conditions); + } + + if (ordercolumn != NULL) { + order = + sqlite3_mprintf + ("ORDER BY %s %s", ordercolumn, + (ordering == NULL ? "ASC" : ordering)); + if (order != NULL) { + query = __merge_strings(query, order, ' '); + sqlite3_free(order); + } + } + if (rowslimit > 0) { // 0 or negative : no limitation + if (rowsoffset >= 0) + limit = + sqlite3_mprintf("LIMIT %d OFFSET %d", rowslimit, + rowsoffset); + else + limit = sqlite3_mprintf("LIMIT %d", rowslimit); + if (limit != NULL) { + query = __merge_strings(query, limit, ' '); + sqlite3_free(limit); + } + } + + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + for (i = 0; i < conds_count; i++) { + if (__bind_value + (stmt, conds[i].type, conds[i].value, (i + 1)) != + SQLITE_OK) { + TRACE_ERROR + ("[BIND][%d][%s]", conds[i].type, + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + switch (gettype) { + case DP_DB_COL_TYPE_INT: + { + int **list_int_p = (int **)list; + *list_int_p[rows_count++] = sqlite3_column_int(stmt, 0); + break; + } + case DP_DB_COL_TYPE_INT64: + { +#ifdef SQLITE_INT64_TYPE + long long **list_long_p = (long long **)list; + *list_long_p[rows_count++] = sqlite3_column_int64(stmt, 0); +#else + int **list_int_p = (int **)list; + *list_int_p[rows_count++] = sqlite3_column_int(stmt, 0); +#endif + break; + } + case DP_DB_COL_TYPE_TEXT: + { + int getbytes = sqlite3_column_bytes(stmt, 0); + if (getbytes > 0) { + char *getstr = (char *)calloc((getbytes + 1), sizeof(char)); + if (getstr != NULL) { + memcpy(getstr, sqlite3_column_text(stmt, 0), + getbytes * sizeof(char)); + getstr[getbytes] = '\0'; + list[rows_count++] = getstr; + } + } + break; + } + default: + break; + } + if (rows_count >= rowslimit) + break; + } + __dp_finalize(stmt); + return rows_count; +} + +static void __dp_db_reset(sqlite3_stmt *stmt) +{ + if (stmt != 0) { + sqlite3_clear_bindings(stmt); + if (sqlite3_reset(stmt) != SQLITE_OK) { + sqlite3 *handle = sqlite3_db_handle(stmt); + TRACE_ERROR("failed sqlite3_reset [%s]", + sqlite3_errmsg(handle)); + } + } +} + +int dp_db_request_new_logging(const int id, const int state, const char *pkgname) +{ + int errorcode = SQLITE_OK; + int ret = -1; + char *query = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (g_dp_db_logging_new_stmt == NULL) { + query = + sqlite3_mprintf("INSERT INTO %s (%s, %s, %s, %s) VALUES (?, ?, ?, DATETIME('now'))", + DP_DB_TABLE_LOG, DP_DB_COL_ID, DP_DB_COL_STATE, + DP_DB_COL_PACKAGENAME, DP_DB_COL_CREATE_TIME); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + TRACE_DEBUG("query:%s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &g_dp_db_logging_new_stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + } + if (sqlite3_bind_int(g_dp_db_logging_new_stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_new_stmt); + return -1; + } + if (sqlite3_bind_int(g_dp_db_logging_new_stmt, 2, state) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_new_stmt); + return -1; + } + if (sqlite3_bind_text(g_dp_db_logging_new_stmt, 3, pkgname, -1, SQLITE_STATIC) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_new_stmt); + return -1; + } + + errorcode = sqlite3_step(g_dp_db_logging_new_stmt); + __dp_db_reset(g_dp_db_logging_new_stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + return 0; + } + return -1; +} + +int dp_db_request_update_status(const int id, const int state, const int download_error) +{ + int errorcode = SQLITE_OK; + int ret = -1; + char *query = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (g_dp_db_logging_status_stmt == NULL) { + query = + sqlite3_mprintf("UPDATE %s SET %s = ?, %s = ?, %s = DATETIME('now') WHERE %s = ?", + DP_DB_TABLE_LOG, DP_DB_COL_STATE, DP_DB_COL_ERRORCODE, + DP_DB_COL_ACCESS_TIME, DP_DB_COL_ID); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + TRACE_DEBUG("query:%s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &g_dp_db_logging_status_stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + } + if (sqlite3_bind_int(g_dp_db_logging_status_stmt, 1, state) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_status_stmt); + return -1; + } + if (sqlite3_bind_int(g_dp_db_logging_status_stmt, 2, download_error) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_status_stmt); + return -1; + } + if (sqlite3_bind_int(g_dp_db_logging_status_stmt, 3, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_status_stmt); + return -1; + } + + errorcode = sqlite3_step(g_dp_db_logging_status_stmt); + __dp_db_reset(g_dp_db_logging_status_stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + return 0; + } + return -1; +} + +int dp_db_get_state(int id) +{ + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (g_dp_db_logging_get_state_stmt == NULL) { + char *query = sqlite3_mprintf("SELECT %s FROM %s WHERE %s = ?", + DP_DB_COL_STATE, DP_DB_TABLE_LOG, DP_DB_COL_ID); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + TRACE_DEBUG("query:%s", query); + + int ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &g_dp_db_logging_get_state_stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + } + + if (sqlite3_bind_int(g_dp_db_logging_get_state_stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_get_state_stmt); + return -1; + } + + int state = DP_STATE_NONE; + if (sqlite3_step(g_dp_db_logging_get_state_stmt) == SQLITE_ROW) { + state = sqlite3_column_int(g_dp_db_logging_get_state_stmt, 0); + } + __dp_db_reset(g_dp_db_logging_get_state_stmt); + return state; +} diff --git a/provider/download-provider-main.c b/provider/download-provider-main.c new file mode 100755 index 0000000..6e71cb5 --- /dev/null +++ b/provider/download-provider-main.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2012 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 <unistd.h> +#include <glib.h> +#include <glib-object.h> +#include <pthread.h> +#include <locale.h> +#include <libintl.h> +#include <systemd/sd-daemon.h> + +#include "vconf.h" + +#include "download-provider-config.h" +#include "download-provider-log.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-slots.h" +#include "download-provider-db.h" +#include "download-provider-network.h" +#include "download-provider-queue.h" +#include "download-provider-notification.h" +#include "download-provider-da-interface.h" + +// declare functions +int dp_lock_pid(char *path); +void *dp_thread_requests_manager(void *arg); +static pthread_t g_dp_thread_queue_manager_pid = 0; + +// declare global variables +// need for libsoup, decided the life-time by mainloop. +GMainLoop *g_main_loop_handle = 0; + +void dp_terminate(int signo) +{ + TRACE_DEBUG("Received SIGTERM:%d", signo); + if (g_main_loop_is_running(g_main_loop_handle)) + g_main_loop_quit(g_main_loop_handle); +} + +static gboolean __dp_idle_start_service(void *data) +{ + // declare all resources + pthread_t thread_pid; + pthread_attr_t thread_attr; + + // initialize + if (pthread_attr_init(&thread_attr) != 0) { + TRACE_STRERROR("failed to init pthread attr"); + dp_terminate(SIGTERM); + return FALSE; + } + if (pthread_attr_setdetachstate(&thread_attr, + PTHREAD_CREATE_DETACHED) != 0) { + TRACE_STRERROR("failed to set detach option"); + dp_terminate(SIGTERM); + return FALSE; + } + + // create thread for managing QUEUEs + if (pthread_create + (&g_dp_thread_queue_manager_pid, NULL, dp_thread_queue_manager, + data) != 0) { + TRACE_STRERROR + ("failed to create pthread for run_manage_download_server"); + dp_terminate(SIGTERM); + } + + // start service, accept url-download ( client package ) + if (pthread_create + (&thread_pid, &thread_attr, dp_thread_requests_manager, + data) != 0) { + TRACE_STRERROR + ("failed to create pthread for run_manage_download_server"); + dp_terminate(SIGTERM); + } + return FALSE; +} + +void __set_locale() +{ + char *str = NULL; + str = vconf_get_str(VCONFKEY_LANGSET); + if (str != NULL) { + setlocale(LC_ALL, str); + bindtextdomain(PKG_NAME, LOCALE_DIR); + textdomain(PKG_NAME); + } + free(str); +} +void __lang_changed_cb(keynode_t *key, void* data) +{ + __set_locale(); +} + +int main(int argc, char **argv) +{ + dp_privates *privates = NULL; + int lock_fd = -1; + + if (chdir("/") < 0) { + TRACE_STRERROR("failed to call setsid or chdir"); + exit(EXIT_FAILURE); + } + +#if 0 + // close all console I/O + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +#endif + + if (signal(SIGTERM, dp_terminate) == SIG_ERR) { + TRACE_ERROR("failed to register signal callback"); + exit(EXIT_FAILURE); + } + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + TRACE_ERROR("failed to register signal callback"); + exit(EXIT_FAILURE); + } + if (signal(SIGINT, dp_terminate) == SIG_ERR) { + TRACE_ERROR("failed to register signal callback"); + exit(EXIT_FAILURE); + } + // write IPC_FD_PATH. and lock + if ((lock_fd = dp_lock_pid(DP_LOCK_PID)) < 0) { + TRACE_ERROR + ("It need to check download-provider is already alive"); + TRACE_ERROR("Or fail to create pid file in (%s)", + DP_LOCK_PID); + exit(EXIT_FAILURE); + } + + g_type_init(); + + // locale + __set_locale(); + if (vconf_notify_key_changed(VCONFKEY_LANGSET, __lang_changed_cb, NULL) != 0) + TRACE_ERROR("Fail to set language changed vconf callback"); + + privates = (dp_privates *) calloc(1, sizeof(dp_privates)); + if (!privates) { + TRACE_ERROR("[CRITICAL] failed to alloc for private info"); + goto DOWNLOAD_EXIT; + } + privates->groups = dp_client_group_slots_new(DP_MAX_GROUP); + if (privates->groups == NULL) { + TRACE_ERROR("[CRITICAL] failed to alloc for groups"); + goto DOWNLOAD_EXIT; + } + privates->requests = dp_request_slots_new(DP_MAX_REQUEST); + if (privates->requests == NULL) { + TRACE_ERROR("[CRITICAL] failed to alloc for requests"); + goto DOWNLOAD_EXIT; + } + + // ready socket ( listen ) + privates->listen_fd = dp_accept_socket_new(); + if (privates->listen_fd < 0) { + TRACE_ERROR("[CRITICAL] failed to bind SOCKET"); + goto DOWNLOAD_EXIT; + } + + dp_db_open(); + + // convert to request type, insert all to privates->requests + // timeout of request thread will start these jobs by queue thread + // load all list from (queue table) + if (dp_db_crashed_list(privates->requests, DP_MAX_REQUEST) > 0) { + int i = 0; + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (!privates->requests[i].request) + continue; + dp_request *request = privates->requests[i].request; + TRACE_DEBUG + ("ID [%d] state[%d]", request->id, request->state); + + // load to memory, Can be started automatically. + if (request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_CONNECTING) { + request->state = DP_STATE_QUEUED; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + } + + if (request->state == DP_STATE_QUEUED) { + int auto_download = dp_db_get_int_column(request->id, + DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_AUTO_DOWNLOAD); + if (auto_download == 1) { + // auto retry... defaultly, show notification + request->auto_notification = 1; + request->start_time = (int)time(NULL); + continue; + } + // do not retry this request + request->state = DP_STATE_FAILED; + request->error = DP_ERROR_SYSTEM_DOWN; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_ERRORCODE, DP_DB_COL_TYPE_INT, + &request->error) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + } + + // if wanna restart, call continue before this line. + // default. update state/error. move to history. unload memory + // remove from memory + dp_request_free(request); + privates->requests[i].request = NULL; + } + } // query crashed_list + +#if 0 + if (argc != 2 || memcmp(argv[1], "service", 7) != 0) { + // in first launch in booting time, not request. terminate by self + if (dp_get_request_count(privates->requests) <= 0) { + TRACE_DEBUG("First Boot, No Request"); + goto DOWNLOAD_EXIT; + } + } +#endif + + dp_clear_downloadinginfo_notification(); + + if (dp_init_agent() != DP_ERROR_NONE) { + TRACE_ERROR("[CRITICAL] failed to init agent"); + goto DOWNLOAD_EXIT; + } + + privates->connection = 0; + privates->network_status = DP_NETWORK_TYPE_OFF; + if (dp_network_connection_init(privates) < 0) { + TRACE_DEBUG("use instant network check"); + privates->connection = 0; + } + + // libsoup need mainloop. + g_main_loop_handle = g_main_loop_new(NULL, 0); + + g_idle_add(__dp_idle_start_service, privates); + + sd_notify(0, "READY=1"); + + g_main_loop_run(g_main_loop_handle); + +DOWNLOAD_EXIT : + + TRACE_DEBUG("Download-Provider will be terminated."); + if (vconf_ignore_key_changed(VCONFKEY_LANGSET, __lang_changed_cb) != 0) + TRACE_ERROR("Fail to unset language changed vconf callback"); + + dp_deinit_agent(); + + if (privates != NULL) { + + if (privates->connection) + dp_network_connection_destroy(privates->connection); + + if (privates->listen_fd >= 0) + privates->listen_fd = -1; + + if (g_dp_thread_queue_manager_pid > 0 && + pthread_kill(g_dp_thread_queue_manager_pid, 0) != ESRCH) { + //pthread_cancel(g_dp_thread_queue_manager_pid); + //send signal to queue thread + dp_thread_queue_manager_wake_up(); + int status; + pthread_join(g_dp_thread_queue_manager_pid, (void **)&status); + g_dp_thread_queue_manager_pid = 0; + } + dp_request_slots_free(privates->requests, DP_MAX_REQUEST); + privates->requests = NULL; + dp_client_group_slots_free(privates->groups, DP_MAX_GROUP); + privates->groups = NULL; + free(privates); + privates = NULL; + } + dp_db_close(); + + // delete pid file + if (access(DP_LOCK_PID, F_OK) == 0) { + close(lock_fd); + unlink(DP_LOCK_PID); + } + exit(EXIT_SUCCESS); +} diff --git a/provider/download-provider-network.c b/provider/download-provider-network.c new file mode 100755 index 0000000..ac3fc5b --- /dev/null +++ b/provider/download-provider-network.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2012 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 "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-pthread.h" +#include "download-provider-network.h" + +#ifdef SUPPORT_WIFI_DIRECT +#include <wifi-direct.h> +#endif + +extern pthread_mutex_t g_dp_queue_mutex; +extern pthread_cond_t g_dp_queue_cond; + +#if 0 +typedef enum +{ + CONNECTION_TYPE_DISCONNECTED = 0, /**< Disconnected */ + CONNECTION_TYPE_WIFI = 1, /**< Wi-Fi type */ + CONNECTION_TYPE_CELLULAR = 2, /**< Cellular type */ + CONNECTION_TYPE_ETHERNET = 3, /**< Ethernet type */ + CONNECTION_TYPE_BT = 4, /**< Bluetooth type */ +} connection_type_e; +typedef enum +{ + CONNECTION_CELLULAR_STATE_OUT_OF_SERVICE = 0, /**< Out of service */ + CONNECTION_CELLULAR_STATE_FLIGHT_MODE = 1, /**< Flight mode */ + CONNECTION_CELLULAR_STATE_ROAMING_OFF = 2, /**< Roaming is turned off */ + CONNECTION_CELLULAR_STATE_CALL_ONLY_AVAILABLE = 3, /**< Call is only available */ + CONNECTION_CELLULAR_STATE_AVAILABLE = 4, /**< Available but not connected yet */ + CONNECTION_CELLULAR_STATE_CONNECTED = 5, /**< Connected */ +} connection_cellular_state_e +typedef enum +{ + CONNECTION_WIFI_STATE_DEACTIVATED = 0, /**< Deactivated state */ + CONNECTION_WIFI_STATE_DISCONNECTED = 1, /**< disconnected state */ + CONNECTION_WIFI_STATE_CONNECTED = 2, /**< Connected state */ +} connection_wifi_state_e; +typedef enum +{ + CONNECTION_ETHERNET_STATE_DEACTIVATED = 0, /**< Deactivated state */ + CONNECTION_ETHERNET_STATE_DISCONNECTED = 1, /**< disconnected state */ + CONNECTION_ETHERNET_STATE_CONNECTED = 2, /**< Connected state */ +} connection_ethernet_state_e; +typedef enum +{ + CONNECTION_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ + CONNECTION_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ + CONNECTION_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory error */ + CONNECTION_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, /**< Invalid Operation */ + CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED = TIZEN_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED, /**< Address family not supported */ + CONNECTION_ERROR_OPERATION_FAILED = TIZEN_ERROR_NETWORK_CLASS|0x0401, /**< Operation failed */ + CONNECTION_ERROR_ITERATOR_END = TIZEN_ERROR_NETWORK_CLASS|0x0402, /**< End of iteration */ + CONNECTION_ERROR_NO_CONNECTION = TIZEN_ERROR_NETWORK_CLASS|0x0403, /**< There is no connection */ + CONNECTION_ERROR_NOW_IN_PROGRESS = TIZEN_ERROR_NOW_IN_PROGRESS, /** Now in progress */ + CONNECTION_ERROR_ALREADY_EXISTS = TIZEN_ERROR_NETWORK_CLASS|0x0404, /**< Already exists */ + CONNECTION_ERROR_OPERATION_ABORTED = TIZEN_ERROR_NETWORK_CLASS|0x0405, /**< Operation is aborted */ + CONNECTION_ERROR_DHCP_FAILED = TIZEN_ERROR_NETWORK_CLASS|0x0406, /**< DHCP failed */ + CONNECTION_ERROR_INVALID_KEY = TIZEN_ERROR_NETWORK_CLASS|0x0407, /**< Invalid key */ + CONNECTION_ERROR_NO_REPLY = TIZEN_ERROR_NETWORK_CLASS|0x0408, /**< No reply */ +} connection_error_e; + + +static void __print_connection_errorcode_to_string(connection_error_e errorcode) +{ + switch(errorcode) + { + case CONNECTION_ERROR_INVALID_PARAMETER : + TRACE_DEBUG("CONNECTION_ERROR_INVALID_PARAMETER"); + break; + case CONNECTION_ERROR_OUT_OF_MEMORY : + TRACE_DEBUG("CONNECTION_ERROR_OUT_OF_MEMORY"); + break; + case CONNECTION_ERROR_INVALID_OPERATION : + TRACE_DEBUG("CONNECTION_ERROR_INVALID_OPERATION"); + break; + case CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED : + TRACE_DEBUG("CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED"); + break; + case CONNECTION_ERROR_OPERATION_FAILED : + TRACE_DEBUG("CONNECTION_ERROR_OPERATION_FAILED"); + break; + case CONNECTION_ERROR_ITERATOR_END : + TRACE_DEBUG("CONNECTION_ERROR_ITERATOR_END"); + break; + case CONNECTION_ERROR_NO_CONNECTION : + TRACE_DEBUG("CONNECTION_ERROR_NO_CONNECTION"); + break; + case CONNECTION_ERROR_NOW_IN_PROGRESS : + TRACE_DEBUG("CONNECTION_ERROR_NOW_IN_PROGRESS"); + break; + case CONNECTION_ERROR_ALREADY_EXISTS : + TRACE_DEBUG("CONNECTION_ERROR_ALREADY_EXISTS"); + break; + case CONNECTION_ERROR_OPERATION_ABORTED : + TRACE_DEBUG("CONNECTION_ERROR_OPERATION_ABORTED"); + break; + case CONNECTION_ERROR_DHCP_FAILED : + TRACE_DEBUG("CONNECTION_ERROR_DHCP_FAILED"); + break; + case CONNECTION_ERROR_INVALID_KEY : + TRACE_DEBUG("CONNECTION_ERROR_INVALID_KEY"); + break; + case CONNECTION_ERROR_NO_REPLY : + TRACE_DEBUG("CONNECTION_ERROR_NO_REPLY"); + break; + default : + TRACE_DEBUG("CONNECTION_ERROR_NONE"); + break; + } +} +#endif + + +#ifdef SUPPORT_WIFI_DIRECT +// support WIFI-Direct +static void __dp_network_wifi_direct_connection_state_changed_cb(wifi_direct_error_e error_code, wifi_direct_connection_state_e connection_state, const char *mac_address, void *data) +{ + dp_privates *privates = (dp_privates*)data; + if (privates == NULL) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return ; + } + + if (connection_state == WIFI_DIRECT_CONNECTION_RSP) { + TRACE_DEBUG("WIFI_DIRECT_CONNECTION_RSP"); + privates->is_connected_wifi_direct = 1; + return ; + } + privates->is_connected_wifi_direct = 0; +} + +//return 0 : connected +int dp_network_wifi_direct_is_connected() +{ + int is_connected = -1; + wifi_direct_state_e wifi_state = WIFI_DIRECT_STATE_DEACTIVATED; + if (wifi_direct_get_state(&wifi_state) == 0) { + switch (wifi_state) + { + case WIFI_DIRECT_STATE_DEACTIVATED : + TRACE_DEBUG("WIFI_DIRECT_STATE_DEACTIVATED"); + break; + case WIFI_DIRECT_STATE_DEACTIVATING : + TRACE_DEBUG("WIFI_DIRECT_STATE_DEACTIVATING"); + break; + case WIFI_DIRECT_STATE_ACTIVATING : + TRACE_DEBUG("WIFI_DIRECT_STATE_ACTIVATING"); + break; + case WIFI_DIRECT_STATE_ACTIVATED : + TRACE_DEBUG("WIFI_DIRECT_STATE_ACTIVATED"); + break; + case WIFI_DIRECT_STATE_DISCOVERING : + TRACE_DEBUG("WIFI_DIRECT_STATE_DISCOVERING"); + break; + case WIFI_DIRECT_STATE_CONNECTING : + TRACE_DEBUG("WIFI_DIRECT_STATE_CONNECTING"); + break; + case WIFI_DIRECT_STATE_DISCONNECTING : + TRACE_DEBUG("WIFI_DIRECT_STATE_DISCONNECTING"); + break; + case WIFI_DIRECT_STATE_CONNECTED : + is_connected = 0; + TRACE_DEBUG("WIFI_DIRECT_STATE_CONNECTED"); + break; + default : + break; + } + } + return is_connected; +} +#endif + + +////////////////////////////////////////////////////////////////////////// +/// @brief check the status in more detail by connection type +/// @return dp_network_type +static dp_network_type __dp_get_network_connection_status(connection_h connection, connection_type_e type) +{ + dp_network_type network_type = DP_NETWORK_TYPE_OFF; + if (type == CONNECTION_TYPE_WIFI) { + connection_wifi_state_e wifi_state; + wifi_state = CONNECTION_WIFI_STATE_DEACTIVATED; + if (connection_get_wifi_state + (connection, &wifi_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_wifi_state"); + if (wifi_state == CONNECTION_WIFI_STATE_CONNECTED) { + TRACE_DEBUG("[CONNECTION_WIFI] CONNECTED"); + network_type = DP_NETWORK_TYPE_WIFI; + } else { + TRACE_DEBUG("[CONNECTION_WIFI] [%d]", wifi_state); + } + } else if (type == CONNECTION_TYPE_CELLULAR) { + connection_cellular_state_e cellular_state; + cellular_state = CONNECTION_CELLULAR_STATE_OUT_OF_SERVICE; + if (connection_get_cellular_state + (connection, &cellular_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_cellular_state"); + if (cellular_state == CONNECTION_CELLULAR_STATE_CONNECTED) { + TRACE_DEBUG("[CONNECTION_CELLULAR] DATA NETWORK CONNECTED"); + network_type = DP_NETWORK_TYPE_DATA_NETWORK; + } else { + TRACE_DEBUG("[CONNECTION_CELLULAR] [%d]", cellular_state); + } + } else if (type == CONNECTION_TYPE_ETHERNET) { + connection_ethernet_state_e ethernet_state; + ethernet_state = CONNECTION_ETHERNET_STATE_DISCONNECTED; + if (connection_get_ethernet_state + (connection, ðernet_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_ethernet_state"); + if (ethernet_state == CONNECTION_ETHERNET_STATE_CONNECTED) { + TRACE_DEBUG("[CONNECTION_ETHERNET] ETHERNET CONNECTED"); + network_type = DP_NETWORK_TYPE_ETHERNET; + } else { + TRACE_DEBUG("[CONNECTION_ETHERNET] [%d]", ethernet_state); + } + } else { + TRACE_DEBUG("[DISCONNECTED]"); + network_type = DP_NETWORK_TYPE_OFF; + } + return network_type; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief [callback] called whenever changed network status +/// @todo care requests by network status +static void __dp_network_connection_type_changed_cb(connection_type_e type, void *data) +{ + dp_privates *privates = (dp_privates*)data; + if (privates == NULL) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return ; + } + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + #if 1 // this callback guarantee that already connectdd + if (type == CONNECTION_TYPE_WIFI) { + TRACE_DEBUG("[CONNECTION_WIFI] CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_WIFI; + } else if (type == CONNECTION_TYPE_CELLULAR) { + TRACE_DEBUG("[CONNECTION_CELLULAR] DATA NETWORK CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_DATA_NETWORK; + } else if (type == CONNECTION_TYPE_ETHERNET) { + TRACE_DEBUG("[CONNECTION_ETHERNET] ETHERNET CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_ETHERNET; + } else { + TRACE_DEBUG("[DISCONNECTED]"); + privates->network_status = DP_NETWORK_TYPE_OFF; + } + if (privates->network_status != DP_NETWORK_TYPE_OFF) + pthread_cond_signal(&g_dp_queue_cond); + #else + privates->network_status = + __dp_get_network_connection_status(privates->connection, type); + #endif + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); +} + +////////////////////////////////////////////////////////////////////////// +/// @brief [callback] called when changed network ip +/// @todo auto resume feature +static void __dp_network_connection_ip_changed_cb(const char *ip, const char *ipv6, void *data) +{ + dp_privates *privates = (dp_privates*)data; + if (privates == NULL) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return ; + } + if (privates->network_status != DP_NETWORK_TYPE_OFF) { + dp_request_slots *requests = privates->requests; + int i = 0; + for (i = 0; i < DP_MAX_REQUEST; i++) { + int locked = pthread_mutex_trylock(&requests[i].mutex); + // locking failure means it used by other thread. + if (locked == 0) { + if (requests[i].request != NULL) { + if (requests[i].request->state == DP_STATE_DOWNLOADING || + (requests[i].request->state == DP_STATE_FAILED && + requests[i].request->error == DP_ERROR_CONNECTION_FAILED)) { + requests[i].request->ip_changed = 1; + } + } + CLIENT_MUTEX_UNLOCK(&requests[i].mutex); + } + } + } +} + +////////////////////////////////////////////////////////////////////////// +/// @brief create connection handle & regist callback +/// @return 0 : success -1 : failed +int dp_network_connection_init(dp_privates *privates) +{ + int retcode = 0; + + if (!privates) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return -1; + } + +#ifdef SUPPORT_WIFI_DIRECT + if (wifi_direct_initialize() == 0) { + wifi_direct_set_connection_state_changed_cb + (__dp_network_wifi_direct_connection_state_changed_cb, privates); + if (dp_network_wifi_direct_is_connected() == 0) + privates->is_connected_wifi_direct = 1; + } +#endif + + if ((retcode = connection_create(&privates->connection)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_create [%d]", retcode); + return -1; + } + if ((retcode = connection_set_type_changed_cb + (privates->connection, __dp_network_connection_type_changed_cb, + privates)) != CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_set_type_changed_cb [%d]", retcode); + connection_destroy(privates->connection); + return -1; + } + if ((retcode = connection_set_ip_address_changed_cb + (privates->connection, __dp_network_connection_ip_changed_cb, + privates)) != CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed __dp_network_connection_ip_changed_cb [%d]", retcode); + connection_destroy(privates->connection); + return -1; + } + connection_type_e type = CONNECTION_TYPE_DISCONNECTED; + if ((retcode = connection_get_type(privates->connection, &type)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_get_type [%d]", retcode); + connection_destroy(privates->connection); + return -1; + } + privates->network_status = + __dp_get_network_connection_status(privates->connection, type); + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief destroy connection handle +void dp_network_connection_destroy(connection_h connection) +{ +#ifdef SUPPORT_WIFI_DIRECT + wifi_direct_unset_connection_state_changed_cb(); + wifi_direct_deinitialize(); +#endif + + connection_unset_type_changed_cb(connection); + connection_unset_ip_address_changed_cb(connection); + connection_destroy(connection); +} + +////////////////////////////////////////////////////////////////////////// +/// @brief check network status using connection API +/// @todo the standard of enabled networking can be changed later +/// @return Network type +dp_network_type dp_get_network_connection_instant_status() +{ + int retcode = 0; + connection_h network_handle = NULL; + dp_network_type network_type = DP_NETWORK_TYPE_OFF; + if ((retcode = connection_create(&network_handle)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_create [%d]", retcode); + return DP_NETWORK_TYPE_OFF; + } + + connection_type_e type = CONNECTION_TYPE_DISCONNECTED; + if ((retcode = connection_get_type(network_handle, &type)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_get_type [%d]", retcode); + connection_destroy(network_handle); + return DP_NETWORK_TYPE_OFF; + } + network_type = + __dp_get_network_connection_status(network_handle, type); + + if (connection_destroy(network_handle) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_destroy"); + + return network_type; +} diff --git a/provider/download-provider-notification.c b/provider/download-provider-notification.c new file mode 100755 index 0000000..f6e2f8b --- /dev/null +++ b/provider/download-provider-notification.c @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2012 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 <time.h> +#include <sys/time.h> +#include <stdlib.h> +#include <stdio.h> + +#include "bundle.h" +#include "notification.h" +#include "appsvc.h" + +#include "download-provider-defs.h" + +#include "download-provider-notification.h" +#include "download-provider-request.h" +#include "download-provider-db.h" +#include "download-provider-log.h" + +#include <libintl.h> +#define S_(s) dgettext("sys_string", s) +#define __(s) dgettext(PKG_NAME, s) + +#define DP_NOTIFICATION_ICON_PATH IMAGE_DIR"/Q02_Notification_download_complete.png" +#define DP_NOTIFICATION_ONGOING_ICON_PATH IMAGE_DIR"/Notification_download_animation.gif" +#define DP_NOTIFICATION_DOWNLOADING_ICON_PATH "reserved://indicator/ani/downloading" +#define DP_NOTIFICATION_FAILED_ICON_PATH IMAGE_DIR"/Q02_Notification_download_failed.png" + +static const char *__noti_error_str( + notification_error_e err) +{ + switch (err) { + case NOTIFICATION_ERROR_INVALID_DATA: + return "NOTIFICATION_ERROR_INVALID_DATA"; + case NOTIFICATION_ERROR_NO_MEMORY: + return "NOTIFICATION_ERROR_NO_MEMORY"; + case NOTIFICATION_ERROR_FROM_DB: + return "NOTIFICATION_ERROR_FROM_DB"; + case NOTIFICATION_ERROR_ALREADY_EXIST_ID: + return "NOTIFICATION_ERROR_ALREADY_EXIST_ID"; + case NOTIFICATION_ERROR_FROM_DBUS: + return "NOTIFICATION_ERROR_FROM_DBUS"; + case NOTIFICATION_ERROR_NOT_EXIST_ID: + return "NOTIFICATION_ERROR_NOT_EXIST_ID"; + default: + break; + } + return "Unknown error"; +} + +static char *__get_string_sender(char *url) +{ + char *temp = NULL; + char *found = NULL; + char *found1 = NULL; + char *sender = NULL; + char *credential_sender = NULL; + + if (url == NULL) + return NULL; + + found = strstr(url, "://"); + if (found) { + temp = found + 3; + } else { + temp = url; + } + found = strchr(temp, '/'); + if (found) { + int len = 0; + len = found - temp; + sender = calloc(len + 1, sizeof(char)); + if (sender == NULL) + return NULL; + snprintf(sender, len + 1, "%s", temp); + } else { + sender = dp_strdup(temp); + } + + // For credential URL + found = strchr(sender, '@'); + found1 = strchr(sender, ':'); + if (found && found1 && found1 < found) { + int len = 0; + found = found + 1; + len = strlen(found); + credential_sender = calloc(len + 1, sizeof(char)); + if (credential_sender == NULL) { + free(sender); + return NULL; + } + snprintf(credential_sender, len + 1, "%s", found); + free(sender); + return credential_sender; + } else { + return sender; + } +} + +static char *__get_string_size(long long file_size) +{ + const char *unitStr[4] = {"B", "KB", "MB", "GB"}; + double doubleTypeBytes = 0.0; + int unit = 0; + long long bytes = file_size; + long long unitBytes = bytes; + char *temp = NULL; + + /* using bit operation to avoid floating point arithmetic */ + for (unit = 0; (unitBytes > 1024 && unit < 4); unit++) { + unitBytes = unitBytes >> 10; + } + unitBytes = 1 << (10 * unit); + + if (unit > 3) + unit = 3; + + char str[64] = {0}; + if (unit == 0) { + snprintf(str, sizeof(str), "%lld %s", bytes, unitStr[unit]); + } else { + doubleTypeBytes = ((double)bytes / (double)unitBytes); + snprintf(str, sizeof(str), "%.2f %s", doubleTypeBytes, unitStr[unit]); + } + + str[63] = '\0'; + temp = dp_strdup(str); + return temp; +} + +static char *__get_string_status(dp_state_type state) +{ + char *message = NULL; + switch (state) { + case DP_STATE_COMPLETED: + message = __("IDS_DM_HEADER_DOWNLOAD_COMPLETE"); + break; + case DP_STATE_CANCELED: + case DP_STATE_FAILED: + message = S_("IDS_COM_POP_DOWNLOAD_FAILED"); + break; + default: + break; + } + return message; +} + +int dp_set_downloadinginfo_notification(int id, char *packagename) +{ + notification_h noti_handle = NULL; + notification_error_e err = NOTIFICATION_ERROR_NONE; + int privId = 0; + bundle *b = NULL; + void *blob_data = NULL; + bundle_raw* b_raw = NULL; + int length; + + noti_handle = notification_create(NOTIFICATION_TYPE_ONGOING); + if (!noti_handle) { + TRACE_ERROR("[FAIL] create notification handle"); + return -1; + } + + char *title = dp_db_get_text_column(id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_TITLE); + if(title != NULL) { + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_TITLE, + title, NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + free(title); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + } else { + char *content_name = + dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + + if (content_name == NULL) + content_name = strdup("No Name"); + + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_TITLE, content_name, NULL, + NOTIFICATION_VARIABLE_TYPE_NONE); + if (content_name) + free(content_name); + } + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_image(noti_handle, + NOTIFICATION_IMAGE_TYPE_ICON, DP_NOTIFICATION_ONGOING_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + blob_data = dp_db_get_blob_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_RAW_BUNDLE_ONGOING, &length); + if (blob_data != NULL) { + b_raw = (bundle_raw*)blob_data; + if (b_raw != NULL) { + b = bundle_decode_raw(b_raw, length); + if (b != NULL) { + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%s]", __noti_error_str(err)); + bundle_free_encoded_rawdata(&b_raw); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free_encoded_rawdata(&b_raw); + bundle_free(b); + } + } + } else { + b = bundle_create(); + if (!b) { + TRACE_ERROR("[FAIL] create bundle"); + notification_free(noti_handle); + return -1; + } + + if (packagename && + appsvc_set_pkgname(b, packagename) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set pkg name"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%s]", __noti_error_str(err)); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free(b); + } + err = notification_set_property(noti_handle, + NOTIFICATION_PROP_DISABLE_TICKERNOTI); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set property [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_image(noti_handle, + NOTIFICATION_IMAGE_TYPE_ICON_FOR_INDICATOR, DP_NOTIFICATION_DOWNLOADING_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon indicator [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_display_applist(noti_handle, NOTIFICATION_DISPLAY_APP_ALL); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set disable icon [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_insert(noti_handle, &privId); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set insert [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + TRACE_DEBUG("m_noti_id [%d]", privId); + notification_free(noti_handle); + return privId; +} + +int dp_set_downloadedinfo_notification(int priv_id, int id, char *packagename, dp_state_type state) +{ + notification_h noti_handle = NULL; + notification_error_e err = NOTIFICATION_ERROR_NONE; + int privId = 0; + bundle *b = NULL; + void *blob_data = NULL; + bundle_raw* b_raw = NULL; + int length = 0; + + if (priv_id >= 0) { + err = notification_delete_by_priv_id(NULL, NOTIFICATION_TYPE_ONGOING, + priv_id); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] delete notification handle [%s]", + __noti_error_str(err)); + } + } + + noti_handle = notification_create(NOTIFICATION_TYPE_NOTI); + + if (!noti_handle) { + TRACE_ERROR("[FAIL] create notification handle"); + return -1; + } + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_TITLE, + __get_string_status(state), NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + char *description = + dp_db_get_text_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_DESCRIPTION); + if(description != NULL) { + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_INFO_2, description, + NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + free(description); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set description [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + } else { + char *url = NULL; + url = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_URL); + if (url) { + char *sender_str = __get_string_sender(url); + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_INFO_2, + sender_str, NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + free(sender_str); + free(url); + notification_free(noti_handle); + return -1; + } + free(sender_str); + free(url); + } + } + + char *title = dp_db_get_text_column(id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_TITLE); + if(title != NULL) { + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_CONTENT, + title, NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + free(title); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + } else { + char *content_name = + dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + if (content_name == NULL) + content_name = strdup("No Name"); + + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_CONTENT, content_name, + NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + free(content_name); + } + + time_t tt = time(NULL); + err = notification_set_time_to_text(noti_handle, + NOTIFICATION_TEXT_TYPE_INFO_SUB_1, tt); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set time [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + if (state == DP_STATE_COMPLETED) { + blob_data = dp_db_get_blob_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_RAW_BUNDLE_COMPLETE, &length); + if (blob_data != NULL) { + b_raw = (bundle_raw*)blob_data; + if (b_raw != NULL) { + b = bundle_decode_raw(b_raw, length); + if (b != NULL) { + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%s]", __noti_error_str(err)); + bundle_free_encoded_rawdata(&b_raw); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free_encoded_rawdata(&b_raw); + } + } + } else { + char *file_path = NULL; + b = bundle_create(); + if (!b) { + TRACE_ERROR("[FAIL] create bundle"); + notification_free(noti_handle); + return -1; + } + if (appsvc_set_operation(b, APPSVC_OPERATION_VIEW) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] appsvc set operation"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + file_path = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_SAVED_PATH); + if (file_path == NULL) { + TRACE_ERROR("[FAIL] get file path"); + bundle_free(b); + notification_free(noti_handle); + return -1; + + } + if (appsvc_set_uri(b, file_path) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] appsvc set uri"); + bundle_free(b); + notification_free(noti_handle); + free(file_path); + return -1; + } + free(file_path); + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option[%s]", __noti_error_str(err)); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + } + long long file_size = dp_db_get_int64_column(id, + DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_SIZE); + char *size_str = __get_string_size(file_size); + + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_INFO_1, + size_str, NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + free(size_str); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + free(size_str); + + err = notification_set_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON, + DP_NOTIFICATION_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + } else if (state == DP_STATE_CANCELED || state == DP_STATE_FAILED) { + blob_data = dp_db_get_blob_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_RAW_BUNDLE_FAIL, &length); + if (blob_data != NULL) { + b_raw = (bundle_raw *)blob_data; + if (b_raw != NULL) { + b = bundle_decode_raw(b_raw, length); + if (b != NULL) { + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%s]", __noti_error_str(err)); + bundle_free_encoded_rawdata(&b_raw); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free_encoded_rawdata(&b_raw); + } + } + } else { + b = bundle_create(); + if (!b) { + TRACE_ERROR("[FAIL] create bundle"); + notification_free(noti_handle); + return -1; + } + if (packagename && + appsvc_set_pkgname(b, packagename) != + APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set pkg name"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option[%s]", __noti_error_str(err)); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + } + err = notification_set_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON, + DP_NOTIFICATION_FAILED_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon [%s]", __noti_error_str(err)); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + } else { + TRACE_ERROR("[CRITICAL] invalid state"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set time [%s]", __noti_error_str(err)); + notification_free(noti_handle); + bundle_free(b); + return -1; + } + + bundle_free(b); + + err = notification_set_property(noti_handle, + NOTIFICATION_PROP_DISABLE_TICKERNOTI); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set property [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_display_applist(noti_handle, + NOTIFICATION_DISPLAY_APP_ALL ^ NOTIFICATION_DISPLAY_APP_INDICATOR); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set disable icon [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_insert(noti_handle, &privId); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set insert [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + TRACE_DEBUG("m_noti_id [%d]", privId); + notification_free(noti_handle); + return privId; +} + +void dp_update_downloadinginfo_notification(int priv_id, double received_size, double file_size) +{ + notification_error_e err = NOTIFICATION_ERROR_NONE; + if (priv_id < 0) { + TRACE_ERROR("[FAIL] Invalid priv_id[%d]", priv_id); + return; + } + + if (file_size > 0) { + double progress; + progress = received_size / file_size; + err = notification_update_progress(NULL, priv_id, progress); + if (err != NOTIFICATION_ERROR_NONE) + TRACE_ERROR("[FAIL] update noti progress[%s]", + __noti_error_str(err)); + } else { + err = notification_update_size(NULL, priv_id, received_size); + if (err != NOTIFICATION_ERROR_NONE) + TRACE_ERROR("[FAIL] update noti progress[%s]", + __noti_error_str(err)); + } +} + +void dp_clear_downloadinginfo_notification() +{ + notification_error_e err = NOTIFICATION_ERROR_NONE; + err = notification_delete_all_by_type(NULL, NOTIFICATION_TYPE_ONGOING); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] clear noti [%s]", __noti_error_str(err)); + } + return; +} diff --git a/provider/download-provider-pid.c b/provider/download-provider-pid.c new file mode 100755 index 0000000..bd1a223 --- /dev/null +++ b/provider/download-provider-pid.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 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 <unistd.h> +#include <fcntl.h> + +////////////////////////////////////////////////////////////////////////// +/// @brief check whether daemon is alive +/// @warning lockfd should be managed without close() +/// @param the patch for locking the file +int dp_lock_pid(char *path) +{ + int lockfd = -1; + if ((lockfd = open(path, O_WRONLY | O_CREAT, (0666 & (~000)))) < 0) { + return -1; + } else if (lockf(lockfd, F_TLOCK, 0) < 0) { + close(lockfd); + return -1; + } + return lockfd; +} diff --git a/provider/download-provider-request.c b/provider/download-provider-request.c new file mode 100755 index 0000000..cbd4af6 --- /dev/null +++ b/provider/download-provider-request.c @@ -0,0 +1,930 @@ +/* + * Copyright (c) 2012 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/time.h> +#include <sys/statfs.h> +#include <sys/smack.h> +#include "download-provider.h" +#include "download-provider-log.h" + +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-db.h" +#include "download-provider-pthread.h" + +#include "download-provider-notification.h" + +///////// below functions are called by main thread of thread-request.c + +////////////////////////////////////////////////////////////////////////// +/// @brief create unique id as integer type +/// @return unique id combined local time and the special calculation +static int __get_download_request_id(void) +{ + static int last_uniquetime = 0; + int uniquetime = 0; + + do { + struct timeval tval; + int cipher = 1; + int c = 0; + + gettimeofday(&tval, NULL); + + int usec = tval.tv_usec; + for (c = 0; ; c++, cipher++) { + if ((usec /= 10) <= 0) + break; + } + if (tval.tv_usec == 0) + tval.tv_usec = (tval.tv_sec & 0x0fff); + int disit_unit = 10; + for (c = 0; c < cipher - 3; c++) + disit_unit = disit_unit * 10; + uniquetime = tval.tv_sec + ((tval.tv_usec << 2) * 100) + + ((tval.tv_usec >> (cipher - 1)) * disit_unit) + + ((tval.tv_usec + (tval.tv_usec % 10)) & 0x0fff); + } while (last_uniquetime == uniquetime); + last_uniquetime = uniquetime; + return uniquetime; +} + +char *dp_print_state(dp_state_type state) +{ + switch(state) + { + case DP_STATE_NONE : + return "NONE"; + case DP_STATE_READY : + return "READY"; + case DP_STATE_QUEUED : + return "QUEUED"; + case DP_STATE_CONNECTING : + return "CONNECTING"; + case DP_STATE_DOWNLOADING : + return "DOWNLOADING"; + case DP_STATE_PAUSE_REQUESTED : + return "PAUSE_REQUESTED"; + case DP_STATE_PAUSED : + return "PAUSED"; + case DP_STATE_COMPLETED : + return "COMPLETED"; + case DP_STATE_CANCELED : + return "CANCELED"; + case DP_STATE_FAILED : + return "FAILED"; + default : + break; + } + return "UNKNOWN"; +} + +char *dp_print_errorcode(dp_error_type errorcode) +{ + switch(errorcode) + { + case DP_ERROR_NONE : + return "NONE"; + case DP_ERROR_INVALID_PARAMETER : + return "INVALID_PARAMETER"; + case DP_ERROR_OUT_OF_MEMORY : + return "OUT_OF_MEMORY"; + case DP_ERROR_IO_ERROR : + return "IO_ERROR"; + case DP_ERROR_NETWORK_UNREACHABLE : + return "NETWORK_UNREACHABLE"; + case DP_ERROR_CONNECTION_TIMED_OUT : + return "CONNECTION_TIMED_OUT"; + case DP_ERROR_NO_SPACE : + return "NO_SPACE"; + case DP_ERROR_FIELD_NOT_FOUND : + return "FIELD_NOT_FOUND"; + case DP_ERROR_INVALID_STATE : + return "INVALID_STATE"; + case DP_ERROR_CONNECTION_FAILED : + return "CONNECTION_FAILED"; + case DP_ERROR_INVALID_URL : + return "INVALID_URL"; + case DP_ERROR_INVALID_DESTINATION : + return "INVALID_DESTINATION"; + case DP_ERROR_QUEUE_FULL : + return "QUEUE_FULL"; + case DP_ERROR_ALREADY_COMPLETED : + return "ALREADY_COMPLETED"; + case DP_ERROR_FILE_ALREADY_EXISTS : + return "FILE_ALREADY_EXISTS"; + case DP_ERROR_TOO_MANY_DOWNLOADS : + return "TOO_MANY_DOWNLOADS"; + case DP_ERROR_NO_DATA : + return "NO_DATA"; + case DP_ERROR_UNHANDLED_HTTP_CODE : + return "UNHANDLED_HTTP_CODE"; + case DP_ERROR_CANNOT_RESUME : + return "CANNOT_RESUME"; + case DP_ERROR_PERMISSION_DENIED : + return "PERMISSION_DENIED"; + case DP_ERROR_RESPONSE_TIMEOUT : + return "RESPONSE_TIMEOUT"; + case DP_ERROR_REQUEST_TIMEOUT : + return "REQUEST_TIMEOUT"; + case DP_ERROR_SYSTEM_DOWN : + return "SYSTEM_DOWN"; + case DP_ERROR_CLIENT_DOWN : + return "CLIENT_DOWN"; + case DP_ERROR_ID_NOT_FOUND : + return "ID_NOT_FOUND"; + default : + break; + } + return "UNKNOWN"; +} + +int dp_is_smackfs_mounted() +{ + if(smack_smackfs_path() != NULL) + return 1; + TRACE_ERROR("[SMACK DISABLE]"); + return 0; +} + +char *dp_strdup(char *src) +{ + char *dest = NULL; + size_t src_len = 0; + + if (src == NULL) { + TRACE_ERROR("[CHECK PARAM]"); + return NULL; + } + + src_len = strlen(src); + if (src_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", src_len); + return NULL; + } + + dest = (char *)calloc(src_len + 1, sizeof(char)); + if (dest == NULL) { + TRACE_STRERROR("[CHECK] allocation"); + return NULL; + } + memcpy(dest, src, src_len * sizeof(char)); + dest[src_len] = '\0'; + + return dest; +} + +// check param +// create new slot +// fill info to new slot +// make new id +// save info to QUEUE(DB) +dp_error_type dp_request_create(int id, dp_client_group *group, dp_request **empty_slot) +{ + if (id != -1) { + TRACE_ERROR("[CHECK PROTOCOL] ID not -1"); + return DP_ERROR_INVALID_STATE; + } + if (group == NULL || empty_slot == NULL) { + TRACE_ERROR("[CHECK INTERNAL][%d]", id); + return DP_ERROR_INVALID_PARAMETER; + } + // New allocation Slot + dp_request *new_request = dp_request_new(); + if (new_request == NULL) { + TRACE_STRERROR("[CHECK MEMORY][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + + int check_id = -1; + do { + new_request->id = __get_download_request_id(); + check_id = dp_db_get_int_column(new_request->id, + DP_DB_TABLE_LOG, DP_DB_COL_ID); + } while (check_id == new_request->id); // means duplicated id + + new_request->group = group; + if (group->pkgname != NULL && strlen(group->pkgname) > 1) + new_request->packagename = dp_strdup(group->pkgname); + if (new_request->packagename == NULL) { + dp_request_free(new_request); + TRACE_ERROR("[ERROR][%d] OUT_OF_MEMORY [PACKAGENAME]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + new_request->state = DP_STATE_READY; + new_request->error = DP_ERROR_NONE; + new_request->create_time = (int)time(NULL); + + if (dp_db_request_new_logging(new_request->id, new_request->state, + new_request->packagename) < 0) { + dp_request_free(new_request); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL]"); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + *empty_slot = new_request; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_url(int id, dp_request *request, char *url) +{ + int length = 0; + if (url == NULL || (length = strlen(url)) <= 1) + return DP_ERROR_INVALID_URL; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_URL, + DP_DB_COL_TYPE_TEXT, url) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_destination(int id, dp_request *request, char *dest) +{ + int length = 0; + if (!dest || (length = strlen(dest)) <= 1) + return DP_ERROR_INVALID_DESTINATION; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_DESTINATION, + DP_DB_COL_TYPE_TEXT, dest) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_filename(int id, dp_request *request, char *filename) +{ + int length = 0; + if (!filename || (length = strlen(filename)) <= 1) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_FILENAME, + DP_DB_COL_TYPE_TEXT, filename) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + + TRACE_SECURE_DEBUG("ID [%d] Filename[%s]", id, filename); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_title(int id, dp_request *request, char *title) +{ + int length = 0; + if (!title || (length = strlen(title)) <= 1) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_TITLE, + DP_DB_COL_TYPE_TEXT, title) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + + TRACE_SECURE_DEBUG("ID [%d] title[%s]", id, title); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_bundle(int id, dp_request *request, int type, bundle_raw *b, unsigned length) +{ + char *column = NULL; + if (b == NULL || (length < 1)) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + switch(type) { + case DP_NOTIFICATION_BUNDLE_TYPE_ONGOING: + column = DP_DB_COL_RAW_BUNDLE_ONGOING; + break; + case DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE: + column = DP_DB_COL_RAW_BUNDLE_COMPLETE; + break; + case DP_NOTIFICATION_BUNDLE_TYPE_FAILED: + column = DP_DB_COL_RAW_BUNDLE_FAIL; + break; + default: + TRACE_ERROR("[CHECK TYPE][%d]", type); + return DP_ERROR_INVALID_PARAMETER; + } + if (dp_db_replace_blob_column + (id, DP_DB_TABLE_NOTIFICATION, column, b, length) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + + //TRACE_SECURE_DEBUG("ID [%d] title[%s]", id, title); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_description(int id, dp_request *request, char *description) +{ + int length = 0; + if (!description || (length = strlen(description)) <= 1) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_DESCRIPTION, + DP_DB_COL_TYPE_TEXT, description) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + + TRACE_SECURE_DEBUG("ID [%d] description[%s]", id, description); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_noti_type(int id, dp_request *request, unsigned type) +{ + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE, + DP_DB_COL_TYPE_INT, &type) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + if (request) + { + if(!type) + request->auto_notification = 0; + else + request->auto_notification = 1; + } + TRACE_SECURE_DEBUG("ID [%d] enable[%d]", id, type); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_notification(int id, dp_request *request, unsigned enable) +{ + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NOTIFICATION_ENABLE, DP_DB_COL_TYPE_INT, + &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + // update memory + if (request) + request->auto_notification = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_auto_download(int id, dp_request *request, unsigned enable) +{ + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_AUTO_DOWNLOAD, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_state_event(int id, dp_request *request, unsigned enable) +{ + if (request == NULL) { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + + if (state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_STATE_EVENT, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + // update memory + if (request != NULL) + request->state_cb = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_progress_event(int id, dp_request *request, unsigned enable) +{ + if (request == NULL) { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + + if (state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_PROGRESS_EVENT, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + // update memory + if (request) + request->progress_cb = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_network_type(int id, dp_request *request, int type) +{ + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_NETWORK_TYPE, + DP_DB_COL_TYPE_INT, &type) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + // update memory + if (request) + request->network_type = type; + return DP_ERROR_NONE; +} + +char *dp_request_get_url(int id, dp_error_type *errorcode) +{ + char *url = NULL; + url = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_URL); + if (url == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return url; +} + +char *dp_request_get_destination(int id, dp_request *request, dp_error_type *errorcode) +{ + char *dest = NULL; + dest = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_DESTINATION); + if (dest == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return dest; +} + +char *dp_request_get_filename(int id, dp_request *request, dp_error_type *errorcode) +{ + char *filename = NULL; + filename = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_FILENAME); + if (filename == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return filename; +} + +char *dp_request_get_title(int id, dp_request *request, dp_error_type *errorcode) +{ + char *title = NULL; + title = dp_db_get_text_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_TITLE); + if (title == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return title; +} + +bundle_raw *dp_request_get_bundle(int id, dp_request *request, dp_error_type *errorcode, char* column, int *length) +{ + void *blob_data = NULL; + blob_data = dp_db_get_blob_column + (id, DP_DB_TABLE_NOTIFICATION, column, length); + if (blob_data == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return (bundle_raw*)blob_data; +} + + +char *dp_request_get_description(int id, dp_request *request, dp_error_type *errorcode) +{ + char *description = NULL; + description = dp_db_get_text_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_DESCRIPTION); + if (description == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return description; +} + +int dp_request_get_noti_type(int id, dp_request *request, dp_error_type *errorcode) +{ + int type = -1; + type = dp_db_get_int_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if (type == -1) + *errorcode = DP_ERROR_NO_DATA; + return type; +} + + + +char *dp_request_get_contentname(int id, dp_request *request, dp_error_type *errorcode) +{ + char *content = NULL; + content = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + if (content == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return content; +} + +char *dp_request_get_etag(int id, dp_request *request, dp_error_type *errorcode) +{ + char *etag = NULL; + etag = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ETAG); + if (etag == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return etag; +} + +char *dp_request_get_savedpath(int id, dp_request *request, dp_error_type *errorcode) +{ + char *savedpath = NULL; + savedpath = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_SAVED_PATH); + if (savedpath == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return savedpath; +} + +char *dp_request_get_tmpsavedpath(int id, dp_request *request, dp_error_type *errorcode) +{ + char *tmppath = NULL; + tmppath = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_TMP_SAVED_PATH); + if (tmppath == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return tmppath; +} + +char *dp_request_get_mimetype(int id, dp_request *request, dp_error_type *errorcode) +{ + char *mimetype = NULL; + mimetype = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_MIMETYPE); + if (mimetype == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return mimetype; +} + +char *dp_request_get_pkg_name(int id, dp_request *request, dp_error_type *errorcode) +{ + char *pkg_name = NULL; + pkg_name = dp_db_get_text_column + (id, DP_DB_TABLE_LOG, DP_DB_COL_PACKAGENAME); + if (pkg_name == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return pkg_name; +} + +dp_request *dp_request_load_from_log(int id, dp_error_type *errorcode) +{ + dp_request *request = NULL; + + request = dp_db_load_logging_request(id); + if (request == NULL) { + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + *errorcode = DP_ERROR_INVALID_STATE; + dp_request_free(request); + return NULL; + } + return request; +} + + +void dp_request_state_response(dp_request *request) +{ + if (request == NULL) { + return ; + } + + TRACE_INFO("[INFO][%d] state:%s error:%s", request->id, + dp_print_state(request->state), + dp_print_errorcode(request->error)); + + if (dp_db_request_update_status(request->id, request->state, + request->error) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + + if (request->group != NULL && request->group->event_socket >= 0 && + request->state_cb == 1) { + dp_ipc_send_event(request->group->event_socket, request->id, + request->state, request->error, 0); + } + + if (request->state == DP_STATE_DOWNLOADING) { + if (request->auto_notification == 1 && + request->packagename != NULL) { + if (request->noti_priv_id != -1) { + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } else { + request->noti_priv_id = dp_set_downloadinginfo_notification + (request->id, request->packagename); + } + } else { + int noti_type = dp_db_get_int_column(request->id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if (noti_type == DP_NOTIFICATION_TYPE_ALL && + request->packagename != NULL) { + if (request->noti_priv_id != -1) { + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } else { + request->noti_priv_id = dp_set_downloadinginfo_notification + (request->id, request->packagename); + } + } + } + request->start_time = (int)time(NULL); + request->pause_time = 0; + request->stop_time = 0; + } else if (request->state == DP_STATE_PAUSED) { + if (request->group != NULL) + request->group->queued_count--; + request->pause_time = (int)time(NULL); + } else { + if (request->group != NULL ) + request->group->queued_count--; + + if (request->auto_notification == 1 && + request->packagename != NULL) { + request->noti_priv_id = dp_set_downloadedinfo_notification + (request->noti_priv_id, request->id, + request->packagename, request->state); + + } else { + int noti_type = dp_db_get_int_column(request->id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if (noti_type > DP_NOTIFICATION_TYPE_NONE && + request->packagename != NULL) + request->noti_priv_id = dp_set_downloadedinfo_notification + (request->noti_priv_id, request->id, + request->packagename, request->state); + } + + request->stop_time = (int)time(NULL); + } +} diff --git a/provider/download-provider-slots.c b/provider/download-provider-slots.c new file mode 100755 index 0000000..ad39ee8 --- /dev/null +++ b/provider/download-provider-slots.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2012 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 <stdio.h> +#include <stdlib.h> + +#include <time.h> +#include <sys/time.h> + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" + +#include "download-provider-slots.h" +#include "download-provider-socket.h" + +dp_group_slots *dp_client_group_slots_new(int size) +{ + dp_group_slots *slots = NULL; + if (size <= 0) + return NULL; + slots = (dp_group_slots *) calloc(size, + sizeof(dp_group_slots)); + return slots; +} + +dp_request_slots *dp_request_slots_new(int size) +{ + int i = 0; + dp_request_slots *slots = NULL; + if (size <= 0) + return NULL; + slots = (dp_request_slots *) calloc(size, + sizeof(dp_request_slots)); + for (; i < size; i++) + CLIENT_MUTEX_INIT(&slots[i].mutex, NULL); + return slots; +} + +void dp_request_init(dp_request *request) +{ + if (request == NULL) + return ; + + request->id = -1; + request->agent_id = -1; + request->create_time = 0; + request->start_time = 0; + request->pause_time = 0; + request->stop_time = 0; + request->state = DP_STATE_NONE; + request->error = DP_ERROR_NONE; + request->state_cb = 0; + request->progress_cb = 0; + request->progress_lasttime = 0; + request->received_size = 0; + request->file_size = 0; + request->network_type = DP_NETWORK_TYPE_ALL; + request->startcount = 0; + request->auto_notification = 0; + request->noti_priv_id = -1; + request->packagename = NULL; + request->group = NULL; +} + +dp_request *dp_request_new() +{ + dp_request *request = NULL; + request = (dp_request *) calloc(1, + sizeof(dp_request)); + if (!request) + return NULL; + dp_request_init(request); + return request; +} + +int dp_request_slot_free(dp_request_slots *request_slot) +{ + if (request_slot == NULL) + return -1; + CLIENT_MUTEX_LOCK(&request_slot->mutex); + dp_request_free(request_slot->request); + request_slot->request = NULL; + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return 0; +} + +int dp_request_free(dp_request *request) +{ + if (request == NULL) + return -1; + free(request->packagename); + dp_request_init(request); + free(request); + return 0; +} + +int dp_client_group_free(dp_client_group *group) +{ + if (group != NULL) { + if (group->cmd_socket > 0) + dp_socket_free(group->cmd_socket); + group->cmd_socket = -1; + if (group->event_socket > 0) + dp_socket_free(group->event_socket); + group->event_socket = -1; + group->queued_count = 0; + free(group->pkgname); + free(group->smack_label); + free(group); + } + return 0; +} + +int dp_client_group_slots_free(dp_group_slots *slots, int size) +{ + int i = 0; + if (slots) { + for (; i < size; i++) { + if (slots->group) + dp_client_group_free(slots->group); + slots->group = NULL; + } + free(slots); + } + slots = NULL; + return 0; +} + +int dp_request_slots_free(dp_request_slots *slots, int size) +{ + int i = 0; + if (slots != NULL) { + for (; i < size; i++) { + dp_request_free(slots[i].request); + slots[i].request = NULL; + CLIENT_MUTEX_DESTROY(&slots[i].mutex); + } + free(slots); + } + slots = NULL; + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return count of requests in slot +int dp_get_request_count(dp_request_slots *slots) +{ + int i = 0; + int count = 0; + + if (!slots) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (slots[i].request != NULL) + count++; + } + return count; +} diff --git a/provider/download-provider-socket.c b/provider/download-provider-socket.c new file mode 100755 index 0000000..7feabf8 --- /dev/null +++ b/provider/download-provider-socket.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2012 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 <unistd.h> + +#include <pthread.h> + +#include <time.h> +#include <sys/time.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <systemd/sd-daemon.h> +#include <signal.h> + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-socket.h" + +////////////////////////////////////////////////////////////////////////// +/// @brief write the error to socket +/// @return if success, return 0 +int dp_ipc_send_errorcode(int fd, dp_error_type errorcode) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + + if (fd >= 0 && write(fd, &errorcode, sizeof(dp_error_type)) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief write the progressinfo to socket +/// @return if success, return 0 +int dp_ipc_send_event(int fd, int id, dp_state_type state, + dp_error_type errorcode, unsigned long long received_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR][%d] CHECK FD[%d]", id, fd); + return -1; + } + + dp_event_info eventinfo; + eventinfo.id = id; + eventinfo.state = state; + eventinfo.err = errorcode; + eventinfo.received_size = received_size; + + // write + if (fd >= 0 && write(fd, &eventinfo, sizeof(dp_event_info)) <= 0) { + TRACE_STRERROR("[ERROR][%d] write FD[%d]", id, fd); + return -1; + } + return 0; +} + +// keep the order/ unsigned , str +char *dp_ipc_read_string(int fd) +{ + unsigned length = 0; + size_t recv_size = 0; + unsigned remain_size = 0; + size_t buffer_size = 0; + char *str = NULL; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return NULL; + } + + // read flexible URL from client. + ssize_t recv_bytes = read(fd, &length, sizeof(unsigned)); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return NULL; + } + if (length < 1 || length > DP_MAX_URL_LEN) { + TRACE_ERROR("[STRING LEGNTH] [%d]", length); + return NULL; + } + str = (char *)calloc((length + 1), sizeof(char)); + if (str == NULL) { + TRACE_STRERROR("[ERROR] calloc length:%d FD[%d]", length, fd); + return NULL; + } + remain_size = length; + do { + buffer_size = 0; + if (remain_size > DP_DEFAULT_BUFFER_SIZE) + buffer_size = DP_DEFAULT_BUFFER_SIZE; + else + buffer_size = remain_size; + recv_size = (size_t)read(fd, str + (int)(length - remain_size), + buffer_size * sizeof(char)); + if (recv_size > DP_DEFAULT_BUFFER_SIZE) { + recv_size = -1; + break; + } + if (recv_size > 0) + remain_size = remain_size - (unsigned)recv_size; + } while (recv_size > 0 && remain_size > 0); + + if (recv_size == 0) { + TRACE_STRERROR("[ERROR] closed peer:%d", fd); + free(str); + return NULL; + } + str[length] = '\0'; + return str; +} + + +// 0 : Socket Error +// -1 : Invalid type +unsigned dp_ipc_read_bundle(int fd, int *type, bundle_raw **b) +{ + unsigned length = 0; + size_t recv_size = 0; + unsigned remain_size = 0; + size_t buffer_size = 0; + bundle_raw *b_raw = NULL; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return 0; + } + + // read flexible URL from client. + ssize_t recv_bytes = read(fd, type, sizeof(int)); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] type[%d]", fd, type); + return 0; + } + if ((*type) != DP_NOTIFICATION_BUNDLE_TYPE_ONGOING && + (*type) != DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE && + (*type) != DP_NOTIFICATION_BUNDLE_TYPE_FAILED) { + TRACE_ERROR("[NOTI TYPE] [%d]", *type); + return -1; + } + // read flexible URL from client. + recv_bytes = read(fd, &length, sizeof(unsigned)); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return 0; + } + if (length < 1 || length > DP_MAX_URL_LEN) { + TRACE_ERROR("[STRING LEGNTH] [%d]", length); + return 0; + } + b_raw = (bundle_raw *)calloc(length, 1); + if (b_raw == NULL) { + TRACE_STRERROR("[ERROR] calloc length:%d FD[%d]", length, fd); + return 0; + } + remain_size = length; + do { + buffer_size = 0; + if (remain_size > DP_DEFAULT_BUFFER_SIZE) + buffer_size = DP_DEFAULT_BUFFER_SIZE; + else + buffer_size = remain_size; + recv_size = (size_t)read(fd, b_raw + (int)(length - remain_size), + buffer_size * sizeof(char)); + if (recv_size > DP_DEFAULT_BUFFER_SIZE) { + recv_size = -1; + break; + } + if (recv_size > 0) + remain_size = remain_size - (unsigned)recv_size; + } while (recv_size > 0 && remain_size > 0); + + if (recv_size <= 0) { + TRACE_STRERROR("[ERROR] closed peer:%d", fd); + bundle_free_encoded_rawdata(&b_raw); + return 0; + } + *b = b_raw; + return length; +} + +// keep the order/ unsigned , str +int dp_ipc_send_string(int fd, const char *str) +{ + unsigned length = 0; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (str == NULL) { + TRACE_ERROR("[ERROR] CHECK STRING FD[%d]", fd); + return -1; + } + + length = strlen(str); + if (length < 1) { + TRACE_ERROR("[ERROR] CHECK LENGTH FD[%d]", fd); + return -1; + } + if (fd >= 0 && write(fd, &length, sizeof(unsigned)) <= 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return -1; + } + if (fd >= 0 && write(fd, str, length * sizeof(char)) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_ipc_send_bundle(int fd, bundle_raw *b, unsigned length) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (b == NULL) { + TRACE_ERROR("[ERROR] CHECK STRING FD[%d]", fd); + return -1; + } + + if (length < 1) { + TRACE_ERROR("[ERROR] CHECK LENGTH FD[%d]", fd); + return -1; + } + if (fd >= 0 && write(fd, &length, sizeof(unsigned)) <= 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return -1; + } + if (fd >= 0 && write(fd, b, length) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_ipc_send_custom_type(int fd, void *value, size_t type_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (value == NULL) { + TRACE_ERROR("[ERROR] CHECK VALUE FD[%d]", fd); + return -1; + } + if (fd >= 0 && write(fd, value, type_size) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_ipc_read_custom_type(int fd, void *value, size_t type_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (value == NULL) { + TRACE_ERROR("[ERROR] CHECK VALUE FD[%d]", fd); + return -1; + } + + ssize_t recv_bytes = read(fd, value, type_size); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_accept_socket_new() +{ + int sockfd = -1; + struct sockaddr_un listenaddr; + + int n = sd_listen_fds(1); + if (n > 1) { + TRACE_STRERROR("too many file descriptors received"); + return -1; + } else if (n == 1) { + int r; + if ((r = sd_is_socket_unix(SD_LISTEN_FDS_START, SOCK_STREAM, 1, DP_IPC, 0)) <= 0) { + TRACE_STRERROR("passed systemd file descriptor is of wrong type"); + return -1; + } + sockfd = SD_LISTEN_FDS_START + 0; + } else { + if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + TRACE_STRERROR("failed to create socket"); + return -1; + } + + bzero(&listenaddr, sizeof(listenaddr)); + listenaddr.sun_family = AF_UNIX; + strcpy(listenaddr.sun_path, DP_IPC); + + if (bind(sockfd, (struct sockaddr *)&listenaddr, sizeof listenaddr) != + 0) { + TRACE_STRERROR("[CRITICAL] bind"); + close(sockfd); + return -1; + } + + if (chmod(listenaddr.sun_path, 0777) < 0) { + TRACE_STRERROR("[CRITICAL] chmod"); + close(sockfd); + return -1; + } + + // need 3 socket per a group + if (listen(sockfd, DP_MAX_GROUP * 3) != 0) { + TRACE_STRERROR("[CRITICAL] listen"); + close(sockfd); + return -1; + } + } + return sockfd; +} + +int dp_socket_free(int sockfd) +{ + TRACE_DEBUG("[%d]", sockfd); + if (sockfd < 0) + return -1; + shutdown(sockfd, 0); + close(sockfd); + return 0; +} diff --git a/provider/download-provider-thread-queue.c b/provider/download-provider-thread-queue.c new file mode 100755 index 0000000..b82618d --- /dev/null +++ b/provider/download-provider-thread-queue.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2012 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 <unistd.h> + +#include <time.h> +#include <sys/time.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#include <signal.h> + +#include <pthread.h> + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-network.h" +#include "download-provider-da-interface.h" + +void dp_terminate(int signo); + +pthread_mutex_t g_dp_queue_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t g_dp_queue_cond = PTHREAD_COND_INITIALIZER; + + +////////////////////////////////////////////////////////////////////////// +/// @brief check network status is matched with the type setted by user +/// @return matched : 0 mispatch : -1 +static int __is_matched_network(dp_network_type now_state, dp_network_type setted_state) +{ + if (now_state == setted_state + || now_state == DP_NETWORK_TYPE_ETHERNET + || setted_state == DP_NETWORK_TYPE_ALL) + return 0; + #if 0 + if (setted_state == DP_NETWORK_TYPE_ALL + || setted_state == DP_NETWORK_TYPE_DATA_NETWORK + || now_state == DP_NETWORK_TYPE_WIFI + || now_state == DP_NETWORK_TYPE_ETHERNET + || (setted_state == DP_NETWORK_TYPE_WIFI + && (now_state == DP_NETWORK_TYPE_WIFI + || now_state == DP_NETWORK_TYPE_ETHERNET)) + ) + return 0; + #endif + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief the count of slot downloading currently +static unsigned __get_active_count(dp_request_slots *requests) +{ + unsigned count = 0; + unsigned i = 0; + + if (requests == NULL) + return 0; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + int locked = pthread_mutex_trylock(&requests[i].mutex); + // locking failure means it used by other thread. + if (locked == 0) { + if (requests[i].request != NULL) { + if (requests[i].request->state == DP_STATE_CONNECTING || + requests[i].request->state == DP_STATE_DOWNLOADING) + count++; + } + CLIENT_MUTEX_UNLOCK(&requests[i].mutex); + } + } + return count; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief index of slot which last time is oldest +static int __get_oldest_request_with_network(dp_request_slots *requests, dp_state_type state, dp_network_type now_state) +{ + int i = 0; + int oldest_time = (int)time(NULL); + oldest_time++; // most last time + int oldest_index = -1; + + if (requests == NULL) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + int locked = pthread_mutex_trylock(&requests[i].mutex); + // locking failure means it used by other thread. + if (locked == 0) { + if (requests[i].request != NULL) { + if (requests[i].request->state == state && + requests[i].request->start_time > 0 && + requests[i].request->start_time < oldest_time && + __is_matched_network(now_state, + requests[i].request->network_type) == 0) { + oldest_time = requests[i].request->start_time; + oldest_index = i; + } + } + CLIENT_MUTEX_UNLOCK(&requests[i].mutex); + } + } + return oldest_index; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief THREAD function for calling da_start_download_with_extension. +/// @warning da_start_download_with_extension can take long time +/// @param the pointer of memory slot allocated for this request. +/// @todo simplify da_start_download_with_extension +static void *__request_download_start_agent(void *args) +{ + dp_error_type errcode = DP_ERROR_NONE; + + dp_request_slots *request_slot = (dp_request_slots *) args; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request_slot"); + pthread_exit(NULL); + return 0; + } + + CLIENT_MUTEX_LOCK(&request_slot->mutex); + + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request"); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + pthread_exit(NULL); + return 0; + } + + dp_request *request = request_slot->request; + + if (dp_is_alive_download(request->agent_id)) { + errcode = dp_resume_agent_download(request->agent_id); + } else { + // call agent start function + errcode = dp_start_agent_download(request_slot); + } + // send to state callback. + if (errcode == DP_ERROR_NONE) { + // CONNECTING + request->state = DP_STATE_CONNECTING; + request->error = DP_ERROR_NONE; + request->startcount++; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STARTCOUNT, + DP_DB_COL_TYPE_INT, &request->startcount) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + } else if (errcode == DP_ERROR_TOO_MANY_DOWNLOADS) { + // PENDED + request->state = DP_STATE_QUEUED; + request->error = DP_ERROR_TOO_MANY_DOWNLOADS; + } else if (errcode == DP_ERROR_CONNECTION_FAILED) { + // FAILED + request->state = DP_STATE_FAILED; + request->error = DP_ERROR_CONNECTION_FAILED; + if (request->group != NULL && + request->group->event_socket >= 0) { + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + } + } else if (errcode == DP_ERROR_INVALID_STATE) { + // API FAILED + request->error = DP_ERROR_INVALID_STATE; + if (request->group != NULL && + request->group->event_socket >= 0) { + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + } + } else { + request->state = DP_STATE_FAILED; + request->error = errcode; + if (request->group != NULL && + request->group->event_socket >= 0) { + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + } + } + if (dp_db_request_update_status(request->id, request->state, request->error) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + } + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + + if (errcode == DP_ERROR_NONE) { + TRACE_DEBUG("try other requests -----------------"); + dp_thread_queue_manager_wake_up(); + } + + pthread_exit(NULL); + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief create thread. +/// @warning if failed to create thread, change to PENED. +/// @param the pointer of memory slot allocated for this request. +static int __request_download_start_thread(dp_request_slots *request_slot) +{ + // declare all resources + pthread_t thread_pid; + pthread_attr_t thread_attr; + + if (request_slot == NULL || request_slot->request == NULL) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + return -1; + } + dp_request *request = request_slot->request; + + // initialize + if (pthread_attr_init(&thread_attr) != 0) { + TRACE_STRERROR("[ERROR][%d] pthread_attr_init", request->id); + return -1; + } + if (pthread_attr_setdetachstate(&thread_attr, + PTHREAD_CREATE_DETACHED) != 0) { + TRACE_STRERROR + ("[ERROR][%d] pthread_attr_setdetachstate", request->id); + return -1; + } + + request->state = DP_STATE_CONNECTING; + if (pthread_create(&thread_pid, &thread_attr, + __request_download_start_agent, request_slot) != 0) { + TRACE_STRERROR("[ERROR][%d] pthread_create", request->id); + pthread_attr_destroy(&thread_attr); + request->state = DP_STATE_QUEUED; + return -1; + } + pthread_attr_destroy(&thread_attr); + return 0; +} + + +void dp_thread_queue_manager_wake_up() +{ + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + pthread_cond_signal(&g_dp_queue_cond); + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); +} + + +// Main role : start download, check status of queue. +// No timeout. Wake up by Signal be sent from other thread +void *dp_thread_queue_manager(void *arg) +{ + int i; + int active_count; + dp_client_group *group = NULL; + dp_request *request = NULL; + + dp_privates *privates = (dp_privates*)arg; + if (!privates) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; + } + + pthread_cond_init(&g_dp_queue_cond, NULL); + while (privates != NULL && privates->listen_fd >= 0) { + + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + pthread_cond_wait(&g_dp_queue_cond, &g_dp_queue_mutex); + + // request thread response instantly + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); + + if (privates == NULL || privates->requests == NULL || + privates->listen_fd < 0) { + TRACE_DEBUG("Terminate Thread"); + break; + } + + // if failed to initialize the callback for checking connection + if (!privates->connection) + privates->network_status = + dp_get_network_connection_instant_status(); + +#ifdef SUPPORT_WIFI_DIRECT + if (privates->is_connected_wifi_direct == 0) { + if (dp_network_wifi_direct_is_connected() == 0) { + // wifi-direct activated. pass. + privates->is_connected_wifi_direct = 1; + } + } +#endif + + if (privates->network_status == DP_NETWORK_TYPE_OFF && + privates->is_connected_wifi_direct == 0) { + TRACE_DEBUG("[CHECK NETWORK STATE]"); + continue; + } + + active_count = __get_active_count(privates->requests); + + TRACE_DEBUG("Status Queue: now active[%d] max[%d]", + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + + // Start Conditions + // 1. state is QUEUED + // 2. 1 QUEUED per 1 Group : need not to check max limitation.!! + // if no group, it will be started later. + // 3. most old last time : below conditions need max limitation. + // 4. match network connection type + // 5. wifi-direct on + + // search group having 1 queued_count + // guarantee 1 instant download per 1 group + if (active_count >= DP_MAX_DOWNLOAD_AT_ONCE) { + for (i = 0; i < DP_MAX_REQUEST; i++) { + CLIENT_MUTEX_LOCK(&privates->requests[i].mutex); + request = privates->requests[i].request; + if (request != NULL && request->state == DP_STATE_QUEUED) { + group = privates->requests[i].request->group; + if (group && group->queued_count == 1) { + if (__is_matched_network + (privates->network_status, + request->network_type) == 0 || + (privates->is_connected_wifi_direct == 1 && + request->network_type == + DP_NETWORK_TYPE_WIFI_DIRECT)) { + if (__request_download_start_thread(&privates->requests[i]) == 0) { + TRACE_DEBUG + ("[Guarantee Intant Download] Group [%s]", group->pkgname); + active_count++; + } + } + } + } + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + } + } + + if (active_count >= DP_MAX_DOWNLOAD_AT_ONCE) { + TRACE_DEBUG("[BUSY] Active[%d] Max[%d]", + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + continue; + } + + // can start download more. + // search oldest request + while(active_count < DP_MAX_DOWNLOAD_AT_ONCE) { + +#ifdef SUPPORT_WIFI_DIRECT + // WIFI-Direct first + if (privates->is_connected_wifi_direct == 1) { + i = __get_oldest_request_with_network(privates->requests, + DP_STATE_QUEUED, DP_NETWORK_TYPE_WIFI_DIRECT); + if (i >= 0) { + TRACE_DEBUG("Found WIFI-Direct request %d", i); + if (__request_download_start_thread(&privates->requests[i]) == 0) + active_count++; + continue; + } + } +#endif + + i = __get_oldest_request_with_network(privates->requests, + DP_STATE_QUEUED, privates->network_status); + if (i < 0) { + TRACE_DEBUG + ("No Request now active[%d] max[%d]", + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + break; + } + TRACE_DEBUG("QUEUE Status now %d active %d/%d", i, + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + __request_download_start_thread(&privates->requests[i]); + active_count++; + } + } + pthread_cond_destroy(&g_dp_queue_cond); + pthread_exit(NULL); + return 0; +} diff --git a/provider/download-provider-thread-request.c b/provider/download-provider-thread-request.c new file mode 100755 index 0000000..6b03c08 --- /dev/null +++ b/provider/download-provider-thread-request.c @@ -0,0 +1,2158 @@ +/* + * Copyright (c) 2012 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 <unistd.h> + +#include <time.h> +#include <sys/time.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <signal.h> + +#include <app_manager.h> +#include <sys/smack.h> +#include <bundle.h> + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-request.h" +#include "download-provider-network.h" +#include "download-provider-da-interface.h" +#include "download-provider-notification.h" + +void dp_terminate(int signo); + +static char *__print_command(dp_command_type cmd) +{ + switch(cmd) + { + case DP_CMD_CREATE : + return "CREATE"; + case DP_CMD_START : + return "START"; + case DP_CMD_PAUSE : + return "PAUSE"; + case DP_CMD_CANCEL : + return "CANCEL"; + case DP_CMD_DESTROY : + return "DESTROY"; + case DP_CMD_FREE : + return "FREE"; + case DP_CMD_ECHO : + return "ECHO"; + case DP_CMD_SET_URL : + return "SET_URL"; + case DP_CMD_SET_DESTINATION : + return "SET_DESTINATION"; + case DP_CMD_SET_FILENAME : + return "SET_FILENAME"; + case DP_CMD_SET_NOTIFICATION : + return "SET_NOTIFICATION"; + case DP_CMD_SET_STATE_CALLBACK : + return "SET_STATE_CALLBACK"; + case DP_CMD_SET_PROGRESS_CALLBACK : + return "SET_PROGRESS_CALLBACK"; + case DP_CMD_SET_AUTO_DOWNLOAD : + return "SET_AUTO_DOWNLOAD"; + case DP_CMD_SET_NETWORK_TYPE : + return "SET_NETWORK_TYPE"; + case DP_CMD_SET_HTTP_HEADER : + return "SET_HTTP_HEADER"; + case DP_CMD_DEL_HTTP_HEADER : + return "DEL_HTTP_HEADER"; + case DP_CMD_GET_HTTP_HEADER : + return "GET_HTTP_HEADER"; + case DP_CMD_GET_URL : + return "GET_URL"; + case DP_CMD_GET_DESTINATION : + return "GET_DESTINATION"; + case DP_CMD_GET_FILENAME : + return "GET_FILENAME"; + case DP_CMD_GET_NOTIFICATION : + return "GET_NOTIFICATION"; + case DP_CMD_GET_STATE_CALLBACK : + return "GET_STATE_CALLBACK"; + case DP_CMD_GET_PROGRESS_CALLBACK : + return "GET_PROGRESS_CALLBACK"; + case DP_CMD_GET_HTTP_HEADERS : + return "GET_HTTP_HEADERS"; + case DP_CMD_GET_HTTP_HEADER_LIST: + return "GET_HTTP_HEADER_LIST"; + case DP_CMD_ADD_EXTRA_PARAM : + return "ADD_EXTRA_PARAM"; + case DP_CMD_GET_EXTRA_PARAM : + return "GET_EXTRA_PARAM"; + case DP_CMD_REMOVE_EXTRA_PARAM : + return "REMOVE_EXTRA_PARAM"; + case DP_CMD_GET_AUTO_DOWNLOAD : + return "GET_AUTO_DOWNLOAD"; + case DP_CMD_GET_NETWORK_TYPE : + return "GET_NETWORK_TYPE"; + case DP_CMD_GET_SAVED_PATH : + return "GET_SAVED_PATH"; + case DP_CMD_GET_TEMP_SAVED_PATH : + return "GET_TEMP_SAVED_PATH"; + case DP_CMD_GET_MIME_TYPE : + return "GET_MIME_TYPE"; + case DP_CMD_GET_RECEIVED_SIZE : + return "GET_RECEIVED_SIZE"; + case DP_CMD_GET_TOTAL_FILE_SIZE : + return "GET_TOTAL_FILE_SIZE"; + case DP_CMD_GET_CONTENT_NAME : + return "GET_CONTENT_NAME"; + case DP_CMD_GET_HTTP_STATUS : + return "GET_HTTP_STATUS"; + case DP_CMD_GET_ETAG : + return "DP_CMD_GET_ETAG"; + case DP_CMD_GET_STATE : + return "GET_STATE"; + case DP_CMD_GET_ERROR : + return "ERROR"; + case DP_CMD_SET_COMMAND_SOCKET : + return "SET_COMMAND_SOCKET"; + case DP_CMD_SET_EVENT_SOCKET : + return "SET_EVENT_SOCKET"; + case DP_CMD_SET_NOTIFICATION_BUNDLE: + return "SET_NOTIFICATION_BUNDLE"; + case DP_CMD_SET_NOTIFICATION_TITLE: + return "SET_NOTIFICATION_TITLE"; + case DP_CMD_SET_NOTIFICATION_DESCRIPTION: + return "SET_NOTIFICATION_DESCRIPTION"; + case DP_CMD_SET_NOTIFICATION_TYPE: + return "SET_NOTIFICATION_TYPE"; + case DP_CMD_GET_NOTIFICATION_BUNDLE: + return "GET_NOTIFICATION_BUNDLE"; + case DP_CMD_GET_NOTIFICATION_TITLE: + return "GET_NOTIFICATION_TITLE"; + case DP_CMD_GET_NOTIFICATION_DESCRIPTION: + return "GET_NOTIFICATION_DESCRIPTION"; + case DP_CMD_GET_NOTIFICATION_TYPE: + return "GET_NOTIFICATION_TYPE"; + default : + break; + } + return "UNKNOWN COMMAND"; +} + +/* compare two string */ +static int __cmp_string(char *s1, char *s2) +{ + size_t s1_len = 0; + size_t s2_len = 0; + + if (s1 == NULL || s2 == NULL) { + TRACE_ERROR("[CHECK PARAM]"); + return -1; + } + + s1_len = strlen(s1); + if (s1_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", s1_len); + return -1; + } + + s2_len = strlen(s2); + if (s2_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", s2_len); + return -1; + } + + if (s1_len != s2_len) { + TRACE_ERROR("[DIFF] len[%d:%d]", s1_len, s2_len); + return -1; + } + + if (strncmp(s1, s2, s1_len) != 0) { + TRACE_ERROR("[DIFF] cmp"); + return -1; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return index of empty slot +static int __get_empty_request_index(dp_request_slots *slots) +{ + int i = 0; + + if (slots == NULL) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (slots[i].request == NULL) + return i; + } + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return index of slot having same ID +/// @param ID want to search +static int __get_same_request_index(dp_request_slots *slots, int id) +{ + int i = 0; + + if (!slots || id < 0) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (slots[i].request != NULL) { + if (slots[i].request->id == id) + return i; + } + } + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return custom value via IPC +static void __send_return_custom_type(int fd, dp_error_type errcode, void *value, size_t type_size) +{ + dp_ipc_send_errorcode(fd, errcode); + dp_ipc_send_custom_type(fd, value, type_size); +} + +static int __is_downloading(dp_state_type state) +{ + if (state == DP_STATE_CONNECTING || state == DP_STATE_DOWNLOADING) + return 0; + return -1; +} + +static int __is_started(dp_state_type state) +{ + if (state == DP_STATE_QUEUED || __is_downloading(state) == 0) + return 0; + return -1; +} + +static int __is_stopped(dp_state_type state) +{ + if (state == DP_STATE_COMPLETED || state == DP_STATE_FAILED || + state == DP_STATE_CANCELED) + return 0; + return -1; +} + +// cancel the downloading if no auto-download, then clear group +static void __clear_group(dp_privates *privates, dp_client_group *group) +{ + dp_request *request = NULL; + int i = 0; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + + CLIENT_MUTEX_LOCK(&privates->requests[i].mutex); + + if (privates->requests[i].request == NULL) { + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + continue; + } + + request = privates->requests[i].request; + + if (request->group == NULL || request->id <= 0 || + request->group != group) { + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + continue; + } + + // if stopped, paused or not started yet, clear request + if (__is_started(request->state) < 0) { + TRACE_DEBUG("[FREE][%d] state[%s]", request->id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + dp_request_slot_free(&privates->requests[i]); + continue; + } + + // disconnect the request from group. + TRACE_SECURE_DEBUG("[DISCONNECT][%d] state:%s pkg:%s sock:%d", + request->id, dp_print_state(request->state), + request->group->pkgname, request->group->cmd_socket); + request->group = NULL; + request->state_cb = 0; + request->progress_cb = 0; + + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + + } + // clear this group + dp_client_group_free(group); +} + +static void __dp_remove_tmpfile(int id, dp_request *request) +{ + dp_error_type errorcode = DP_ERROR_NONE; + char *path = + dp_request_get_tmpsavedpath(id, request, &errorcode); + if (errorcode == DP_ERROR_NONE && + (path != NULL && strlen(path) > 0) && + dp_is_file_exist(path) == 0) { + TRACE_SECURE_DEBUG("[REMOVE][%d] TEMP FILE [%s]", id, path); + if (unlink(path) != 0) + TRACE_STRERROR("[ERROR][%d] remove file", id); + } + free(path); +} + +static int __dp_check_valid_directory(dp_request *request, char *dir) +{ + int ret = -1; + int ret_val = 0; + char *dir_label = NULL; + struct stat dir_state; + + ret_val = stat(dir, &dir_state); + if (ret_val == 0) { + dp_credential cred; + if (request->group == NULL) { + cred.uid = dp_db_cond_get_int(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_UID, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + cred.gid = dp_db_cond_get_int(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_GID, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + } else { + cred = request->group->credential; + } + if (dir_state.st_uid == cred.uid + && (dir_state.st_mode & (S_IRUSR | S_IWUSR)) + == (S_IRUSR | S_IWUSR)) { + ret = 0; + } else if (dir_state.st_gid == cred.gid + && (dir_state.st_mode & (S_IRGRP | S_IWGRP)) + == (S_IRGRP | S_IWGRP)) { + ret = 0; + } else if ((dir_state.st_mode & (S_IROTH | S_IWOTH)) + == (S_IROTH | S_IWOTH)) { + ret = 0; + } + } + + if (ret != 0) + return ret; + + ret_val = smack_getlabel(dir, &dir_label, SMACK_LABEL_ACCESS); + if (ret_val != 0) { + TRACE_SECURE_ERROR("[ERROR][%d][SMACK ERROR]", request->id); + free(dir_label); + return -1; + } + + char *smack_label = NULL; + if (request->group == NULL) { + // get smack_label from sql + smack_label = dp_db_cond_get_text(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_SMACK_LABEL, DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + if (smack_label != NULL) { + ret_val = smack_have_access(smack_label, dir_label, "rw"); + free(smack_label); + } + } else { + if (request->group->smack_label != NULL) { + ret_val = smack_have_access(request->group->smack_label, dir_label, "rw"); + } + } + if (ret_val == 0) { + TRACE_SECURE_ERROR("[ERROR][%d][SMACK NO RULE]", request->id); + ret = -1; + } else if (ret_val < 0){ + TRACE_SECURE_ERROR("[ERROR][%d][SMACK ERROR]", request->id); + ret = -1; + } + free(dir_label); + return ret; +} + +static int __dp_call_cancel_agent(dp_request *request) +{ + int ret = -1; + if (request != NULL) { + if (request->agent_id >= 0) { + TRACE_INFO("[%d]cancel_agent(%d) state:%s", request->id, + request->agent_id, dp_print_state(request->state)); + if (dp_cancel_agent_download(request->agent_id) == 0) + ret = 0; + } else { + TRACE_DEBUG("[CHECK] agent-id"); + } + } + return ret; +} + +static int __dp_add_extra_param(int fd, int id) +{ + dp_error_type ret = DP_ERROR_NONE; + int length = 0; + int i = 0; + unsigned values_length = 0; + char *key = NULL; + char **values = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK] socket"); + return DP_ERROR_IO_ERROR; + } + if (id < 0) { + TRACE_ERROR("[CHECK] ID"); + return DP_ERROR_INVALID_PARAMETER; + } + + if (dp_ipc_read_custom_type(fd, &length, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", id); + ret = DP_ERROR_IO_ERROR; + } else { + TRACE_DEBUG("[RECV] length %d", length); + if (length <= 0) { + ret = DP_ERROR_INVALID_PARAMETER; + } else { + key = dp_ipc_read_string(fd); + if (key == NULL) { + TRACE_ERROR("[ERROR][%d][IO_ERROR] key", id); + ret = DP_ERROR_IO_ERROR; + } else { + if (length > 1) { + TRACE_SECURE_DEBUG("[RECV] key : %s", key); + // get values + values = (char **)calloc((length - 1), sizeof(char *)); + if (values == NULL) { + ret = DP_ERROR_OUT_OF_MEMORY; + } else { + for (i = 0; i < length - 1; i++) { + values[i] = dp_ipc_read_string(fd); + if (values[i] == NULL) { + ret = DP_ERROR_IO_ERROR; + break; + } + values_length++; + } + } + } else { + TRACE_ERROR("[ERROR][%d] length [%d]", id, length); + ret = DP_ERROR_INVALID_PARAMETER; + } + } + } + } + if (ret == DP_ERROR_NONE) { + // store to DB + for (i = 0; i < length - 1; i++) { + int conds_count = 3; + db_conds_list_fmt conds_p[conds_count]; // id + key + value + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + conds_p[0].column = DP_DB_COL_ID; + conds_p[0].type = DP_DB_COL_TYPE_INT; + conds_p[0].value = &id; + conds_p[1].column = DP_DB_COL_EXTRA_KEY; + conds_p[1].type = DP_DB_COL_TYPE_TEXT; + conds_p[1].value = key; + conds_p[2].column = DP_DB_COL_EXTRA_VALUE; + conds_p[2].type = DP_DB_COL_TYPE_TEXT; + conds_p[2].value = values[i]; + int check_key = + dp_db_get_conds_rows_count(DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_ID, "AND", conds_count, conds_p); + if (check_key <= 0) { // create newly + // insert + if (dp_db_insert_columns(DP_DB_TABLE_NOTIFICATION, + conds_count, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + ret = DP_ERROR_NO_SPACE; + } else { + ret = DP_ERROR_OUT_OF_MEMORY; + } + break; + } + } // else skip. already exist + } + } + free(key); + for (i = 0; i < values_length; i++) { + free(values[i]); + } + free(values); + return ret; +} + +static int __dp_get_extra_param_values(int fd, int id, char ***values, + unsigned *count) +{ + dp_error_type ret = DP_ERROR_NONE; + int length = 0; + char *key = NULL; + char **rows_array = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK] socket"); + return DP_ERROR_IO_ERROR; + } + if (id < 0) { + TRACE_ERROR("[CHECK] ID"); + return DP_ERROR_INVALID_PARAMETER; + } + + if (dp_ipc_read_custom_type(fd, &length, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", id); + ret = DP_ERROR_IO_ERROR; + } else { + TRACE_DEBUG("[RECV] length %d", length); + if (length != 1) { // only a key + ret = DP_ERROR_INVALID_PARAMETER; + } else { + if ((key = dp_ipc_read_string(fd)) == NULL) { + TRACE_ERROR("[ERROR][%d][IO_ERROR] key", id); + ret = DP_ERROR_IO_ERROR; + } + } + } + if (ret == DP_ERROR_NONE) { + int conds_count = 2; + db_conds_list_fmt conds_p[conds_count]; // id + key + value + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + conds_p[0].column = DP_DB_COL_ID; + conds_p[0].type = DP_DB_COL_TYPE_INT; + conds_p[0].value = &id; + conds_p[1].column = DP_DB_COL_EXTRA_KEY; + conds_p[1].type = DP_DB_COL_TYPE_TEXT; + conds_p[1].value = key; + int check_rows = dp_db_get_conds_rows_count + (DP_DB_TABLE_NOTIFICATION, DP_DB_COL_EXTRA_VALUE, "AND", + conds_count, conds_p); + if (check_rows <= 0) { + // NO_DATA + ret = DP_ERROR_NO_DATA; + } else { + rows_array = (char **)calloc(check_rows, sizeof(char *)); + if (rows_array == NULL) { + ret = DP_ERROR_OUT_OF_MEMORY; + } else { + // getting the array from DB with key condition + int rows_count = + dp_db_get_conds_list(DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_VALUE, DP_DB_COL_TYPE_TEXT, + (void **)rows_array, check_rows, -1, NULL, NULL, + "AND", conds_count, conds_p); + if (rows_count <= 0) { + // NO_DATA + ret = DP_ERROR_NO_DATA; + free(rows_array); + } else { + *count = rows_count; + *values = rows_array; + } + } + free(key); + } + } + return ret; +} + +static int __dp_remove_extra_param(int fd, int id) +{ + dp_error_type ret = DP_ERROR_NONE; + char *key = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK] socket"); + return DP_ERROR_IO_ERROR; + } + if (id < 0) { + TRACE_ERROR("[CHECK] ID"); + return DP_ERROR_INVALID_PARAMETER; + } + + if ((key = dp_ipc_read_string(fd)) == NULL) { + TRACE_ERROR("[ERROR][%d] INVALID_PARAMETER", id); + ret = DP_ERROR_IO_ERROR; + } + if (ret == DP_ERROR_NONE) { + if (dp_db_cond_remove(id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_KEY, DP_DB_COL_TYPE_TEXT, key) < 0) { + TRACE_ERROR("[fail][%d][sql]", id); + TRACE_SECURE_ERROR("[fail]key:%s", key); + ret = DP_ERROR_OUT_OF_MEMORY; + } + } + TRACE_DEBUG("[ERROR][%d][%s]", id, dp_print_errorcode(ret)); + TRACE_SECURE_DEBUG("key:%s", key); + free(key); + return ret; +} + +static int __dp_get_http_header_fields(int fd, int id, char ***values, + unsigned *count) +{ + dp_error_type ret = DP_ERROR_NONE; + char **rows_array = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK] socket"); + return DP_ERROR_IO_ERROR; + } + if (id < 0) { + TRACE_ERROR("[CHECK] ID"); + return DP_ERROR_INVALID_PARAMETER; + } + + db_conds_list_fmt conds_p; + conds_p.column = DP_DB_COL_ID; + conds_p.type = DP_DB_COL_TYPE_INT; + conds_p.value = &id; + conds_p.is_like = 0; + int check_rows = dp_db_get_conds_rows_count + (DP_DB_TABLE_HTTP_HEADERS, DP_DB_COL_HEADER_FIELD, "AND", + 1, &conds_p); + if (check_rows <= 0) { + // NO_DATA + ret = DP_ERROR_NO_DATA; + } else { + rows_array = (char **)calloc(check_rows, sizeof(char *)); + if (rows_array == NULL) { + ret = DP_ERROR_OUT_OF_MEMORY; + } else { + int rows_count = + dp_db_get_conds_list(DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, + (void **)rows_array, check_rows, -1, NULL, NULL, + "AND", 1, &conds_p); + if (rows_count <= 0) { + // NO_DATA + ret = DP_ERROR_NO_DATA; + free(rows_array); + } else { + *count = rows_count; + *values = rows_array; + } + } + } + return ret; +} + +static int __dp_set_group_new(int clientfd, dp_group_slots *groups, + dp_credential credential, fd_set *listen_fdset) +{ + // search in groups. + // if same group. update it. + // search same pkg or pid in groups + int pkg_len = 0; + int i = 0; + struct timeval tv_timeo; // 2.5 sec + char *pkgname = NULL; + char *smack_label = NULL; + int ret = 0; + + tv_timeo.tv_sec = 2; + tv_timeo.tv_usec = 500000; + if (setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv_timeo, + sizeof(tv_timeo)) < 0) { + TRACE_STRERROR("[CRITICAL] setsockopt SO_RCVTIMEO"); + return -1; + } + + // getting the package name via pid + if (app_manager_get_package(credential.pid, &pkgname) == + APP_MANAGER_ERROR_NONE) { + TRACE_SECURE_DEBUG("package : %s", pkgname); + } else + TRACE_ERROR("[CRITICAL] app_manager_get_package"); + + //// TEST CODE ... to allow sample client ( no package name ). + if (pkgname == NULL) { + pkgname = dp_strdup("unknown_app"); + TRACE_DEBUG("default package naming : %s", pkgname); + } + + if (pkgname == NULL) { + TRACE_ERROR("[CRITICAL] app_manager_get_package"); + return -1; + } + if ((pkg_len = strlen(pkgname)) <= 0) { + TRACE_ERROR("[CRITICAL] pkgname:%s", pkgname); + free(pkgname); + return -1; + } + + for (i = 0; i < DP_MAX_GROUP; i++) { + if (groups[i].group != NULL) { + // clean garbage slot + if (groups[i].group->cmd_socket <= 0 || + groups[i].group->pkgname == NULL) { + dp_client_group_free(groups[i].group); + groups[i].group = NULL; + continue; + } + if (strlen(groups[i].group->pkgname) == pkg_len && + strncmp(groups[i].group->pkgname, pkgname, + pkg_len) == 0 ) { + // Found Same Group + TRACE_SECURE_INFO("UPDATE Group: slot:%d pid:%d sock:%d [%s]", + i, credential.pid, clientfd, pkgname); + if (groups[i].group->cmd_socket > 0 && + groups[i].group->cmd_socket != clientfd) { + FD_CLR(groups[i].group->cmd_socket, listen_fdset); + dp_socket_free(groups[i].group->cmd_socket); + } + groups[i].group->cmd_socket = clientfd; + free(pkgname); + return 0; + } + } + } + + // new client + // search emtpy slot in groups + for (i = 0; i < DP_MAX_GROUP; i++) + if (groups[i].group == NULL) + break; + if (i >= DP_MAX_GROUP) { + TRACE_ERROR("[CRITICAL] No space in groups"); + free(pkgname); + return -1; + } + // allocation + groups[i].group = + (dp_client_group *)calloc(1, sizeof(dp_client_group)); + if (groups[i].group == NULL) { + TRACE_ERROR("[CRITICAL] calloc, ignore this client"); + free(pkgname); + return -1; + } + + // fill info + groups[i].group->cmd_socket = clientfd; + groups[i].group->event_socket = -1; + groups[i].group->queued_count = 0; + groups[i].group->pkgname = dp_strdup(pkgname); + groups[i].group->credential.pid = credential.pid; + groups[i].group->credential.uid = credential.uid; + groups[i].group->credential.gid = credential.gid; + + int conds_count = 4; + db_conds_list_fmt conds_p[conds_count]; + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + conds_p[0].column = DP_DB_GROUPS_COL_UID; + conds_p[0].type = DP_DB_COL_TYPE_INT; + conds_p[0].value = &credential.uid; + conds_p[1].column = DP_DB_GROUPS_COL_GID; + conds_p[1].type = DP_DB_COL_TYPE_INT; + conds_p[1].value = &credential.gid; + conds_p[2].column = DP_DB_GROUPS_COL_PKG; + conds_p[2].type = DP_DB_COL_TYPE_TEXT; + conds_p[2].value = pkgname; + + if (dp_is_smackfs_mounted() == 1) { + ret = smack_new_label_from_socket(clientfd, &smack_label); + if (ret < 0) { + TRACE_ERROR("[CRITICAL] cannot get smack label"); + free(pkgname); + free(smack_label); + return -1; + } + TRACE_SECURE_INFO("credential label:[%s]", smack_label); + groups[i].group->smack_label = smack_label; + + conds_p[3].column = DP_DB_GROUPS_COL_SMACK_LABEL; + conds_p[3].type = DP_DB_COL_TYPE_TEXT; + conds_p[3].value = smack_label; + } else { + conds_count = 3; // ignore smack label + groups[i].group->smack_label = NULL; + } + + if (dp_db_insert_columns(DP_DB_TABLE_GROUPS, conds_count, conds_p) < 0) { + free(pkgname); + free(smack_label); + if (dp_db_is_full_error() == 0) + TRACE_ERROR("[SQLITE_FULL]"); + return -1; + } + + TRACE_SECURE_INFO("New Group: slot:%d pid:%d sock:%d [%s]", i, + credential.pid, clientfd, pkgname); + free(pkgname); + return 0; +} + + +static int __dp_set_group_event_sock(int clientfd, + dp_group_slots *groups, dp_credential credential) +{ + int i = 0; + + TRACE_DEBUG("Check event pid:%d sock:%d", credential.pid, clientfd); + // search same pid in groups + for (i = 0; i < DP_MAX_GROUP; i++) { + if (groups[i].group != NULL && + groups[i].group->credential.pid == credential.pid) { + if (groups[i].group->event_socket > 0 && + groups[i].group->event_socket != clientfd) + dp_socket_free(groups[i].group->event_socket); + groups[i].group->event_socket = clientfd; + TRACE_SECURE_INFO + ("Found Group : slot:%d pid:%d csock:%d esock:%d [%s]", + i, credential.pid, groups[i].group->cmd_socket, + clientfd, groups[i].group->pkgname); + break; + } + } + if (i >= DP_MAX_GROUP) { + TRACE_ERROR + ("[CRITICAL] Not found group for PID [%d]", credential.pid); + return -1; + } + return 0; +} + +static dp_error_type __dp_do_get_command(int sock, dp_command* cmd, dp_request *request) +{ + unsigned is_checked = 1; + dp_error_type errorcode = DP_ERROR_NONE; + + char *read_str = NULL; + // No read(), write a string + switch(cmd->cmd) { + case DP_CMD_GET_URL: + read_str = dp_request_get_url(cmd->id, &errorcode); + break; + case DP_CMD_GET_DESTINATION: + read_str = dp_request_get_destination(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_FILENAME: + read_str = dp_request_get_filename(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_SAVED_PATH: + read_str = dp_request_get_savedpath(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_TEMP_SAVED_PATH: + read_str = dp_request_get_tmpsavedpath(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_MIME_TYPE: + read_str = dp_request_get_mimetype(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_CONTENT_NAME: + read_str = dp_request_get_contentname(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_ETAG: + read_str = dp_request_get_etag(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_NOTIFICATION_TITLE: + read_str = dp_request_get_title(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_NOTIFICATION_DESCRIPTION: + read_str = dp_request_get_description(cmd->id, request, &errorcode); + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (read_str == NULL || strlen(read_str) < 1) + errorcode = DP_ERROR_NO_DATA; + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_string(sock, read_str); + } else { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + free(read_str); + return errorcode; + } + + // No read(), write a integer variable + int read_int = 0; + errorcode = DP_ERROR_NONE; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_GET_NOTIFICATION: + read_int = dp_db_get_int_column(cmd->id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NOTIFICATION_ENABLE); + break; + case DP_CMD_GET_AUTO_DOWNLOAD: + read_int = dp_db_get_int_column(cmd->id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_AUTO_DOWNLOAD); + break; + case DP_CMD_GET_NETWORK_TYPE: + read_int = dp_db_get_int_column(cmd->id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NETWORK_TYPE); + break; + case DP_CMD_GET_HTTP_STATUS: + read_int = dp_db_get_int_column(cmd->id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_HTTP_STATUS); + break; + case DP_CMD_GET_STATE: + if (request == NULL) { + read_int = dp_db_get_state(cmd->id); + } else { + read_int = request->state; + } + break; + case DP_CMD_GET_NOTIFICATION_TYPE: + TRACE_DEBUG("DP_CMD_GET_NOTIFICATION_TYPE"); + read_int = dp_request_get_noti_type(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_ERROR: + if (request == NULL) { + read_int = dp_db_get_int_column(cmd->id, + DP_DB_TABLE_LOG, DP_DB_COL_ERRORCODE); + } else { + read_int = request->error; + } + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (read_int < 0) + errorcode = DP_ERROR_NO_DATA; + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_custom_type(sock, &read_int, sizeof(int)); + } else { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + return errorcode; + } + + // No read(), write a long long variable + unsigned long long recv_long = 0; + errorcode = DP_ERROR_NONE; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_GET_RECEIVED_SIZE: + if (request == NULL) + errorcode = DP_ERROR_NO_DATA; + else + recv_long = request->received_size; + break; + case DP_CMD_GET_TOTAL_FILE_SIZE: + if (request != NULL) { + recv_long = request->file_size; + } else { + long long file_size = + dp_db_get_int64_column(cmd->id, + DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_SIZE); + if (file_size < 0) + errorcode = DP_ERROR_NO_DATA; + else // casting + recv_long = file_size; + } + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_custom_type(sock, &recv_long, + sizeof(unsigned long long)); + } else { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + return errorcode; + } + + // No read(), write a bundle variable + bundle_raw *b_raw = NULL; + int length = -1; + char *column = NULL; + errorcode = DP_ERROR_NONE; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_GET_NOTIFICATION_BUNDLE: + TRACE_DEBUG("DP_CMD_GET_NOTIFICATION_BUNDLE"); + dp_ipc_send_errorcode(sock, DP_ERROR_NONE); + if ((dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0)) { + TRACE_ERROR("DP_CMD_GET_NOTIFICATION_BUNDLE read fail"); + errorcode = DP_ERROR_IO_ERROR; + break; + } + switch(read_int) { + case DP_NOTIFICATION_BUNDLE_TYPE_ONGOING: + column = DP_DB_COL_RAW_BUNDLE_ONGOING; + break; + case DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE: + column = DP_DB_COL_RAW_BUNDLE_COMPLETE; + break; + case DP_NOTIFICATION_BUNDLE_TYPE_FAILED: + column = DP_DB_COL_RAW_BUNDLE_FAIL; + break; + default: + TRACE_ERROR("[CHECK TYPE][%d]", read_int); + errorcode = DP_ERROR_INVALID_PARAMETER; + break; + } + b_raw = dp_request_get_bundle(cmd->id, request, + &errorcode, column, &length); + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_bundle(sock, b_raw, length); + } else { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + bundle_free_encoded_rawdata(&b_raw); + return errorcode; + } + + dp_ipc_send_errorcode(sock, DP_ERROR_NONE); + // complex read() and write(). + char *read_str2 = NULL; + errorcode = DP_ERROR_NONE; + read_str = NULL; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_GET_HTTP_HEADER: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + read_str2 = dp_db_cond_get_text_column(cmd->id, + DP_DB_TABLE_HTTP_HEADERS, DP_DB_COL_HEADER_DATA, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, read_str); + if (read_str2 == NULL) + errorcode = DP_ERROR_NO_DATA; + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) + dp_ipc_send_string(sock, read_str2); + break; + case DP_CMD_GET_HTTP_HEADER_LIST: + { + char **values = NULL; + unsigned rows_count = 0; + errorcode = __dp_get_http_header_fields(sock, + cmd->id, &values, &rows_count); + if (errorcode == DP_ERROR_NONE) { + __send_return_custom_type(sock, DP_ERROR_NONE, + &rows_count, sizeof(int)); + // sending strings + int i = 0; + for (i = 0; i < rows_count; i++) { + if (dp_ipc_send_string(sock, values[i]) < 0) + break; + } + for (i = 0; i < rows_count; i++) + free(values[i]); + } else { + if (errorcode != DP_ERROR_IO_ERROR) + dp_ipc_send_errorcode(sock, errorcode); + } + free(values); + break; + } + case DP_CMD_GET_EXTRA_PARAM: + { + char **values = NULL; + unsigned rows_count = 0; + errorcode = __dp_get_extra_param_values(sock, + cmd->id, &values, &rows_count); + if (errorcode == DP_ERROR_NONE) { + __send_return_custom_type(sock, DP_ERROR_NONE, + &rows_count, sizeof(int)); + // sending strings + int i = 0; + for (i = 0; i < rows_count; i++) { + if (dp_ipc_send_string(sock, values[i]) < 0) + break; + } + for (i = 0; i < rows_count; i++) + free(values[i]); + } else { + if (errorcode != DP_ERROR_IO_ERROR) + dp_ipc_send_errorcode(sock, errorcode); + } + free(values); + break; + } + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (errorcode != DP_ERROR_NONE) { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + free(read_str); + free(read_str2); + return errorcode; + } + return DP_ERROR_UNKNOWN; +} + +static dp_error_type __dp_do_set_command(int sock, dp_command *cmd, dp_request *request) +{ + unsigned is_checked = 1; + int read_int = 0; + dp_error_type errorcode = DP_ERROR_NONE; + char *read_str = NULL; + bundle_raw *b_raw = NULL; + unsigned bundle_length = 0; + int noti_bundle_type = 0; + + dp_ipc_send_errorcode(sock, DP_ERROR_NONE); + // read a interger or a string, return errorcode. + errorcode = DP_ERROR_NONE; + switch(cmd->cmd) { + case DP_CMD_SET_STATE_CALLBACK: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_state_event(cmd->id, request, read_int); + break; + case DP_CMD_SET_PROGRESS_CALLBACK: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_progress_event(cmd->id, request, read_int); + break; + case DP_CMD_SET_NETWORK_TYPE: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if (read_int == DP_NETWORK_TYPE_ALL || + read_int == DP_NETWORK_TYPE_WIFI || + read_int == DP_NETWORK_TYPE_DATA_NETWORK || + read_int == DP_NETWORK_TYPE_ETHERNET || + read_int == DP_NETWORK_TYPE_WIFI_DIRECT) + errorcode = dp_request_set_network_type(cmd->id, request, read_int); + else + errorcode = DP_ERROR_INVALID_PARAMETER; + break; + case DP_CMD_SET_AUTO_DOWNLOAD: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_auto_download(cmd->id, request, read_int); + break; + case DP_CMD_SET_NOTIFICATION: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_notification(cmd->id, request, read_int); + break; + case DP_CMD_SET_URL: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_url(cmd->id, request, read_str); + break; + case DP_CMD_SET_DESTINATION: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if (dp_is_smackfs_mounted() == 1 && + __dp_check_valid_directory(request, read_str) != 0){ + errorcode = DP_ERROR_PERMISSION_DENIED; + break; + } + errorcode = dp_request_set_destination(cmd->id, request, read_str); + break; + case DP_CMD_SET_FILENAME: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_filename(cmd->id, request, read_str); + break; + case DP_CMD_SET_NOTIFICATION_BUNDLE: + bundle_length = dp_ipc_read_bundle(sock, ¬i_bundle_type, &b_raw); + if (bundle_length == 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } else if (bundle_length == -1) { + errorcode = DP_ERROR_INVALID_PARAMETER; + break; + } + errorcode = dp_request_set_bundle(cmd->id, request, + noti_bundle_type, b_raw, bundle_length); + break; + case DP_CMD_SET_NOTIFICATION_TITLE: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_title(cmd->id, request, read_str); + break; + case DP_CMD_SET_NOTIFICATION_DESCRIPTION: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_description(cmd->id, request, read_str); + break; + case DP_CMD_SET_NOTIFICATION_TYPE: + if ((dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0)) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if (read_int == DP_NOTIFICATION_TYPE_NONE || + read_int == DP_NOTIFICATION_TYPE_COMPLETE_ONLY || + read_int == DP_NOTIFICATION_TYPE_ALL) + errorcode = dp_request_set_noti_type(cmd->id, request, read_int); + else + errorcode = DP_ERROR_INVALID_PARAMETER; + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + free(read_str); + bundle_free_encoded_rawdata(&b_raw); + if (errorcode != DP_ERROR_NONE) { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + if (errorcode == DP_ERROR_IO_ERROR) + return errorcode; + dp_ipc_send_errorcode(sock, errorcode); + return errorcode; + } + + // complex read() and write(). + char *read_str2 = NULL; + errorcode = DP_ERROR_NONE; + read_str = NULL; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_SET_HTTP_HEADER: + { + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if ((read_str2 = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + int conds_count = 3; // id + field + data + db_conds_list_fmt conds_p[conds_count]; + memset(&conds_p, 0x00, + conds_count * sizeof(db_conds_list_fmt)); + conds_p[0].column = DP_DB_COL_ID; + conds_p[0].type = DP_DB_COL_TYPE_INT; + conds_p[0].value = &cmd->id; + conds_p[1].column = DP_DB_COL_HEADER_FIELD; + conds_p[1].type = DP_DB_COL_TYPE_TEXT; + conds_p[1].value = read_str; + conds_p[2].column = DP_DB_COL_HEADER_DATA; + conds_p[2].type = DP_DB_COL_TYPE_TEXT; + conds_p[2].value = read_str2; + if (dp_db_get_conds_rows_count(DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_ID, "AND", 2, conds_p) <= 0) { // insert + if (dp_db_insert_columns(DP_DB_TABLE_HTTP_HEADERS, + conds_count, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", cmd->id); + errorcode = DP_ERROR_NO_SPACE; + } else { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + } + } else { // update data by field + if (dp_db_cond_set_column(cmd->id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_DATA, DP_DB_COL_TYPE_TEXT, read_str2, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, read_str) < 0) { + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", cmd->id); + errorcode = DP_ERROR_NO_SPACE; + } else { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + } + } + dp_ipc_send_errorcode(sock, errorcode); + break; + } + case DP_CMD_DEL_HTTP_HEADER: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if (dp_db_get_cond_rows_count(cmd->id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, + read_str) <= 0) { + errorcode = DP_ERROR_NO_DATA; + } else { + if (dp_db_cond_remove(cmd->id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, read_str) < 0) + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + dp_ipc_send_errorcode(sock, errorcode); + break; + case DP_CMD_ADD_EXTRA_PARAM: + errorcode = __dp_add_extra_param(sock, cmd->id); + if (errorcode != DP_ERROR_IO_ERROR) + dp_ipc_send_errorcode(sock, errorcode); + break; + case DP_CMD_REMOVE_EXTRA_PARAM: + errorcode = __dp_remove_extra_param(sock, cmd->id); + if (errorcode != DP_ERROR_IO_ERROR) + dp_ipc_send_errorcode(sock, errorcode); + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (errorcode != DP_ERROR_NONE) { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + free(read_str); + free(read_str2); + return errorcode; + } + return DP_ERROR_UNKNOWN; +} + +static dp_error_type __dp_do_action_command(int sock, dp_command* cmd, dp_request *request) +{ + unsigned is_checked = 1; + int read_int = 0; + dp_error_type errorcode = DP_ERROR_NONE; + + read_int = 0; + errorcode = DP_ERROR_NONE; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_DESTROY: + if (request != NULL) {// just update the state + if (__is_started(request->state) == 0) { + read_int = DP_STATE_CANCELED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &read_int) < 0) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + if (__dp_call_cancel_agent(request) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", cmd->id); + request->state = DP_STATE_CANCELED; + if (request->auto_notification == 1 && + request->packagename != NULL) { + request->noti_priv_id = dp_set_downloadedinfo_notification + (request->noti_priv_id, request->id, + request->packagename, request->state); + } else { + int noti_type = dp_db_get_int_column(request->id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if (noti_type > DP_NOTIFICATION_TYPE_NONE && + request->packagename != NULL) + request->noti_priv_id = dp_set_downloadedinfo_notification + (request->noti_priv_id, request->id, + request->packagename, request->state); + } + } + } + } + break; + case DP_CMD_PAUSE: + { + // to check fastly, divide the case by request value + if (request == NULL) { + dp_state_type state = dp_db_get_state(cmd->id); + // already paused or stopped + if (state > DP_STATE_DOWNLOADING) { + errorcode = DP_ERROR_INVALID_STATE; + } else { + // change state to paused. + state = DP_STATE_PAUSED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, &state) < 0) + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + break; + } + + if (request->state > DP_STATE_DOWNLOADING) { + errorcode = DP_ERROR_INVALID_STATE; + break; + } + + // before downloading including QUEUED + if (__is_downloading(request->state) < 0) { + dp_state_type state = DP_STATE_PAUSED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &state) < 0) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + request->state = state; + } + break; + } + // only downloading. + dp_state_type state = DP_STATE_PAUSE_REQUESTED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, &state) < 0) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + TRACE_INFO("[%s][%d]pause_agent(%d) state:%s", + __print_command(cmd->cmd), cmd->id, + request->agent_id, + dp_print_state(request->state)); + if (dp_pause_agent_download + (request->agent_id) < 0) { + TRACE_ERROR("[fail][%d]pause_agent(%d)", + cmd->id, request->agent_id); + } + request->state = state; + request->error = DP_ERROR_NONE; + request->pause_time = (int)time(NULL); + dp_db_update_date(cmd->id, DP_DB_TABLE_LOG, + DP_DB_COL_ACCESS_TIME); + } + break; + } + case DP_CMD_CANCEL: + { + // to check fastly, divide the case by request value + if (request == NULL) { + dp_state_type state = dp_db_get_state(cmd->id); + // already paused or stopped + if (__is_stopped(state) == 0) { + errorcode = DP_ERROR_INVALID_STATE; + } else { + // change state to canceled. + state = DP_STATE_CANCELED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, &state) < 0) + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + break; + } + + if (__is_stopped(request->state) == 0) { + TRACE_ERROR("[%s][%d]__is_stopped agentid:%d state:%s", + __print_command(cmd->cmd), cmd->id, + request->agent_id, dp_print_state(request->state)); + errorcode = DP_ERROR_INVALID_STATE; + } else { + // change state to canceled. + dp_state_type state = DP_STATE_CANCELED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &state) < 0) + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + if (errorcode == DP_ERROR_NONE) { + if (__dp_call_cancel_agent(request) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", cmd->id); + request->state = DP_STATE_CANCELED; + } + dp_db_update_date(cmd->id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME); + break; + } + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (errorcode != DP_ERROR_NONE) { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + dp_ipc_send_errorcode(sock, errorcode); + return errorcode; + } + return DP_ERROR_UNKNOWN; +} + +static dp_error_type __do_dp_start_command(int sock, int id, dp_privates *privates, + dp_client_group *group, dp_request *request) +{ + dp_error_type errorcode = DP_ERROR_NONE; + + // need URL at least + char *url = dp_request_get_url(id, &errorcode); + free(url); + if (errorcode == DP_ERROR_NO_DATA) { + errorcode = DP_ERROR_INVALID_URL; + dp_ipc_send_errorcode(sock, errorcode); + return errorcode; + } + + if (request == NULL) { // Support Re-download + // Support Re-download + int index = __get_empty_request_index(privates->requests); + if (index < 0) { // Busy, No Space in slot + errorcode = DP_ERROR_QUEUE_FULL; + TRACE_SECURE_ERROR("[ERROR][%d][RESTORE][%s]", id, + dp_print_errorcode(errorcode)); + } else { + CLIENT_MUTEX_LOCK(&privates->requests[index].mutex); + dp_request *tmp_request = + dp_request_load_from_log (id, &errorcode); + if (tmp_request != NULL) { + // restore callback info + tmp_request->state_cb = dp_db_get_int_column(id, + DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_STATE_EVENT); + tmp_request->progress_cb = dp_db_get_int_column(id, + DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_PROGRESS_EVENT); + tmp_request->auto_notification = + dp_db_get_int_column(id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NOTIFICATION_ENABLE); + if (group != NULL) // for send event to client + tmp_request->group = group; + privates->requests[index].request = tmp_request; + request = privates->requests[index].request; + } else { + TRACE_SECURE_ERROR("[ERROR][%d][RESTORE][%s]", id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode(sock, errorcode); + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + return errorcode; + } + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + } + } + + // check status + if (errorcode == DP_ERROR_NONE && + (__is_started(request->state) == 0 || + request->state == DP_STATE_COMPLETED)) + errorcode = DP_ERROR_INVALID_STATE; + + if (errorcode == DP_ERROR_NONE) { + + dp_state_type state = DP_STATE_QUEUED; + if (dp_db_set_column(id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, &state) < 0) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + if (group != NULL) + group->queued_count++; + request->start_time = (int)time(NULL); + request->pause_time = 0; + request->stop_time = 0; + request->state = state; + request->error = DP_ERROR_NONE; + dp_db_update_date(id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME); + } + + } + + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode != DP_ERROR_NONE) { + TRACE_SECURE_ERROR("[ERROR][%d][START][%s]", id, + dp_print_errorcode(errorcode)); + } + return errorcode; +} + +// in url-download, make 3 connection before send CREATE command. +// after accepting, fill info to pacakgelist. +// 3 socket per 1 package ( info/request/progress ) +void *dp_thread_requests_manager(void *arg) +{ + fd_set rset, eset, listen_fdset, except_fdset; + struct timeval timeout; // for timeout of select + long flexible_timeout = DP_CARE_CLIENT_MAX_INTERVAL; + int listenfd, clientfd, maxfd; + socklen_t clientlen; + struct sockaddr_un clientaddr; + dp_credential credential; + unsigned i, is_timeout; + int prev_timeout = 0; + dp_error_type errorcode = DP_ERROR_NONE; + + dp_privates *privates = (dp_privates*)arg; + if (privates == NULL || privates->groups == NULL) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; + } + + listenfd = privates->listen_fd; + maxfd = listenfd; + + TRACE_DEBUG("Ready to listen [%d][%s]", listenfd, DP_IPC); + + FD_ZERO(&listen_fdset); + FD_ZERO(&except_fdset); + FD_SET(listenfd, &listen_fdset); + FD_SET(listenfd, &except_fdset); + + while (privates != NULL && privates->listen_fd >= 0) { + + // select with timeout + // initialize timeout structure for calling timeout exactly + memset(&timeout, 0x00, sizeof(struct timeval)); + timeout.tv_sec = flexible_timeout; + clientfd = -1; + credential.pid = -1; + credential.uid = -1; + credential.gid = -1; + is_timeout = 1; + + rset = listen_fdset; + eset = except_fdset; + + if (select((maxfd + 1), &rset, 0, &eset, &timeout) < 0) { + TRACE_STRERROR("[CRITICAL] select"); + break; + } + + if (privates == NULL || privates->listen_fd < 0) { + TRACE_DEBUG("Terminate Thread"); + break; + } + + if (FD_ISSET(listenfd, &eset) > 0) { + TRACE_ERROR("[CRITICAL] listenfd Exception of socket"); + break; + } else if (FD_ISSET(listenfd, &rset) > 0) { + // new client(package) called url_download_create API. + // update g_dp_request_max_fd & g_dp_info_max_fd + // add to socket to g_dp_request_rset & g_dp_info_rset + + is_timeout = 0; + + // Anyway accept client. + clientlen = sizeof(clientaddr); + clientfd = accept(listenfd, + (struct sockaddr*)&clientaddr, + &clientlen); + if (clientfd < 0) { + TRACE_ERROR("[CRITICAL] accept provider was crashed ?"); + // provider need the time of refresh. + break; + } + + dp_command_type connect_cmd = DP_CMD_NONE; + if (dp_ipc_read_custom_type(clientfd, + &connect_cmd, sizeof(dp_command_type)) < 0) { + TRACE_ERROR("[CRITICAL] CAPI not support CONNECT CMD"); + close(clientfd); + continue; + } + if (connect_cmd <= 0) { + TRACE_ERROR("[CRITICAL] peer terminate ?"); + close(clientfd); + continue; + } + +#ifdef SO_PEERCRED + // getting the info of client + socklen_t cr_len = sizeof(credential); + if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED, + &credential, &cr_len) == 0) { + TRACE_DEBUG + ("credential : pid=%d, uid=%d, gid=%d", + credential.pid, credential.uid, credential.gid); + } +#else // In case of not supported SO_PEERCRED + int client_pid = 0; + if (dp_ipc_read_custom_type(clientfd, + &client_pid, sizeof(int)) < 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } + if (client_pid <= 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } + credential.pid = client_pid; + if (dp_ipc_read_custom_type(clientfd, + &credential.uid, sizeof(int)) < 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } + if (dp_ipc_read_custom_type(clientfd, + &credential.gid, sizeof(int)) < 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } +#endif + + switch(connect_cmd) { + case DP_CMD_SET_COMMAND_SOCKET: + if (__dp_set_group_new(clientfd, privates->groups, + credential, &listen_fdset) == 0) { + FD_SET(clientfd, &listen_fdset); + if (clientfd > maxfd) + maxfd = clientfd; + } else { + close(clientfd); + } + break; + case DP_CMD_SET_EVENT_SOCKET: + if (__dp_set_group_event_sock(clientfd, + privates->groups, credential) < 0) + close(clientfd); + break; + default: + TRACE_ERROR("[CRITICAL] Bad access,ignore this client"); + close(clientfd); + break; + } + } // New Connection + + // listen cmd_socket of all group + for (i = 0; i < DP_MAX_GROUP; i++) { + dp_client_group *group = privates->groups[i].group; + if (group == NULL || group->cmd_socket < 0) + continue; + const int sock = group->cmd_socket; + int time_of_job = (int)time(NULL); + if (FD_ISSET(sock, &rset) > 0) { + int index = -1; + dp_command command; + + is_timeout = 0; + command.cmd = DP_CMD_NONE; + command.id = -1; + + if (dp_ipc_read_custom_type(sock, &command, + sizeof(dp_command)) < 0) { + TRACE_STRERROR("failed to read cmd"); + //Resource temporarily unavailable + continue; + } + + if (command.cmd == 0 || command.cmd >= DP_CMD_LAST_SECT) { // Client meet some Error. + TRACE_SECURE_INFO("[Closed Peer] pkg:%s sock:%d slot:%d", + group->pkgname, sock, i); + // check all request included to this group + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + continue; + } + + // Echo .client can check whether provider is busy + if (command.cmd == DP_CMD_ECHO) { + // provider can clear read buffer here + TRACE_DEBUG("[ECHO] sock:%d", sock); + if (dp_ipc_send_errorcode(sock, + DP_ERROR_NONE) < 0) { + // disconnect this group, bad client + TRACE_ERROR("[ECHO] IO ERROR CLEAN sock:%d", sock); + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + } + continue; + } + + if (command.cmd == DP_CMD_CREATE) { + TRACE_SECURE_INFO("[CREATE] id:%d sock:%d pid:%d gindex:%d pkg:%s", + command.id, sock, + group->credential.pid, i, group->pkgname); + // search empty slot in privates->requests + index = + __get_empty_request_index(privates->requests); + if (index < 0) { + TRACE_ERROR("[CHECK] [DP_ERROR_QUEUE_FULL]"); + // Busy, No Space in slot + dp_ipc_send_errorcode(sock, DP_ERROR_QUEUE_FULL); + } else { + errorcode = DP_ERROR_NONE; + CLIENT_MUTEX_LOCK(&privates->requests[index].mutex); + errorcode = dp_request_create(command.id, group, + &privates->requests[index].request); + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_custom_type(sock, + &privates->requests[index].request->id, + sizeof(int)); + TRACE_DEBUG("[CREATE] GOOD id:%d slot:%d time:%d", + privates->requests[index].request->id, + index, ((int)time(NULL) - time_of_job)); + } else { + TRACE_ERROR("[ERROR][%s]", + dp_print_errorcode(errorcode)); + } + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + } + continue; + } + + // below commands must need ID + // check exception before searching slots. + if (command.id < 0) { + TRACE_ERROR("[CHECK PROTOCOL] ID should not be -1"); + dp_ipc_send_errorcode(sock, + DP_ERROR_INVALID_PARAMETER); + // disconnect this group, bad client + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + continue; + } + + // search id in requests slot + index = __get_same_request_index(privates->requests, + command.id); + + dp_request_slots *request_slot = NULL; + unsigned is_loaded = 0; + errorcode = DP_ERROR_NONE; + + if (index >= 0) { + CLIENT_MUTEX_LOCK(&privates->requests[index].mutex); + if (privates->requests[index].request != NULL) + is_loaded = 1; + else + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + } + + errorcode = DP_ERROR_UNKNOWN; // check matched command + + // divide on memory or from DB + if (is_loaded == 1 && index >= 0) { // already locked + + TRACE_SECURE_INFO("[%s] id:%d sock:%d pid:%d gindex:%d slot:%d pkg:%s", + __print_command(command.cmd), command.id, sock, + group->credential.pid, i, index, group->pkgname); + + request_slot = &privates->requests[index]; + + // auth by pkgname + if (__cmp_string(group->pkgname, request_slot->request->packagename) < 0) { + TRACE_SECURE_ERROR("[ERROR][%d] Auth [%s]/[%s]", + command.id, group->pkgname, request_slot->request->packagename); + TRACE_ERROR("[ERROR][%d][INVALID_PARAMETER]", command.id); + dp_ipc_send_errorcode(sock, DP_ERROR_INVALID_PARAMETER); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + continue; + } + + // if no group, update group. + if (request_slot->request->group == NULL) + request_slot->request->group = group; + + if (command.cmd == DP_CMD_START) + errorcode = __do_dp_start_command(sock, command.id, privates, group, request_slot->request); + else if (command.cmd > DP_CMD_SET_SECT && command.cmd < DP_CMD_LAST_SECT) + errorcode = __dp_do_set_command(sock, &command, request_slot->request); + else if (command.cmd > DP_CMD_GET_SECT && command.cmd < DP_CMD_SET_SECT) + errorcode = __dp_do_get_command(sock, &command, request_slot->request); + else if (command.cmd > DP_CMD_ACTION_SECT && command.cmd < DP_CMD_GET_SECT) + errorcode = __dp_do_action_command(sock, &command, request_slot->request); + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + + if (command.cmd == DP_CMD_START && errorcode == DP_ERROR_NONE) { + //send signal to queue thread + dp_thread_queue_manager_wake_up(); + } + if (command.cmd == DP_CMD_FREE) {// enter after unlock + dp_request_slot_free(request_slot); + errorcode = DP_ERROR_NONE; + } + + } else { // not found on the slots + + TRACE_SECURE_INFO("[%s] id:%d sock:%d pid:%d gindex:%d slot:no pkg:%s", + __print_command(command.cmd), command.id, sock, + group->credential.pid, i, group->pkgname); + + int check_id = dp_db_get_int_column(command.id, + DP_DB_TABLE_LOG, DP_DB_COL_ID); + if (check_id < 0 || check_id != command.id) { + errorcode = DP_ERROR_ID_NOT_FOUND; + TRACE_ERROR("[ERROR][%d][ID_NOT_FOUND]", command.id); + dp_ipc_send_errorcode(sock, DP_ERROR_ID_NOT_FOUND); + continue; + } + // auth by pkgname + char *auth_pkgname = dp_db_get_text_column(command.id, + DP_DB_TABLE_LOG, DP_DB_COL_PACKAGENAME); + if (auth_pkgname == NULL) { + TRACE_ERROR("[ERROR][%d][ID_NOT_FOUND]", command.id); + dp_ipc_send_errorcode(sock, DP_ERROR_ID_NOT_FOUND); + continue; + } + // auth by pkgname + if (__cmp_string(group->pkgname, auth_pkgname) < 0) { + TRACE_SECURE_ERROR("[ERROR][%d] Auth [%s]/[%s]", + command.id, group->pkgname, auth_pkgname); + TRACE_ERROR("[ERROR][%d][INVALID_PARAMETER]", command.id); + dp_ipc_send_errorcode(sock, DP_ERROR_INVALID_PARAMETER); + free(auth_pkgname); + continue; + } + free(auth_pkgname); + + if (command.cmd == DP_CMD_START) + errorcode = __do_dp_start_command(sock, command.id, privates, group, NULL); + else if (command.cmd == DP_CMD_FREE) + errorcode = DP_ERROR_NONE; + else if (command.cmd > DP_CMD_SET_SECT && command.cmd < DP_CMD_LAST_SECT) + errorcode = __dp_do_set_command(sock, &command, NULL); + else if (command.cmd > DP_CMD_GET_SECT && command.cmd < DP_CMD_SET_SECT) + errorcode = __dp_do_get_command(sock, &command, NULL); + else if (command.cmd > DP_CMD_ACTION_SECT && command.cmd < DP_CMD_GET_SECT) + errorcode = __dp_do_action_command(sock, &command, NULL); + + if (command.cmd == DP_CMD_START && errorcode == DP_ERROR_NONE) { + //send signal to queue thread + dp_thread_queue_manager_wake_up(); + } + } + + if (errorcode == DP_ERROR_IO_ERROR) { + TRACE_ERROR("[IO_ERROR][%d] slot:%d pid:%d", + command.id, i, + privates->groups[i].group->credential.pid); + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + continue; + } + if (errorcode == DP_ERROR_UNKNOWN) { // UnKnown Command + TRACE_INFO("[UNKNOWN][%d] slot:%d pid:%d", + command.id, i, + privates->groups[i].group->credential.pid); + dp_ipc_send_errorcode(sock, DP_ERROR_IO_ERROR); + // disconnect this group, bad client + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + } + + } // FD_ISSET + } // DP_MAX_GROUP + + // timeout + if (is_timeout == 1) { + int now_timeout = (int)time(NULL); + TRACE_DEBUG("[TIMEOUT] prev %ld, now %ld, setted %ld sec", + prev_timeout, now_timeout, flexible_timeout); + if (prev_timeout == 0) { + prev_timeout = now_timeout; + } else if (now_timeout < prev_timeout || + (now_timeout - prev_timeout) > flexible_timeout) { + TRACE_ERROR("[WARN] check system date prev[%ld]now[%ld]", + prev_timeout, now_timeout); + } else { + if ((now_timeout - prev_timeout) < + DP_CARE_CLIENT_MIN_INTERVAL) { + // this is error. + // terminate Process + TRACE_STRERROR + ("[CRITICAL] Sock exception prev[%ld]now[%ld][%ld]", + prev_timeout, now_timeout, flexible_timeout); + break; + } + } + + // get 48hour old request from log DB + // clear old request + dp_db_limit_rows(DP_LOG_DB_LIMIT_ROWS); + int old_request_count = dp_db_get_count_by_limit_time(); + if (old_request_count > 0) { + if (old_request_count > DP_LOG_DB_CLEAR_LIMIT_ONE_TIME) + old_request_count = DP_LOG_DB_CLEAR_LIMIT_ONE_TIME; + + TRACE_DEBUG + ("[CLEAR] [%d] old reqeusts", old_request_count); + + dp_request_slots *old_requests = + dp_request_slots_new(old_request_count); + if (old_requests != NULL) { + int list_count = dp_db_get_list_by_limit_time + (old_requests, old_request_count); + for (i = 0; i < list_count; i++) { + // search on slots by ID. + int index = __get_same_request_index + (privates->requests, + old_requests[i].request->id); + if (index >= 0) { + CLIENT_MUTEX_LOCK(&privates->requests[index].mutex); + dp_request *request = + privates->requests[index].request; + // if downloading..remain it. + if (request == NULL || __is_downloading(request->state) == 0) { + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + continue; + } + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + TRACE_DEBUG("[CLEAR][%d] 48 hour state:%s", + request->id, + dp_print_state(request->state)); + // unload from slots ( memory ) + dp_request_slot_free(&privates->requests[index]); + } + // remove tmp file regardless + __dp_remove_tmpfile + (old_requests[i].request->id, + old_requests[i].request); + // remove from DB + dp_db_remove_all(old_requests[i].request->id); + } + dp_request_slots_free(old_requests, old_request_count); + } + } + + // clean slots + int ready_requests = 0; + for (i = 0; i < DP_MAX_REQUEST; i++) { + CLIENT_MUTEX_LOCK(&privates->requests[i].mutex); + if (privates->requests[i].request == NULL) { + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + continue; + } + dp_request *request = privates->requests[i].request; + + // If downloading is too slow ? how to deal this request? + // can limit too slot download using starttime.(48 hours) + if (__is_downloading(request->state) == 0) { + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + continue; + } + + // paused & agent_id not exist.... unload from memory. + if (request->state == DP_STATE_PAUSED && + dp_is_alive_download(request->agent_id) == 0) { + TRACE_DEBUG("[FREE][%d] dead agent-id(%d) state:%s", + request->id, request->agent_id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + dp_request_slot_free(&privates->requests[i]); + continue; + } + + // client should call START command in 60 sec + // unload from memory + if (request->start_time <= 0 && + (now_timeout - request->create_time) > + DP_CARE_CLIENT_MAX_INTERVAL) { + int download_id = request->id; + dp_state_type state = DP_STATE_FAILED; + errorcode = DP_ERROR_RESPONSE_TIMEOUT; + TRACE_DEBUG + ("[FREE][%d] start in %d sec state:%s last:%ld", + download_id, DP_CARE_CLIENT_MAX_INTERVAL, + dp_print_state(request->state), + request->start_time); + if (request->group != NULL && + request->state_cb == 1 && + request->group->event_socket >= 0) + dp_ipc_send_event(request->group->event_socket, + download_id, state, errorcode, 0); + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + dp_request_slot_free(&privates->requests[i]); + // no problem although updating is failed. + if (dp_db_request_update_status(download_id, state, errorcode) < 0) { + TRACE_ERROR("[fail][%d][sql] set state:%s error:%s", + download_id, dp_print_state(state), dp_print_errorcode(errorcode)); + } + continue; + } + + // stopped by ipchanged. decide auto resume + if (request->stop_time <= 0 && + request->state == DP_STATE_FAILED && + request->error == DP_ERROR_CONNECTION_FAILED) { + if (dp_get_network_connection_instant_status() != + DP_NETWORK_TYPE_OFF && + request->ip_changed == 1) { + TRACE_DEBUG("[RESUME][%d] will be queued", + request->id); + request->state = DP_STATE_QUEUED; + request->error = DP_ERROR_NONE; + ready_requests++; + } else { + dp_request_state_response(request); + } + } + + // client should call DESTROY command in MAX_INTERVAL sec after finished + if (request->stop_time > 0 && + (now_timeout - request->stop_time) > + DP_CARE_CLIENT_MAX_INTERVAL) { + // check state again. stop_time means it's stopped + if (__is_stopped(request->state) == 0) { + TRACE_DEBUG + ("[FREE][%d] by timeout state:%s stop:%ld", + request->id, + dp_print_state(request->state), + request->stop_time); + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + dp_request_slot_free(&privates->requests[i]); + continue; + } + } + + // check after clear + if (request->state == DP_STATE_QUEUED) + ready_requests++; + + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + } + + if (ready_requests > 0) { + //send signal to queue thread will check queue. + dp_thread_queue_manager_wake_up(); + } else { + // if no request & timeout is bigger than 60 sec + // terminate by self. + if ((now_timeout - prev_timeout) >= flexible_timeout && + dp_get_request_count(privates->requests) <= 0) { + TRACE_DEBUG("No Request. Terminate Daemon"); + break; + } + } + prev_timeout = now_timeout; + } else { + prev_timeout = 0; + } + } + TRACE_DEBUG("terminate main thread ..."); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; +} diff --git a/provider/include/download-provider-config.h b/provider/include/download-provider-config.h new file mode 100755 index 0000000..96862ec --- /dev/null +++ b/provider/include/download-provider-config.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_CONFIG_H +#define DOWNLOAD_PROVIDER2_CONFIG_H + +#include <download-provider.h> +#include <download-provider-slots.h> + +#include <net_connection.h> + +#define DP_LOCK_PID "/tmp/download-provider.lock" + +#define DP_CARE_CLIENT_MIN_INTERVAL 5 +#define DP_CARE_CLIENT_MAX_INTERVAL 120 + +// check this value should be lower than DP_MAX_REQUEST +#define DP_MAX_DOWNLOAD_AT_ONCE 50 + +#define DP_LOG_DB_LIMIT_ROWS 1000 +#define DP_LOG_DB_CLEAR_LIMIT_ONE_TIME 100 + +// Share the structure for all threads +typedef struct { + int listen_fd; + int is_connected_wifi_direct; + connection_h connection; + dp_network_type network_status; + dp_group_slots *groups; + dp_request_slots *requests; +} dp_privates; + +#endif diff --git a/provider/include/download-provider-da-interface.h b/provider/include/download-provider-da-interface.h new file mode 100755 index 0000000..ab6e716 --- /dev/null +++ b/provider/include/download-provider-da-interface.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_DA_INTERFACE_H +#define DOWNLOAD_PROVIDER2_DA_INTERFACE_H + +int dp_is_file_exist(const char *file_path); +int dp_init_agent(); +void dp_deinit_agent(); +dp_error_type dp_start_agent_download(dp_request_slots *request); +dp_error_type dp_resume_agent_download(int req_id); +dp_error_type dp_pause_agent_download(int req_id); +dp_error_type dp_cancel_agent_download(int req_id); +int dp_is_alive_download(int req_id); + +#endif diff --git a/provider/include/download-provider-db.h b/provider/include/download-provider-db.h new file mode 100755 index 0000000..24a7d27 --- /dev/null +++ b/provider/include/download-provider-db.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_DB_H +#define DOWNLOAD_PROVIDER2_DB_H + +#include "download-provider-config.h" +#include "download-provider-slots.h" + +/* + * Memory ( sync with logging ) : id, state, errorcode, startcount, packagename + * DB TABLES + * logging : id, state, errorcode, startcount, createtime, accesstime, packagename + * requestinfo : id, auto_download, network_type, filename, destination, url + * downloadinfo : id, http_status, content_size, mimetype, contentname, saved_path, tmp_saved_path, etag + * httpheaders : id, header_field, header_data + * notification : id, noti_enable, extra_key, extra_data + */ +/* +CREATE TABLE IF NOT EXISTS groups +( + id INTEGER UNIQUE PRIMARY KEY, + uid INTEGER DEFAULT 0, + gid INTEGER DEFAULT 0, + extra_int INTEGER DEFAULT 0, + packagename TEXT DEFAULT NULL, + smack_label TEXT DEFAULT NULL, + extra TEXT DEFAULT NULL, + date_first_connected DATE, + date_last_connected DATE +); + +CREATE TABLE logging +( + id INTEGER UNIQUE PRIMARY KEY, + state INTEGER DEFAULT 0, + errorcode INTEGER DEFAULT 0, + startcount INTEGER DEFAULT 0, + packagename TEXT DEFAULT NULL, + createtime DATE, + accesstime DATE +); + +CREATE TABLE requestinfo +( + id INTEGER UNIQUE PRIMARY KEY, + auto_download BOOLEAN DEFAULT 0, + state_event BOOLEAN DEFAULT 0, + progress_event BOOLEAN DEFAULT 0, + network_type TINYINT DEFAULT 0, + filename TEXT DEFAULT NULL, + destination TEXT DEFAULT NULL, + url TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE downloadinfo +( + id INTEGER UNIQUE PRIMARY KEY, + http_status INTEGER DEFAULT 0, + content_size UNSIGNED BIG INT DEFAULT 0, + mimetype VARCHAR(64) DEFAULT NULL, + content_name TEXT DEFAULT NULL, + saved_path TEXT DEFAULT NULL, + tmp_saved_path TEXT DEFAULT NULL, + etag TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE httpheaders +( + id INTEGER NOT NULL, + header_field TEXT DEFAULT NULL, + header_data TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE notification +( + id INTEGER UNIQUE PRIMARY KEY, + noti_enable BOOLEAN DEFAULT 0, + extra_key TEXT DEFAULT NULL, + extra_data TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX requests_index ON logging (id, state, errorcode, packagename, createtime, accesstime); +*/ + +#define DP_DB_TABLE_GROUPS "groups" +#define DP_DB_TABLE_LOG "logging" +#define DP_DB_TABLE_REQUEST_INFO "requestinfo" +#define DP_DB_TABLE_DOWNLOAD_INFO "downloadinfo" +#define DP_DB_TABLE_HTTP_HEADERS "httpheaders" +#define DP_DB_TABLE_NOTIFICATION "notification" + +#define DP_DB_COL_ID "id" +#define DP_DB_COL_STATE "state" +#define DP_DB_COL_ERRORCODE "errorcode" +#define DP_DB_COL_NETWORK_TYPE "network_type" +#define DP_DB_COL_HTTP_STATUS "http_status" +#define DP_DB_COL_AUTO_DOWNLOAD "auto_download" +#define DP_DB_COL_STATE_EVENT "state_event" +#define DP_DB_COL_PROGRESS_EVENT "progress_event" +#define DP_DB_COL_CONTENT_SIZE "content_size" +#define DP_DB_COL_CREATE_TIME "createtime" +#define DP_DB_COL_ACCESS_TIME "accesstime" +#define DP_DB_COL_STARTCOUNT "startcount" +#define DP_DB_COL_PACKAGENAME "packagename" +#define DP_DB_COL_DESTINATION "destination" +#define DP_DB_COL_FILENAME "filename" +#define DP_DB_COL_CONTENT_NAME "content_name" +#define DP_DB_COL_MIMETYPE "mimetype" +#define DP_DB_COL_ETAG "etag" +#define DP_DB_COL_SAVED_PATH "saved_path" +#define DP_DB_COL_TMP_SAVED_PATH "tmp_saved_path" +#define DP_DB_COL_URL "url" +#define DP_DB_COL_HEADER_FIELD "header_field" +#define DP_DB_COL_HEADER_DATA "header_data" +#define DP_DB_COL_NOTIFICATION_ENABLE "noti_enable" +#define DP_DB_COL_EXTRA_KEY "extra_key" +#define DP_DB_COL_DISTINCT_EXTRA_KEY "DISTINCT extra_key" +#define DP_DB_COL_EXTRA_VALUE "extra_data" +#define DP_DB_COL_RAW_BUNDLE_ONGOING "raw_bundle_data_ongoing_state" +#define DP_DB_COL_RAW_BUNDLE_COMPLETE "raw_bundle_data_complete_state" +#define DP_DB_COL_RAW_BUNDLE_FAIL "raw_bundle_data_fail_state" +#define DP_DB_COL_TITLE "title" +#define DP_DB_COL_DESCRIPTION "description" +#define DP_DB_COL_NOTI_TYPE "noti_type" + +#define DP_DB_GROUPS_COL_UID "uid" +#define DP_DB_GROUPS_COL_GID "gid" +#define DP_DB_GROUPS_COL_PKG "packagename" +#define DP_DB_GROUPS_COL_SMACK_LABEL "smack_label" + + +typedef enum { + DP_DB_COL_TYPE_NONE = 0, + DP_DB_COL_TYPE_INT = 10, + DP_DB_COL_TYPE_INT64 = 20, + DP_DB_COL_TYPE_TEXT = 30 +} db_column_data_type; + +typedef struct { + char *column; + db_column_data_type type; + int is_like; + void *value; +} db_conds_list_fmt; + +int dp_db_open(); +void dp_db_close(); + +int dp_db_is_full_error(); + +int dp_db_remove_all(int id); +int dp_db_remove(int id, char *table); +int dp_db_insert_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +int dp_db_insert_blob_column(int id, char *table, char *column, + void *value, unsigned length); +int dp_db_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +int dp_db_set_blob_column(int id, char *table, char *column, + void *value, unsigned length); +int dp_db_replace_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +int dp_db_replace_blob_column(int id, char *table, char *column, + void *value, unsigned length); +char *dp_db_get_text_column(int id, char *table, char *column); +void *dp_db_get_blob_column(int id, char *table, char *column, int *length); +int dp_db_get_int_column(int id, char *table, char *column); +long long dp_db_get_int64_column(int id, char *table, char *column); +int dp_db_update_date(int id, char *table, char *column); + +// cond : id & cond +int dp_db_cond_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +char *dp_db_cond_get_text_column(int id, char *table, char *column, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +int dp_db_cond_remove(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +int dp_db_get_cond_rows_count(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue); + +char *dp_db_cond_get_text(char *table, char *column, char *condcolumn, + db_column_data_type condtype, void *condvalue); +int dp_db_cond_get_int(char *table, char *column, char *condcolumn, + db_column_data_type condtype, void *condvalue); + +// Special API for http headers +int dp_db_get_http_headers_list(int id, char **headers); + +// For auto-download in booting time +int dp_db_crashed_list(dp_request_slots *requests, int limit); + +// For loading to memory when no in memory +dp_request *dp_db_load_logging_request(int id); + +// For limitation by 48 hours & 1000 rows +int dp_db_limit_rows(int limit); +int dp_db_get_count_by_limit_time(); +int dp_db_get_list_by_limit_time(dp_request_slots *requests, int limit); + +int dp_db_insert_columns(char *table, int column_count, + db_conds_list_fmt *columns); +int dp_db_update_columns(int id, char *table, int column_count, + db_conds_list_fmt *columns); + +int dp_db_get_conds_rows_count(char *table, char *getcolumn, char *op, + int conds_count, db_conds_list_fmt *conds); + +int dp_db_get_conds_list(char *table, char *getcolumn, + db_column_data_type gettype, void **list, + int rowslimit, int rowsoffset, + char *ordercolumn, char *ordering, + char *op, int conds_count, + db_conds_list_fmt *conds); + +int dp_db_request_new_logging(const int id, const int state, const char *pkgname); +int dp_db_request_update_status(const int id, const int state, const int download_error); +int dp_db_get_state(int id); +int dp_db_request_new_logging(const int id, + const int state, const char *pkgname); +#endif diff --git a/provider/include/download-provider-defs.h b/provider/include/download-provider-defs.h new file mode 100755 index 0000000..4387e49 --- /dev/null +++ b/provider/include/download-provider-defs.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013 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_DEFS_H +#define DOWNLOAD_PROVIDER_DEFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + DP_STATE_NONE = 0, + DP_STATE_READY = DP_STATE_NONE + 5, // created id, set some info. + DP_STATE_QUEUED = DP_STATE_NONE + 10, // request to start + DP_STATE_CONNECTING = DP_STATE_NONE + 15, // try to connect to url + DP_STATE_DOWNLOADING = DP_STATE_NONE + 20, // started + DP_STATE_PAUSE_REQUESTED = DP_STATE_NONE + 25, + DP_STATE_PAUSED = DP_STATE_NONE + 30, // paused actually + DP_STATE_COMPLETED = DP_STATE_NONE + 40, + DP_STATE_CANCELED = DP_STATE_NONE + 45, // stopped with error + DP_STATE_FAILED = DP_STATE_NONE + 50 // failed with error +} dp_state_type; + +typedef enum { + DP_ERROR_NONE = 10, + DP_ERROR_INVALID_PARAMETER = DP_ERROR_NONE + 1, + DP_ERROR_OUT_OF_MEMORY = DP_ERROR_NONE + 2, + DP_ERROR_IO_ERROR = DP_ERROR_NONE + 3, + DP_ERROR_NETWORK_UNREACHABLE = DP_ERROR_NONE + 4, + DP_ERROR_CONNECTION_TIMED_OUT = DP_ERROR_NONE + 5, + DP_ERROR_NO_SPACE = DP_ERROR_NONE + 6, + DP_ERROR_FIELD_NOT_FOUND = DP_ERROR_NONE + 7, + DP_ERROR_INVALID_STATE = DP_ERROR_NONE + 8, + DP_ERROR_CONNECTION_FAILED = DP_ERROR_NONE + 9, + DP_ERROR_INVALID_URL = DP_ERROR_NONE + 10, + DP_ERROR_INVALID_DESTINATION = DP_ERROR_NONE + 11, + DP_ERROR_QUEUE_FULL = DP_ERROR_NONE + 12, + DP_ERROR_ALREADY_COMPLETED = DP_ERROR_NONE + 13, + DP_ERROR_FILE_ALREADY_EXISTS = DP_ERROR_NONE + 14, + DP_ERROR_TOO_MANY_DOWNLOADS = DP_ERROR_NONE + 15, + DP_ERROR_NO_DATA = DP_ERROR_NONE + 17, + DP_ERROR_UNHANDLED_HTTP_CODE = DP_ERROR_NONE + 18, + DP_ERROR_CANNOT_RESUME = DP_ERROR_NONE + 19, + DP_ERROR_PERMISSION_DENIED = DP_ERROR_NONE + 20, + DP_ERROR_INVALID_NETWORK_TYPE = DP_ERROR_NONE + 21, + DP_ERROR_RESPONSE_TIMEOUT = DP_ERROR_NONE + 50, + DP_ERROR_REQUEST_TIMEOUT = DP_ERROR_NONE + 55, + DP_ERROR_SYSTEM_DOWN = DP_ERROR_NONE + 60, + DP_ERROR_CLIENT_DOWN = DP_ERROR_NONE + 65, + DP_ERROR_ID_NOT_FOUND = DP_ERROR_NONE + 90, + DP_ERROR_IO_EAGAIN = DP_ERROR_NONE + 97, + DP_ERROR_IO_EINTR = DP_ERROR_NONE + 98, + DP_ERROR_IO_TIMEOUT = DP_ERROR_NONE + 99, + DP_ERROR_UNKNOWN = DP_ERROR_NONE + 100 +} dp_error_type; + +typedef enum { + DP_NETWORK_TYPE_OFF = -1, + DP_NETWORK_TYPE_ALL = 0, + DP_NETWORK_TYPE_WIFI = 1, + DP_NETWORK_TYPE_DATA_NETWORK = 2, + DP_NETWORK_TYPE_ETHERNET = 3, + DP_NETWORK_TYPE_WIFI_DIRECT = 4 +} dp_network_type; + +typedef enum { + DP_NOTIFICATION_BUNDLE_TYPE_ONGOING = 0, // Ongoing, Failed + DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE, // Completed + DP_NOTIFICATION_BUNDLE_TYPE_FAILED // Failed +} dp_notification_bundle_type; + +typedef enum { + DP_NOTIFICATION_TYPE_NONE = 0, // Not register Noti. + DP_NOTIFICATION_TYPE_COMPLETE_ONLY, // Success, Failed + DP_NOTIFICATION_TYPE_ALL // Ongoing, Success, Failed +} dp_notification_type; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/provider/include/download-provider-log.h b/provider/include/download-provider-log.h new file mode 100755 index 0000000..e828905 --- /dev/null +++ b/provider/include/download-provider-log.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_LOG_H +#define DOWNLOAD_PROVIDER2_LOG_H + +#ifdef SUPPORT_LOG_MESSAGE +#include <errno.h> +#include <dlog.h> +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "DOWNLOAD_PROVIDER" +#define TRACE_DEBUG(format, ARG...) LOGD(format, ##ARG) +#define TRACE_ERROR(format, ARG...) LOGE(format, ##ARG) +#define TRACE_STRERROR(format, ARG...) LOGE(format" [%s]", ##ARG, strerror(errno)) +#define TRACE_INFO(format, ARG...) LOGI(format, ##ARG) + +#ifdef SECURE_LOGD +#define TRACE_SECURE_DEBUG(format, ARG...) SECURE_LOGD(format, ##ARG) +#else +#define TRACE_SECURE_DEBUG(...) do { } while(0) +#endif +#ifdef SECURE_LOGI +#define TRACE_SECURE_INFO(format, ARG...) SECURE_LOGI(format, ##ARG) +#else +#define TRACE_SECURE_INFO(...) do { } while(0) +#endif +#ifdef SECURE_LOGE +#define TRACE_SECURE_ERROR(format, ARG...) SECURE_LOGE(format, ##ARG) +#else +#define TRACE_SECURE_ERROR(...) do { } while(0) +#endif + +#else +#define TRACE_DEBUG(...) do { } while(0) +#define TRACE_ERROR(...) do { } while(0) +#define TRACE_STRERROR(...) do { } while(0) +#define TRACE_INFO(...) do { } while(0) +#define TRACE_SECURE_DEBUG(...) do { } while(0) +#define TRACE_SECURE_INFO(...) do { } while(0) +#define TRACE_SECURE_ERROR(...) do { } while(0) +#endif + +#endif diff --git a/provider/include/download-provider-network.h b/provider/include/download-provider-network.h new file mode 100755 index 0000000..98a9b43 --- /dev/null +++ b/provider/include/download-provider-network.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_NETWORK_H +#define DOWNLOAD_PROVIDER2_NETWORK_H + +#include <net_connection.h> + +#include "download-provider.h" + +dp_network_type dp_get_network_connection_status(connection_h connection, connection_type_e type); +void dp_network_connection_type_changed_cb(connection_type_e type, void *data); +int dp_network_connection_init(dp_privates *privates); +void dp_network_connection_destroy(connection_h connection); +dp_network_type dp_get_network_connection_instant_status(); + +#ifdef SUPPORT_WIFI_DIRECT +int dp_network_wifi_direct_is_connected(); +#endif + +#endif diff --git a/provider/include/download-provider-notification.h b/provider/include/download-provider-notification.h new file mode 100755 index 0000000..5a9f5b9 --- /dev/null +++ b/provider/include/download-provider-notification.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_NOTIFICATION_H +#define DOWNLOAD_PROVIDER2_NOTIFICATION_H + +#include "download-provider-config.h" + +int dp_set_downloadinginfo_notification(int id, char *packagename); +int dp_set_downloadedinfo_notification(int priv_id, int id, char *packagename, dp_state_type state); +void dp_update_downloadinginfo_notification(int priv_id, double received_size, double file_size); +void dp_clear_downloadinginfo_notification(void); + +#endif diff --git a/provider/include/download-provider-pthread.h b/provider/include/download-provider-pthread.h new file mode 100755 index 0000000..8c02c59 --- /dev/null +++ b/provider/include/download-provider-pthread.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_PTHREAD_H +#define DOWNLOAD_PROVIDER2_PTHREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <unistd.h> +#include <pthread.h> +#include <errno.h> + +// download-provider use default style mutex. + +#define CLIENT_MUTEX_LOCK(mutex_add) {\ + int ret = 0;\ + ret = pthread_mutex_lock(mutex_add);\ + if (EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with EINVAL.");\ + } else if (EDEADLK == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with EDEADLK.");\ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with %d.", ret);\ + } \ +} + +#define CLIENT_MUTEX_UNLOCK(mutex_add) {\ + int ret = 0;\ + ret = pthread_mutex_unlock(mutex_add);\ + if (EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with EINVAL.");\ + } else if (EDEADLK == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with EDEADLK.");\ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with %d.", ret);\ + } \ +} + +#define CLIENT_MUTEX_DESTROY(mutex_add) { \ + int ret = 0; \ + ret = pthread_mutex_destroy(mutex_add); \ + if(EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EINVAL."); \ + } else if(ENOMEM == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with ENOMEM."); \ + } else if(EBUSY == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EBUSY."); \ + if (pthread_mutex_unlock(mutex_add) == 0) \ + pthread_mutex_destroy(mutex_add); \ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with %d.", ret); \ + } \ +} + +#define CLIENT_MUTEX_INIT(mutex_add, attr) { \ + int ret = 0; \ + unsigned retry = 3; \ + while (retry > 0) { \ + ret = pthread_mutex_init(mutex_add, attr); \ + if (0 == ret) { \ + break; \ + } else if(EINVAL == ret) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with EINVAL."); \ + } else if(ENOMEM == ret) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with ENOMEM."); \ + usleep(1000); \ + } else if(EBUSY == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EBUSY."); \ + if (pthread_mutex_unlock(mutex_add) == 0) \ + pthread_mutex_destroy(mutex_add); \ + } else if (ret != 0) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with %d.", ret); \ + } \ + retry--; \ + } \ +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/provider/include/download-provider-queue.h b/provider/include/download-provider-queue.h new file mode 100755 index 0000000..71bb62b --- /dev/null +++ b/provider/include/download-provider-queue.h @@ -0,0 +1,23 @@ +/*
+ * Copyright (c) 2012 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_PROVIDER2_QUEUE_H
+#define DOWNLOAD_PROVIDER2_QUEUE_H
+
+void dp_thread_queue_manager_wake_up();
+void *dp_thread_queue_manager(void *arg);
+
+#endif
diff --git a/provider/include/download-provider-request.h b/provider/include/download-provider-request.h new file mode 100755 index 0000000..bc26953 --- /dev/null +++ b/provider/include/download-provider-request.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_REQUEST_H +#define DOWNLOAD_PROVIDER2_REQUEST_H + +#include "download-provider.h" +#include <bundle.h> + +// for Debugging +char *dp_print_state(dp_state_type state); +char *dp_print_errorcode(dp_error_type errorcode); + +int dp_is_smackfs_mounted(void); +char *dp_strdup(char *src); + +dp_error_type dp_request_create(int id, dp_client_group *group, dp_request** empty_slot); +dp_error_type dp_request_set_url(int id, dp_request *request, char *url); +dp_error_type dp_request_set_destination(int id, dp_request *request, char *dest); +dp_error_type dp_request_set_filename(int id, dp_request *request, char *filename); +dp_error_type dp_request_set_title(int id, dp_request *request, char *filename); +dp_error_type dp_request_set_bundle(int id, dp_request *request, int type, bundle_raw *b, unsigned length); +dp_error_type dp_request_set_description(int id, dp_request *request, char *description); +dp_error_type dp_request_set_noti_type(int id, dp_request *request, unsigned type); +dp_error_type dp_request_set_notification(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_auto_download(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_state_event(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_progress_event(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_network_type(int id, dp_request *request, int type); +char *dp_request_get_url(int id, dp_error_type *errorcode); +char *dp_request_get_destination(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_filename(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_title(int id, dp_request *request, dp_error_type *errorcode); +bundle_raw *dp_request_get_bundle(int id, dp_request *request, dp_error_type *errorcode, char* column, int* length); +char *dp_request_get_description(int id, dp_request *request, dp_error_type *errorcode); +int dp_request_get_noti_type(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_contentname(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_etag(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_savedpath(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_tmpsavedpath(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_mimetype(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_pkg_name(int id, dp_request *request, dp_error_type *errorcode); +dp_request *dp_request_load_from_log(int id, dp_error_type *errorcode); +void dp_request_state_response(dp_request *request); +#endif diff --git a/provider/include/download-provider-slots.h b/provider/include/download-provider-slots.h new file mode 100755 index 0000000..53859f8 --- /dev/null +++ b/provider/include/download-provider-slots.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_SLOTS_H +#define DOWNLOAD_PROVIDER2_SLOTS_H + +#include "download-provider.h" +#include "download-provider-pthread.h" + +// Backgound Daemon should has the limitation of resource. +#define DP_MAX_GROUP 15 +#define DP_MAX_REQUEST 64 + +typedef struct { + pid_t pid; + uid_t uid; + gid_t gid; +} dp_credential; + +typedef struct { + // send command * get return value. + int cmd_socket; + // send event to client + int event_socket; + unsigned queued_count; // start ++, finish or failed -- ( queue & active ) + // fill by app-manager + char *pkgname; + dp_credential credential; + char *smack_label; +} dp_client_group; + +typedef struct { + int id; // ID created in create request in requests thread. + int agent_id; + int create_time; + int start_time; // setted by START command + int pause_time; // setted by PAUSE command + int stop_time; // time setted by finished_cb + int noti_priv_id; + unsigned state_cb; // set : 1 unset : 0 + unsigned progress_cb; // set : 1 unset : 0 + unsigned startcount; + unsigned auto_notification; + unsigned ip_changed; + dp_state_type state; // downloading state + dp_error_type error; + dp_network_type network_type; + size_t progress_lasttime; + unsigned long long received_size; // progress + unsigned long long file_size; + char *packagename; + dp_client_group *group; // indicate dp_client_group included this request +} dp_request; + +typedef struct { + dp_client_group *group; +} dp_group_slots; + +typedef struct { + pthread_mutex_t mutex; + dp_request *request; +} dp_request_slots; + + +// functions +dp_group_slots *dp_client_group_slots_new(int size); +dp_request_slots *dp_request_slots_new(int size); +int dp_client_group_free(dp_client_group *group); +int dp_client_group_slots_free(dp_group_slots *slots, int size); +dp_request *dp_request_new(); +void dp_request_init(dp_request *request); +int dp_request_free(dp_request *request); +int dp_request_slot_free(dp_request_slots *request_slot); +int dp_request_slots_free(dp_request_slots *slots, int size); +int dp_get_request_count(dp_request_slots *slots); + +#endif diff --git a/provider/include/download-provider-socket.h b/provider/include/download-provider-socket.h new file mode 100755 index 0000000..b5a37cb --- /dev/null +++ b/provider/include/download-provider-socket.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_SOCKET_H +#define DOWNLOAD_PROVIDER2_SOCKET_H + +#include <bundle.h> +#include "download-provider.h" +#include "download-provider-slots.h" + +int dp_ipc_send_errorcode(int fd, dp_error_type errorcode); +int dp_ipc_send_event(int fd, int id, dp_state_type state, + dp_error_type errorcode, unsigned long long received_size); +#if 0 +int dp_ipc_send_progressinfo(int fd, int id, + unsigned long long received_size, unsigned long long file_size, + unsigned int chunk_size); +int dp_ipc_send_stateinfo(int fd, int id, dp_state_type state, + dp_error_type errorcode); +#endif +char *dp_ipc_read_string(int fd); +unsigned dp_ipc_read_bundle(int fd, int *type, bundle_raw **b); +int dp_ipc_send_string(int fd, const char *str); +int dp_ipc_send_bundle(int fd, bundle_raw *b, unsigned length); +int dp_ipc_send_custom_type(int fd, void *value, size_t type_size); +int dp_ipc_read_custom_type(int fd, void *value, size_t type_size); +int dp_accept_socket_new(); +int dp_socket_free(int sockfd); + +#endif diff --git a/provider/include/download-provider.h b/provider/include/download-provider.h new file mode 100755 index 0000000..a1f22f3 --- /dev/null +++ b/provider/include/download-provider.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2012 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_PROVIDER2_H +#define DOWNLOAD_PROVIDER2_H + +#include "download-provider-defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DP_IPC "/tmp/download-provider" + +#define DP_MAX_STR_LEN_64 64 +#define DP_MAX_STR_LEN 256 +#define DP_MAX_PATH_LEN DP_MAX_STR_LEN +#define DP_MAX_URL_LEN 2048 +#define DP_DEFAULT_BUFFER_SIZE 1024 + +typedef enum { + DP_CMD_NONE = 0, + DP_CMD_ECHO, + DP_CMD_CREATE, + DP_CMD_START, + DP_CMD_SET_COMMAND_SOCKET, + DP_CMD_SET_EVENT_SOCKET, + DP_CMD_FREE, + + DP_CMD_ACTION_SECT = 100, + DP_CMD_PAUSE, + DP_CMD_CANCEL, + DP_CMD_DESTROY, + + DP_CMD_GET_SECT = 200, + DP_CMD_GET_URL, + DP_CMD_GET_DESTINATION, + DP_CMD_GET_FILENAME, + DP_CMD_GET_NOTIFICATION, + DP_CMD_GET_STATE_CALLBACK, + DP_CMD_GET_PROGRESS_CALLBACK, + DP_CMD_GET_HTTP_HEADERS, + DP_CMD_GET_AUTO_DOWNLOAD, + DP_CMD_GET_NETWORK_TYPE, + DP_CMD_GET_SAVED_PATH, + DP_CMD_GET_TEMP_SAVED_PATH, + DP_CMD_GET_MIME_TYPE, + DP_CMD_GET_HTTP_HEADER, + DP_CMD_GET_HTTP_HEADER_LIST, + DP_CMD_GET_EXTRA_PARAM, + DP_CMD_GET_RECEIVED_SIZE, + DP_CMD_GET_TOTAL_FILE_SIZE, + DP_CMD_GET_CONTENT_NAME, + DP_CMD_GET_HTTP_STATUS, + DP_CMD_GET_ETAG, + DP_CMD_GET_STATE, + DP_CMD_GET_ERROR, + DP_CMD_GET_NOTIFICATION_BUNDLE, + DP_CMD_GET_NOTIFICATION_TITLE, + DP_CMD_GET_NOTIFICATION_DESCRIPTION, + DP_CMD_GET_NOTIFICATION_TYPE, + + DP_CMD_SET_SECT = 300, + DP_CMD_SET_URL, + DP_CMD_SET_DESTINATION, + DP_CMD_SET_FILENAME, + DP_CMD_SET_NOTIFICATION, + DP_CMD_SET_STATE_CALLBACK, + DP_CMD_SET_PROGRESS_CALLBACK, + DP_CMD_SET_AUTO_DOWNLOAD, + DP_CMD_SET_NETWORK_TYPE, + DP_CMD_SET_HTTP_HEADER, + DP_CMD_SET_EXTRA_PARAM, // prevent build error + DP_CMD_DEL_HTTP_HEADER, + DP_CMD_ADD_EXTRA_PARAM, + DP_CMD_REMOVE_EXTRA_PARAM, + DP_CMD_SET_NOTIFICATION_BUNDLE, + DP_CMD_SET_NOTIFICATION_TITLE, + DP_CMD_SET_NOTIFICATION_DESCRIPTION, + DP_CMD_SET_NOTIFICATION_TYPE, + + DP_CMD_LAST_SECT = 400 +} dp_command_type; + +typedef struct { + unsigned int length; + char *str; +} dp_string; + +typedef struct { + int id; + dp_state_type state; + dp_error_type err; + unsigned long long received_size; +} dp_event_info; + +typedef struct { + dp_command_type cmd; + int id; +} dp_command; + + // Usage IPC : send(dp_command);send(dp_string); + +#ifdef __cplusplus +} +#endif +#endif |