diff options
Diffstat (limited to 'agent')
39 files changed, 11154 insertions, 0 deletions
diff --git a/agent/CMakeLists.txt b/agent/CMakeLists.txt new file mode 100755 index 0000000..92c1677 --- /dev/null +++ b/agent/CMakeLists.txt @@ -0,0 +1,85 @@ +PROJECT(downloadagent2 C) + +IF("${CMAKE_BUILD_TYPE}" STREQUAL "") + SET(CMAKE_BUILD_TYPE "Debug") +ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "") +MESSAGE("Build type: ${CMAKE_BUILD_TYPE}") + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) +SET(VERSION "0.0.1") +FIND_PROGRAM(UNAME NAMES uname) +EXEC_PROGRAM("${UNAME}" ARGS "-m" OUTPUT_VARIABLE "ARCH") + +#DA Engine Include Directory +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include) + +INCLUDE(FindPkgConfig) +pkg_check_modules(subpkgs REQUIRED + libsoup-2.4 + xdgmime + vconf + capi-network-connection + glib-2.0 + dlog + libtzplatform-config +) + +FOREACH(flag ${subpkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -Wall") + +IF("${ARCH}" MATCHES "^arm.*") + ADD_DEFINITIONS("-D_TARGET") + SET(CMAKE_C_FLAGS_RELEASE "-mabi=aapcs-linux -msoft-float -O2") +ENDIF("${ARCH}" MATCHES "^arm.*") + +ADD_DEFINITIONS("-D_EFL_PLATFORM") +#allow to install widget, deb pkg and apk for testing +ADD_DEFINITIONS("-DDA_DEBUG_USING_DLOG") +#This should be removed when release a target +ADD_DEFINITIONS("-D_SAMSUNG_MIME_POLICY") + +############################################################################# +#+++++++++++++++++++++++++DA ENGINE+++++++++++++++++++++++++++++++++++++++++++ +############################################################################# + +SET(SRCS_PATH ".") +SET(SRCS_DA_ENGINE + ${SRCS_PATH}/download-agent-debug.c + ${SRCS_PATH}/download-agent-interface.c + ${SRCS_PATH}/download-agent-client-mgr.c + ${SRCS_PATH}/download-agent-dl-mgr.c + ${SRCS_PATH}/download-agent-dl-info-util.c + ${SRCS_PATH}/download-agent-http-queue.c + ${SRCS_PATH}/download-agent-http-misc.c + ${SRCS_PATH}/download-agent-http-mgr.c + ${SRCS_PATH}/download-agent-http-msg-handler.c + ${SRCS_PATH}/download-agent-encoding.c + ${SRCS_PATH}/download-agent-utils.c + ${SRCS_PATH}/download-agent-utils-dl-id-history.c + ${SRCS_PATH}/download-agent-basic.c + ${SRCS_PATH}/download-agent-file.c + ${SRCS_PATH}/download-agent-plugin-libsoup.c + ${SRCS_PATH}/download-agent-plugin-conf.c + ${SRCS_PATH}/download-agent-mime-util.c +) + +SET(HEADERS + include/download-agent-defs.h + include/download-agent-interface.h +) + +ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS_DA_ENGINE}) +#TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${subpkgs_LDFLAGS} "-ldl") +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${subpkgs_LDFLAGS}) +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION 0.0.1) + +############################################################################# +#+++++++++++++++++++++++++INSTALLATION++++++++++++++++++++++++++++++++++++++++ +############################################################################# + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR} COMPONENT RuntimeLibraries) + diff --git a/agent/download-agent-basic.c b/agent/download-agent-basic.c new file mode 100755 index 0000000..73fc892 --- /dev/null +++ b/agent/download-agent-basic.c @@ -0,0 +1,431 @@ +/* + * 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 <stdlib.h> + +#include "download-agent-basic.h" +#include "download-agent-debug.h" +#include "download-agent-client-mgr.h" +#include "download-agent-utils.h" +#include "download-agent-http-mgr.h" +#include "download-agent-http-misc.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-pthread.h" +#include "download-agent-file.h" + +static void* __thread_start_download(void* data); +void __thread_clean_up_handler_for_start_download(void *arg); + +static da_result_t __make_source_info_basic_download( + stage_info *stage, + client_input_t *client_input); +static da_result_t __download_content(stage_info *stage); + +da_result_t start_download(const char *url , int *dl_id) +{ + DA_LOG_FUNC_LOGD(Default); + return start_download_with_extension(url, dl_id, NULL); +} + +da_result_t start_download_with_extension( + const char *url, + int *dl_id, + extension_data_t *extension_data) +{ + da_result_t ret = DA_RESULT_OK; + int slot_id = 0; + const char **request_header = DA_NULL; + const char *install_path = DA_NULL; + const char *file_name = DA_NULL; + const char *etag = DA_NULL; + const char *temp_file_path = DA_NULL; + const char *pkg_name = DA_NULL; + int request_header_count = 0; + void *user_data = DA_NULL; + client_input_t *client_input = DA_NULL; + client_input_basic_t *client_input_basic = DA_NULL; + download_thread_input *thread_info = DA_NULL; + pthread_attr_t thread_attr; + + DA_LOG_FUNC_LOGV(Default); + + if (extension_data) { + request_header = extension_data->request_header; + if (extension_data->request_header_count) + request_header_count = extension_data->request_header_count; + install_path = extension_data->install_path; + file_name = extension_data->file_name; + user_data = extension_data->user_data; + etag = extension_data->etag; + temp_file_path = extension_data->temp_file_path; + pkg_name = extension_data->pkg_name; + } + + ret = get_available_slot_id(&slot_id); + if (DA_RESULT_OK != ret) + return ret; + + *dl_id = GET_DL_ID(slot_id); + + client_input = (client_input_t *)calloc(1, sizeof(client_input_t)); + if (!client_input) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + client_input->user_data = user_data; + if (install_path) { + int install_path_len = strlen(install_path); + if (install_path[install_path_len-1] == '/') + install_path_len--; + + client_input->install_path = (char *)calloc(install_path_len+1, sizeof(char)); + if (client_input->install_path) + strncpy(client_input->install_path, install_path, install_path_len); + } + + if (file_name) { + client_input->file_name = (char *)calloc(strlen(file_name)+1, sizeof(char)); + if (client_input->file_name) + strncpy(client_input->file_name, file_name, strlen(file_name)); + } + + if (etag) { + client_input->etag = (char *)calloc(strlen(etag)+1, sizeof(char)); + if (client_input->etag) + strncpy(client_input->etag, etag, strlen(etag)); + + } + + if (temp_file_path) { + client_input->temp_file_path = (char *)calloc(strlen(temp_file_path)+1, sizeof(char)); + if (client_input->temp_file_path) + strncpy(client_input->temp_file_path, temp_file_path, strlen(temp_file_path)); + } + + if (pkg_name) { + client_input->pkg_name = (char *)calloc(strlen(pkg_name)+1, sizeof(char)); + if (client_input->pkg_name) + strncpy(client_input->pkg_name, pkg_name, strlen(pkg_name)); + } + client_input_basic = &(client_input->client_input_basic); + client_input_basic->req_url = (char *)calloc(strlen(url)+1, sizeof(char)); + if(DA_NULL == client_input_basic->req_url) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + strncpy(client_input_basic->req_url ,url,strlen(url)); + + if (request_header_count > 0) { + int i = 0; + client_input_basic->user_request_header = + (char **)calloc(1, sizeof(char *)*request_header_count); + if(DA_NULL == client_input_basic->user_request_header) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + for (i = 0; i < request_header_count; i++) + { + client_input_basic->user_request_header[i] = strdup(request_header[i]); + } + client_input_basic->user_request_header_count = request_header_count; + } + } + + thread_info = (download_thread_input *)calloc(1, sizeof(download_thread_input)); + if (!thread_info) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + thread_info->slot_id = slot_id; + thread_info->client_input = client_input; + } + if (pthread_attr_init(&thread_attr) != 0) { + ret = DA_ERR_FAIL_TO_CREATE_THREAD; + goto ERR; + } + + if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0) { + ret = DA_ERR_FAIL_TO_CREATE_THREAD; + goto ERR; + } + + if (pthread_create(&GET_DL_THREAD_ID(slot_id), &thread_attr, + __thread_start_download, thread_info) < 0) { + DA_LOG_ERR(Thread, "making thread failed.."); + ret = DA_ERR_FAIL_TO_CREATE_THREAD; + } else { + if (GET_DL_THREAD_ID(slot_id) < 1) { + DA_LOG_ERR(Thread, "The thread start is failed before calling this"); +// When http resource is leaked, the thread ID is initialized at error handling section of thread_start_download() +// Because the thread ID is initialized, the ptrhead_detach should not be called. This is something like timing issue between threads. +// thread info and basic_dl_input is freed at thread_start_download(). And it should not returns error code in this case. + goto ERR; + } + } + DA_LOG_DEBUG(Thread, "download thread create slot_id[%d] thread id[%lu]", + slot_id,GET_DL_THREAD_ID(slot_id)); + +ERR: + if (DA_RESULT_OK != ret) { + if (client_input) { + clean_up_client_input_info(client_input); + free(client_input); + client_input = DA_NULL; + } + if (thread_info) { + free(thread_info); + thread_info = DA_NULL; + } + destroy_download_info(slot_id); + } + return ret; +} + +da_result_t __make_source_info_basic_download( + stage_info *stage, + client_input_t *client_input) +{ + da_result_t ret = DA_RESULT_OK; + client_input_basic_t *client_input_basic = DA_NULL; + source_info_t *source_info = DA_NULL; + source_info_basic_t *source_info_basic = DA_NULL; + + DA_LOG_FUNC_LOGV(Default); + + if (!stage) { + DA_LOG_ERR(Default, "no stage; DA_ERR_INVALID_ARGUMENT"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + client_input_basic = &(client_input->client_input_basic); + if (DA_NULL == client_input_basic->req_url) { + DA_LOG_ERR(Default, "DA_ERR_INVALID_URL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + source_info_basic = (source_info_basic_t*)calloc(1, sizeof(source_info_basic_t)); + if (DA_NULL == source_info_basic) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + + source_info_basic->url = client_input_basic->req_url; + client_input_basic->req_url = DA_NULL; + + if (client_input_basic->user_request_header) { + source_info_basic->user_request_header = + client_input_basic->user_request_header; + source_info_basic->user_request_header_count = + client_input_basic->user_request_header_count; + client_input_basic->user_request_header = DA_NULL; + client_input_basic->user_request_header_count = 0; + } + + source_info = GET_STAGE_SOURCE_INFO(stage); + memset(source_info, 0, sizeof(source_info_t)); + + source_info->source_info_type.source_info_basic = source_info_basic; + +// DA_SECURE_LOGI("BASIC HTTP STARTED: URL=%s", +// source_info->source_info_type.source_info_basic->url); +ERR: + return ret; +} + +void __thread_clean_up_handler_for_start_download(void *arg) +{ + DA_LOG_CRITICAL(Default, "cleanup for thread id = %d", pthread_self()); +} + +static void *__thread_start_download(void *data) +{ + da_result_t ret = DA_RESULT_OK; + download_thread_input *thread_info = DA_NULL; + client_input_t *client_input = DA_NULL; + stage_info *stage = DA_NULL; + download_state_t download_state = 0; + + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_LOGV(Thread); + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, DA_NULL); + + thread_info = (download_thread_input*)data; + if (DA_NULL == thread_info) { + DA_LOG_ERR(Thread, "thread_info is NULL.."); + ret = DA_ERR_INVALID_ARGUMENT; + return DA_NULL; + } else { + slot_id = thread_info->slot_id; + client_input = thread_info->client_input; + + if(thread_info) { + free(thread_info); + thread_info = DA_NULL; + } + } + + pthread_cleanup_push(__thread_clean_up_handler_for_start_download, (void *)NULL); + + if (DA_FALSE == is_valid_slot_id(slot_id)) { + ret = DA_ERR_INVALID_ARGUMENT; + DA_LOG_ERR(Default, "Invalid Download ID"); + goto ERR; + } + + if (!client_input) { + ret = DA_ERR_INVALID_ARGUMENT; + DA_LOG_ERR(Default, "Invalid client_input"); + goto ERR; + } + + stage = Add_new_download_stage(slot_id); + if (!stage) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + DA_LOG_ERR(Default, "STAGE ADDITION FAIL!"); + goto ERR; + } + DA_LOG_VERBOSE(Default, "new added Stage : %p", stage); + + GET_DL_USER_DATA(slot_id) = client_input->user_data; + client_input->user_data = DA_NULL; + GET_DL_USER_INSTALL_PATH(slot_id) = client_input->install_path; + client_input->install_path = DA_NULL; + GET_DL_USER_FILE_NAME(slot_id) = client_input->file_name; + client_input->file_name = DA_NULL; + GET_DL_USER_ETAG(slot_id) = client_input->etag; + client_input->etag = DA_NULL; + GET_DL_USER_TEMP_FILE_PATH(slot_id) = client_input->temp_file_path; + client_input->temp_file_path = DA_NULL; + + ret = __make_source_info_basic_download(stage, client_input); + + if (ret == DA_RESULT_OK) { + /* to save memory */ + if (client_input) { + clean_up_client_input_info(client_input); + free(client_input); + client_input = DA_NULL; + } + + ret = __download_content(stage); + if (stage != GET_DL_CURRENT_STAGE(slot_id)) { + DA_LOG_ERR(Default,"Playready download case. The next stage is present stage"); + stage = GET_DL_CURRENT_STAGE(slot_id); + } + } +ERR: + if (client_input) { + clean_up_client_input_info(client_input); + free(client_input); + client_input = DA_NULL; + } + + if (DA_RESULT_OK == ret) { + char *installed_path = NULL; + char *etag = DA_NULL; + req_dl_info *request_info = NULL; + file_info *file_storage = NULL; + DA_LOG_VERBOSE(Default, "Whole download flow is finished."); + _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + download_state = GET_DL_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + if (download_state == DOWNLOAD_STATE_ABORTED) { + DA_LOG(Default, "Abort case. Do not call client callback"); +#ifdef PAUSE_EXIT + } else if (download_state == DOWNLOAD_STATE_PAUSED) { + DA_LOG(Default, "Finish case from paused state"); + destroy_download_info(slot_id); +#endif + } else { + request_info = GET_STAGE_TRANSACTION_INFO(stage); + etag = GET_REQUEST_HTTP_HDR_ETAG(request_info); + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + installed_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage); + send_user_noti_and_finish_download_flow(slot_id, installed_path, + etag); + } + } else { + char *etag = DA_NULL; + req_dl_info *request_info = NULL; + request_info = GET_STAGE_TRANSACTION_INFO(stage); + DA_LOG_CRITICAL(Default, "Download Failed -Return = %d", ret); + if (request_info) { + etag = GET_REQUEST_HTTP_HDR_ETAG(request_info); + send_client_finished_info(slot_id, GET_DL_ID(slot_id), + DA_NULL, etag, ret, get_http_status(slot_id)); + } + destroy_download_info(slot_id); + } + + pthread_cleanup_pop(0); + DA_LOG_CRITICAL(Thread, "==thread_start_download - EXIT=="); + pthread_exit((void *)NULL); + return DA_NULL; +} + +da_result_t __download_content(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state = 0; + da_bool_t isDownloadComplete = DA_FALSE; + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_LOGV(Default); + + slot_id = GET_STAGE_DL_ID(stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD, stage); + + do { + stage = GET_DL_CURRENT_STAGE(slot_id); + _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + download_state = GET_DL_STATE_ON_STAGE(stage); + DA_LOG_VERBOSE(Default, "download_state to - [%d] ", download_state); + _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + + switch(download_state) { + case DOWNLOAD_STATE_NEW_DOWNLOAD: + ret = requesting_download(stage); + + _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + download_state = GET_DL_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(stage)]); + if (download_state == DOWNLOAD_STATE_CANCELED || + download_state == DOWNLOAD_STATE_ABORTED || + download_state == DOWNLOAD_STATE_PAUSED) { + break; + } else { + if (DA_RESULT_OK == ret) { + ret = handle_after_download(stage); + } + } + break; + default: + isDownloadComplete = DA_TRUE; + break; + } + }while ((DA_RESULT_OK == ret) && (DA_FALSE == isDownloadComplete)); + + return ret; +} diff --git a/agent/download-agent-client-mgr.c b/agent/download-agent-client-mgr.c new file mode 100755 index 0000000..b27a2d4 --- /dev/null +++ b/agent/download-agent-client-mgr.c @@ -0,0 +1,631 @@ +/* + * 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 "download-agent-client-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-utils.h" +#include "download-agent-file.h" + +#define IS_CLIENT_Q_HAVING_DATA(QUEUE) (QUEUE->having_data) + +static client_app_mgr_t client_app_mgr; + +static da_result_t __launch_client_thread(void); +static void *__thread_for_client_noti(void *data); +void __thread_clean_up_handler_for_client_thread(void *arg); +static void __pop_client_noti(client_noti_t **out_client_noti); + +void __client_q_goto_sleep_without_lock(void); +void __client_q_wake_up_without_lock(void); +void destroy_client_noti(client_noti_t *client_noti); + +da_result_t init_client_app_mgr() +{ + DA_LOG_FUNC_LOGV(ClientNoti); + + if(client_app_mgr.is_init) + return DA_RESULT_OK; + + client_app_mgr.is_init = DA_TRUE; + client_app_mgr.client_app_info.client_user_agent = DA_NULL; + client_app_mgr.is_thread_init = DA_FALSE; + client_app_mgr.thread_id = 0; + + return DA_RESULT_OK; +} + +da_bool_t is_client_app_mgr_init(void) +{ + return client_app_mgr.is_init; +} + +da_result_t reg_client_app( + da_client_cb_t *da_client_callback) +{ + da_result_t ret = DA_RESULT_OK; + client_queue_t *queue = DA_NULL; + client_noti_t *client_noti = DA_NULL; + + DA_LOG_FUNC_LOGD(ClientNoti); + + memset(&(client_app_mgr.client_app_info.client_callback), + 0, sizeof(da_client_cb_t)); + memcpy(&(client_app_mgr.client_app_info.client_callback), + da_client_callback, sizeof(da_client_cb_t)); + + _da_thread_mutex_init(&(client_app_mgr.mutex_client_mgr), DA_NULL); + + /* If some noti is existed at queue, delete all */ + do { + __pop_client_noti(&client_noti); + destroy_client_noti(client_noti); + } while(client_noti != DA_NULL); + + queue = &(client_app_mgr.client_queue); + DA_LOG_VERBOSE(ClientNoti, "client queue = %p", queue); + _da_thread_mutex_init(&(queue->mutex_client_queue), DA_NULL); + _da_thread_cond_init(&(queue->cond_client_queue), DA_NULL); + + ret = __launch_client_thread(); + + return ret; +} + +da_result_t dereg_client_app(void) +{ + client_noti_t *client_noti = DA_NULL; + + DA_LOG_FUNC_LOGV(ClientNoti); + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = DA_INVALID_ID; + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_TERMINATE; + client_noti->next = DA_NULL; + + _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr)); + if (client_app_mgr.is_thread_init != DA_TRUE) { + DA_LOG_CRITICAL(ClientNoti, "try to cancel client mgr thread id[%lu]", + client_app_mgr.thread_id); + if (client_app_mgr.thread_id && + pthread_cancel(client_app_mgr.thread_id) < 0) { + DA_LOG_ERR(ClientNoti, "cancel thread is failed!!!"); + } + free(client_noti); + } else { + void *t_return = NULL; + DA_LOG_VERBOSE(ClientNoti, "pushing Q_CLIENT_NOTI_TYPE_TERMINATE"); + push_client_noti(client_noti); + DA_LOG_DEBUG(Thread, "===try to join client mgr thread id[%lu]===", + client_app_mgr.thread_id); + if (client_app_mgr.thread_id && + pthread_join(client_app_mgr.thread_id, &t_return) < 0) { + DA_LOG_ERR(Thread, "join client thread is failed!!!"); + } + DA_LOG_DEBUG(Thread, "===thread join return[%d]===", (char*)t_return); + } + _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr)); + + /* ToDo: This clean up should be done at the end of client_thread. */ + if(client_app_mgr.client_app_info.client_user_agent) { + free(client_app_mgr.client_app_info.client_user_agent); + client_app_mgr.client_app_info.client_user_agent = DA_NULL; + } + _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr)); + client_app_mgr.is_thread_init = DA_FALSE; + _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr)); + _da_thread_mutex_destroy(&(client_app_mgr.mutex_client_mgr)); + return DA_RESULT_OK; +} + +da_result_t send_client_paused_info(int slot_id) +{ + client_noti_t *client_noti = DA_NULL; + user_paused_info_t *paused_info = DA_NULL; + download_state_t state = GET_DL_STATE_ON_ID(slot_id); + + DA_LOG_FUNC_LOGD(ClientNoti); + + if (!GET_DL_ENABLE_PAUSE_UPDATE(slot_id)) { + DA_LOG(ClientNoti, "Do not call pause cb"); + return DA_RESULT_OK; + } + if (!is_valid_slot_id(slot_id)) { + DA_LOG_ERR(ClientNoti, "Download ID is not valid"); + return DA_RESULT_OK; + } + + DA_LOG_VERBOSE(ClientNoti, "slot_id[%d]", slot_id); + if ((DOWNLOAD_STATE_PAUSED != state)) { + DA_LOG(ClientNoti, "The state is not paused. state:%d", state); + return DA_ERR_INVALID_STATE; + } + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = slot_id; + client_noti->user_data = GET_DL_USER_DATA(slot_id); + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_PAUSED_INFO; + client_noti->next = DA_NULL; + + paused_info = (user_paused_info_t *)&(client_noti->type.paused_info); + paused_info->download_id= GET_DL_ID(slot_id); + DA_LOG(ClientNoti, "pushing paused info. slot_id=%d, dl_id=%d", + slot_id, GET_DL_ID(slot_id)); + + push_client_noti(client_noti); + + return DA_RESULT_OK; +} + +da_result_t send_client_update_progress_info ( + int slot_id, + int dl_id, + unsigned long int received_size + ) +{ + client_noti_t *client_noti = DA_NULL; + user_progress_info_t *progress_info = DA_NULL; + + DA_LOG_FUNC_LOGV(ClientNoti); + + if (!is_valid_slot_id(slot_id)) { + DA_LOG_ERR(ClientNoti, "Download ID is not valid"); + return DA_ERR_INVALID_DL_REQ_ID; + } + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = slot_id; + client_noti->user_data = GET_DL_USER_DATA(slot_id); + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_PROGRESS_INFO; + client_noti->next = DA_NULL; + + progress_info = (user_progress_info_t *)&(client_noti->type.update_progress_info); + progress_info->download_id= dl_id; + progress_info->received_size = received_size; + + DA_LOG_VERBOSE(ClientNoti, "pushing received_size=%lu, slot_id=%d, dl_id=%d", + received_size, slot_id, dl_id); + + push_client_noti(client_noti); + + return DA_RESULT_OK; +} + +da_result_t send_client_update_dl_info ( + int slot_id, + int dl_id, + char *file_type, + unsigned long int file_size, + char *tmp_saved_path, + char *pure_file_name, + char *etag, + char *extension) +{ + client_noti_t *client_noti = DA_NULL; + user_download_info_t *update_dl_info = DA_NULL; + int len = 0; + + DA_LOG_FUNC_LOGV(ClientNoti); + + if (!is_valid_slot_id(slot_id)) { + DA_LOG_ERR(ClientNoti, "Download ID is not valid"); + return DA_ERR_INVALID_DL_REQ_ID; + } + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = slot_id; + client_noti->user_data = GET_DL_USER_DATA(slot_id); + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_STARTED_INFO; + client_noti->next = DA_NULL; + + update_dl_info = (user_download_info_t *)&(client_noti->type.update_dl_info); + update_dl_info->download_id = dl_id; + update_dl_info->file_size = file_size; + if (pure_file_name && extension) { + len = strlen(pure_file_name) + strlen(extension) + 1; + update_dl_info->content_name = (char *)calloc(len + 1, sizeof(char)); + if (!update_dl_info->content_name) { + free(client_noti); + return DA_ERR_FAIL_TO_MEMALLOC; + } + snprintf(update_dl_info->content_name, len + 1, "%s.%s", + pure_file_name, extension); + } + + /* These strings MUST be copied to detach __thread_for_client_noti from download_info */ + if (file_type) + update_dl_info->file_type = strdup(file_type); + + if (tmp_saved_path) + update_dl_info->tmp_saved_path = strdup(tmp_saved_path); + + if (etag) + update_dl_info->etag = strdup(etag); + DA_LOG_DEBUG(ClientNoti, "pushing slot_id=%d, dl_id=%d", slot_id, dl_id); + + push_client_noti(client_noti); + + return DA_RESULT_OK; +} + +da_result_t send_client_finished_info ( + int slot_id, + int dl_id, + char *saved_path, + char *etag, + int error, + int http_status + ) +{ + client_noti_t *client_noti = DA_NULL; + user_finished_info_t *finished_info = DA_NULL; + + DA_LOG_FUNC_LOGV(ClientNoti); + + if (!is_valid_slot_id(slot_id)) { + DA_LOG_ERR(ClientNoti, "Download ID is not valid"); + return DA_ERR_INVALID_DL_REQ_ID; + } + + client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t)); + if (!client_noti) { + DA_LOG_ERR(ClientNoti, "calloc fail"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + client_noti->slot_id = slot_id; + client_noti->user_data = GET_DL_USER_DATA(slot_id); + client_noti->noti_type = Q_CLIENT_NOTI_TYPE_FINISHED_INFO; + client_noti->next = DA_NULL; + + finished_info = (user_finished_info_t *)&(client_noti->type.finished_info); + finished_info->download_id = dl_id; + finished_info->err = error; + finished_info->http_status = http_status; + + if (saved_path) { + finished_info->saved_path = strdup(saved_path); + DA_SECURE_LOGD("saved path=%s", saved_path); + } + if (etag) { + finished_info->etag = strdup(etag); + DA_SECURE_LOGD("pushing finished info. etag[%s]", etag); + } + DA_LOG_VERBOSE(ClientNoti, "user_data=%p", client_noti->user_data); + DA_LOG_VERBOSE(ClientNoti, "http_status=%d", http_status); + DA_LOG(ClientNoti, "pushing slot_id=%d, dl_id=%d err=%d", slot_id, dl_id, error); + + push_client_noti(client_noti); + + return DA_RESULT_OK; +} + +da_result_t __launch_client_thread(void) +{ + pthread_t thread_id = 0; + + DA_LOG_FUNC_LOGV(Thread); + + if (pthread_create(&thread_id, DA_NULL, + __thread_for_client_noti,DA_NULL) < 0) { + DA_LOG_ERR(Thread, "making thread failed.."); + return DA_ERR_FAIL_TO_CREATE_THREAD; + } + DA_LOG_VERBOSE(Thread, "client mgr thread id[%d]", thread_id); + client_app_mgr.thread_id = thread_id; + return DA_RESULT_OK; +} + +void destroy_client_noti(client_noti_t *client_noti) +{ + if (client_noti) { + if (client_noti->noti_type == Q_CLIENT_NOTI_TYPE_STARTED_INFO) { + user_download_info_t *update_dl_info = DA_NULL; + update_dl_info = (user_download_info_t*)&(client_noti->type.update_dl_info); + if (update_dl_info->file_type) { + free(update_dl_info->file_type); + update_dl_info->file_type = DA_NULL; + } + if (update_dl_info->tmp_saved_path) { + free(update_dl_info->tmp_saved_path); + update_dl_info->tmp_saved_path = DA_NULL; + } + if (update_dl_info->etag) { + free(update_dl_info->etag); + update_dl_info->etag = DA_NULL; + } + } else if (client_noti->noti_type == + Q_CLIENT_NOTI_TYPE_FINISHED_INFO) { + user_finished_info_t *finished_info = DA_NULL; + finished_info = (user_finished_info_t*) + &(client_noti->type.finished_info); + if (finished_info->saved_path) { + free(finished_info->saved_path); + finished_info->saved_path = DA_NULL; + } + if (finished_info->etag) { + free(finished_info->etag); + finished_info->etag = DA_NULL; + } + } + free(client_noti); + } +} + + +void push_client_noti(client_noti_t *client_noti) +{ + client_queue_t *queue = DA_NULL; + client_noti_t *head = DA_NULL; + client_noti_t *pre = DA_NULL; + client_noti_t *cur = DA_NULL; + + queue = &(client_app_mgr.client_queue); + _da_thread_mutex_lock (&(queue->mutex_client_queue)); + + head = queue->client_q_head; + if (!head) { + queue->client_q_head = client_noti; + } else { + cur = head; + while (cur->next) { + pre = cur; + cur = pre->next; + } +#if 0 + if (cur->noti_type == Q_CLIENT_NOTI_TYPE_PROGRESS_INFO) { + /* For UI performance. If the update noti info is existed at queue, + replace it with new update noti info */ + if (cur->slot_id == client_noti->slot_id) { + /* DA_LOG(ClientNoti, "exchange queue's tail and pushing item"); */ + if (pre == DA_NULL) + queue->client_q_head = client_noti; + else + pre->next = client_noti; + destroy_client_noti(cur); + } else { + cur->next = client_noti; + } + } else { + cur->next = client_noti; + } +#else + cur->next = client_noti; +#endif + } + + queue->having_data = DA_TRUE; + + __client_q_wake_up_without_lock(); + if (queue->client_q_head->next) { + DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti[%p]", + queue->client_q_head, queue->client_q_head->next); + } else { + DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti is NULL", + queue->client_q_head); + } + + _da_thread_mutex_unlock (&(queue->mutex_client_queue)); +} + +void __pop_client_noti(client_noti_t **out_client_noti) +{ + client_queue_t *queue = DA_NULL; + + queue = &(client_app_mgr.client_queue); + + _da_thread_mutex_lock (&(queue->mutex_client_queue)); + + if (queue->client_q_head) { + *out_client_noti = queue->client_q_head; + queue->client_q_head = queue->client_q_head->next; + if (queue->client_q_head) { + DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti[%p]", + *out_client_noti, queue->client_q_head); + } else { + DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti is NULL", + *out_client_noti); + } + } else { + *out_client_noti = DA_NULL; + } + + if (queue->client_q_head == DA_NULL) { + queue->having_data = DA_FALSE; + } + + _da_thread_mutex_unlock (&(queue->mutex_client_queue)); +} + +void __client_q_goto_sleep_without_lock(void) +{ + client_queue_t *queue = DA_NULL; + queue = &(client_app_mgr.client_queue); + _da_thread_cond_wait(&(queue->cond_client_queue), &(queue->mutex_client_queue)); +} + +void __client_q_wake_up_without_lock(void) +{ + client_queue_t *queue = DA_NULL; + queue = &(client_app_mgr.client_queue); + _da_thread_cond_signal(&(queue->cond_client_queue)); +} + +void __thread_clean_up_handler_for_client_thread(void *arg) +{ + DA_LOG_CRITICAL(Thread, "cleanup for thread id = %d", pthread_self()); +} + +static void *__thread_for_client_noti(void *data) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t need_wait = DA_TRUE; + client_queue_t *queue = DA_NULL; + client_noti_t *client_noti = DA_NULL; + + DA_LOG_FUNC_LOGV(Thread); + + _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr)); + client_app_mgr.is_thread_init = DA_TRUE; + _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr)); + + queue = &(client_app_mgr.client_queue); + DA_LOG_VERBOSE(ClientNoti, "client queue = %p", queue); + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, DA_NULL); + pthread_cleanup_push(__thread_clean_up_handler_for_client_thread, (void *)DA_NULL); + + do { + _da_thread_mutex_lock(&(queue->mutex_client_queue)); + if (DA_FALSE == IS_CLIENT_Q_HAVING_DATA(queue)) { + DA_LOG_VERBOSE(Thread, "Sleep @ thread_for_client_noti!"); + __client_q_goto_sleep_without_lock(); + DA_LOG_VERBOSE(Thread, "Woke up @ thread_for_client_noti"); + } + _da_thread_mutex_unlock(&(queue->mutex_client_queue)); + + do { + __pop_client_noti(&client_noti); + if (client_noti == DA_NULL) { + DA_LOG_ERR(ClientNoti, "There is no data on client queue!"); + ret = DA_ERR_INVALID_STATE; + need_wait = DA_FALSE; + } else { + DA_LOG_VERBOSE(ClientNoti, "noti type[%d]", + client_noti->noti_type); + switch (client_noti->noti_type) { + case Q_CLIENT_NOTI_TYPE_STARTED_INFO: + { + user_download_info_t *update_dl_info = DA_NULL;; + update_dl_info = (user_download_info_t*)(&(client_noti->type.update_dl_info)); + if (client_app_mgr.client_app_info.client_callback.update_dl_info_cb) { + client_app_mgr.client_app_info.client_callback.update_dl_info_cb(update_dl_info, client_noti->user_data); + if (update_dl_info->etag) + DA_SECURE_LOGD("Etag:[%s]", update_dl_info->etag); + DA_SECURE_LOGD("file size=%lu", update_dl_info->file_size); + DA_LOG(ClientNoti, "Update download info for slot_id=%d, dl_id=%d- DONE", + client_noti->slot_id, + update_dl_info->download_id + ); + } + } + break; + case Q_CLIENT_NOTI_TYPE_PROGRESS_INFO: + { + user_progress_info_t *progress_info = DA_NULL;; + progress_info = (user_progress_info_t*)(&(client_noti->type.update_progress_info)); + if (client_app_mgr.client_app_info.client_callback.update_progress_info_cb) { + client_app_mgr.client_app_info.client_callback.update_progress_info_cb(progress_info, client_noti->user_data); + DA_LOG_VERBOSE(ClientNoti, "Update downloading info for slot_id=%d, dl_id=%d, received size=%lu - DONE", + client_noti->slot_id, + progress_info->download_id, + progress_info->received_size); + } + } + break; + case Q_CLIENT_NOTI_TYPE_FINISHED_INFO: + { + user_finished_info_t *finished_info = DA_NULL;; + finished_info = (user_finished_info_t*)(&(client_noti->type.finished_info)); + if (client_app_mgr.client_app_info.client_callback.finished_info_cb) { + client_app_mgr.client_app_info.client_callback.finished_info_cb( + finished_info, client_noti->user_data); + DA_LOG(ClientNoti, "Completed info for slot_id=%d, dl_id=%d, err=%d http_state=%d user_data=%p- DONE", + client_noti->slot_id, + finished_info->download_id, + finished_info->err, + finished_info->http_status, + client_noti->user_data); + if (finished_info->etag) + DA_SECURE_LOGD("Completed info for etag=%s - DONE", + finished_info->etag); + + } + } + break; + case Q_CLIENT_NOTI_TYPE_PAUSED_INFO: + { + user_paused_info_t *da_paused_info = DA_NULL; + da_paused_info = (user_paused_info_t *)(&(client_noti->type.paused_info)); + + if (client_app_mgr.client_app_info.client_callback.paused_info_cb) { + DA_LOG(ClientNoti, "User Paused info for slot_id=%d, dl_id=%d - Done", + client_noti->slot_id, + da_paused_info->download_id); + client_app_mgr.client_app_info.client_callback.paused_info_cb( + da_paused_info, client_noti->user_data); + } + } + break; + case Q_CLIENT_NOTI_TYPE_TERMINATE: + DA_LOG_VERBOSE(ClientNoti, "Q_CLIENT_NOTI_TYPE_TERMINATE"); + need_wait = DA_FALSE; + break; + } + destroy_client_noti(client_noti); + } + + if(DA_TRUE == need_wait) { + _da_thread_mutex_lock(&(queue->mutex_client_queue)); + if (DA_FALSE == IS_CLIENT_Q_HAVING_DATA(queue)) { + _da_thread_mutex_unlock (&(queue->mutex_client_queue)); + break; + } else { + _da_thread_mutex_unlock (&(queue->mutex_client_queue)); + } + } else { + break; + } + } while (1); + } while (DA_TRUE == need_wait); + + _da_thread_mutex_destroy(&(queue->mutex_client_queue)); + _da_thread_cond_destroy(&(queue->cond_client_queue)); + + pthread_cleanup_pop(0); + DA_LOG_DEBUG(Thread, "=====thread_for_client_noti- EXIT====="); + pthread_exit((void *)NULL); + return DA_NULL; +} + +char *get_client_user_agent_string(void) +{ + if (!client_app_mgr.is_init) + return DA_NULL; + + return client_app_mgr.client_app_info.client_user_agent; +} diff --git a/agent/download-agent-debug.c b/agent/download-agent-debug.c new file mode 100755 index 0000000..07078b3 --- /dev/null +++ b/agent/download-agent-debug.c @@ -0,0 +1,205 @@ +/* + * 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 <stdlib.h> +#include <string.h> + +#include "download-agent-debug.h" +#include "download-agent-utils.h" + +#define STRING_IT(x) #x +#define TURN_ON_LOG(channel) (DALogBitMap |= (0x1<<(channel))) + +int DALogBitMap; + +char *__get_log_env(void); +char **__parsing_log_env(char *in_log_env); +char *__copying_str(char *source, int length); +char *__get_channel_name_from_enum(da_log_channel channel_enum); + +da_result_t init_log_mgr(void) { + da_result_t ret = DA_RESULT_OK; + static da_bool_t did_log_mgr_init = DA_FALSE; + char *log_env = DA_NULL; + char **parsed_log_env = DA_NULL; + char **cur_parsed_log_env = DA_NULL; + int i = 0; + + if (did_log_mgr_init) + return ret; + + did_log_mgr_init = DA_TRUE; + + log_env = __get_log_env(); + if (!log_env) { + /* If no environment values are found, do behave like all logs are turned on except for Soup log */ + DALogBitMap = ~(0x1 << Soup); + return ret; + } + + TURN_ON_LOG(Default); + + parsed_log_env = __parsing_log_env(log_env); + if (parsed_log_env) { + char *channel_keyward = DA_NULL; + for (cur_parsed_log_env = parsed_log_env; *cur_parsed_log_env; cur_parsed_log_env++) { + if (!*cur_parsed_log_env) + break; + for (i = 0; i < DA_LOG_CHANNEL_MAX; i++) { + channel_keyward = __get_channel_name_from_enum(i); + if (channel_keyward && !strcmp(*cur_parsed_log_env, + channel_keyward)) { + TURN_ON_LOG(i); + break; + } + } + free(*cur_parsed_log_env); + } + free(parsed_log_env); + } + + if (log_env) + free(log_env); + + return ret; +} + +char *__get_log_env(void) { + char *log_env = DA_NULL; + + /* environment value has higher priority than configure file */ + log_env = getenv(DA_DEBUG_ENV_KEY); + if (log_env && strlen(log_env)) + return strdup(log_env); + + if (read_data_from_file(DA_DEBUG_CONFIG_FILE_PATH, &log_env)) + return log_env; + + return DA_NULL; +} + +char **__parsing_log_env(char *in_log_env) { + char **out_parsed_result = DA_NULL; + + char **temp_result_array = DA_NULL; + char **cur_temp_result_array = DA_NULL; + int how_many_item = 0; + int how_many_delimeter = 0; + + char delimiter = ','; + + char *org_str = in_log_env; + char *cur_char = org_str; + char *start = org_str; + char *end = org_str; + int target_len = 0; + + if (!org_str) + return DA_NULL; + + /* counting delimiter to know how many items should be memory allocated. + * This causes two round of loop (counting delimiter and real operation). + * But I think it is tolerable, because input parameter is from console. + * And it is also a reason why we should not use fixed length array. + * Users are hard to input very long environment, but it is possible. */ + for (cur_char = org_str; *cur_char; cur_char++) { + if (*cur_char == delimiter) + how_many_delimeter++; + } + how_many_item = how_many_delimeter + 1; + temp_result_array = (char**) calloc(1, how_many_item + 1); + if (!(temp_result_array)) + goto ERR; + + cur_temp_result_array = temp_result_array; + cur_char = org_str; + while (1) { + if (*cur_char == delimiter) { + end = cur_char; + target_len = (int) (end - start); + *cur_temp_result_array++ = __copying_str(start, + target_len); + start = ++cur_char; + continue; + } else if (!(*cur_char)) { + end = cur_char; + target_len = (int) (end - start); + *cur_temp_result_array++ = __copying_str(start, + target_len); + *cur_temp_result_array = DA_NULL; + break; + } else { + cur_char++; + } + } + out_parsed_result = temp_result_array; +ERR: + return out_parsed_result; +} + +char *__copying_str(char *source, int length) { + char *copied_str = DA_NULL; + char *cur_pos = DA_NULL; + char white_space = ' '; + char end_of_line = 10; /* ASCII for LF */ + int i = 0; + + if (!source || !(length > 0)) + return DA_NULL; + + copied_str = (char*) calloc(1, length + 1); + if (copied_str) { + cur_pos = copied_str; + for (i = 0; i < length; i++) { + if ((source[i] != white_space) && (source[i] + != end_of_line)) + *cur_pos++ = source[i]; + } + } + + return copied_str; +} + +char *__get_channel_name_from_enum(da_log_channel channel_enum) { + switch (channel_enum) { + case Soup: + return STRING_IT(Soup); + case HTTPManager: + return STRING_IT(HTTPManager); + case FileManager: + return STRING_IT(FileManager); + case DRMManager: + return STRING_IT(DRMManager); + case DownloadManager: + return STRING_IT(DownloadManager); + case ClientNoti: + return STRING_IT(ClientNoti); + case HTTPMessageHandler: + return STRING_IT(HTTPMessageHandler); + case Encoding: + return STRING_IT(Encoding); + case QueueManager: + return STRING_IT(QueueManager); + case Parsing: + return STRING_IT(Parsing); + case Thread: + return STRING_IT(Thread); + case Default: + return STRING_IT(Default); + default: + return DA_NULL; + } +} diff --git a/agent/download-agent-dl-info-util.c b/agent/download-agent-dl-info-util.c new file mode 100755 index 0000000..2b66357 --- /dev/null +++ b/agent/download-agent-dl-info-util.c @@ -0,0 +1,498 @@ +/* + * 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 <string.h> + +#include "download-agent-client-mgr.h" +#include "download-agent-dl-info-util.h" +#include "download-agent-debug.h" +#include "download-agent-utils.h" +#include "download-agent-file.h" +#include "download-agent-http-mgr.h" +#include "download-agent-plugin-conf.h" + +pthread_mutex_t mutex_download_state[DA_MAX_DOWNLOAD_ID]; +static pthread_mutex_t mutex_download_mgr = PTHREAD_MUTEX_INITIALIZER; +download_mgr_t download_mgr; + +void cleanup_source_info_basic_download(source_info_basic_t *source_info_basic); +void cleanup_req_dl_info_http(req_dl_info *http_download); +void destroy_file_info(file_info *file); + +da_result_t init_download_mgr() { + da_result_t ret = DA_RESULT_OK; + int i = 0; + + DA_LOG_FUNC_LOGV(Default); + + _da_thread_mutex_lock(&mutex_download_mgr); + + if (download_mgr.is_init == DA_FALSE) { + download_mgr.is_init = DA_TRUE; + + for (i = 0; i < DA_MAX_DOWNLOAD_ID; i++) { + _da_thread_mutex_init(&mutex_download_state[i], DA_NULL); + init_download_info(i); + } + init_dl_id_history(&(download_mgr.dl_id_history)); + } + + _da_thread_mutex_unlock(&mutex_download_mgr); + + return ret; +} + +da_result_t deinit_download_mgr(void) { + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGV(Default); + + _da_thread_mutex_lock(&mutex_download_mgr); + if (download_mgr.is_init == DA_TRUE) { + int i = 0; + dl_info_t *dl_info = DA_NULL; + void *t_return = NULL; + for (i = 0; i < DA_MAX_DOWNLOAD_ID; i++) { + dl_info = &(download_mgr.dl_info[i]); + if (dl_info && dl_info->is_using) { + request_to_abort_http_download(GET_DL_CURRENT_STAGE(i)); + DA_LOG_CRITICAL(Thread, "===download id[%d] thread id[%lu] join===",i, GET_DL_THREAD_ID(i)); +/* Because the download daemon can call the deinit function, the resources of pthread are not freed + FIXME later : It is needed to change the termination flow again. + if (pthread_join(GET_DL_THREAD_ID(i), &t_return) < 0) { + DA_LOG_ERR(Thread, "join client thread is failed!!!"); + } +*/ + DA_LOG_CRITICAL(Thread, "===download id[%d] thread join return[%d]===",i, (char*)t_return); + } + } + download_mgr.is_init = DA_FALSE; + deinit_dl_id_history(&(download_mgr.dl_id_history)); + } + _da_thread_mutex_unlock(&mutex_download_mgr); + return ret; +} + +void init_download_info(int slot_id) +{ + dl_info_t *dl_info = DA_NULL; + + DA_LOG_FUNC_LOGV(Default); + + _da_thread_mutex_lock(&mutex_download_state[slot_id]); + dl_info = &(download_mgr.dl_info[slot_id]); + dl_info->is_using = DA_FALSE; + dl_info->state = DOWNLOAD_STATE_IDLE; + dl_info->download_stage_data = DA_NULL; + dl_info->dl_id = 0; + dl_info->http_status = 0; + dl_info->enable_pause_update = DA_FALSE; + dl_info->user_install_path = DA_NULL; + dl_info->user_file_name = DA_NULL; + dl_info->user_etag = DA_NULL; + dl_info->user_temp_file_path = DA_NULL; + dl_info->user_data = DA_NULL; + + Q_init_queue(&(dl_info->queue)); + + DA_LOG_VERBOSE(Default, "Init slot_id [%d] Info END", slot_id); + _da_thread_mutex_unlock(&mutex_download_state[slot_id]); + + return; +} + +void destroy_download_info(int slot_id) +{ + dl_info_t *dl_info = DA_NULL; + + DA_LOG_VERBOSE(Default, "Destroying slot_id [%d] Info", slot_id); + + if (slot_id == DA_INVALID_ID) { + DA_LOG_ERR(Default, "invalid slot_id"); + return; + } + + dl_info = &(download_mgr.dl_info[slot_id]); + if (DA_FALSE == dl_info->is_using) { + return; + } + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + dl_info->state = DOWNLOAD_STATE_IDLE; + dl_info->active_dl_thread_id = 0; + + if (dl_info->download_stage_data != DA_NULL) { + remove_download_stage(slot_id, dl_info->download_stage_data); + dl_info->download_stage_data = DA_NULL; + } + dl_info->dl_id = 0; + dl_info->enable_pause_update = DA_FALSE; + if (dl_info->user_install_path) { + free(dl_info->user_install_path); + dl_info->user_install_path = DA_NULL; + } + + if (dl_info->user_file_name) { + free(dl_info->user_file_name); + dl_info->user_file_name = DA_NULL; + } + + if (dl_info->user_etag) { + free(dl_info->user_etag); + dl_info->user_etag = DA_NULL; + } + + if (dl_info->user_temp_file_path) { + free(dl_info->user_temp_file_path); + dl_info->user_temp_file_path = DA_NULL; + } + + dl_info->user_data = DA_NULL; + + Q_destroy_queue(&(dl_info->queue)); + dl_info->http_status = 0; + + dl_info->is_using = DA_FALSE; + + DA_LOG_DEBUG(Default, "Destroying slot_id [%d] Info END", slot_id); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + return; +} + +void *Add_new_download_stage(int slot_id) +{ + stage_info *download_stage_data = NULL; + stage_info *new_download_stage_data = NULL; + + DA_LOG_FUNC_LOGV(Default); + + new_download_stage_data = (stage_info*)calloc(1, sizeof(stage_info)); + if (!new_download_stage_data) + goto ERR; + + new_download_stage_data->dl_id = slot_id; + download_stage_data = GET_DL_CURRENT_STAGE(slot_id); + if (download_stage_data) { + while (download_stage_data->next_stage_info) { + download_stage_data + = download_stage_data->next_stage_info; + }; + download_stage_data->next_stage_info = new_download_stage_data; + } else { + GET_DL_CURRENT_STAGE(slot_id) = new_download_stage_data; + } + DA_LOG_VERBOSE(Default, "NEW STAGE ADDED FOR DOWNLOAD ID[%d] new_stage[%p]", slot_id,new_download_stage_data); + +ERR: + return new_download_stage_data; +} + +void remove_download_stage(int slot_id, stage_info *in_stage) +{ + stage_info *stage = DA_NULL; + + DA_LOG_FUNC_LOGV(Default); + + stage = GET_DL_CURRENT_STAGE(slot_id); + if (DA_NULL == stage) { + DA_LOG_VERBOSE(Default, "There is no stage field on slot_id = %d", slot_id); + goto ERR; + } + + if (DA_NULL == in_stage) { + DA_LOG_VERBOSE(Default, "There is no in_stage to remove."); + goto ERR; + } + + if (in_stage == stage) { + DA_LOG_VERBOSE(Default, "Base stage will be removed. in_stage[%p]",in_stage); + DA_LOG_VERBOSE(Default, "next stage[%p]",stage->next_stage_info); + GET_DL_CURRENT_STAGE(slot_id) = stage->next_stage_info; + empty_stage_info(in_stage); + free(in_stage); + in_stage = DA_NULL; + } else { + while (in_stage != stage->next_stage_info) { + stage = stage->next_stage_info; + } + if (in_stage == stage->next_stage_info) { + stage->next_stage_info + = stage->next_stage_info->next_stage_info; + DA_LOG_VERBOSE(Default, "Stage will be removed. in_stage[%p]",in_stage); + DA_LOG_VERBOSE(Default, "next stage[%p]",stage->next_stage_info); + empty_stage_info(in_stage); + free(in_stage); + in_stage = DA_NULL; + } + } + +ERR: + return; +} + +void empty_stage_info(stage_info *in_stage) +{ + source_info_t *source_information = NULL; + req_dl_info *request_download_info = NULL; + file_info *file_information = NULL; + + DA_LOG_VERBOSE(Default, "Stage to Remove:[%p]", in_stage); + source_information = GET_STAGE_SOURCE_INFO(in_stage); + + cleanup_source_info_basic_download( + GET_SOURCE_BASIC(source_information)); + + request_download_info = GET_STAGE_TRANSACTION_INFO(in_stage); + + cleanup_req_dl_info_http(request_download_info); + + file_information = GET_STAGE_CONTENT_STORE_INFO(in_stage); + destroy_file_info(file_information); +} + +void cleanup_source_info_basic_download(source_info_basic_t *source_info_basic) +{ + if (NULL == source_info_basic) + goto ERR; + + DA_LOG_FUNC_LOGV(Default); + + if (NULL != source_info_basic->url) { + free(source_info_basic->url); + source_info_basic->url = DA_NULL; + } + +ERR: + return; + +} + +void cleanup_req_dl_info_http(req_dl_info *http_download) +{ + DA_LOG_FUNC_LOGV(Default); + + if (http_download->http_info.http_msg_request) { + http_msg_request_destroy( + &(http_download->http_info.http_msg_request)); + http_download->http_info.http_msg_request = DA_NULL; + } + + if (http_download->http_info.http_msg_response) { + http_msg_response_destroy( + &(http_download->http_info.http_msg_response)); + http_download->http_info.http_msg_response = DA_NULL; + } + + if (DA_NULL != http_download->location_url) { + free(http_download->location_url); + http_download->location_url = DA_NULL; + } + if (DA_NULL != http_download->content_type_from_header) { + free(http_download->content_type_from_header); + http_download->content_type_from_header = DA_NULL; + } + + if (DA_NULL != http_download->etag_from_header) { + free(http_download->etag_from_header); + http_download->etag_from_header = DA_NULL; + } + + http_download->invloved_transaction_id = DA_INVALID_ID; + http_download->content_len_from_header = 0; + http_download->downloaded_data_size = 0; + + _da_thread_mutex_destroy(&(http_download->mutex_http_state)); + + return; +} + +void destroy_file_info(file_info *file_information) +{ + DA_LOG_FUNC_LOGV(Default); + + if (!file_information) + return; + + if (file_information->file_name_final) { + free(file_information->file_name_final); + file_information->file_name_final = NULL; + } + + if (file_information->content_type) { + free(file_information->content_type); + file_information->content_type = NULL; + } + + if (file_information->pure_file_name) { + free(file_information->pure_file_name); + file_information->pure_file_name = NULL; + } + + if (file_information->extension) { + free(file_information->extension); + file_information->extension = NULL; + } + return; +} + +void clean_up_client_input_info(client_input_t *client_input) +{ + DA_LOG_FUNC_LOGV(Default); + + if (client_input) { + client_input->user_data = NULL; + + if (client_input->install_path) { + free(client_input->install_path); + client_input->install_path = DA_NULL; + } + + if (client_input->file_name) { + free(client_input->file_name); + client_input->file_name = DA_NULL; + } + + if (client_input->etag) { + free(client_input->etag); + client_input->etag = DA_NULL; + } + + if (client_input->temp_file_path) { + free(client_input->temp_file_path); + client_input->temp_file_path = DA_NULL; + } + + if (client_input->pkg_name) { + free(client_input->pkg_name); + client_input->pkg_name = DA_NULL; + } + + client_input_basic_t *client_input_basic = + &(client_input->client_input_basic); + + if (client_input_basic && client_input_basic->req_url) { + free(client_input_basic->req_url); + client_input_basic->req_url = DA_NULL; + } + + if (client_input_basic && client_input_basic->user_request_header) { + int i = 0; + int count = client_input_basic->user_request_header_count; + for (i = 0; i < count; i++) + { + if (client_input_basic->user_request_header[i]) { + free(client_input_basic->user_request_header[i]); + client_input_basic->user_request_header[i] = DA_NULL; + } + } + + free(client_input_basic->user_request_header); + client_input_basic->user_request_header = DA_NULL; + client_input_basic->user_request_header_count = 0; + } + } else { + DA_LOG_ERR(Default, "client_input is NULL."); + } + + return; +} + +da_result_t get_slot_id_for_dl_id( + int dl_id, + int* slot_id) +{ + da_result_t ret = DA_ERR_INVALID_DL_REQ_ID; + int iter = 0; + + if (dl_id < 0) { + DA_LOG_ERR(Default, "dl_id is less than 0 - %d", dl_id); + return DA_ERR_INVALID_DL_REQ_ID; + } + + _da_thread_mutex_lock(&mutex_download_mgr); + for (iter = 0; iter < DA_MAX_DOWNLOAD_ID; iter++) { + if (download_mgr.dl_info[iter].is_using == DA_TRUE) { + if (download_mgr.dl_info[iter].dl_id == + dl_id) { + *slot_id = iter; + ret = DA_RESULT_OK; + break; + } + } + } + _da_thread_mutex_unlock(&mutex_download_mgr); + + return ret; +} + + +da_result_t get_available_slot_id(int *available_id) +{ + da_result_t ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + int i; + + _da_thread_mutex_lock(&mutex_download_mgr); + for (i = 0; i < DA_MAX_DOWNLOAD_ID; i++) { + if (download_mgr.dl_info[i].is_using == DA_FALSE) { + init_download_info(i); + + download_mgr.dl_info[i].is_using = DA_TRUE; + + download_mgr.dl_info[i].dl_id + = get_available_dl_id(&(download_mgr.dl_id_history)); + + *available_id = i; + DA_LOG_VERBOSE(Default, "available download id = %d", *available_id); + ret = DA_RESULT_OK; + + break; + } + } + _da_thread_mutex_unlock(&mutex_download_mgr); + + return ret; +} + +da_bool_t is_valid_slot_id(int slot_id) +{ + da_bool_t ret = DA_FALSE; + + if (slot_id >= 0 && slot_id < DA_MAX_DOWNLOAD_ID) { + if (download_mgr.dl_info[slot_id].is_using == DA_TRUE) + ret = DA_TRUE; + } + + return ret; +} + +void store_http_status(int dl_id, int status) +{ + if (status < 100 || status > 599) { + DA_LOG_ERR(Default, "Invalid status code [%d]", status); + return; + } + DA_LOG_VERBOSE(Default, "store_http_status id[%d]status[%d] ",dl_id, status); + download_mgr.dl_info[dl_id].http_status = status; +} + +int get_http_status(int slot_id) +{ + if (!download_mgr.dl_info[slot_id].is_using) { + DA_LOG_ERR(Default, "Invalid slot_id [%d]", slot_id); + return 0; + } + return download_mgr.dl_info[slot_id].http_status; +} diff --git a/agent/download-agent-dl-mgr.c b/agent/download-agent-dl-mgr.c new file mode 100755 index 0000000..7a2ce0d --- /dev/null +++ b/agent/download-agent-dl-mgr.c @@ -0,0 +1,314 @@ +/* + * 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-agent-client-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-utils.h" +#include "download-agent-http-mgr.h" +#include "download-agent-file.h" +#include "download-agent-plugin-conf.h" + + +static da_result_t __cancel_download_with_slot_id(int slot_id); +static da_result_t __suspend_download_with_slot_id(int slot_id); + + +da_result_t requesting_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + req_dl_info *request_session = DA_NULL; + + DA_LOG_FUNC_LOGV(Default); + + if (!stage) { + DA_LOG_ERR(Default, "stage is null.."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + ret = make_req_dl_info_http(stage, GET_STAGE_TRANSACTION_INFO(stage)); + if (ret != DA_RESULT_OK) + goto ERR; + + request_session = GET_STAGE_TRANSACTION_INFO(stage); + ret = request_http_download(stage); + if (DA_RESULT_OK == ret) { + DA_LOG_VERBOSE(Default, "Http download is complete."); + } else { + DA_LOG_ERR(Default, "Http download is failed. ret = %d", ret); + goto ERR; + } +ERR: + return ret; +} + +da_result_t handle_after_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + da_mime_type_id_t mime_type = DA_MIME_TYPE_NONE; + + DA_LOG_FUNC_LOGV(Default); + + mime_type = get_mime_type_id( + GET_CONTENT_STORE_CONTENT_TYPE(GET_STAGE_CONTENT_STORE_INFO(stage))); + + switch (mime_type) { + case DA_MIME_TYPE_NONE: + DA_LOG(Default, "DA_MIME_TYPE_NONE"); + ret = DA_ERR_MISMATCH_CONTENT_TYPE; + break; + default: + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_FINISH, stage); + break; + } /* end of switch */ + + return ret; +} + +static da_result_t __cancel_download_with_slot_id(int slot_id) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state; + stage_info *stage = DA_NULL; + + DA_LOG_FUNC_LOGD(Default); + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + download_state = GET_DL_STATE_ON_ID(slot_id); + DA_LOG(Default, "download_state = %d", GET_DL_STATE_ON_ID(slot_id)); + + if (download_state == DOWNLOAD_STATE_FINISH || + download_state == DOWNLOAD_STATE_CANCELED) { + DA_LOG_CRITICAL(Default, "Already download is finished. Do not send cancel request"); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + return ret; + } + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + + stage = GET_DL_CURRENT_STAGE(slot_id); + if (!stage) + return DA_RESULT_OK; + + ret = request_to_cancel_http_download(stage); + if (ret != DA_RESULT_OK) + goto ERR; + DA_LOG(Default, "Download cancel Successful for download id - %d", slot_id); +ERR: + return ret; +} + +da_result_t cancel_download(int dl_id) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_LOGD(Default); + + ret = get_slot_id_for_dl_id(dl_id, &slot_id); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(Default, "dl req ID is not Valid"); + goto ERR; + } + + if (DA_FALSE == is_valid_slot_id(slot_id)) { + DA_LOG_ERR(Default, "Download ID is not Valid"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + ret = __cancel_download_with_slot_id(slot_id); + +ERR: + return ret; + +} + +static da_result_t __suspend_download_with_slot_id(int slot_id) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state; + stage_info *stage = DA_NULL; + + DA_LOG_FUNC_LOGD(Default); + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + download_state = GET_DL_STATE_ON_ID(slot_id); + DA_LOG(Default, "download_state = %d", GET_DL_STATE_ON_ID(slot_id)); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + + stage = GET_DL_CURRENT_STAGE(slot_id); + if (!stage) + return DA_ERR_CANNOT_SUSPEND; + + ret = request_to_suspend_http_download(stage); + if (ret != DA_RESULT_OK) + goto ERR; + DA_LOG(Default, "Download Suspend Successful for download id-%d", slot_id); +ERR: + return ret; +} + +da_result_t suspend_download(int dl_id, da_bool_t is_enable_cb) +{ + da_result_t ret = DA_RESULT_OK; + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_LOGD(Default); + + ret = get_slot_id_for_dl_id(dl_id, &slot_id); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(Default, "dl req ID is not Valid"); + goto ERR; + } + GET_DL_ENABLE_PAUSE_UPDATE(slot_id) = is_enable_cb; + if (DA_FALSE == is_valid_slot_id(slot_id)) { + DA_LOG_ERR(Default, "Download ID is not Valid"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + ret = __suspend_download_with_slot_id(slot_id); + +ERR: + return ret; + +} + +static da_result_t __resume_download_with_slot_id(int slot_id) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state; + stage_info *stage = DA_NULL; + + DA_LOG_FUNC_LOGD(Default); + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + download_state = GET_DL_STATE_ON_ID(slot_id); + DA_LOG(Default, "download_state = %d", GET_DL_STATE_ON_ID(slot_id)); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + + stage = GET_DL_CURRENT_STAGE(slot_id); + + ret = request_to_resume_http_download(stage); + if (ret != DA_RESULT_OK) + goto ERR; + DA_LOG(Default, "Download Resume Successful for download id-%d", slot_id); +ERR: + return ret; +} + +da_result_t resume_download(int dl_id) +{ + da_result_t ret = DA_RESULT_OK; + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_LOGD(Default); + + ret = get_slot_id_for_dl_id(dl_id, &slot_id); + if (ret != DA_RESULT_OK) + goto ERR; + + if (DA_FALSE == is_valid_slot_id(slot_id)) { + DA_LOG_ERR(Default, "Download ID is not Valid"); + ret = DA_ERR_INVALID_DL_REQ_ID; + goto ERR; + } + + ret = __resume_download_with_slot_id(slot_id); + +ERR: + return ret; +} + +da_result_t send_user_noti_and_finish_download_flow( + int slot_id, char *installed_path, char *etag) +{ + da_result_t ret = DA_RESULT_OK; + download_state_t download_state = HTTP_STATE_READY_TO_DOWNLOAD; + da_bool_t need_destroy_download_info = DA_FALSE; + + DA_LOG_FUNC_LOGV(Default); + + _da_thread_mutex_lock (&mutex_download_state[slot_id]); + download_state = GET_DL_STATE_ON_ID(slot_id); + DA_LOG_DEBUG(Default, "state = %d", download_state); + _da_thread_mutex_unlock (&mutex_download_state[slot_id]); + + switch (download_state) { + case DOWNLOAD_STATE_FINISH: + send_client_finished_info(slot_id, GET_DL_ID(slot_id), + installed_path, DA_NULL, DA_RESULT_OK, + get_http_status(slot_id)); + need_destroy_download_info = DA_TRUE; + break; + case DOWNLOAD_STATE_CANCELED: + send_client_finished_info(slot_id, GET_DL_ID(slot_id), + installed_path, etag, DA_RESULT_USER_CANCELED, + get_http_status(slot_id)); + need_destroy_download_info = DA_TRUE; + break; +#ifdef PAUSE_EXIT + case DOWNLOAD_STATE_PAUSED: + need_destroy_download_info = DA_TRUE; + break; +#endif + default: + DA_LOG(Default, "download state = %d", download_state); + break; + } + + if (need_destroy_download_info == DA_TRUE) { + destroy_download_info(slot_id); + } else { + DA_LOG_CRITICAL(Default, "download info is not destroyed"); + } + + return ret; +} + +da_bool_t is_valid_download_id(int dl_id) +{ + + da_bool_t ret = DA_TRUE; + int slot_id = DA_INVALID_ID; + + DA_LOG_VERBOSE(Default, "[is_valid_download_id]download_id : %d", dl_id); + + ret = get_slot_id_for_dl_id(dl_id, &slot_id); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(Default, "dl req ID is not Valid"); + ret = DA_FALSE; + goto ERR; + } else { + ret = DA_TRUE; + } + + if (DA_FALSE == is_valid_slot_id(slot_id)) { + DA_LOG_ERR(Default, "Download ID is not Valid"); + ret = DA_FALSE; + goto ERR; + } + if (GET_DL_THREAD_ID(slot_id) < 1) { + DA_LOG_ERR(Default, "Download thread is not alive"); + ret = DA_FALSE; + goto ERR; + } + +ERR: + return ret; +} diff --git a/agent/download-agent-encoding.c b/agent/download-agent-encoding.c new file mode 100755 index 0000000..1955fcd --- /dev/null +++ b/agent/download-agent-encoding.c @@ -0,0 +1,282 @@ +/* + * 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 <string.h> +#include <stdlib.h> +#include <glib.h> + +#include "download-agent-encoding.h" +#include "download-agent-debug.h" + +da_result_t _parsing_base64_encoded_str(const char *in_encoded_str, + char **out_charset_type, + char *out_encoding_type, + char **out_raw_encoded_str); + +da_bool_t is_base64_encoded_word(const char *in_str) +{ + const char *haystack = DA_NULL; + char first_needle[8] = {0,}; + char second_needle[8] = {0,}; + char *found_str = DA_NULL; + + if (!in_str) { + DA_LOG_ERR(Default, "input string is NULL"); + return DA_FALSE; + } + + haystack = in_str; + if (haystack[0] == '"') { + snprintf(first_needle, sizeof(first_needle), "%s", "\"=?"); // "=? + snprintf(second_needle, sizeof(second_needle), "%s", "?=\""); // ?=" + } else { + snprintf(first_needle, sizeof(first_needle), "%s", "=?"); // =? + snprintf(second_needle, sizeof(second_needle), "%s", "?="); // ?= + } + +// DA_SECURE_LOGD("needle = [%s], haystack = [%s]", first_needle, haystack); + + found_str = strstr(haystack, first_needle); + if (found_str) { + if (found_str == haystack) { +// DA_SECURE_LOGD("Input string is starting with %s", needle); + haystack = haystack + strlen(haystack) - strlen(second_needle); +// DA_SECURE_LOGD("second haystack is [%s]", haystack); + if(!strcmp(haystack, second_needle)) + return DA_TRUE; + } + } + return DA_FALSE; +} + +da_result_t decode_base64_encoded_str(const char *in_encoded_str, + char **out_decoded_ascii_str) +{ + da_result_t ret = DA_RESULT_OK; + + const char *org_str = DA_NULL; + char *charset_type = NULL; + char encoding_type = '\0'; + char *raw_encoded_str = NULL; + char *decoded_str = NULL; + const gchar *g_encoded_text = NULL; + guchar *g_decoded_text = NULL; + gsize g_decoded_text_len = 0; + + DA_SECURE_LOGD("input str = [%s]", in_encoded_str); + + org_str = in_encoded_str; + if(!org_str) { + DA_LOG_ERR(Default, "Input string is NULL"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + ret = _parsing_base64_encoded_str(org_str, &charset_type, + &encoding_type, &raw_encoded_str); + if(ret != DA_RESULT_OK) { + goto ERR; + } + +// DA_SECURE_LOGD("charset = [%s], encoding = [%c], raw = [%s]", charset_type, encoding_type, raw_encoded_str); + + if(encoding_type != 'B') { + DA_LOG_ERR(Default, "Encoded Word is not encoded with Base64, but %c. We can only handle Base64.", encoding_type); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + /* + * on glib/gtype.h + * typedef char gchar; + * typedef unsigned char guchar; + * + */ + g_encoded_text = (const gchar*)raw_encoded_str; + g_decoded_text = g_base64_decode(g_encoded_text, &g_decoded_text_len); + + if(g_decoded_text) { + DA_SECURE_LOGD("g_decoded_text = [%s]", g_decoded_text); + decoded_str = (char*)calloc(1, g_decoded_text_len+1); + if(!decoded_str) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + memcpy(decoded_str, g_decoded_text, g_decoded_text_len); + } + } + DA_SECURE_LOGD("decoded_str = [%s]", decoded_str); + +ERR: + *out_decoded_ascii_str = decoded_str; + + if(charset_type) { + free(charset_type); + charset_type = NULL; + } + + if(raw_encoded_str) { + free(raw_encoded_str); + raw_encoded_str = NULL; + } + + if(g_decoded_text) { + g_free(g_decoded_text); + } + + return ret; +} + + +da_result_t _parsing_base64_encoded_str(const char *in_encoded_str, + char **out_charset_type, + char *out_encoding_type, + char **out_raw_encoded_str) +{ + da_result_t ret = DA_RESULT_OK; + + const char *org_str = DA_NULL; // e.g. =?UTF-8?B?7Jew7JWE7JmA7IKs7J6QLmpwZw==?= + char *charset_type = NULL; // e.g. UTF-8 + char encoding_type = '\0'; // e.g. B (means Base64) + char *raw_encoded_str = NULL; // e.g. 7Jew7JWE7JmA7IKs7J6QLmpwZw== + + char *haystack = DA_NULL; + char needle[8] = {0,}; + + char *wanted_str = DA_NULL; + int wanted_str_len = 0; + char *wanted_str_start = DA_NULL; + char *wanted_str_end = DA_NULL; + + org_str = in_encoded_str; + if (!org_str) { + DA_LOG_ERR(Default, "Input string is NULL"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + // strip "=?" + haystack = (char*)org_str; + snprintf(needle, sizeof(needle), "=?"); + wanted_str_end = strstr(haystack, needle); + if (!wanted_str_end) { + DA_LOG_ERR(Default, "DA_ERR_INVALID_ARGUMENT"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } else { + wanted_str = wanted_str_end + strlen(needle); + DA_SECURE_LOGD("strip [%s]", wanted_str); + } + + // for charset + haystack = wanted_str_start = wanted_str; + needle[0] = '?'; + wanted_str_end = strchr(haystack, needle[0]); + if (!wanted_str_end) { + DA_LOG_ERR(Default, "DA_ERR_INVALID_ARGUMENT"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } else { + wanted_str_len = wanted_str_end - wanted_str_start + 1; + wanted_str = (char*)calloc(1, wanted_str_len+1); + if (!wanted_str) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + snprintf(wanted_str, wanted_str_len+1, "%s", wanted_str_start); + charset_type = wanted_str; + wanted_str = DA_NULL; + } + + DA_LOG(Default, "charset [%s]", charset_type); + } + + + // for encoding + encoding_type = *(++wanted_str_end); + DA_LOG(Default, "encoding [%c]", encoding_type); + + // for raw encoded str + haystack = wanted_str_start = wanted_str_end + 1; + snprintf(needle, sizeof(needle), "?="); + wanted_str_end = strstr(haystack, needle); + if (!wanted_str_end) { + DA_LOG_ERR(Default, "DA_ERR_INVALID_ARGUMENT"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } else { + wanted_str_len = wanted_str_end - wanted_str_start + 1; + wanted_str = (char*)calloc(1, wanted_str_len+1); + if (!wanted_str) { + DA_LOG_ERR(Default, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + snprintf(wanted_str, wanted_str_len+1, "%s", wanted_str_start); + raw_encoded_str = wanted_str; + wanted_str = NULL; + } + + DA_SECURE_LOGD("raw encoded str [%s]", raw_encoded_str); + } + +ERR: + if (ret != DA_RESULT_OK) { + if (charset_type) { + free(charset_type); + charset_type = NULL; + } + } + + *out_charset_type = charset_type; + *out_encoding_type = encoding_type; + *out_raw_encoded_str = raw_encoded_str; + + return ret; +} + +void decode_url_encoded_str(const char *in_encoded_str, char **out_str) +{ + char *in = NULL; + char *out = NULL; + *out_str = calloc(1, strlen(in_encoded_str) + 1); + if (*out_str == NULL) + return; + out = *out_str; + in = (char *)in_encoded_str; + while (*in) + { + if (*in == '%') { + int hex = 0; + in++; + if (sscanf(in, "%2x", &hex) <= 0) { + return; + } else { + *out = hex; + in++; + } + } else if (*in == '+') { + *out = ' '; + } else { + *out = *in; + } + in++; + out++; + } +} + diff --git a/agent/download-agent-file.c b/agent/download-agent-file.c new file mode 100755 index 0000000..f871b7c --- /dev/null +++ b/agent/download-agent-file.c @@ -0,0 +1,1138 @@ +/* + * 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 <dirent.h> +#include <unistd.h> +#include <math.h> +#include <errno.h> + +#include "download-agent-client-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-utils.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-file.h" +#include "download-agent-mime-util.h" +#include "download-agent-http-mgr.h" +#include "download-agent-plugin-conf.h" + +#define NO_NAME_TEMP_STR "No name" + +static da_result_t __set_file_size(stage_info *stage); +static da_result_t __saved_file_open(stage_info *stage); + +static char *__derive_extension(stage_info *stage); +static da_result_t __divide_file_name_into_pure_name_N_extesion( + const char *in_file_name, + char **out_pure_file_name, + char **out_extension); +static da_result_t __get_candidate_file_name(stage_info *stage, + char **out_pure_file_name, char **out_extension); + +static da_result_t __file_write_buf_make_buf(file_info *file_storage); +static da_result_t __file_write_buf_destroy_buf(file_info *file_storage); +static da_result_t __file_write_buf_flush_buf(stage_info *stage, + file_info *file_storage); +static da_result_t __file_write_buf_copy_to_buf(file_info *file_storage, + char *body, int body_len); +static da_result_t __file_write_buf_directly_write(stage_info *stage, + file_info *file_storage, char *body, int body_len); + +da_result_t clean_files_from_dir(char *dir_path) +{ + da_result_t ret = DA_RESULT_OK; + struct dirent *d = DA_NULL; + DIR *dir; + char file_path[DA_MAX_FULL_PATH_LEN] = { 0, }; + + DA_LOG_FUNC_LOGD(FileManager); + + if (dir_path == DA_NULL) + return DA_ERR_INVALID_ARGUMENT; + + if (is_dir_exist(dir_path)) { + dir = opendir(dir_path); + if (DA_NULL == dir) { + DA_LOG_ERR(FileManager, "opendir() is failed."); + ret = DA_ERR_INVALID_INSTALL_PATH; + } else { + while (DA_NULL != (d = readdir(dir))) { + DA_SECURE_LOGD("%s",d->d_name); + if (0 == strncmp(d->d_name, ".", strlen(".")) + || 0 == strncmp(d->d_name, + "..", + strlen(".."))) { + continue; + } + + memset(file_path, 0x00, DA_MAX_FULL_PATH_LEN); + snprintf(file_path, DA_MAX_FULL_PATH_LEN, + "%s/%s", dir_path, d->d_name); + if (remove(file_path) < 0) { + DA_LOG_ERR(FileManager, "fail to remove file"); + } + } + + closedir(dir); + if (remove(dir_path) < 0) { + DA_LOG_ERR(FileManager, "fail to remove dir"); + } + } + } + return ret; +} + +/* Priority to obtain MIME Type + * 1. HTTP response header's <Content-Type> field + * 2. from OMA descriptor file's <content-type> attribute (mandatory field) + * 3. Otherwise, leave blank for MIME Type + */ +da_result_t get_mime_type(stage_info *stage, char **out_mime_type) +{ + char *mime_type = DA_NULL; + + if (!GET_STAGE_SOURCE_INFO(stage)) + return DA_ERR_INVALID_ARGUMENT; + + /* Priority 1 */ + if (GET_REQUEST_HTTP_HDR_CONT_TYPE(GET_STAGE_TRANSACTION_INFO(stage))) { + mime_type = GET_REQUEST_HTTP_HDR_CONT_TYPE(GET_STAGE_TRANSACTION_INFO(stage)); +// DA_SECURE_LOGI("content type from HTTP response header [%s]", mime_type); + } + + if (!mime_type) { + DA_LOG(FileManager, "no content type derived"); + return DA_RESULT_OK; + } + + /* FIXME really need memory allocation? */ + *out_mime_type = (char *)calloc(1, strlen(mime_type) + 1); + if (*out_mime_type) { + strncpy(*out_mime_type, mime_type, strlen(mime_type)); +// DA_SECURE_LOGD("out_mime_type str[%s] ptr[%p] len[%d]", +// *out_mime_type,*out_mime_type,strlen(*out_mime_type)); + } else { + DA_LOG_ERR(FileManager, "fail to allocate memory"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + +// DA_SECURE_LOGD("mime type = %s", *out_mime_type); + return DA_RESULT_OK; +} + +da_bool_t is_file_exist(const char *file_path) +{ + struct stat dir_state; + int stat_ret; + + if (file_path == DA_NULL) { + DA_LOG_ERR(FileManager, "file path is DA_NULL"); + return DA_FALSE; + } + + stat_ret = stat(file_path, &dir_state); + + if (stat_ret == 0) { + if (dir_state.st_mode & S_IFREG) { + DA_SECURE_LOGD("Exist! %s is a regular file & its size = %lu", file_path, dir_state.st_size); + return DA_TRUE; + } + + return DA_FALSE; + } + return DA_FALSE; + +} + +da_bool_t is_dir_exist(const char *file_path) +{ + struct stat dir_state; + int stat_ret; + + if (file_path == DA_NULL) { + DA_LOG_ERR(FileManager, "file path is DA_NULL"); + return DA_FALSE; + } + + stat_ret = stat(file_path, &dir_state); + + if (stat_ret == 0) { + if (dir_state.st_mode & S_IFDIR) { + DA_LOG_VERBOSE(FileManager, "Existed directory."); + return DA_TRUE; + } + + return DA_FALSE; + } + return DA_FALSE; +} + +void get_file_size(char *file_path, unsigned long long *out_file_size) +{ + struct stat dir_state; + int stat_ret; + + *out_file_size = -1; + + if (file_path == DA_NULL) { + DA_LOG_ERR(FileManager, "file path is DA_NULL"); + return; + } + + /* Please do not use ftell() to obtain file size, use stat instead. + * This is a guide from www.securecoding.cert.org + * : FIO19-C. Do not use fseek() and ftell() to compute the size of a file + */ + stat_ret = stat(file_path, &dir_state); + if (stat_ret == 0) { + if (dir_state.st_mode & S_IFREG) { + DA_LOG(FileManager, "size = %lu", dir_state.st_size); + *out_file_size = dir_state.st_size; + } + } + return; +} + +da_result_t __saved_file_open(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_storage = DA_NULL; + char *actual_file_path = DA_NULL; + void *fd = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) + return DA_ERR_INVALID_ARGUMENT; + + actual_file_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage); + if (!actual_file_path) + return DA_ERR_INVALID_ARGUMENT; + + fd = fopen(actual_file_path, "a+"); // for resume + if (fd == DA_NULL) { + DA_LOG_ERR(FileManager, "File open failed"); + if (errno == ENOSPC) + ret = DA_ERR_DISK_FULL; + else + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + GET_CONTENT_STORE_FILE_HANDLE(file_storage) = fd; + +// DA_SECURE_LOGD("file path for saving = %s", +// GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage)); + +ERR: + if (DA_RESULT_OK != ret) { + GET_CONTENT_STORE_FILE_HANDLE(file_storage) = DA_NULL; + } + return ret; +} + +da_result_t __set_file_size(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + req_dl_info *stage_req_info = DA_NULL; + file_info *file_storage = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + if (!stage) { + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + stage_req_info = GET_STAGE_TRANSACTION_INFO(stage); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) + goto ERR; + + if (GET_REQUEST_HTTP_HDR_CONT_LEN(stage_req_info) != 0) { + GET_CONTENT_STORE_FILE_SIZE(file_storage) + = GET_REQUEST_HTTP_HDR_CONT_LEN(stage_req_info); + } else { + GET_CONTENT_STORE_FILE_SIZE(file_storage) = 0; + } + DA_LOG_VERBOSE(FileManager, "file size = %d", GET_CONTENT_STORE_FILE_SIZE(file_storage)); +ERR: + return ret; + +} + +/* Priority to derive extension + * 1. according to MIME-Type + * 2. if MIME-Type is ambiguous or blank, + * 2-1. derived from <Content-Disposition> field's "filename" attribute + * 2-2. derived from url + * 3. if url does not have extension, leave blank for extension + */ +char *__derive_extension(stage_info *stage) +{ + if (!stage) + return DA_NULL; + + source_info_t *source_info = GET_STAGE_SOURCE_INFO(stage); + req_dl_info *request_info = GET_STAGE_TRANSACTION_INFO(stage); + file_info *file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + char *extension = DA_NULL; + char *url = DA_NULL; + + /* Priority 1 */ + char *mime_type = DA_NULL; + mime_type = GET_CONTENT_STORE_CONTENT_TYPE(file_info_data); + if (mime_type && !is_ambiguous_MIME_Type(mime_type)) { + char *extension = DA_NULL; + da_result_t ret = get_extension_from_mime_type(mime_type, &extension); + if (ret == DA_RESULT_OK && extension) + return extension; + } + + /* Priority 2-1 */ + http_msg_response_t *http_msg_response = DA_NULL; + http_msg_response = request_info->http_info.http_msg_response; + if (http_msg_response) { + char *file_name = DA_NULL; + da_bool_t b_ret = http_msg_response_get_content_disposition(http_msg_response, + DA_NULL, &file_name); + if (b_ret && file_name) { + char *extension = DA_NULL; + DA_SECURE_LOGD("Name from Content-Disposition :[%s]", file_name); + __divide_file_name_into_pure_name_N_extesion(file_name, DA_NULL, &extension); + if (file_name) { + free(file_name); + file_name = DA_NULL; + } + if (extension) + return extension; + } + } + /* Priority 2-2 */ + /* If there is location url from response header in case of redirection, + * it try to parse the extention name from the location url */ + if (GET_REQUEST_HTTP_REQ_LOCATION(request_info)) + url = GET_REQUEST_HTTP_REQ_LOCATION(request_info); + else + url = GET_SOURCE_BASIC_URL(source_info); + if (url) { + DA_SECURE_LOGD("url:[%s]", url); + da_bool_t b_ret = da_get_extension_name_from_url(url, &extension); + if (b_ret && extension) + return extension; + } + + return DA_NULL; +} + +/** Priority for deciding file name + * 1. what client wants, which is conveyed by DA_FEATURE_FILE_NAME + * 2. 'filename' option on HTTP response header's Content-Disposition field + * 3. requesting URL + * 4. Otherwise, define it as "No name" + */ +da_result_t __get_candidate_file_name(stage_info *stage, char **out_pure_file_name, char **out_extension) +{ + da_result_t ret = DA_RESULT_OK; + source_info_t *source_info = DA_NULL; + char *pure_file_name = DA_NULL; + char *extension = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + if (!stage || !out_pure_file_name) + return DA_ERR_INVALID_ARGUMENT; + + source_info = GET_STAGE_SOURCE_INFO(stage); + if (!source_info) + return DA_ERR_INVALID_ARGUMENT; + + /* Priority 1 */ + if (!pure_file_name && GET_DL_USER_FILE_NAME(GET_STAGE_DL_ID(stage))) { + __divide_file_name_into_pure_name_N_extesion( + GET_DL_USER_FILE_NAME(GET_STAGE_DL_ID(stage)), + &pure_file_name, &extension); + } + + /* Priority 2 */ + if (!pure_file_name) { + req_dl_info *request_info = GET_STAGE_TRANSACTION_INFO(stage); + http_msg_response_t *http_msg_response = DA_NULL; + http_msg_response = request_info->http_info.http_msg_response; + if (http_msg_response) { + char *file_name = DA_NULL; + da_bool_t b_ret = http_msg_response_get_content_disposition(http_msg_response, + DA_NULL, &file_name); + if (b_ret && file_name) { + DA_SECURE_LOGD("Name from Content-Disposition :[%s]", file_name); + __divide_file_name_into_pure_name_N_extesion(file_name, &pure_file_name, &extension); + if (file_name) { + free(file_name); + file_name = DA_NULL; + } + } + } + } + + /* Priority 3 */ + if (!pure_file_name) { + char *url = DA_NULL; + req_dl_info *request_info = GET_STAGE_TRANSACTION_INFO(stage); + /* If there is location url from response header in case of redirection, + * it try to parse the file name from the location url */ + if (GET_REQUEST_HTTP_REQ_LOCATION(request_info)) + url = GET_REQUEST_HTTP_REQ_LOCATION(request_info); + else + url = GET_SOURCE_BASIC_URL(source_info); + if (url) { + DA_SECURE_LOGD("url: [%s]", url); + da_get_file_name_from_url(url, &pure_file_name); + } + } + + /* Priority 4 */ + if (!pure_file_name) { + pure_file_name = strdup(NO_NAME_TEMP_STR); + if (!pure_file_name) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + } + + *out_pure_file_name = pure_file_name; + pure_file_name = DA_NULL; + DA_SECURE_LOGD("candidate file name [%s]", *out_pure_file_name); + + if (out_extension) { + if (extension) { + *out_extension = extension; + extension = DA_NULL; + } else { + *out_extension = __derive_extension(stage); + DA_SECURE_LOGD("candidate extension [%s]", *out_extension); + } + } + + if (extension) + free(extension); + + return DA_RESULT_OK; + +ERR: + if (extension) + free(extension); + + return ret; +} + +da_result_t __decide_file_path(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + char *temp_dir = DA_NULL; + char *extension = DA_NULL; + char *file_name_without_extension = DA_NULL; + char *tmp_file_path = DA_NULL; + char *user_install_path = DA_NULL; + file_info *file_info_data = DA_NULL; + int len = 0; + DA_LOG_FUNC_LOGV(FileManager); + + file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_info_data) + return DA_ERR_INVALID_ARGUMENT; + + + /* The destination path is set by user or by CAPI already + */ + user_install_path = GET_DL_USER_INSTALL_PATH(GET_STAGE_DL_ID(stage)); + len = strlen(user_install_path); + temp_dir = (char *)calloc(len + 1, sizeof(char)); + if (!temp_dir) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + memcpy(temp_dir, user_install_path, len); + temp_dir[len] = '\0'; + + + + ret = __get_candidate_file_name(stage, &file_name_without_extension, &extension); + if (ret != DA_RESULT_OK) + goto ERR; + + // for resume + tmp_file_path = get_full_path_avoided_duplication(temp_dir, file_name_without_extension, extension); + if (tmp_file_path) { + GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)) + = tmp_file_path; + tmp_file_path = DA_NULL; + } else { + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + + if (file_name_without_extension && !GET_CONTENT_STORE_PURE_FILE_NAME(file_info_data)) { + GET_CONTENT_STORE_PURE_FILE_NAME(file_info_data) = file_name_without_extension; + file_name_without_extension = DA_NULL; + } + + if (extension && !GET_CONTENT_STORE_EXTENSION(file_info_data)) { + GET_CONTENT_STORE_EXTENSION(file_info_data) = extension; + extension = DA_NULL; + } + +ERR: + DA_SECURE_LOGI("decided file path = %s", GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_info_data)); + if (temp_dir) { + free(temp_dir); + temp_dir = DA_NULL; + } + if (file_name_without_extension) { + free(file_name_without_extension); + file_name_without_extension = DA_NULL; + } + if (extension) { + free(extension); + extension = DA_NULL; + } + return ret; +} + +char *get_full_path_avoided_duplication(char *in_dir, char *in_candidate_file_name, char *in_extension) +{ + char *dir = in_dir; + char *file_name = in_candidate_file_name; + char *extension = in_extension; + char *final_path = DA_NULL; + + int final_path_len = 0; + int extension_len = 0; + + int suffix_count = 0; /* means suffix on file name. up to "_1000000000" */ + const int max_suffix_count = 1000000000; + int suffix_len = (int)log10(max_suffix_count+1) + 1; /* 1 means "_" */ + + if (!in_dir || !in_candidate_file_name) + return DA_NULL; + +// DA_SECURE_LOGD("in_candidate_file_name=[%s], in_extension=[%s]", in_candidate_file_name, in_extension); + + if (extension) + extension_len = strlen(extension); + + /* first 1 for "/", second 1 for ".", last 1 for DA_NULL */ + final_path_len = strlen(dir) + 1 + strlen(file_name) + 1 + + suffix_len + extension_len + 1; + + final_path = (char*)calloc(1, final_path_len); + if (!final_path) { + DA_LOG_ERR(FileManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_NULL; + } + + do { + /* e.g) /tmp/abc.jpg + * if there is no extension name, just make a file name without extension */ + if (0 == extension_len) { + if (suffix_count == 0) { + snprintf(final_path, final_path_len, + "%s/%s", dir, file_name); + } else { + snprintf(final_path, final_path_len, + "%s/%s_%d", dir, file_name, suffix_count); + } + } else { + if (suffix_count == 0) { + snprintf(final_path, final_path_len, + "%s/%s.%s", dir, file_name, extension); + } else { + snprintf(final_path, final_path_len, + "%s/%s_%d.%s", + dir, file_name, suffix_count, extension); + } + } + + if (is_file_exist(final_path)) { + suffix_count++; + if (suffix_count > max_suffix_count) { + free(final_path); + final_path = DA_NULL; + break; + } else { + memset(final_path, 0x00, final_path_len); + continue; + } + } + + break; + } while (1); + +// DA_SECURE_LOGD("decided path = [%s]", final_path); + return final_path; +} + +da_result_t __divide_file_name_into_pure_name_N_extesion(const char *in_file_name, char **out_pure_file_name, char **out_extension) +{ + char *file_name = DA_NULL; + char *tmp_ptr = DA_NULL; + char temp_file[DA_MAX_FILE_PATH_LEN] = {0,}; + char tmp_ext[DA_MAX_STR_LEN] = {0,}; + int len = 0; + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGD(FileManager); + + if (!in_file_name) + return DA_ERR_INVALID_ARGUMENT; + + file_name = (char *)in_file_name; + tmp_ptr = strrchr(file_name, '.'); + if (tmp_ptr) + tmp_ptr++; + if (tmp_ptr && out_extension) { + strncpy((char*) tmp_ext, tmp_ptr, sizeof(tmp_ext) - 1); + *out_extension = strdup((const char*) tmp_ext); + DA_SECURE_LOGD("extension [%s]", *out_extension); + } + + if (!out_pure_file_name) + return ret; + + if (tmp_ptr) + len = tmp_ptr - file_name - 1; + else + len = strlen(file_name); + + if (len >= DA_MAX_FILE_PATH_LEN) { + strncpy((char*) temp_file, file_name, + DA_MAX_FILE_PATH_LEN - 1); + } else { + strncpy((char*) temp_file, file_name, len); + } + + delete_prohibited_char((char*) temp_file, + strlen((char*) temp_file)); + if (strlen(temp_file) < 1) { + *out_pure_file_name = strdup(NO_NAME_TEMP_STR); + } else { + *out_pure_file_name = strdup( + (const char*) temp_file); + } + + DA_SECURE_LOGD("pure file name [%s]", *out_pure_file_name); + return ret; +} + +da_result_t __file_write_buf_make_buf(file_info *file_storage) +{ + da_result_t ret = DA_RESULT_OK; + char *buffer = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + buffer = (char*) calloc(DOWNLOAD_NOTIFY_LIMIT, 1); + if (DA_NULL == buffer) { + DA_LOG_ERR(FileManager, "Calloc failure "); + ret = DA_ERR_FAIL_TO_MEMALLOC; + } else { + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) = 0; + GET_CONTENT_STORE_FILE_BUFFER(file_storage) = buffer; + } + + return ret; +} + +da_result_t __file_write_buf_destroy_buf(file_info *file_storage) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGV(FileManager); + + if (GET_CONTENT_STORE_FILE_BUFFER(file_storage)) + free(GET_CONTENT_STORE_FILE_BUFFER(file_storage)); + + GET_CONTENT_STORE_FILE_BUFFER(file_storage) = DA_NULL; + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) = 0; + + return ret; +} + +da_result_t __file_write_buf_flush_buf(stage_info *stage, file_info *file_storage) +{ + da_result_t ret = DA_RESULT_OK; + char *buffer = DA_NULL; + int buffer_size = 0; + int write_success_len = 0; + void *fd = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + buffer = GET_CONTENT_STORE_FILE_BUFFER(file_storage); + buffer_size = GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage); + + if (buffer_size == 0) { + DA_LOG_ERR(FileManager, "no data on buffer.."); + return ret; + } + + fd = GET_CONTENT_STORE_FILE_HANDLE(file_storage); + if (DA_NULL == fd) { + DA_LOG_ERR(FileManager, "There is no file handle."); + + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + write_success_len = fwrite(buffer, sizeof(char), buffer_size, + (FILE *) fd); + /* FIXME : This can be necessary later due to progressive download. + * The solution for reducing fflush is needed */ + //fflush((FILE *) fd); + if (write_success_len != buffer_size) { + DA_LOG_ERR(FileManager, "write fails "); + if (errno == ENOSPC) + ret = DA_ERR_DISK_FULL; + else + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + += write_success_len; + DA_LOG_VERBOSE(FileManager, "write %d bytes", write_success_len); + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(file_storage) = DA_TRUE; + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) = 0; + +ERR: + return ret; +} + +da_result_t __file_write_buf_copy_to_buf(file_info *file_storage, char *body, + int body_len) +{ + da_result_t ret = DA_RESULT_OK; + char *buffer = DA_NULL; + int buffer_size = 0; + + DA_LOG_FUNC_LOGV(FileManager); + + buffer = GET_CONTENT_STORE_FILE_BUFFER(file_storage); + buffer_size = GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage); + + memcpy(buffer + buffer_size, body, body_len); + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) += body_len; + + return ret; +} + +da_result_t __file_write_buf_directly_write(stage_info *stage, + file_info *file_storage, char *body, int body_len) +{ + da_result_t ret = DA_RESULT_OK; + int write_success_len = 0; + void *fd = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + fd = GET_CONTENT_STORE_FILE_HANDLE(file_storage); + if (DA_NULL == fd) { + DA_LOG_ERR(FileManager, "There is no file handle."); + + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + write_success_len = fwrite(body, sizeof(char), body_len, + (FILE *) fd); + /* FIXME : This can be necessary later due to progressive download. + * The solution for reducing fflush is needed */ + //fflush((FILE *) fd); + if (write_success_len != body_len) { + DA_LOG_ERR(FileManager, "write fails "); + if (errno == ENOSPC) + ret = DA_ERR_DISK_FULL; + else + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + += write_success_len; + DA_LOG(FileManager, "write %d bytes", write_success_len); + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(file_storage) = DA_TRUE; + +ERR: + return ret; +} + +da_result_t file_write_ongoing(stage_info *stage, char *body, int body_len) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_storage = DA_NULL; + int buffer_size = 0; + char *buffer = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) { + DA_LOG_ERR(FileManager, "file_info is empty."); + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + + buffer = GET_CONTENT_STORE_FILE_BUFFER(file_storage); + buffer_size = GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage); + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(file_storage) = DA_FALSE; + + if (DA_NULL == buffer) { + if (body_len < DOWNLOAD_NOTIFY_LIMIT) { + ret = __file_write_buf_make_buf(file_storage); + if (ret != DA_RESULT_OK) + goto ERR; + + __file_write_buf_copy_to_buf(file_storage, body, body_len); + } else { + ret = __file_write_buf_directly_write(stage, + file_storage, body, body_len); + if (ret != DA_RESULT_OK) + goto ERR; + } + } else { + if (DOWNLOAD_NOTIFY_LIMIT <= body_len) { + ret = __file_write_buf_flush_buf(stage, file_storage); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = __file_write_buf_directly_write(stage, + file_storage, body, body_len); + if (ret != DA_RESULT_OK) + goto ERR; + + } else if ((DOWNLOAD_NOTIFY_LIMIT - buffer_size) <= body_len) { + ret = __file_write_buf_flush_buf(stage, file_storage); + if (ret != DA_RESULT_OK) + goto ERR; + + __file_write_buf_copy_to_buf(file_storage, body, body_len); + } else { + __file_write_buf_copy_to_buf(file_storage, body, body_len); + } + } + +ERR: + if (ret != DA_RESULT_OK) { + if (file_storage) { + GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage) = 0; + if (GET_CONTENT_STORE_FILE_BUFFER(file_storage)) { + free( + GET_CONTENT_STORE_FILE_BUFFER(file_storage)); + GET_CONTENT_STORE_FILE_BUFFER(file_storage) + = DA_NULL; + } + } + } + return ret; +} + +da_result_t file_write_complete(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info*file_storage = DA_NULL; + char *buffer = DA_NULL; + unsigned int buffer_size = 0; + void *fd = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) { + DA_LOG_ERR(FileManager, "file_info is DA_NULL."); + ret = DA_ERR_FAIL_TO_ACCESS_FILE; + goto ERR; + } + + buffer = GET_CONTENT_STORE_FILE_BUFFER(file_storage); + buffer_size = GET_CONTENT_STORE_FILE_BUFF_LEN(file_storage); + + if (DA_NULL == buffer) { + DA_LOG_ERR(FileManager, "file buffer is DA_NULL"); + } else { + if (buffer_size != 0) { + ret = __file_write_buf_flush_buf(stage, file_storage); + if (ret != DA_RESULT_OK) + goto ERR; + } + __file_write_buf_destroy_buf(file_storage); + } + fd = GET_CONTENT_STORE_FILE_HANDLE(file_storage); + + if (fd) { + fclose(fd); + fd = DA_NULL; + } + GET_CONTENT_STORE_FILE_HANDLE(file_storage) = DA_NULL; +ERR: + return ret; +} + +da_result_t start_file_writing(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_info_data = DA_NULL; + + DA_LOG_FUNC_LOGV(FileManager); + + file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + ret = get_mime_type(stage, + &GET_CONTENT_STORE_CONTENT_TYPE(file_info_data)); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = __decide_file_path(stage); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = __set_file_size(stage); + if (DA_RESULT_OK != ret) + goto ERR; + + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + = 0; + + ret = __saved_file_open(stage); + +ERR: + return ret; +} + + +da_result_t start_file_writing_append(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGD(FileManager); + + ret = __saved_file_open(stage); + + return ret; +} + +// for resume with new download request +da_result_t start_file_writing_append_with_new_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_storage = DA_NULL; + char *original_file_path = DA_NULL; + char *temp_file_path = DA_NULL; + char *extension = DA_NULL; + char *file_name_without_extension = DA_NULL; + req_dl_info *request_info = DA_NULL; + unsigned long long temp_file_size = 0; + + DA_LOG_FUNC_LOGD(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + if (!file_storage) + return DA_ERR_INVALID_ARGUMENT; + request_info = GET_STAGE_TRANSACTION_INFO(stage); + if (!request_info) + return DA_ERR_INVALID_ARGUMENT; + temp_file_path = GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH(request_info); + if (!temp_file_path) + return DA_ERR_INVALID_ARGUMENT; + original_file_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage); + + GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_storage) = strdup(temp_file_path); + + if (original_file_path) + free(original_file_path); + + ret = get_mime_type(stage, + &GET_CONTENT_STORE_CONTENT_TYPE(file_storage)); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = __get_candidate_file_name(stage, &file_name_without_extension, &extension); + if (ret != DA_RESULT_OK) + goto ERR; + + if (file_name_without_extension) { + if (!GET_CONTENT_STORE_PURE_FILE_NAME(file_storage)) { + GET_CONTENT_STORE_PURE_FILE_NAME(file_storage) = file_name_without_extension; + file_name_without_extension = DA_NULL; + } else { + free(file_name_without_extension); + file_name_without_extension = DA_NULL; + } + } + + if (extension) { + if (!GET_CONTENT_STORE_EXTENSION(file_storage)) { + GET_CONTENT_STORE_EXTENSION(file_storage) = extension; + extension = DA_NULL; + } else { + free(extension); + extension = DA_NULL; + } + } + + ret = __set_file_size(stage); + if (DA_RESULT_OK != ret) + goto ERR; + get_file_size(temp_file_path, &temp_file_size); + if (temp_file_size < 1) + goto ERR; + + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + = temp_file_size; + + ret = __saved_file_open(stage); + return ret; +ERR: + if (file_name_without_extension) { + free(file_name_without_extension); + file_name_without_extension = DA_NULL; + } + + if (extension) { + free(extension); + extension = DA_NULL; + } + return ret; +} + +da_result_t discard_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + file_info *file_storage = DA_NULL; + FILE *f_handle = DA_NULL; + + DA_LOG_FUNC_LOGD(FileManager); + + file_storage = GET_STAGE_CONTENT_STORE_INFO(stage); + + f_handle = GET_CONTENT_STORE_FILE_HANDLE(file_storage); + if (f_handle) { + fclose(f_handle); + GET_CONTENT_STORE_FILE_HANDLE(file_storage) = DA_NULL; + } + return ret; +} + +void clean_paused_file(stage_info *stage) +{ + file_info *file_info_data = DA_NULL; + char *paused_file_path = DA_NULL; + FILE *fd = DA_NULL; + + DA_LOG_FUNC_LOGD(FileManager); + + file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + + fd = GET_CONTENT_STORE_FILE_HANDLE(file_info_data); + if (fd) { + fclose(fd); + GET_CONTENT_STORE_FILE_HANDLE(file_info_data) = DA_NULL; + } + + paused_file_path = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_info_data); + remove_file((const char*) paused_file_path); + + return; +} + +da_result_t replace_content_file_in_stage(stage_info *stage, + const char *dest_dd_file_path) +{ + da_result_t ret = DA_RESULT_OK; + char *dd_file_path = DA_NULL; + int len; + + DA_LOG_FUNC_LOGD(FileManager); + + if (!dest_dd_file_path + && (DA_FALSE == is_file_exist(dest_dd_file_path))) { + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + dd_file_path + =GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)); + + if (DA_NULL != dd_file_path) { + remove_file((const char*) dd_file_path); + free(dd_file_path); + } + len = strlen(dest_dd_file_path); + dd_file_path = calloc(1, len + 1); + if (!dd_file_path) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + strncpy(dd_file_path, dest_dd_file_path, len); + GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)) + = dd_file_path; + +ERR: + return ret; + +} + +da_result_t copy_file(const char *src, const char *dest) +{ + FILE *fs = DA_NULL; + FILE *fd = DA_NULL; + int freadnum = 0; + int fwritenum = 0; + char buff[4096] = { 0, }; + + DA_LOG_FUNC_LOGD(FileManager); + + /* open files to copy */ + fs = fopen(src, "rb"); + if (!fs) { + DA_LOG_ERR(FileManager, "Fail to open src file"); + return DA_ERR_FAIL_TO_ACCESS_FILE; + } + + fd = fopen(dest, "wb"); + if (!fd) { + DA_LOG_ERR(FileManager, "Fail to open dest file"); + + fclose(fs); + return DA_ERR_FAIL_TO_ACCESS_FILE; + } + + /* actual copy */ + while (!feof(fs)) { + memset(buff, 0x00, 4096); + freadnum = fread(buff, sizeof(char), sizeof(buff), fs); + if (freadnum > 0) { + fwritenum = fwrite(buff, sizeof(char), freadnum, fd); + if (fwritenum <= 0) { + DA_LOG(FileManager, "written = %d",fwritenum); + break; + } + } else { + DA_LOG(FileManager, "read = %d",freadnum); + break; + } + } + + fclose(fd); + fclose(fs); + + return DA_RESULT_OK; +} + diff --git a/agent/download-agent-http-mgr.c b/agent/download-agent-http-mgr.c new file mode 100755 index 0000000..e951308 --- /dev/null +++ b/agent/download-agent-http-mgr.c @@ -0,0 +1,1744 @@ +/* + * 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-agent-utils.h" +#include "download-agent-debug.h" +#include "download-agent-client-mgr.h" +#include "download-agent-http-mgr.h" +#include "download-agent-http-misc.h" +#include "download-agent-http-msg-handler.h" +#include "download-agent-file.h" +#include "download-agent-plugin-conf.h" +#include "download-agent-plugin-http-interface.h" + +da_result_t make_default_http_request_hdr(const char *url, + char **user_request_header, + int user_request_heaer_count, + http_msg_request_t **out_http_msg_request, + char *user_request_etag, + char *user_request_temp_file_path); +da_result_t create_resume_http_request_hdr(stage_info *stage, + http_msg_request_t **out_resume_request); + +da_result_t start_new_transaction(stage_info *stage); +da_result_t set_http_request_hdr(stage_info *stage); +da_result_t make_transaction_info_and_start_transaction(stage_info *stage); + +da_result_t pause_for_flow_control(stage_info *stage); +da_result_t unpause_for_flow_control(stage_info *stage); + +da_result_t handle_any_input(stage_info *stage); +da_result_t handle_event_control(stage_info *stage, q_event_t *event); +da_result_t handle_event_http(stage_info *stage, q_event_t *event); +da_result_t handle_event_http_packet(stage_info *stage, q_event_t *event); +da_result_t handle_event_http_final(stage_info *stage, q_event_t *event); +da_result_t handle_event_http_abort(stage_info *stage, q_event_t *event); + +da_result_t exchange_url_from_header_for_redirection(stage_info *stage, + http_msg_response_t *http_msg_response); + +da_result_t handle_event_abort(stage_info *stage); +da_result_t handle_event_cancel(stage_info *stage); +da_result_t handle_event_suspend(stage_info *stage); +da_result_t handle_event_resume(stage_info *stage); +da_result_t handle_http_hdr(stage_info *stage, + http_msg_response_t *http_msg_response, int http_status); +da_result_t handle_http_status_code(stage_info *stage, + http_msg_response_t *http_msg_response, int http_status); +da_result_t handle_http_body(stage_info *stage, char *body, int body_len); + +da_result_t set_hdr_fields_on_download_info(stage_info *stage); + +da_result_t _check_content_type_is_matched(stage_info *stage); +da_result_t _check_downloaded_file_size_is_same_with_header_content_size( + stage_info *stage); + +da_result_t _check_resume_download_is_available(stage_info *stage, + http_msg_response_t *new_http_msg_response); +da_result_t _check_this_partial_download_is_available(stage_info *stage, + http_msg_response_t *new_http_msg_response); + +da_result_t _cancel_transaction(stage_info *stage); +da_result_t _disconnect_transaction(stage_info *stage); + +void __parsing_user_request_header(char *user_request_header, + char **out_field, char **out_value); + +http_mgr_t http_mgr; + +da_result_t init_http_mgr(void) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (http_mgr.is_http_init == DA_FALSE) { + http_mgr.is_http_init = DA_TRUE; + ret = PI_http_init(); + } + + return ret; +} + + +da_result_t request_to_abort_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_LOGD(HTTPManager); + if (!stage) { + DA_LOG_ERR(HTTPManager, "Stage is NULL. download info is already destroyed"); + return DA_ERR_INVALID_ARGUMENT; + } + + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_ABORT"); + ret = Q_make_control_event(Q_EVENT_TYPE_CONTROL_ABORT, &q_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager, "fail to make q_control_event"); + goto ERR; + } else { + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(GET_STAGE_DL_ID(stage))); + Q_push_event(GET_DL_QUEUE(GET_STAGE_DL_ID(stage)), q_event); + } + +ERR: + return ret; +} + +void deinit_http_mgr(void) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (http_mgr.is_http_init == DA_TRUE) { + http_mgr.is_http_init = DA_FALSE; + PI_http_deinit(); + } + + return; +} + +da_result_t request_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = DA_INVALID_ID; + http_state_t http_state = 0; + da_bool_t need_wait = DA_TRUE; + + queue_t *queue = DA_NULL; + req_dl_info *req_info = DA_NULL; + + slot_id = GET_STAGE_DL_ID(stage); + queue = GET_DL_QUEUE(slot_id); + req_info = GET_STAGE_TRANSACTION_INFO(stage); + + DA_LOG_VERBOSE(HTTPManager, "queue = %p", GET_DL_QUEUE(slot_id)); + + CHANGE_HTTP_STATE(HTTP_STATE_READY_TO_DOWNLOAD, stage); + + do { + ret = handle_any_input(stage); + if (ret != DA_RESULT_OK) { + if (DA_RESULT_OK == GET_REQUEST_HTTP_RESULT(req_info)) { + GET_REQUEST_HTTP_RESULT(req_info) = ret; + DA_LOG_CRITICAL(HTTPManager, "setting internal error [%d]", ret); + } + _cancel_transaction(stage); + } + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG_VERBOSE(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_READY_TO_DOWNLOAD: + ret = start_new_transaction(stage); + if (ret != DA_RESULT_OK) { + if (DA_RESULT_OK == GET_REQUEST_HTTP_RESULT(req_info)) { + GET_REQUEST_HTTP_RESULT(req_info) = ret; + DA_LOG_CRITICAL(HTTPManager, "setting internal error [%d]", ret); + } + DA_LOG(HTTPManager, "exiting with error..."); + need_wait = DA_FALSE; + break; + } + + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_REQUESTED, stage); + break; + + case HTTP_STATE_CANCELED: + case HTTP_STATE_DOWNLOAD_FINISH: + case HTTP_STATE_ABORTED: +#ifdef PAUSE_EXIT + case HTTP_STATE_PAUSED: +#endif + DA_LOG_VERBOSE(HTTPManager, "exiting..."); + need_wait = DA_FALSE; + break; + + default: + break; + } + + if (need_wait == DA_TRUE) { + _da_thread_mutex_lock(&(queue->mutex_queue)); + if (DA_FALSE == GET_IS_Q_HAVING_DATA(queue)) { + unpause_for_flow_control(stage); + +// DA_LOG_VERBOSE(HTTPManager, "Waiting for input"); + Q_goto_sleep(queue); +// DA_LOG_VERBOSE(HTTPManager, "Woke up to receive new packet or control event"); + } + _da_thread_mutex_unlock (&(queue->mutex_queue)); + + } + + } while (need_wait == DA_TRUE); + + ret = GET_REQUEST_HTTP_RESULT(req_info); + DA_LOG_DEBUG(HTTPManager, "Exit request_http_download! ret[%d]slot_id[%d]", + ret, slot_id); + return ret; +} + +da_result_t request_to_cancel_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_LOGD(HTTPManager); + + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_CANCEL"); + ret = Q_make_control_event(Q_EVENT_TYPE_CONTROL_CANCEL, &q_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager, "fail to make q_control_event"); + goto ERR; + } else { + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(GET_STAGE_DL_ID(stage))); + Q_push_event(GET_DL_QUEUE(GET_STAGE_DL_ID(stage)), q_event); + } + +ERR: + return ret; +} + +da_result_t request_to_suspend_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_LOGD(HTTPManager); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_PAUSED: + case HTTP_STATE_REQUEST_PAUSE: + DA_LOG_CRITICAL(HTTPManager, "Already paused. http_state = %d", http_state); + ret = DA_ERR_ALREADY_SUSPENDED; + break; + + default: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_SUSPEND"); + ret = Q_make_control_event(Q_EVENT_TYPE_CONTROL_SUSPEND, + &q_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager, "fail to make q_control_event"); + goto ERR; + } else { + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(GET_STAGE_DL_ID(stage))); + Q_push_event(GET_DL_QUEUE(GET_STAGE_DL_ID(stage)), + q_event); + } + + break; + } + +ERR: + return ret; +} + +da_result_t request_to_resume_http_download(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_LOGD(HTTPManager); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "[%d] http_state = %d", GET_STAGE_DL_ID(stage), http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_PAUSED: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_RESUME"); + ret = Q_make_control_event(Q_EVENT_TYPE_CONTROL_RESUME, + &q_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager, "fail to make q_control_event"); + goto ERR; + } else { + DA_LOG(HTTPManager, "queue = %p", GET_DL_QUEUE(GET_STAGE_DL_ID(stage))); + Q_push_event(GET_DL_QUEUE(GET_STAGE_DL_ID(stage)), + q_event); + } + + break; + + case HTTP_STATE_REQUEST_PAUSE: + DA_LOG_ERR(HTTPManager, "[%d] Fail to resume. Previous pause is not finished. http_state = %d", GET_STAGE_DL_ID(stage), http_state); + ret = DA_ERR_INVALID_STATE; + + break; + + case HTTP_STATE_RESUMED: + ret = DA_ERR_ALREADY_RESUMED; + + break; + + default: + DA_LOG_ERR(HTTPManager, "[%d] Fail to resume. This is not a paused ID. http_state = %d", GET_STAGE_DL_ID(stage), http_state); + ret = DA_ERR_INVALID_STATE; + + break; + } + +ERR: + return ret; +} + +da_result_t start_new_transaction(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + ret = set_http_request_hdr(stage); + if (ret != DA_RESULT_OK) + return ret; + + ret = make_transaction_info_and_start_transaction(stage); + return ret; +} + +da_result_t make_default_http_request_hdr(const char *url, + char **user_request_header, + int user_request_header_count, + http_msg_request_t **out_http_msg_request, + char *user_request_etag, + char *user_request_temp_file_path) +{ + da_result_t ret = DA_RESULT_OK; + + http_msg_request_t *http_msg_request = NULL; + char *user_agent = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!url) { + DA_LOG_ERR(HTTPManager, "DA_ERR_NO_URL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + ret = http_msg_request_create(&http_msg_request); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = http_msg_request_set_url(http_msg_request, url); + if (ret != DA_RESULT_OK) + goto ERR; + + user_agent = get_user_agent(); + if (user_agent) + http_msg_request_add_field(http_msg_request, HTTP_FIELD_UAGENT, + user_agent); + + http_msg_request_add_field(http_msg_request, HTTP_FIELD_ACCEPT_LANGUAGE, "en"); + http_msg_request_add_field(http_msg_request, HTTP_FIELD_ACCEPT_CHARSET, "utf-8"); + + if (user_request_header && user_request_header_count > 0) { + int i = 0; + for (i = 0; i < user_request_header_count; i++) + { + char *field = NULL; + char *value = NULL; + __parsing_user_request_header(user_request_header[i], + &field, &value); + if (field && value) { + http_msg_request_add_field(http_msg_request, field, value); + if (field) { + free(field); + field = NULL; + } + if (value) { + free(value); + value= NULL; + } + } else { + if (field) { + free(field); + field = NULL; + } + if (value) { + free(value); + value= NULL; + } + DA_LOG_ERR(HTTPManager, "Fail to parse user request header"); + } + } + } else + DA_LOG(HTTPManager, "no user reqeust header inserted"); + + if (user_request_etag) { + char buff[64] = {0,}; + unsigned long long size = 0; + http_msg_request_add_field(http_msg_request, + HTTP_FIELD_IF_RANGE, user_request_etag); + get_file_size(user_request_temp_file_path, &size); + snprintf(buff, sizeof(buff)-1, "bytes=%llu-", size); + http_msg_request_add_field(http_msg_request, + HTTP_FIELD_RANGE, buff); + } + + *out_http_msg_request = http_msg_request; + +ERR: + if (ret != DA_RESULT_OK) + http_msg_request_destroy(&http_msg_request); + if (user_agent) + free(user_agent); + return ret; +} + +da_result_t set_http_request_hdr(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + req_dl_info *request_info = DA_NULL; + + char *url = DA_NULL; + char **user_request_header = DA_NULL; + int user_request_header_count = 0; + char *user_request_etag = DA_NULL; + char *user_request_temp_file_path = DA_NULL; + http_msg_request_t* http_msg_request = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + if (DA_NULL == + (url = GET_REQUEST_HTTP_REQ_URL(request_info))) { + DA_LOG_ERR(HTTPManager, "DA_ERR_NO_URL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + user_request_header = GET_REQUEST_HTTP_USER_REQUEST_HEADER( + request_info); + user_request_header_count = GET_REQUEST_HTTP_USER_REQUEST_HEADER_COUNT( + request_info); + user_request_etag = GET_REQUEST_HTTP_USER_REQUEST_ETAG( + request_info); + user_request_temp_file_path = GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH( + request_info); + if (user_request_etag) { + DA_SECURE_LOGD("user_request_etag[%s]",user_request_etag); + } else { + DA_LOG_VERBOSE(HTTPManager, "user_request_etag is NULL"); + } + ret = make_default_http_request_hdr(url, user_request_header, + user_request_header_count, &http_msg_request, + user_request_etag, user_request_temp_file_path); + if (ret == DA_RESULT_OK) + request_info->http_info.http_msg_request = http_msg_request; + +ERR: + return ret; + +} + +da_result_t make_transaction_info_and_start_transaction(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = DA_INVALID_ID; + req_dl_info *request_info = DA_NULL; + + input_for_tranx_t *input_for_tranx = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + if (GET_REQUEST_HTTP_REQ_URL(request_info) == DA_NULL) { + DA_LOG_ERR(HTTPManager, "url is NULL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + input_for_tranx = (input_for_tranx_t*) calloc(1, + sizeof(input_for_tranx_t)); + if (input_for_tranx == DA_NULL) { + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } else { + input_for_tranx->proxy_addr = get_proxy_address(); + input_for_tranx->queue = GET_DL_QUEUE(slot_id); + + input_for_tranx->http_method = PI_HTTP_METHOD_GET; + input_for_tranx->http_msg_request + = request_info->http_info.http_msg_request; + } + + ret = PI_http_start_transaction(input_for_tranx, + &(GET_REQUEST_HTTP_TRANS_ID(request_info))); + if (ret != DA_RESULT_OK) + goto ERR; + +ERR: + if (input_for_tranx) { + free(input_for_tranx); + input_for_tranx = DA_NULL; + } + + return ret; +} + +da_result_t make_req_dl_info_http(stage_info *stage, req_dl_info *out_info) +{ + char *url = DA_NULL; + char **user_request_header = DA_NULL; + int user_request_header_count = 0; + char *user_request_etag = DA_NULL; + char *user_request_temp_file_path = DA_NULL; + int dl_id = -1; + source_info_t *source_info = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!stage) { + DA_LOG_ERR(HTTPManager, "stage is NULL"); + return DA_ERR_INVALID_ARGUMENT; + } + + source_info = GET_STAGE_SOURCE_INFO(stage); + + url = source_info->source_info_type.source_info_basic->url; + user_request_header = + source_info->source_info_type.source_info_basic->user_request_header; + user_request_header_count = + source_info->source_info_type.source_info_basic->user_request_header_count; + dl_id = source_info->source_info_type.source_info_basic->dl_id; + user_request_etag = GET_DL_USER_ETAG(GET_STAGE_DL_ID(stage)); + user_request_temp_file_path = GET_DL_USER_TEMP_FILE_PATH(GET_STAGE_DL_ID(stage)); + + DA_SECURE_LOGD("url [%s]", url); + + if (url) { + GET_REQUEST_HTTP_REQ_URL(out_info) = url; + GET_REQUEST_HTTP_USER_REQUEST_HEADER(out_info) = user_request_header; + GET_REQUEST_HTTP_USER_REQUEST_HEADER_COUNT(out_info) = + user_request_header_count; + GET_REQUEST_HTTP_USER_REQUEST_ETAG(out_info) = + user_request_etag; + GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH(out_info) = + user_request_temp_file_path; + } else { + DA_LOG_ERR(HTTPManager, "DA_ERR_NO_URL"); + return DA_ERR_INVALID_URL; + } + + _da_thread_mutex_init(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage)), NULL); + + return DA_RESULT_OK; +} + +da_result_t pause_for_flow_control(stage_info *stage) +{ + return DA_RESULT_OK; +} + +da_result_t unpause_for_flow_control(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGV(HTTPManager); + + PI_http_unpause_transaction( + GET_REQUEST_HTTP_TRANS_ID(GET_STAGE_TRANSACTION_INFO(stage))); + return ret; +} +da_result_t handle_event_abort(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t state = 0; + + DA_LOG_FUNC_LOGD(HTTPManager); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + switch (state) { + case HTTP_STATE_READY_TO_DOWNLOAD: + case HTTP_STATE_REDIRECTED: + case HTTP_STATE_DOWNLOAD_REQUESTED: + case HTTP_STATE_DOWNLOAD_STARTED: + case HTTP_STATE_DOWNLOADING: + case HTTP_STATE_REQUEST_CANCEL: + case HTTP_STATE_REQUEST_PAUSE: + case HTTP_STATE_REQUEST_RESUME: + case HTTP_STATE_CANCELED: + case HTTP_STATE_PAUSED: + case HTTP_STATE_RESUMED: + case HTTP_STATE_ABORTED: + /* IF the network session is terminated due to some error, + * the state can be aborted.(data aborted case) */ + CHANGE_HTTP_STATE(HTTP_STATE_ABORTED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_ABORTED, stage); + _disconnect_transaction(stage); + break; + case HTTP_STATE_DOWNLOAD_FINISH: + break; + default: + DA_LOG_ERR(HTTPManager, "have to check the flow for this case"); + break; + } + return ret; +} + +da_result_t handle_event_cancel(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t state = 0; + + DA_LOG_FUNC_LOGD(HTTPManager); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + switch (state) { + case HTTP_STATE_READY_TO_DOWNLOAD: + CHANGE_HTTP_STATE(HTTP_STATE_CANCELED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_CANCELED, stage); + break; + + case HTTP_STATE_PAUSED: + discard_download(stage); + CHANGE_HTTP_STATE(HTTP_STATE_CANCELED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_CANCELED, stage); + break; + + case HTTP_STATE_DOWNLOAD_REQUESTED: + case HTTP_STATE_DOWNLOAD_STARTED: + case HTTP_STATE_DOWNLOADING: + case HTTP_STATE_REQUEST_RESUME: + case HTTP_STATE_RESUMED: + _cancel_transaction(stage); + CHANGE_HTTP_STATE(HTTP_STATE_REQUEST_CANCEL, stage); + break; + + case HTTP_STATE_DOWNLOAD_FINISH: + break; + + case HTTP_STATE_REQUEST_CANCEL: + DA_LOG(HTTPManager, "HTTP_STATE_REQUEST_CANCEL : cancel is already in progress... "); + break; + + default: + DA_LOG_ERR(HTTPManager, "have to check the flow for this case"); + break; + } + + return ret; +} + +da_result_t handle_event_suspend(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_REQUEST_PAUSE: + DA_LOG(HTTPManager, "already requested to pause! do nothing"); + break; + + case HTTP_STATE_READY_TO_DOWNLOAD: + CHANGE_HTTP_STATE(HTTP_STATE_PAUSED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_PAUSED, stage); + send_client_paused_info(GET_STAGE_DL_ID(stage)); + break; + + default: + //send_client_paused_info(GET_STAGE_DL_ID(stage)); + _cancel_transaction(stage); + GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage)) = DA_RESULT_OK; + DA_LOG_CRITICAL(HTTPManager, "[%d] cleanup internal error", GET_STAGE_DL_ID(stage)); + CHANGE_HTTP_STATE(HTTP_STATE_REQUEST_PAUSE,stage); + break; + } + + return ret; +} + +da_result_t handle_event_resume(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + http_msg_request_t *resume_request = NULL; + + http_state_t http_state = 0; + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + if (http_state != HTTP_STATE_PAUSED) { + DA_LOG_ERR(HTTPManager, "Not HTTP_STATE_PAUSED! http_state = %d", http_state); + ret = DA_ERR_INVALID_STATE; + goto ERR; + } + + GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage)) = DA_RESULT_OK; + DA_LOG_CRITICAL(HTTPManager, "[%d] cleanup internal error", GET_STAGE_DL_ID(stage)); + + CHANGE_HTTP_STATE(HTTP_STATE_REQUEST_RESUME,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD,stage); + + ret = create_resume_http_request_hdr(stage, &resume_request); + if (ret != DA_RESULT_OK) + goto ERR; + + if (GET_STAGE_TRANSACTION_INFO(stage)->http_info.http_msg_request) + free(GET_STAGE_TRANSACTION_INFO(stage)->http_info.http_msg_request); + + GET_STAGE_TRANSACTION_INFO(stage)->http_info.http_msg_request + = resume_request; + + make_transaction_info_and_start_transaction(stage); + +ERR: + return ret; + +} + +da_result_t create_resume_http_request_hdr(stage_info *stage, + http_msg_request_t **out_resume_request) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + + req_dl_info *request_info = NULL; + + http_msg_response_t *first_response = NULL; + http_msg_request_t *resume_request = NULL; + + char *value = NULL; + char *url = NULL; + unsigned int downloaded_data_size = 0; + char downloaded_data_size_to_str[32] = { 0, }; + + char *etag_from_response = NULL; + char *date_from_response = NULL; + + DA_LOG_FUNC_LOGD(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + if (!(url = GET_REQUEST_HTTP_REQ_URL(GET_STAGE_TRANSACTION_INFO(stage)))) { + DA_LOG_ERR(HTTPManager, "DA_ERR_NO_URL"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + first_response = request_info->http_info.http_msg_response; + if (first_response) { + b_ret = http_msg_response_get_ETag(first_response, &value); + if (b_ret) { + etag_from_response = value; + value = NULL; + DA_SECURE_LOGD("[ETag][%s]", etag_from_response); + } + + b_ret = http_msg_response_get_date(first_response, &value); + if (b_ret) { + date_from_response = value; + value = NULL; + DA_SECURE_LOGD("[Date][%s]", date_from_response); + } + + downloaded_data_size + = GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)); + DA_LOG(HTTPManager, "downloaded_data_size = %u", downloaded_data_size); + snprintf(downloaded_data_size_to_str, sizeof(downloaded_data_size_to_str), "bytes=%u-", + downloaded_data_size); + DA_LOG(HTTPManager, "downloaded_data_size_to_str = %s", downloaded_data_size_to_str); + } + + ret = make_default_http_request_hdr(url, NULL, 0, &resume_request, NULL, NULL); + if (ret != DA_RESULT_OK) + goto ERR; + + if (etag_from_response) { + http_msg_request_add_field(resume_request, HTTP_FIELD_IF_RANGE, + etag_from_response); + } else { + if (date_from_response) { + http_msg_request_add_field(resume_request, + HTTP_FIELD_IF_RANGE, date_from_response); + } + } + + if (strlen(downloaded_data_size_to_str) > 0) + http_msg_request_add_field(resume_request, HTTP_FIELD_RANGE, + downloaded_data_size_to_str); + + *out_resume_request = resume_request; + +ERR: + if (etag_from_response) { + free(etag_from_response); + etag_from_response = NULL; + } + + if (date_from_response) { + free(date_from_response); + date_from_response = NULL; + } + + return ret; +} + +da_result_t handle_any_input(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = GET_STAGE_DL_ID(stage); + + queue_t *queue = DA_NULL; + q_event_t *event = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + queue = GET_DL_QUEUE(slot_id); + + Q_pop_event(queue, &event); + if (event == DA_NULL) { + DA_LOG_DEBUG(HTTPManager, "There is no data on the queue!"); + return DA_RESULT_OK; + } + + switch (event->event_type) { + case Q_EVENT_TYPE_CONTROL: + ret = handle_event_control(stage, event); + break; + + case Q_EVENT_TYPE_DATA_HTTP: + ret = handle_event_http(stage, event); + break; + + case Q_EVENT_TYPE_DATA_DRM: + break; + + default: + break; + } + Q_destroy_q_event(&event); + + return ret; +} + +da_result_t handle_event_control(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGD(HTTPManager); + + if (event->event_type == Q_EVENT_TYPE_CONTROL) { + switch (event->type.q_event_control.control_type) { + case Q_EVENT_TYPE_CONTROL_CANCEL: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_CANCEL"); + ret = handle_event_cancel(stage); + break; + + case Q_EVENT_TYPE_CONTROL_SUSPEND: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_SUSPEND"); + ret = handle_event_suspend(stage); + break; + + case Q_EVENT_TYPE_CONTROL_RESUME: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_RESUME"); + ret = handle_event_resume(stage); + break; + case Q_EVENT_TYPE_CONTROL_ABORT: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_ABORT"); + ret = handle_event_abort(stage); + break; + /* Fixme: need to think how we use this type. For now, this type is not used. */ + case Q_EVENT_TYPE_CONTROL_NET_DISCONNECTED: + DA_LOG(HTTPManager, "Q_EVENT_TYPE_CONTROL_NET_DISCONNECTED"); + break; + case Q_EVENT_TYPE_CONTROL_NONE: + default: + DA_LOG_ERR(HTTPManager, "Cannot enter here"); + break; + } + } + + return ret; +} + +da_result_t handle_event_http(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + q_event_data_http_t *q_event_data_http = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (event->event_type == Q_EVENT_TYPE_DATA_HTTP) { + q_event_data_http = &(event->type.q_event_data_http); + switch (q_event_data_http->data_type) { + case Q_EVENT_TYPE_DATA_PACKET: + ret = handle_event_http_packet(stage, event); + break; + case Q_EVENT_TYPE_DATA_FINAL: + DA_LOG_VERBOSE(HTTPManager, "Q_EVENT_TYPE_DATA_FINAL"); + ret = handle_event_http_final(stage, event); + break; + case Q_EVENT_TYPE_DATA_ABORT: + DA_LOG_VERBOSE(HTTPManager, "Q_EVENT_TYPE_DATA_ABORT"); + ret = handle_event_http_abort(stage, event); + break; + } + } + return ret; +} + +da_result_t handle_event_http_packet(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t is_handle_hdr_success = DA_TRUE; + q_event_data_http_t *received_data = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + received_data = &(event->type.q_event_data_http); + + if (received_data->http_response_msg) { + ret = handle_http_hdr(stage, received_data->http_response_msg, + received_data->http_response_msg->status_code); + if (DA_RESULT_OK != ret) { + is_handle_hdr_success = DA_FALSE; + } + + received_data->http_response_msg = NULL; + } + + if (received_data->body_len > 0) { + if (is_handle_hdr_success == DA_TRUE) { + ret = handle_http_body(stage, received_data->body_data, + received_data->body_len); + } + /*For all cases body_data should be deleted*/ + free(received_data->body_data); + received_data->body_data = DA_NULL; + } + return ret; +} + +da_result_t handle_event_http_final(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + int slot_id = DA_INVALID_ID; + int http_status = 0; + q_event_data_http_t *received_data = DA_NULL; + + + DA_LOG_FUNC_LOGV(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + _disconnect_transaction(stage); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_REDIRECTED: + CHANGE_HTTP_STATE(HTTP_STATE_READY_TO_DOWNLOAD,stage); + break; + + case HTTP_STATE_DOWNLOAD_REQUESTED: + DA_LOG(HTTPManager, "case HTTP_STATE_DOWNLOAD_REQUESTED"); + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_FINISH, stage); + /* Most of http status is decided when got headers. + * But it is ignored for credential url when got headers in case of 401 status code. + * So, when the download is finished, it means unauthorized error if the status code has still 401. + */ + received_data = &(event->type.q_event_data_http); + http_status = received_data->status_code; + if (http_status == 401) { + store_http_status(slot_id, http_status); + ret = DA_ERR_NETWORK_FAIL; + } + break; + + case HTTP_STATE_DOWNLOADING: + DA_LOG_VERBOSE(HTTPManager, "case HTTP_STATE_DOWNLOADING"); + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) { + discard_download(stage); + goto ERR; + } + /* Sometimes, the server can send "0" byte package data although whole data are not sent. + * At that case, the libsoup call finished callback function with 200 Ok. + * Only if the DA know content size form response header, it can check the error or not + */ + ret = _check_downloaded_file_size_is_same_with_header_content_size(stage); + if(ret != DA_RESULT_OK) { + discard_download(stage) ; + goto ERR; + } + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_FINISH, stage); + send_client_update_progress_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + break; + + case HTTP_STATE_REQUEST_PAUSE: + if (GET_CONTENT_STORE_FILE_HANDLE(GET_STAGE_CONTENT_STORE_INFO(stage))) { + ret = file_write_complete(stage); + + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(GET_STAGE_CONTENT_STORE_INFO(stage)) + = DA_FALSE; + + send_client_update_progress_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + + } + CHANGE_HTTP_STATE(HTTP_STATE_PAUSED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_PAUSED, stage); + send_client_paused_info(GET_STAGE_DL_ID(stage)); + DA_LOG(HTTPManager, "Server Notification code is set to NULL"); + break; + + case HTTP_STATE_ABORTED: + case HTTP_STATE_CANCELED: + discard_download(stage); + break; + + case HTTP_STATE_REQUEST_CANCEL: + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + discard_download(stage); + CHANGE_HTTP_STATE(HTTP_STATE_CANCELED, stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_CANCELED, stage); + break; + + default: + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + discard_download(stage); + CHANGE_HTTP_STATE(HTTP_STATE_ABORTED,stage); + break; + } + +ERR: + /* When file complete is failed */ + if (DA_RESULT_OK != ret) { + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_FINISH, stage); + } + return ret; +} + +da_result_t handle_event_http_abort(stage_info *stage, q_event_t *event) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + DA_LOG_FUNC_LOGD(HTTPManager); + + GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage)) + = event->type.q_event_data_http.error_type; + DA_LOG_CRITICAL(HTTPManager, "set internal error code : [%d]", GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage))); + _disconnect_transaction(stage); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_REQUEST_PAUSE: + CHANGE_HTTP_STATE(HTTP_STATE_PAUSED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_PAUSED, stage); + send_client_paused_info(GET_STAGE_DL_ID(stage)); + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + break; + + case HTTP_STATE_REQUEST_CANCEL: + CHANGE_HTTP_STATE(HTTP_STATE_CANCELED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_CANCELED, stage); + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + discard_download(stage); + break; + + default: + CHANGE_HTTP_STATE(HTTP_STATE_ABORTED,stage); + ret = file_write_complete(stage); + if (ret != DA_RESULT_OK) + goto ERR; + discard_download(stage); + break; + } +ERR: + return ret; +} + +da_result_t handle_http_hdr(stage_info *stage, + http_msg_response_t *http_msg_response, int http_status) +{ + da_result_t ret = DA_RESULT_OK; + int slot_id = DA_INVALID_ID; + http_state_t http_state = 0; + + DA_LOG_FUNC_LOGV(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG_DEBUG(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_DOWNLOAD_REQUESTED: + case HTTP_STATE_REQUEST_PAUSE: + case HTTP_STATE_REQUEST_RESUME: + case HTTP_STATE_REDIRECTED: + ret = handle_http_status_code(stage, http_msg_response, + http_status); + if (ret != DA_RESULT_OK) + goto ERR; + break; + + case HTTP_STATE_REQUEST_CANCEL: + DA_LOG(HTTPManager, "Cancel is in progress.. http_state = %d", http_state); + break; + + default: + DA_LOG_ERR(HTTPManager, "http_state = %d", http_state); + goto ERR; + } + +ERR: + return ret; +} + +da_result_t handle_http_status_code(stage_info *stage, + http_msg_response_t *http_msg_response, int http_status) +{ + da_result_t ret = DA_RESULT_OK; + + int slot_id = DA_INVALID_ID; + req_dl_info *request_info = DA_NULL; + http_state_t http_state = 0; + + DA_LOG_FUNC_LOGV(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + GET_STAGE_TRANSACTION_INFO(stage)->http_info.http_msg_response + = http_msg_response; + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + DA_LOG_VERBOSE(HTTPManager, "http_state = %d", http_state); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + store_http_status(slot_id, http_status); + + switch (http_status) { + case 200: + case 201: + case 202: + case 203: + if (http_state == HTTP_STATE_REQUEST_RESUME) + clean_paused_file(stage); + ret = set_hdr_fields_on_download_info(stage); + if (ret != DA_RESULT_OK) + goto ERR; + ret = _check_content_type_is_matched(stage); + if (ret != DA_RESULT_OK) + goto ERR; + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_STARTED,stage); + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD,stage); // ? + break; + + case 206: + DA_LOG(HTTPManager, "HTTP Status is %d - Partial download for resume!",http_status); + /* The resume can be started with start API. + * So the state should be not HTTP_STATE_RESUME_REQUESTED but HTTP_STATE_DOWNLOAD_REQUESTED*/ + if (http_state == HTTP_STATE_DOWNLOAD_REQUESTED) { + ret = _check_resume_download_is_available(stage, + http_msg_response); + if (ret != DA_RESULT_OK) + goto ERR; + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOAD_STARTED,stage); + + } else if (http_state == HTTP_STATE_REQUEST_RESUME) { + ret = _check_this_partial_download_is_available(stage, + http_msg_response); + if (ret != DA_RESULT_OK) + goto ERR; + CHANGE_HTTP_STATE(HTTP_STATE_RESUMED,stage); + } else { + DA_LOG_ERR(HTTPManager, "This download is not resumed, revoke"); + ret = DA_ERR_INVALID_STATE; + goto ERR; + } + CHANGE_DOWNLOAD_STATE(DOWNLOAD_STATE_NEW_DOWNLOAD,stage); + break; + + case 300: + case 301: + case 302: + case 303: + case 305: + case 306: + case 307: + DA_LOG(HTTPManager, "HTTP Status is %d - redirection!",http_status); + ret = exchange_url_from_header_for_redirection(stage, http_msg_response); + if (ret != DA_RESULT_OK) + goto ERR; + CHANGE_HTTP_STATE(HTTP_STATE_REDIRECTED,stage); + http_msg_response_destroy(&http_msg_response); + request_info->http_info.http_msg_response = DA_NULL; + break; + + case 100: + case 101: + case 102: + case 204: + case 304: + DA_LOG(HTTPManager, "HTTP Status is %d - 204 means server got the request, but no content to reply back, 304 means not modified!",http_status); + ret = DA_ERR_SERVER_RESPOND_BUT_SEND_NO_CONTENT; + break; + + case 416: // Requested range not satisfiable + case 503: + case 504: + default: + GET_REQUEST_HTTP_RESULT(request_info) + = DA_ERR_UNREACHABLE_SERVER; + DA_LOG_CRITICAL(HTTPManager, "set internal error code : DA_ERR_UNREACHABLE_SERVER [%d]", DA_ERR_UNREACHABLE_SERVER); + break; + } + +ERR: + return ret; +} + +da_result_t exchange_url_from_header_for_redirection(stage_info *stage, + http_msg_response_t *http_msg_response) +{ + da_result_t ret = DA_RESULT_OK; + char *location = DA_NULL; + + DA_LOG_FUNC_LOGD(HTTPManager); + + if (http_msg_response_get_location(http_msg_response, &location)) { + DA_SECURE_LOGD("location = %s\n", location); + GET_REQUEST_HTTP_REQ_LOCATION(GET_STAGE_TRANSACTION_INFO(stage)) = location; + } + + return ret; +} + +da_result_t handle_http_body(stage_info *stage, char *body, int body_len) +{ + da_result_t ret = DA_RESULT_OK; + http_state_t http_state = 0; + int slot_id = DA_INVALID_ID; + + DA_LOG_FUNC_LOGV(HTTPManager); + + slot_id = GET_STAGE_DL_ID(stage); + + if (DA_RESULT_OK + != GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage))) { + DA_LOG_CRITICAL(HTTPManager, "ignore because internal error code is set with [%d]", + GET_REQUEST_HTTP_RESULT(GET_STAGE_TRANSACTION_INFO(stage))); + return ret; + } + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + if (http_state == HTTP_STATE_DOWNLOAD_STARTED) { + // resume case + if (GET_REQUEST_HTTP_USER_REQUEST_ETAG(GET_STAGE_TRANSACTION_INFO(stage))) + ret = start_file_writing_append_with_new_download(stage); + else + ret = start_file_writing(stage); + if (DA_RESULT_OK != ret) + goto ERR; + + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOADING, stage); + send_client_update_dl_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CONTENT_TYPE(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_PURE_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_REQUEST_HTTP_HDR_ETAG(GET_STAGE_TRANSACTION_INFO(stage)), + GET_CONTENT_STORE_EXTENSION(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + } else if (http_state == HTTP_STATE_RESUMED) { + ret = start_file_writing_append(stage); + if (DA_RESULT_OK != ret) + goto ERR; + + CHANGE_HTTP_STATE(HTTP_STATE_DOWNLOADING,stage); + send_client_update_dl_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CONTENT_TYPE(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_ACTUAL_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_CONTENT_STORE_PURE_FILE_NAME(GET_STAGE_CONTENT_STORE_INFO(stage)), + GET_REQUEST_HTTP_HDR_ETAG(GET_STAGE_TRANSACTION_INFO(stage)), + GET_CONTENT_STORE_EXTENSION(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + } + + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + http_state = GET_HTTP_STATE_ON_STAGE(stage); + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + + switch (http_state) { + case HTTP_STATE_REDIRECTED: + DA_LOG(HTTPManager, "Just ignore http body, because this body is not for redirection one."); + break; + + case HTTP_STATE_DOWNLOADING: + /* Should this function before updating download info + * Because it extract mime type at once only if first download updating at client */ + ret = file_write_ongoing(stage, body, body_len); + if (ret != DA_RESULT_OK) + goto ERR; + if ((DA_TRUE == + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(GET_STAGE_CONTENT_STORE_INFO(stage)))) { + + IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(GET_STAGE_CONTENT_STORE_INFO(stage)) + = DA_FALSE; + send_client_update_progress_info( + slot_id, + GET_DL_ID(slot_id), + GET_CONTENT_STORE_CURRENT_FILE_SIZE(GET_STAGE_CONTENT_STORE_INFO(stage)) + ); + + } + break; + + case HTTP_STATE_REQUEST_PAUSE: + ret = file_write_ongoing(stage, body, body_len); + if (ret != DA_RESULT_OK) + goto ERR; + break; + + default: + DA_LOG(HTTPManager, "Do nothing! http_state is in case %d", http_state); + + goto ERR; + } + +ERR: + return ret; +} + +/* Function should be renamed , as it is actually not setting the header fields in download info */ +da_result_t set_hdr_fields_on_download_info(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + + req_dl_info *request_info = DA_NULL; + http_msg_response_t *http_msg_response = NULL; + + char *value = NULL; + unsigned long long size = 0; + + DA_LOG_FUNC_LOGV(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + + http_msg_response + = request_info->http_info.http_msg_response; + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "There is no header data!!"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + b_ret = http_msg_response_get_content_type(http_msg_response, &value); + if (b_ret) { + GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info) = value; + value = NULL; +// DA_SECURE_LOGD("[Content-Type][%s] - stored", GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info)); + } + + b_ret = http_msg_response_get_content_length(http_msg_response, + &size); + if (b_ret) { + GET_REQUEST_HTTP_HDR_CONT_LEN(request_info) = size; + size = 0; + } + + b_ret = http_msg_response_get_ETag(http_msg_response, &value); + if (b_ret) { + GET_REQUEST_HTTP_HDR_ETAG(request_info) = value; + value = NULL; + DA_SECURE_LOGD("[ETag][%s] - stored ", GET_REQUEST_HTTP_HDR_ETAG(request_info)); + } + +ERR: + return ret; +} + +da_result_t _check_content_type_is_matched(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + req_dl_info *request_info = DA_NULL; + source_info_t *source_info = DA_NULL; + char *content_type_from_server = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + source_info = GET_STAGE_SOURCE_INFO(stage); + + content_type_from_server = GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info); + if (content_type_from_server == DA_NULL) { + DA_LOG(HTTPManager, "http header has no Content-Type field, no need to compare"); + return DA_RESULT_OK; + } + + return ret; +} + +da_result_t _check_this_partial_download_is_available(stage_info *stage, + http_msg_response_t *new_http_msg_response) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + char *origin_ETag = NULL; + char *new_ETag = NULL; + unsigned long long remained_content_len = 0; + char *value = NULL; + unsigned long long size = 0; + + DA_LOG_FUNC_LOGD(HTTPManager); + + origin_ETag + = GET_REQUEST_HTTP_HDR_ETAG(GET_STAGE_TRANSACTION_INFO(stage)); + + b_ret = http_msg_response_get_content_length(new_http_msg_response, + &size); + if (b_ret) { + remained_content_len = size; + size = 0; + DA_LOG(HTTPManager, "[remained_content_len][%lu]", remained_content_len); + } + + b_ret = http_msg_response_get_ETag(new_http_msg_response, &value); + if (b_ret) { + new_ETag = value; + value = NULL; + DA_SECURE_LOGD("[new ETag][%s]", new_ETag); + } else { + goto ERR; + } + + if (origin_ETag && new_ETag && + 0 != strncmp(origin_ETag, new_ETag, strlen(new_ETag))) { + DA_LOG_ERR(HTTPManager, "ETag is not identical! revoke!"); + /* FIXME Later : Need to detail error exception handling */ + ret = DA_ERR_NETWORK_FAIL; + /*ret = DA_ERR_MISMATCH_HTTP_HEADER; */ + goto ERR; + } + +ERR: + if (new_ETag) { + free(new_ETag); + new_ETag = DA_NULL; + } + + return ret; +} + +da_result_t _check_resume_download_is_available(stage_info *stage, + http_msg_response_t *new_http_msg_response) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + char *origin_ETag = NULL; + char *new_ETag = NULL; + unsigned long long remained_content_len = 0; + char *value = NULL; + unsigned long long size = 0; + char *temp_file_path = DA_NULL; + req_dl_info *request_info = DA_NULL; + + DA_LOG_FUNC_LOGD(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + origin_ETag + = GET_REQUEST_HTTP_USER_REQUEST_ETAG(request_info); + + b_ret = http_msg_response_get_content_length(new_http_msg_response, + &size); + if (b_ret) { + remained_content_len = size; + size = 0; + DA_LOG(HTTPManager, "[remained_content_len][%lu]", remained_content_len); + } + + b_ret = http_msg_response_get_ETag(new_http_msg_response, &value); + if (b_ret) { + new_ETag = value; + value = NULL; + DA_SECURE_LOGD("[new ETag][%s]", new_ETag); + } else { + goto ERR; + } + + if (origin_ETag && new_ETag && + 0 != strncmp(origin_ETag, new_ETag, strlen(new_ETag))) { + DA_LOG_ERR(HTTPManager, "ETag is not identical! revoke!"); + /* FIXME Later : Need to detail error exception handling */ + ret = DA_ERR_NETWORK_FAIL; + /*ret = DA_ERR_MISMATCH_HTTP_HEADER; */ + goto ERR; + } + + b_ret = http_msg_response_get_content_type(new_http_msg_response, &value); + if (b_ret) { + GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info) = value; + value = NULL; + DA_SECURE_LOGD("[Content-Type][%s]", + GET_REQUEST_HTTP_HDR_CONT_TYPE(request_info)); + } + temp_file_path = GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH(request_info); + get_file_size(temp_file_path, &size); + GET_REQUEST_HTTP_HDR_CONT_LEN(request_info) = remained_content_len + size; + DA_SECURE_LOGD("[Content-Length][%llu]", + GET_REQUEST_HTTP_HDR_CONT_LEN(request_info)); + + +ERR: + if (new_ETag) { + free(new_ETag); + new_ETag = DA_NULL; + } + + return ret; +} + + +da_result_t _check_downloaded_file_size_is_same_with_header_content_size( + stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + + req_dl_info *request_info = DA_NULL; + file_info *file_info_data = DA_NULL; + + char *real_file_path = DA_NULL; + unsigned long long content_size_from_real_file = 0; + unsigned long long content_size_from_http_header = 0; + + DA_LOG_FUNC_LOGD(HTTPManager); + + request_info = GET_STAGE_TRANSACTION_INFO(stage); + file_info_data = GET_STAGE_CONTENT_STORE_INFO(stage); + + content_size_from_http_header + = GET_CONTENT_STORE_FILE_SIZE(file_info_data); + + if (content_size_from_http_header > 0) { + real_file_path + = GET_CONTENT_STORE_ACTUAL_FILE_NAME(file_info_data); + + get_file_size(real_file_path, + &content_size_from_real_file); + + if (content_size_from_real_file + != content_size_from_http_header) { + DA_SECURE_LOGE("size from header = %llu, real size = %llu,\ + DA_ERR_MISMATCH_CONTENT_SIZE", + content_size_from_http_header, content_size_from_real_file); + ret = DA_ERR_MISMATCH_CONTENT_SIZE; + } + } + + return ret; +} + +da_result_t _disconnect_transaction(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + int transaction_id = DA_INVALID_ID; + + DA_LOG_FUNC_LOGV(HTTPManager); + + transaction_id + = GET_REQUEST_HTTP_TRANS_ID(GET_STAGE_TRANSACTION_INFO(stage)); + + DA_LOG(HTTPManager, "transaction_id = %d slot_id = %d", transaction_id, GET_STAGE_DL_ID(stage)); + + if (transaction_id != DA_INVALID_ID) { + ret = PI_http_disconnect_transaction(transaction_id); + GET_REQUEST_HTTP_TRANS_ID(GET_STAGE_TRANSACTION_INFO(stage)) + = DA_INVALID_ID; + } + + return ret; +} + +da_result_t _cancel_transaction(stage_info *stage) +{ + da_result_t ret = DA_RESULT_OK; + int transaction_id = DA_INVALID_ID; + + DA_LOG_FUNC_LOGD(HTTPManager); + + transaction_id + = GET_REQUEST_HTTP_TRANS_ID(GET_STAGE_TRANSACTION_INFO(stage)); + + DA_LOG(HTTPManager, "transaction_id = %d", transaction_id); + + if (transaction_id != DA_INVALID_ID) { + http_state_t state = 0; + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + state = GET_HTTP_STATE_ON_STAGE(stage); + if (state <= HTTP_STATE_DOWNLOAD_REQUESTED) { + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + ret = PI_http_cancel_transaction(transaction_id, DA_TRUE); + } else { + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(stage))); + ret = PI_http_cancel_transaction(transaction_id, DA_FALSE); + } + + } + return ret; +} + +void __parsing_user_request_header(char *user_request_header, + char **out_field, char **out_value) +{ + int len = 0; + char *pos = NULL; + char *temp_pos = NULL; + char *field = NULL; + char *value = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!user_request_header) { + DA_LOG_ERR(HTTPManager, "user_request_header is NULL"); + goto ERR; + } + + pos = strchr(user_request_header, ':'); + if (!pos) { + DA_LOG_ERR(HTTPManager, "Fail to parse"); + goto ERR; + } + temp_pos = (char *)user_request_header; + while (*temp_pos) + { + if (temp_pos == pos || *temp_pos == ' ') { + len = temp_pos - user_request_header; + break; + } + temp_pos++; + } + if (len < 1) { + DA_LOG_ERR(HTTPManager, "Wrong field name"); + goto ERR; + } + field = (char *)calloc(1, len + 1); + if (!field) { + DA_LOG_ERR(HTTPManager, "Fail to calloc"); + goto ERR; + } + strncpy(field, user_request_header, len); + pos++; + while (*pos) + { + if (*pos != ' ') + break; + pos++; + } + len = strlen(pos) + 1; + value = (char *)calloc(1, len + 1); + if (!value) { + DA_LOG_ERR(HTTPManager, "Fail to calloc"); + goto ERR; + } + strncpy(value, pos, len); + *out_field = field; + *out_value = value; + DA_SECURE_LOGD("field[%s], value[%s]", field, value); + + return; +ERR: + if (field) { + free(field); + field = NULL; + } + return; +} + diff --git a/agent/download-agent-http-misc.c b/agent/download-agent-http-misc.c new file mode 100755 index 0000000..08cd8ec --- /dev/null +++ b/agent/download-agent-http-misc.c @@ -0,0 +1,64 @@ +/* + * 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-agent-http-misc.h" +#include "download-agent-debug.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-plugin-conf.h" +#include "download-agent-client-mgr.h" + +#define DEFAULT_HTTP_ACCEPT_HEADERS \ + "Accept-Language: en\r\n" \ + "Accept-Charset: utf-8\r\n" \ + + +char *get_user_agent() +{ + char *uagent_str = DA_NULL; + + DA_LOG_FUNC_LOGV(Default); + + uagent_str = get_client_user_agent_string(); + if (!uagent_str) { + da_result_t ret = DA_RESULT_OK; + ret = get_user_agent_string(&uagent_str); + if (ret != DA_RESULT_OK) + return NULL; + } + return uagent_str; +} + +da_bool_t is_supporting_protocol(const char *protocol) +{ + if((protocol == NULL) || (1 > strlen(protocol))) + { + return DA_FALSE; + } + + if(!strcasecmp(protocol, "http")) + { + return DA_TRUE; + } + else if(!strcasecmp(protocol, "https")) + { + return DA_TRUE; + } + else + { + return DA_FALSE; + } + +} diff --git a/agent/download-agent-http-msg-handler.c b/agent/download-agent-http-msg-handler.c new file mode 100755 index 0000000..6064c37 --- /dev/null +++ b/agent/download-agent-http-msg-handler.c @@ -0,0 +1,1333 @@ +/* + * 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 <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include "download-agent-http-msg-handler.h" +#include "download-agent-debug.h" +#include "download-agent-http-misc.h" +#include "download-agent-encoding.h" + +// '.' and ';' are request from Vodafone +#define IS_TERMINATING_CHAR(c) ( ((c) == ';') || ((c) == '\0') || ((c) == 0x0d) || ((c) == 0x0a) || ((c) == 0x20) ) +#define IS_TERMINATING_CHAR_EX(c) ( ((c) == '"') || ((c) == ';') || ((c) == '\0') || ((c) == 0x0d) || ((c) == 0x0a) || ((c) == 0x20) ) +#define IS_URI_TERMINATING_CHAR(c) ( ((c) == '\0') || ((c) == 0x0d) || ((c) == 0x0a) || ((c) == 0x20) ) + +enum parsing_type { + WITH_PARSING_OPTION, + WITHOUT_PARSING_OPTION +}; + +static da_result_t __http_header_add_field(http_header_t **head, + const char *field, const char *value, enum parsing_type type); +static void __http_header_destroy_all_field(http_header_t **head); +static da_bool_t __get_http_header_for_field( + http_msg_response_t *http_msg_response, const char *in_field, + http_header_t **out_header); +static void __exchange_header_value(http_header_t *header, + const char *in_raw_value); + +static http_header_options_t *__create_http_header_option(const char *field, + const char *value); +static void __http_header_destroy_all_option(http_header_options_t **head); +static da_bool_t __get_http_header_option_for_field( + http_header_options_t *header_option, const char *in_field, + char **out_value); + +static http_header_options_t *__parsing_N_create_option_str(char *org_str); +static http_header_options_t *__parsing_options(char *org_str); +static void __parsing_raw_value(http_header_t *http_header); + +da_result_t http_msg_request_create(http_msg_request_t **http_msg_request) +{ + http_msg_request_t *temp_http_msg_request = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + temp_http_msg_request = (http_msg_request_t *)calloc(1, + sizeof(http_msg_request_t)); + if (!temp_http_msg_request) { + *http_msg_request = NULL; + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + temp_http_msg_request->http_method = NULL; + temp_http_msg_request->url = NULL; + temp_http_msg_request->head = NULL; + temp_http_msg_request->http_body = NULL; + + *http_msg_request = temp_http_msg_request; + DA_LOG_DEBUG(HTTPManager, "http_msg_request: %x", (unsigned int)(*http_msg_request)); + + return DA_RESULT_OK; +} + +void http_msg_request_destroy(http_msg_request_t **http_msg_request) +{ + http_msg_request_t *temp_http_msg_request = *http_msg_request; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (temp_http_msg_request) { + if (temp_http_msg_request->http_method) { + free(temp_http_msg_request->http_method); + temp_http_msg_request->http_method = NULL; + } + + if (temp_http_msg_request->url) { + free(temp_http_msg_request->url); + temp_http_msg_request->url = NULL; + } + + if (temp_http_msg_request->http_body) { + free(temp_http_msg_request->http_body); + temp_http_msg_request->http_body = NULL; + } + + __http_header_destroy_all_field(&(temp_http_msg_request->head)); + + free(temp_http_msg_request); + *http_msg_request = NULL; + } + +} + +da_result_t http_msg_request_set_method(http_msg_request_t *http_msg_request, + const char *method) +{ + DA_LOG_FUNC_LOGD(HTTPManager); + + if (!http_msg_request || !method) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + // ToDo: check method is valid + + http_msg_request->http_method = strdup(method); + + DA_LOG(HTTPManager, "http method : %s", http_msg_request->http_method); + + return DA_RESULT_OK; +} + +da_result_t http_msg_request_get_method(http_msg_request_t *http_msg_request, + const char **method) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (http_msg_request->http_method) { + *method = http_msg_request->http_method; + return DA_RESULT_OK; + } else { + *method = DA_NULL; + return DA_ERR_INVALID_ARGUMENT; + } +} + +da_result_t http_msg_request_set_url(http_msg_request_t *http_msg_request, + const char *url) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "http_msg_request is NULL; DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (!url) { + DA_LOG_ERR(HTTPManager, "url is NULL; DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_URL; + } + + http_msg_request->url = strdup(url); + + //DA_SECURE_LOGD("http url : %s", http_msg_request->url); + + return DA_RESULT_OK; +} + +da_result_t http_msg_request_get_url(http_msg_request_t *http_msg_request, + const char **url) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "http_msg_request is NULL; DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (http_msg_request->url) { + *url = http_msg_request->url; + return DA_RESULT_OK; + } else { + *url = DA_NULL; + return DA_ERR_INVALID_ARGUMENT; + } +} + +da_result_t http_msg_request_set_body(http_msg_request_t *http_msg_request, + const char *body) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (!body) + return DA_RESULT_OK; + + http_msg_request->http_body = strdup(body); + + DA_SECURE_LOGD("http body : %s", http_msg_request->http_body); + + return DA_RESULT_OK; +} + +da_result_t http_msg_request_get_body(http_msg_request_t *http_msg_request, + const char **body) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + if (http_msg_request->http_body) { + *body = http_msg_request->http_body; + return DA_RESULT_OK; + } else { + *body = DA_NULL; + return DA_ERR_INVALID_ARGUMENT; + } +} + +/* FIXME later : check to free filed and value after this API is called */ +da_result_t http_msg_request_add_field(http_msg_request_t *http_msg_request, + const char *field, const char *value) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + return __http_header_add_field(&(http_msg_request->head), field, value, WITHOUT_PARSING_OPTION); +} + +da_result_t http_msg_response_create(http_msg_response_t **http_msg_response) +{ + http_msg_response_t *temp_http_msg_response = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + temp_http_msg_response = (http_msg_response_t *)calloc(1, + sizeof(http_msg_response_t)); + if (!temp_http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_ERR_FAIL_TO_MEMALLOC; + } else { + temp_http_msg_response->status_code = 0; + temp_http_msg_response->head = NULL; + + *http_msg_response = temp_http_msg_response; + + return DA_RESULT_OK; + } +} + +void http_msg_response_destroy(http_msg_response_t **http_msg_response) +{ + http_msg_response_t *temp_http_msg_response = *http_msg_response; + + DA_LOG_FUNC_LOGV(HTTPManager); + if (temp_http_msg_response) { + __http_header_destroy_all_field(&(temp_http_msg_response->head)); + + free(temp_http_msg_response); + *http_msg_response = DA_NULL; + } +} + +da_result_t http_msg_response_set_status_code( + http_msg_response_t *http_msg_response, int status_code) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + http_msg_response->status_code = status_code; + + return DA_RESULT_OK; +} + +da_result_t http_msg_response_get_status_code( + http_msg_response_t *http_msg_response, int *status_code) +{ + DA_LOG_FUNC_LOGD(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + *status_code = http_msg_response->status_code; + + return DA_RESULT_OK; +} + +da_result_t http_msg_response_add_field(http_msg_response_t *http_msg_response, + const char *field, const char *value) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + return __http_header_add_field(&(http_msg_response->head), field, value, WITH_PARSING_OPTION); +} + +da_result_t __http_header_add_field(http_header_t **head, + const char *field, const char *value, enum parsing_type type) +{ + http_header_t *pre = NULL; + http_header_t *cur = NULL; + + //DA_SECURE_LOGD("[%s][%s]", field, value); + + pre = cur = *head; + while (cur) { + pre = cur; + /* Replace default value with user wanted value + * Remove the value which is stored before and add a new value. + */ + if (cur->field && cur->raw_value && + strncasecmp(cur->field, field, strlen(field)) == 0) { + DA_SECURE_LOGD("Remove value for replacement [%s][%s]", cur->field, cur->raw_value); + if (cur->field) { + free(cur->field); + cur->field = NULL; + } + if (cur->raw_value) { + free(cur->raw_value); + cur->raw_value= NULL; + } + } + cur = cur->next; + } + + cur = (http_header_t *)calloc(1, sizeof(http_header_t)); + if (cur) { + cur->field = strdup(field); + cur->raw_value = strdup(value); + cur->options = NULL; + cur->next = NULL; + + if (type == WITHOUT_PARSING_OPTION) { + cur->value = strdup(value); + cur->options = NULL; + } else { + __parsing_raw_value(cur); + } + + if (pre) + pre->next = cur; + else + *head = cur; + } else { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_ERR_FAIL_TO_MEMALLOC; + } + + return DA_RESULT_OK; +} + +void __http_header_destroy_all_field(http_header_t **head) +{ + http_header_t *pre = NULL; + http_header_t *cur = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + cur = *head; + + while (cur) { + if (cur->field) { + free(cur->field); + cur->field = DA_NULL; + } + + if (cur->value) { + free(cur->value); + cur->value = DA_NULL; + } + + if (cur->raw_value) { + free(cur->raw_value); + cur->raw_value = DA_NULL; + } + + __http_header_destroy_all_option(&(cur->options)); + + pre = cur; + cur = cur->next; + + free(pre); + } + + *head = DA_NULL; +} + +http_header_options_t *__create_http_header_option(const char *field, + const char *value) +{ + http_header_options_t *option = NULL; + + option = (http_header_options_t *)calloc(1, + sizeof(http_header_options_t)); + if (option) { + if (field) + option->field = strdup(field); + + if (value) + option->value = strdup(value); + + option->next = NULL; + } + + return option; +} + +void __http_header_destroy_all_option(http_header_options_t **head) +{ + http_header_options_t *pre = NULL; + http_header_options_t *cur = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + cur = *head; + + while (cur) { + if (cur->field) { + DA_LOG_VERBOSE("field= %s", cur->field); + free(cur->field); + cur->field = DA_NULL; + } + + if (cur->value) { + free(cur->value); + cur->value = DA_NULL; + } + + pre = cur; + cur = cur->next; + + free(pre); + } + + *head = DA_NULL; +} + +da_result_t http_msg_request_get_iter(http_msg_request_t *http_msg_request, + http_msg_iter_t *http_msg_iter) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_request) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + *http_msg_iter = http_msg_request->head; + + return DA_RESULT_OK; +} + +da_result_t http_msg_response_get_iter(http_msg_response_t *http_msg_response, + http_msg_iter_t *http_msg_iter) +{ + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + *http_msg_iter = http_msg_response->head; + // DA_LOG_VERBOSE(HTTPManager, "retrieve iter = 0x%x", (unsigned int)http_msg_iter); + + return DA_RESULT_OK; +} + +da_bool_t http_msg_get_field_with_iter(http_msg_iter_t *http_msg_iter, + char **out_field, char **out_value) +{ + http_header_t *cur = *http_msg_iter; + + // DA_LOG_VERBOSE(HTTPManager, "getting iter = 0x%x", (unsigned int)cur); + + if (cur) { + *out_field = cur->field; + *out_value = cur->value; + *http_msg_iter = cur->next; + + return DA_TRUE; + } else { + // DA_LOG_VERBOSE(HTTPManager, "end of iter"); + return DA_FALSE; + } +} + +da_bool_t http_msg_get_header_with_iter(http_msg_iter_t *http_msg_iter, + char **out_field, http_header_t **out_header) +{ + http_header_t *cur = *http_msg_iter; + + // DA_LOG_VERBOSE(HTTPManager, "getting iter = 0x%x", (unsigned int)cur); + + if (cur) { + *out_field = cur->field; + *out_header = cur; + *http_msg_iter = cur->next; + + return DA_TRUE; + } else { + // DA_LOG_VERBOSE(HTTPManager, "end of iter"); + return DA_FALSE; + } +} + +http_header_options_t *__parsing_N_create_option_str(char *org_str) +{ + char *option_field = NULL; + char *option_value = NULL; + int option_field_len = 0; + int option_value_len = 0; + + char *org_pos = NULL; + int org_str_len = 0; + + char *working_str = NULL; + char *working_pos = NULL; + char *working_pos_field_start = NULL; + char *working_pos_value_start = NULL; + + da_bool_t is_inside_quotation = DA_FALSE; + da_bool_t is_working_for_field = DA_TRUE; + int i = 0; + http_header_options_t *option = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!org_str) + return NULL; + + org_str_len = strlen(org_str); + if (org_str_len <= 0) + return NULL; + + working_str = (char *)calloc(1, org_str_len + 1); + if (!working_str) + return NULL; + + org_pos = org_str; + working_pos_field_start = working_pos = working_str; + + for (i = 0; i < org_str_len; i++) { + if (*org_pos == '"') + is_inside_quotation = !is_inside_quotation; + + if (is_inside_quotation) { + // Leave anything including blank if it is inside of double quotation mark. + *working_pos = *org_pos; + is_working_for_field ? option_field_len++ + : option_value_len++; + working_pos++; + org_pos++; + } else { + if (*org_pos == ' ') { + org_pos++; + } else if (*org_pos == '=') { + if (is_working_for_field) { + is_working_for_field = DA_FALSE; + working_pos_value_start = working_pos; + } + + org_pos++; + } else { + *working_pos = *org_pos; + is_working_for_field ? option_field_len++ + : option_value_len++; + working_pos++; + org_pos++; + } + } + } + + if (option_field_len > 0 && working_pos_field_start) { + option_field = (char *)calloc(1, option_field_len + 1); + if (option_field) + strncpy(option_field, working_pos_field_start, + option_field_len); + } + + if (option_value_len > 0 && working_pos_value_start) { + option_value = (char *)calloc(1, option_value_len + 1); + if (option_value) + strncpy(option_value, working_pos_value_start, + option_value_len); + } + + if (working_str) { + free(working_str); + working_pos = working_str = NULL; + } + +// DA_SECURE_LOGD("option_field = [%s], option_value = [%s]", +// option_field, option_value); + + if (option_field || option_value) { + option = __create_http_header_option( + option_field, option_value); + if (option_field) { + free(option_field); + option_field = NULL; + } + + if (option_value) { + free(option_value); + option_value = NULL; + } + } + return option; +} + +http_header_options_t *__parsing_options(char *org_str) +{ + da_result_t ret = DA_RESULT_OK; + http_header_options_t *head = NULL; + http_header_options_t *pre = NULL; + http_header_options_t *cur = NULL; + + int wanted_str_len = 0; + char *wanted_str = NULL; + char *wanted_str_start = NULL; + char *wanted_str_end = NULL; + char *cur_pos = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!org_str) + return NULL; + + /* Do Not use strtok(). It's not thread safe. */ + // DA_SECURE_LOGD("org_str = %s", org_str); + + cur_pos = org_str; + + while (cur_pos) { + wanted_str_start = cur_pos; + wanted_str_end = strchr(cur_pos, ';'); + if (wanted_str_end) { + cur_pos = wanted_str_end + 1; + } else { + wanted_str_end = org_str + strlen(org_str); + cur_pos = NULL; + } + + wanted_str_len = wanted_str_end - wanted_str_start; + wanted_str = (char *)calloc(1, wanted_str_len + 1); + if (!wanted_str) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + strncpy(wanted_str, wanted_str_start, wanted_str_len); + + // DA_SECURE_LOGD("wanted_str = [%s]", wanted_str); + cur = __parsing_N_create_option_str(wanted_str); + if (pre) { + pre->next = cur; + pre = cur; + } else { + head = pre = cur; + } + + free(wanted_str); + wanted_str = NULL; + } + +ERR: + if (ret != DA_RESULT_OK) + __http_header_destroy_all_option(&head); + + return head; +} + +void __parsing_raw_value(http_header_t *http_header_field) +{ + char *raw_value = NULL; + char *option_str_start = NULL; + + char *trimed_value = NULL; + int trimed_value_len = 0; + + char *trimed_value_start = NULL; + char *trimed_value_end = NULL; + + raw_value = http_header_field->raw_value; + // DA_SECURE_LOGD("raw_value = [%s]", raw_value); + + if (!raw_value) + return; + + trimed_value_start = raw_value; + + trimed_value_end = strchr(raw_value, ';'); + if (!trimed_value_end) { + // No options + http_header_field->value = strdup(raw_value); + http_header_field->options = NULL; + + return; + } + + // for trimed value + trimed_value_len = trimed_value_end - trimed_value_start; + + trimed_value = (char *)calloc(1, trimed_value_len + 1); + if (!trimed_value) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return; + } + strncpy(trimed_value, trimed_value_start, trimed_value_len); + http_header_field->value = trimed_value; + + // for option parsing + option_str_start = trimed_value_end + 1; + + http_header_field->options = __parsing_options(option_str_start); + + /////////////// show + http_header_options_t *cur = NULL; + + cur = http_header_field->options; + while (cur) { +// DA_SECURE_LOGD("field = [%s], value = [%s]", cur->field, cur->value); + cur = cur->next; + } + +} + +da_bool_t __get_http_header_option_for_field( + http_header_options_t *header_option, const char *in_field, + char **out_value) +{ + http_header_options_t *cur = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!header_option) { + DA_LOG_ERR(HTTPManager, "input header_option is NULL."); + return DA_FALSE; + } + + cur = header_option; + while (cur) { + if (cur->field) { + if (!strncasecmp(cur->field, in_field, strlen(cur->field)) && + cur->value) { + DA_SECURE_LOGD("[%s][%s]", cur->field, cur->value); + *out_value = cur->value; + return DA_TRUE; + } + + } + cur = cur->next; + } + + return DA_FALSE; +} + +da_bool_t __get_http_header_for_field(http_msg_response_t *http_msg_response, + const char *in_field, http_header_t **out_header) +{ + http_msg_iter_t http_msg_iter; + http_header_t *header = NULL; + char *field = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + http_msg_response_get_iter(http_msg_response, &http_msg_iter); + while (http_msg_get_header_with_iter(&http_msg_iter, &field, &header)) { + if (field && header && !strncasecmp(field, in_field, strlen(field))) { +// DA_SECURE_LOGD("[%s][%s]", field, header->value); + *out_header = header; + return DA_TRUE; + } + } + + return DA_FALSE; +} + +void __exchange_header_value(http_header_t *header, const char *in_raw_value) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!header || !in_raw_value) + return; + + __http_header_destroy_all_option(&(header->options)); + + if (header->value) { + free(header->value); + header->value = DA_NULL; + } + + if (header->raw_value) + free(header->raw_value); + header->raw_value = strdup(in_raw_value); + + __parsing_raw_value(header); +} + +da_bool_t http_msg_response_get_content_type( + http_msg_response_t *http_msg_response, char **out_type) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, "Content-Type", + &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Content-Type"); + return DA_FALSE; + } + + if (out_type) + *out_type = strdup(header->value); + + return DA_TRUE; +} + +void http_msg_response_set_content_type(http_msg_response_t *http_msg_response, + const char *in_type) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_response || !in_type) + return; + + b_ret = __get_http_header_for_field(http_msg_response, "Content-Type", + &header); + if (b_ret) { + if (header->raw_value && (!strncmp(header->raw_value, in_type, + strlen(header->raw_value)))) + return; + + DA_SECURE_LOGD("exchange Content-Type to [%s] from [%s]", in_type, header->value); + __exchange_header_value(header, in_type); + } else { + __http_header_add_field(&(http_msg_response->head), + "Content-Type", in_type, WITH_PARSING_OPTION); + } +} + +da_bool_t http_msg_response_get_content_length( + http_msg_response_t *http_msg_response, unsigned long long *out_length) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, + "Content-Length", &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Content-Length"); + return DA_FALSE; + } + + if (out_length) + *out_length = atoll(header->value); + + return DA_TRUE; +} + +da_bool_t http_msg_response_get_content_disposition( + http_msg_response_t *http_msg_response, char **out_disposition, + char **out_file_name) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + char *file_name = NULL; + + char *wanted_str = NULL; + char *wanted_str_start = NULL; + char *wanted_str_end = NULL; + char *decoded_str = NULL; + int wanted_str_len = 0; + + DA_LOG_FUNC_LOGV(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, + "Content-Disposition", &header); + if (!b_ret) { + DA_LOG_VERBOSE(HTTPManager, "no Content-Disposition"); + return DA_FALSE; + } + + if (out_disposition) + *out_disposition = strdup(header->value); + + if (!out_file_name) + return DA_FALSE; + + b_ret = __get_http_header_option_for_field(header->options, "filename", + &file_name); + if (!b_ret) { + DA_LOG(HTTPManager, "no option"); + return DA_FALSE; + } + + // eliminate double quotation mark if it exists on derived value + wanted_str_start = strchr(file_name, '"'); + if (!wanted_str_start) { + *out_file_name = strdup(file_name); + return DA_TRUE; + } else { + // DA_SECURE_LOGD("wanted_str_start = [%s]", wanted_str_start); + wanted_str_start++; + wanted_str_end = strchr(wanted_str_start, '"'); + if (wanted_str_end) { + wanted_str_len = wanted_str_end - wanted_str_start; + wanted_str = (char*)calloc(1, wanted_str_len + 1); + if (!wanted_str) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_FALSE; + } + strncpy(wanted_str, wanted_str_start, wanted_str_len); + + b_ret = is_base64_encoded_word(wanted_str); + if (b_ret) { + DA_LOG(HTTPManager, "It's base64 encoded-word string"); + if (DA_RESULT_OK == decode_base64_encoded_str( + wanted_str, &decoded_str)) { + DA_SECURE_LOGD("base64 decoded str = [%s]", decoded_str); + free(wanted_str); + wanted_str = decoded_str; + decoded_str = NULL; + } else { + DA_LOG(HTTPManager, "Fail to base64 decode. Just use un-decoded string."); + } + } else { + DA_LOG(HTTPManager, "It's NOT base64 encoded-word string"); + } + decode_url_encoded_str(wanted_str, &decoded_str); + /* If it is url encoded string */ + if (decoded_str) { + DA_SECURE_LOGD("Url decoded str = [%s]", decoded_str); + free(wanted_str); + wanted_str = decoded_str; + decoded_str = NULL; + } + + *out_file_name = wanted_str; + + DA_SECURE_LOGD("out_file_name = [%s]", *out_file_name); + + return DA_TRUE; + } else { + DA_LOG_ERR(HTTPManager, "Not matched \" !"); + return DA_FALSE; + } + } +} + +da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response, + char **out_value) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, "ETag", &header); + if (!b_ret) { + DA_LOG_VERBOSE(HTTPManager, "no ETag"); + return DA_FALSE; + } + + if (out_value) + *out_value = strdup(header->value); + + return DA_TRUE; +} + +da_bool_t http_msg_response_get_date(http_msg_response_t *http_msg_response, + char **out_value) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, "Date", &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Date"); + return DA_FALSE; + } + + if (out_value) + *out_value = strdup(header->value); + + return DA_TRUE; +} + +da_bool_t http_msg_response_get_location(http_msg_response_t *http_msg_response, + char **out_value) +{ + da_bool_t b_ret = DA_FALSE; + http_header_t *header = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + b_ret = __get_http_header_for_field(http_msg_response, "Location", &header); + if (!b_ret) { + DA_LOG(HTTPManager, "no Location"); + return DA_FALSE; + } + if (out_value) + *out_value = strdup(header->value); + + return DA_TRUE; +} + +da_result_t http_msg_response_get_boundary( + http_msg_response_t *http_msg_response, char **out_val) +{ + da_result_t ret = DA_RESULT_OK; + + http_msg_iter_t http_msg_iter; + char *field = NULL; + char *value = NULL; + char *boundary = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!http_msg_response) { + DA_LOG_ERR(HTTPManager, "DA_ERR_INVALID_ARGUMENT"); + return DA_ERR_INVALID_ARGUMENT; + } + + http_msg_response_get_iter(http_msg_response, &http_msg_iter); + while (http_msg_get_field_with_iter(&http_msg_iter, &field, &value)) { + if ((field != DA_NULL) && (value != DA_NULL)) { + if (!strncasecmp(field, "Content-Type", + strlen("Content-Type"))) { + char *org_str = NULL; + char *boundary_str_start = NULL; + char *boundary_value_start = NULL; + char *boundary_value_end = NULL; + int boundary_value_len = 0; + + org_str = value; + + boundary_str_start + = strstr(org_str, "boundary"); + if (boundary_str_start) { + DA_LOG(HTTPManager, "boundary_str_start = %s", boundary_str_start); + // this "Content-Type" value has "boundary" in it, so get the value + boundary_value_start = strchr( + boundary_str_start, '"'); + boundary_value_start += 1; // start without " + + boundary_value_end = strchr( + boundary_value_start, '"'); + boundary_value_len = boundary_value_end + - boundary_value_start; + + DA_LOG(HTTPManager, "boundary_value_start = %s", boundary_value_start); + DA_LOG(HTTPManager, "boundary_value_end = %s", boundary_value_end); + DA_LOG(HTTPManager, "boundary_value_len = %d", boundary_value_len); + + } else { + // no "boundary" field on this "Content-Type" value + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + // end of clear + + boundary = (char *)calloc(1, + boundary_value_len + 1); + if (!boundary) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + + goto ERR; + } + strncpy(boundary, boundary_value_start, + boundary_value_len); + DA_SECURE_LOGD("[boundary][%s]", boundary); + break; + } + } + } + + *out_val = boundary; + +ERR: + return ret; +} + +char *get_http_response_header_raw(http_msg_response_t *http_msg_response) +{ + http_msg_iter_t http_msg_iter; + http_header_t *header = NULL; + char *field = NULL; + char tmp_buf[1024*4] = {0,}; + char line_buf[1024] = {0,}; + int len = 0; + char *buff = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + http_msg_response_get_iter(http_msg_response, &http_msg_iter); + while (http_msg_get_header_with_iter(&http_msg_iter, &field, &header)) { + if (field && header) { + // FIXME later :: buffer length is more than total length. think about getting header's conent length from libsoup + len = strlen(field) + strlen(header->value) + 2; + snprintf(line_buf, len,"%s:%s", field, header->value); + strncat(tmp_buf, line_buf, len); + strcat(tmp_buf, "\n"); + } + } + if (strlen(tmp_buf) > 0) { + buff = (char *)calloc(1, strlen(tmp_buf) + 1); + if (buff == DA_NULL) { + DA_LOG_ERR(HTTPManager, "DA_ERR_FAIL_TO_MEMALLOC"); + return DA_NULL; + } + memcpy(buff, tmp_buf, strlen(tmp_buf)); + DA_SECURE_LOGD("\n---raw response header---\n%s\n------\n",buff); + return buff; + } else { + return DA_NULL; + } +} + +char *_stristr(const char *long_str, const char *find_str) +{ + int i = 0; + int length_long = 0; + int length_find = 0; + char *ret_ptr = NULL; + char *org_ptr = NULL; + char *look_ptr = NULL; + + if (long_str == NULL || find_str == NULL) { + DA_LOG_ERR(Default,"INVALID ARGUMENT"); + return NULL; + } + + length_long = strlen(long_str); + length_find = strlen(find_str); + + org_ptr = (char*)calloc(1, length_long + 1); + + if (org_ptr == NULL) { + DA_LOG_ERR(Default,"INVALID ARGUMENT"); + return NULL; + } + + look_ptr = (char*)calloc(1, length_find + 1); + + if (look_ptr == NULL) { + DA_LOG_ERR(Default,"INVALID ARGUMENT"); + free(org_ptr); + return NULL; + } + + while (i < length_long) { + if (isalpha(long_str[i]) != 0) { + if (isupper(long_str[i]) != 0) { + org_ptr[i] = long_str[i]; + } else { + org_ptr[i] = toupper(long_str[i]); + } + } else { + org_ptr[i] = long_str[i]; + } + + i++; + } + + i = 0; + + while (i < length_find) { + if (isalpha(find_str[i]) != 0) { + if (isupper(find_str[i]) != 0) { + look_ptr[i] = find_str[i]; + } else { + look_ptr[i] = toupper(find_str[i]); + } + } else { + look_ptr[i] = find_str[i]; + } + + i++; + } + + ret_ptr = strstr(org_ptr, look_ptr); + + if (ret_ptr == 0) { + free(org_ptr); + free(look_ptr); + return NULL; + } else { + i = ret_ptr - org_ptr; + } + + free(org_ptr); + free(look_ptr); + + return (char*)(long_str + i); +} + +/* This is not used. But it can be needed if there is no http header parser at http library.*/ +da_bool_t extract_attribute_from_header( + char *szHeadStr, + const char *szFindStr, + char **ppRtnValue) +{ + + char *pValuePos = NULL; + int index = 0; + int startPos = 0; + int strLen = 0; + int need_to_end_quataion_mark = 0; + + if (szHeadStr == DA_NULL || szFindStr == DA_NULL) { + DA_LOG_ERR(Default,"INVALID ARGUMENT"); + return DA_FALSE; + } + + if (strlen(szHeadStr) <= 0 || strlen(szFindStr) <= 0) { + DA_LOG_ERR(Default,"INVALID ARGUMENT");; + + return DA_FALSE; + } + + if (ppRtnValue == NULL) { + return DA_FALSE; + } + + pValuePos = _stristr(szHeadStr, (char*)szFindStr); + if (pValuePos == NULL) { + *ppRtnValue = NULL; + goto ERR; + } + + index = strlen(szFindStr); + + while (pValuePos[index] != ':' && pValuePos[index] != '=') { + index++; + + if (pValuePos[index] == '\0') { + return DA_FALSE; + } + } + + index++; + + /* jump space */ + while (pValuePos[index] == ' ') { + index++; + } + + /* jump quatation mark */ + while (pValuePos[index] == '"') { + need_to_end_quataion_mark = 1; + index++; + } + + startPos = index; + + /* Find the end of data. */ + if (0 == strncasecmp(szFindStr, "Location", strlen("Location")))//terminate character list does not contain ';' in case of URI + { + while (DA_FALSE == IS_URI_TERMINATING_CHAR(pValuePos[index])) { + index++; + } + } else if (need_to_end_quataion_mark) { + while (DA_FALSE == IS_TERMINATING_CHAR_EX(pValuePos[index])) { + index++; + } + } else { + while (DA_FALSE == IS_TERMINATING_CHAR(pValuePos[index])) { + index++; + } + } + + strLen = index - startPos; + + if (strLen < 1) { + DA_LOG_ERR(Default," strLen is < 1"); + goto ERR; + } + + *ppRtnValue = (char*)calloc(1, sizeof(char) * (strLen + 1)); + + if (*ppRtnValue == NULL) { + DA_LOG_ERR(Default," *ppRtnValue is NULL"); + goto ERR; + } + + strncpy(*ppRtnValue, pValuePos + startPos, strLen); + *(*ppRtnValue + strLen) = '\0'; + + return DA_TRUE; + +ERR: + + if (*ppRtnValue) { + free(*ppRtnValue); + *ppRtnValue = NULL; + } + + return DA_FALSE; +} + diff --git a/agent/download-agent-http-queue.c b/agent/download-agent-http-queue.c new file mode 100755 index 0000000..06fe084 --- /dev/null +++ b/agent/download-agent-http-queue.c @@ -0,0 +1,390 @@ +/* + * 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-agent-http-queue.h" +#include "download-agent-http-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-pthread.h" + +void init_q_event_data_http(q_event_t *q_event); +void init_q_event_control(q_event_t *q_event); + +void Q_init_queue(queue_t *queue) +{ + queue->having_data = DA_FALSE; + queue->control_head = DA_NULL; + queue->data_head = DA_NULL; + queue->queue_size = 0; + + _da_thread_mutex_init(&(queue->mutex_queue), DA_NULL); + _da_thread_cond_init(&(queue->cond_queue), DA_NULL); +} + +void Q_destroy_queue(queue_t *queue) +{ + q_event_t *event = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + do { + Q_pop_event(queue, &event); + Q_destroy_q_event(&event); + } while(event); + + queue->having_data = DA_FALSE; + queue->control_head = DA_NULL; + queue->data_head = DA_NULL; + queue->queue_size = 0; + + _da_thread_mutex_destroy(&(queue->mutex_queue)); + _da_thread_cond_destroy(&(queue->cond_queue)); +} + +void Q_init_q_event(q_event_t *q_event) +{ + switch(q_event->event_type) { + case Q_EVENT_TYPE_DATA_HTTP: + init_q_event_data_http(q_event); + break; + + case Q_EVENT_TYPE_DATA_DRM: + break; + + case Q_EVENT_TYPE_CONTROL: + init_q_event_control(q_event); + break; + } + + q_event->size = 0; + q_event->next = DA_NULL; +} + +void Q_destroy_q_event(q_event_t **in_q_event) +{ + q_event_t *q_event = DA_NULL; + q_event = *in_q_event; + + if(q_event == DA_NULL) + return; + + switch(q_event->event_type) { + case Q_EVENT_TYPE_DATA_HTTP: + init_q_event_data_http(q_event); + q_event->size = 0; + q_event->next = DA_NULL; + free(q_event); + break; + + case Q_EVENT_TYPE_DATA_DRM: + q_event->size = 0; + q_event->next = DA_NULL; + free(q_event); + break; + + case Q_EVENT_TYPE_CONTROL: + init_q_event_control(q_event); + q_event->size = 0; + q_event->next = DA_NULL; + free(q_event); + break; + } +} + +da_result_t Q_make_control_event(q_event_type_control control_type, q_event_t **out_event) +{ + da_result_t ret = DA_RESULT_OK; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_LOGD(HTTPManager); + + q_event = (q_event_t *)calloc(1, sizeof(q_event_t)); + if(q_event == DA_NULL) { + DA_LOG_ERR(HTTPManager, "calloc fail for q_event"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + + *out_event = DA_NULL; + } else { + q_event->event_type = Q_EVENT_TYPE_CONTROL; + q_event->type.q_event_control.control_type = control_type; + q_event->next = DA_NULL; + + *out_event = q_event; + } + + return ret; +} + +da_result_t Q_make_http_data_event(q_event_type_data data_type, q_event_t **out_event) +{ + da_result_t ret = DA_RESULT_OK; + q_event_t *q_event = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + q_event = (q_event_t *)calloc(1, sizeof(q_event_t)); + if(q_event == DA_NULL) { + DA_LOG_ERR(HTTPManager, "calloc fail for q_event"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + *out_event = DA_NULL; + } else { + q_event->event_type = Q_EVENT_TYPE_DATA_HTTP; + q_event->type.q_event_data_http.data_type = data_type; + q_event->next = DA_NULL; + + *out_event = q_event; + +// DA_LOG_VERBOSE(HTTPManager, "made event = %x", *out_event); + } + + return ret; + +} + +da_result_t Q_set_status_code_on_http_data_event(q_event_t *q_event, int status_code) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if(q_event->event_type != Q_EVENT_TYPE_DATA_HTTP) { + DA_LOG_ERR(HTTPManager, "status_code can be set only for Q_EVENT_TYPE_DATA_HTTP."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + q_event->type.q_event_data_http.status_code = status_code; + +// DA_LOG_VERBOSE(HTTPManager, "status_code = %d, q_event = %x", q_event->type.q_event_data_http.status_code, q_event); + +ERR: + return ret; + +} + +da_result_t Q_set_http_body_on_http_data_event(q_event_t *q_event, int body_len, char *body_data) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if(q_event->event_type != Q_EVENT_TYPE_DATA_HTTP) { + DA_LOG_ERR(HTTPManager, "http body can be set only for Q_EVENT_TYPE_DATA_HTTP."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + q_event->type.q_event_data_http.body_len = body_len; + q_event->type.q_event_data_http.body_data = body_data; + q_event->size = body_len; + +// DA_LOG_VERBOSE(HTTPManager, "body_len = %d, body_data = %x, q_event = %x", q_event->type.q_event_data_http.body_len, q_event->type.q_event_data_http.body_data, q_event); + +ERR: + return ret; + +} + +da_result_t Q_set_error_type_on_http_data_event(q_event_t *q_event, int error_type) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if(q_event->event_type != Q_EVENT_TYPE_DATA_HTTP) { + DA_LOG_ERR(HTTPManager, "error_type can be set only for Q_EVENT_TYPE_DATA_HTTP."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + q_event->type.q_event_data_http.error_type = error_type; + + DA_LOG_VERBOSE(HTTPManager, "error_type = %d, q_event = %p", q_event->type.q_event_data_http.error_type, q_event); + +ERR: + return ret; + +} + +da_bool_t Q_push_event(const queue_t *in_queue, const q_event_t *in_event) +{ + da_bool_t b_ret = DA_FALSE; + queue_t *queue = (queue_t *)in_queue; + + _da_thread_mutex_lock (&(queue->mutex_queue)); + b_ret = Q_push_event_without_lock(in_queue, in_event); + _da_thread_mutex_unlock (&(queue->mutex_queue)); + + return b_ret; +} + +da_bool_t Q_push_event_without_lock(const queue_t *in_queue, const q_event_t *in_event) +{ + da_bool_t b_ret = DA_FALSE; + queue_t *queue = (queue_t *)in_queue; + q_event_t *event = (q_event_t *)in_event; + q_event_type event_type; + q_event_t *head = DA_NULL; + q_event_t *cur = DA_NULL; + +// DA_LOG_VERBOSE(HTTPManager, "queue = %x", in_queue); + + event_type = event->event_type; + +// _da_thread_mutex_lock (&(queue->mutex_queue)); + + if(event_type == Q_EVENT_TYPE_CONTROL) { + head = queue->control_head; + if(head == DA_NULL) { + queue->control_head = event; + } else { + cur = head; + + while(cur->next != DA_NULL) { + cur = cur->next; + } + cur->next= event; + } + b_ret = DA_TRUE; + } else { + if((event->size == 0) || (queue->queue_size < MAX_QUEUE_SIZE)) { + head = queue->data_head; + if(head == DA_NULL) { + queue->data_head = event; + } else { + cur = head; + while(cur->next != DA_NULL) { + cur = cur->next; + } + cur->next= event ; + } + + queue->queue_size += event->size; +// DA_LOG_VERBOSE(HTTPManager, "queue size is %d", queue->queue_size); + + b_ret = DA_TRUE; + } else { + DA_LOG_CRITICAL(HTTPManager, "rejected event's size is %d queue_size %d", event->size, queue->queue_size); + b_ret = DA_FALSE; + } + } + + queue->having_data = DA_TRUE; + Q_wake_up(queue); +// _da_thread_mutex_unlock (&(queue->mutex_queue)); + return b_ret; +} + +void Q_pop_event(const queue_t *in_queue, q_event_t **out_event) +{ + queue_t *queue = (queue_t*)in_queue; + +// DA_LOG_VERBOSE(HTTPManager, "queue = %x", in_queue); + + /** Pop Priority + * 1. If there are control event, control event should pop first + * 2. If there is no control event, data event should pop + * 3. If there is no control and data event on queue, pop NULL + */ + + _da_thread_mutex_lock (&(queue->mutex_queue)); + + if(queue->control_head != DA_NULL) {/* Priority 1 */ + *out_event = queue->control_head; + queue->control_head = queue->control_head->next; + } else { + if(queue->data_head != DA_NULL) {/* Priority 2 */ + *out_event = queue->data_head; + queue->data_head = queue->data_head->next; + queue->queue_size -= (*out_event)->size; + DA_LOG_VERBOSE(HTTPManager, "queue size is %d", queue->queue_size); + } else {/* Priority 3 */ + *out_event = DA_NULL; + } + } + + if((queue->control_head == DA_NULL) && (queue->data_head == DA_NULL)) { + queue->having_data = DA_FALSE; + } else { + queue->having_data = DA_TRUE; + } + + _da_thread_mutex_unlock (&(queue->mutex_queue)); + +} + +void Q_goto_sleep(const queue_t *in_queue) +{ + DA_LOG_VERBOSE(HTTPManager, "sleep for %p", in_queue); + +//** SHOULD NOT use mutex **// + +// _da_thread_mutex_lock (&(in_queue->mutex_queue)); + _da_thread_cond_wait((pthread_cond_t*)(&(in_queue->cond_queue)),(pthread_mutex_t*) (&(in_queue->mutex_queue))); +// _da_thread_mutex_unlock (&(in_queue->mutex_queue)); +} + +void Q_wake_up(const queue_t *in_queue) +{ + DA_LOG_VERBOSE(HTTPManager, "wake up for %p", in_queue); + +//** SHOULD NOT use mutex **// + +// _da_thread_mutex_lock (&(in_queue->mutex_queue)); + _da_thread_cond_signal((pthread_cond_t*)(&(in_queue->cond_queue))); +// _da_thread_mutex_unlock (&(in_queue->mutex_queue)); +} + +void init_q_event_data_http(q_event_t *q_event) +{ + q_event_data_http_t *q_event_data_http; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if(q_event->event_type == Q_EVENT_TYPE_DATA_HTTP) { + q_event_data_http = &(q_event->type.q_event_data_http); + + if(q_event_data_http) { + q_event_data_http->status_code = 0; + if(q_event_data_http->http_response_msg) { + http_msg_response_destroy(&(q_event_data_http->http_response_msg)); + q_event_data_http->http_response_msg = DA_NULL; + } + + if(q_event_data_http->body_len > 0 ) { + if (q_event_data_http->body_data) { + free(q_event_data_http->body_data); + q_event_data_http->body_data = DA_NULL; + } + } + q_event_data_http->error_type = 0; + } + } +} + +void init_q_event_control(q_event_t *q_event) +{ + q_event_control_t *q_event_control; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if(q_event->event_type == Q_EVENT_TYPE_CONTROL) { + q_event_control = &(q_event->type.q_event_control); + if(q_event_control) { + q_event_control->control_type = Q_EVENT_TYPE_CONTROL_NONE; + } + } + +} diff --git a/agent/download-agent-interface.c b/agent/download-agent-interface.c new file mode 100755 index 0000000..7d994e6 --- /dev/null +++ b/agent/download-agent-interface.c @@ -0,0 +1,234 @@ +/* + * 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-agent-interface.h" +#include "download-agent-debug.h" +#include "download-agent-utils.h" +#include "download-agent-http-mgr.h" +#include "download-agent-http-misc.h" +#include "download-agent-client-mgr.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-basic.h" +#include "download-agent-file.h" + +int da_init( + da_client_cb_t *da_client_callback) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGD(Default); + + if (!da_client_callback) { + ret = DA_ERR_INVALID_ARGUMENT; + return ret; + } + + ret = init_log_mgr(); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = init_client_app_mgr(); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = reg_client_app(da_client_callback); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = init_http_mgr(); + if (ret != DA_RESULT_OK) + goto ERR; + + ret = init_download_mgr(); + if (ret != DA_RESULT_OK) + goto ERR; + +ERR: + if (DA_RESULT_OK != ret) + da_deinit(); + + DA_LOG_CRITICAL(Default, "Return ret = %d", ret); + + return ret; +} + +/* TODO:: deinit should clean up all the clients... */ +int da_deinit() +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGV(Default); + + deinit_http_mgr(); + deinit_download_mgr(); + /* Do not clean temporary download path + * The client can resume or restart download with temporary file in case of failed download. + */ + dereg_client_app(); + DA_LOG(Default, "====== da_deinit EXIT ====="); + + return ret; +} + +int da_start_download( + const char *url, + int *download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_FUNC_LOGD(Default); + + *download_id = DA_INVALID_ID; + + if (DA_FALSE == is_valid_url(url, &ret)) + goto ERR; + + DA_SECURE_LOGI("url = %s", url); + + ret = start_download(url, download_id); + if (ret != DA_RESULT_OK) + goto ERR; + +ERR: + DA_LOG_CRITICAL(Default, "Return: Dl req id = %d, ret = %d", *download_id, ret); + return ret; +} + +int da_start_download_with_extension( + const char *url, + extension_data_t *extension_data, + int *download_id +) +{ + da_result_t ret = DA_RESULT_OK; + int req_header_count = 0; + int i = 0; + + DA_LOG_FUNC_LOGV(Default); + + *download_id = DA_INVALID_ID; + + if (DA_FALSE == is_valid_url(url, &ret)) + goto ERR; + + DA_SECURE_LOGI("url = %s", url); + + if (ret != DA_RESULT_OK) + goto ERR; + if (!extension_data) { + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + if (extension_data->request_header_count > 0) { + DA_LOG_VERBOSE(Default, "input request_header_count = [%d]", + extension_data->request_header_count); + for (i = 0; i < extension_data->request_header_count; i++) { + if (extension_data->request_header[i]) { + req_header_count++; + DA_SECURE_LOGI("request_header = [%s]", + extension_data->request_header[i]); + } + } + DA_LOG_VERBOSE(Default, "actual request_header_count = [%d]", req_header_count); + if (extension_data->request_header_count != req_header_count) { + DA_LOG_ERR(Default, "Request header count is not matched with number of request header array"); + extension_data->request_header = NULL; + extension_data->request_header_count = 0; + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + } + + if (extension_data->install_path) { + if (!is_dir_exist(extension_data->install_path)) + return DA_ERR_INVALID_INSTALL_PATH; + DA_SECURE_LOGI("install_path = [%s]", extension_data->install_path); + } + + if (extension_data->file_name) + DA_SECURE_LOGI("file_name = [%s]", extension_data->file_name); + if (extension_data->temp_file_path) + DA_SECURE_LOGI("temp_file_path = [%s]", extension_data->temp_file_path); + if (extension_data->etag) + DA_SECURE_LOGI("etag = [%s]", extension_data->etag); + if (extension_data->pkg_name) + DA_SECURE_LOGI("pkg_name = [%s]", extension_data->pkg_name); + if (extension_data->user_data) + DA_LOG_VERBOSE(Default, "user_data = [%p]", extension_data->user_data); + + ret = start_download_with_extension(url, download_id, extension_data); + +ERR: + DA_LOG_CRITICAL(Default, "Return: Dl req id = %d, ret = %d", *download_id, ret); + return ret; +} + +int da_cancel_download(int download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_VERBOSE(Default, "Cancel for dl_id = %d", download_id); + + ret = cancel_download(download_id); + + DA_LOG_CRITICAL(Default, "Return: Cancel id = %d, ret = %d", download_id, ret); + return ret; +} + +int da_suspend_download(int download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_VERBOSE(Default, "Suspend for dl_id = %d", download_id); + + ret = suspend_download(download_id, DA_TRUE); + + DA_LOG_CRITICAL(Default, "Return: Suspend id = %d, ret = %d", download_id, ret); + return ret; +} + +int da_suspend_download_without_update(int download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_VERBOSE(Default, "Suspend for dl_id = %d", download_id); + + ret = suspend_download(download_id, DA_FALSE); + + DA_LOG_CRITICAL(Default, "Return: Suspend id = %d, ret = %d", download_id, ret); + return ret; +} + + +int da_resume_download(int download_id) +{ + da_result_t ret = DA_RESULT_OK; + + DA_LOG_VERBOSE(Default, "Resume for dl_id = %d", download_id); + + ret = resume_download(download_id); + + DA_LOG_CRITICAL(Default, "Return: Resume id = %d, ret = %d", download_id, ret); + return ret; +} + +int da_is_valid_download_id(int download_id) +{ + da_bool_t ret = DA_FALSE; + ret = is_valid_download_id(download_id); + return ret; +} diff --git a/agent/download-agent-mime-util.c b/agent/download-agent-mime-util.c new file mode 100755 index 0000000..53bab8b --- /dev/null +++ b/agent/download-agent-mime-util.c @@ -0,0 +1,401 @@ +/* + * 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 <string.h> +#include <stdlib.h> + +#include <xdgmime.h> + +#include "download-agent-debug.h" +#include "download-agent-mime-util.h" +#include "download-agent-pthread.h" + +#define IS_PROHIBITED_CHAR(c) ((c) == ';' || (c) == '\\' || (c) == '/' || (c) == ':' || (c) == '*' || (c) == '?' || (c) == '"' || (c) == '>' || (c) == '<' || (c) == '|' || (c) == '(' || (c) == ')') +#define IS_SPACE_CHARACTER(c) ((c) == '\t') + +#define MAX_EXT_TABLE_INDEX 16 +Ext_translation_table ext_trans_table [MAX_EXT_TABLE_INDEX] = { + {"*.xla", "*.xls"}, + {"*.pot", "*.ppt"}, + {"*.xsl", "*.xml"}, + {"*.spl", "*.swf"}, + {"*.oga", "*.ogg"}, + {"*.jpe", "*.jpg"},//5 + {"*.CSSL", "*.css"}, + {"*.htm", "*.html"}, + {"*.hxx", "*.hpp"}, + {"*.c++", "*.cpp"}, + {"CMakeLists.txt", "*.cmake"},//10 + {"*.ime", "*.imy"}, + {"Makefile", "makefile"}, + {"*.3g2", "*.3gp"}, + {"*.mp2", "*.mpg"}, + {"*.divx", "*.avi"},//15 + }; +/* This is samsung mime policy + * 1. if the mime is audio/m4a, the extension name is defined as "m4a" for launching music player +*/ +#ifdef _SAMSUNG_MIME_POLICY +#define MAX_SEC_MIME_TABLE_INDEX 1 +struct sec_mime_table_t { + char *mime; + char *ext; +}; +struct sec_mime_table_t sec_mime_table [MAX_SEC_MIME_TABLE_INDEX] = { + {"audio/m4a", "m4a"}, +}; +#endif + +const char *ambiguous_MIME_Type_list[] = { + "text/plain", + "application/octet-stream" +}; + +/* Because xdgmime is not thread safety, this mutex is necessary */ +pthread_mutex_t mutex_for_xdgmime = PTHREAD_MUTEX_INITIALIZER; + +da_bool_t is_ambiguous_MIME_Type(const char *in_mime_type) +{ + DA_LOG_FUNC_LOGV(Default); + + if (!in_mime_type) + return DA_FALSE; + + int index = 0; + int list_size = sizeof(ambiguous_MIME_Type_list) / sizeof(const char *); + for (index = 0 ; index < list_size ; index++) { + if (0 == strncmp(in_mime_type, ambiguous_MIME_Type_list[index], + strlen(ambiguous_MIME_Type_list[index]))) { + DA_SECURE_LOGD("It is ambiguous! [%s]", ambiguous_MIME_Type_list[index]); + return DA_TRUE; + } + } + + return DA_FALSE; +} + +da_result_t da_mime_get_ext_name(char *mime, char **ext) +{ + da_result_t ret = DA_RESULT_OK; + const char **extlist = DA_NULL; + const char *unaliased_mimetype = DA_NULL; + char ext_temp[DA_MAX_STR_LEN] = {0,}; + char *temp = NULL; + + DA_LOG_FUNC_LOGV(Default); + + if (DA_NULL == mime || DA_NULL == ext) { + ret = DA_ERR_INVALID_ARGUMENT; + DA_LOG_ERR(Default,"Invalid mime type"); + goto ERR; + } +// DA_SECURE_LOGD("mime str[%s]ptr[%p]len[%d]",mime,mime,strlen(mime)); + /* unaliased_mimetype means representative mime among similar types */ + _da_thread_mutex_lock(&mutex_for_xdgmime); + unaliased_mimetype = xdg_mime_unalias_mime_type(mime); + _da_thread_mutex_unlock(&mutex_for_xdgmime); + + if (unaliased_mimetype == DA_NULL) { + ret = DA_ERR_INVALID_MIME_TYPE; + DA_LOG_ERR(Default,"Invalid mime type : No unsaliased mime type"); + goto ERR; + } + DA_SECURE_LOGD("unaliased_mimetype[%s]\n",unaliased_mimetype); + + /* Get extension name from shared-mime-info */ + _da_thread_mutex_lock(&mutex_for_xdgmime); + extlist = xdg_mime_get_file_names_from_mime_type(unaliased_mimetype); + _da_thread_mutex_unlock(&mutex_for_xdgmime); + if (extlist == DA_NULL || *extlist == DA_NULL) { + int i = 0; + ret = DA_ERR_INVALID_MIME_TYPE; + DA_LOG(Default,"No extension list"); +#ifdef _SAMSUNG_MIME_POLICY + for (i = 0; i < MAX_SEC_MIME_TABLE_INDEX; i++) + { + if (strncmp(sec_mime_table[i].mime, mime, strlen(mime)) == 0) { + strncpy(ext_temp, sec_mime_table[i].ext, DA_MAX_STR_LEN-1); + ret = DA_RESULT_OK; + break; + } + } +#endif + } else { /* For drm case, this else statement is needed */ + DA_SECURE_LOGD("extlist[%s]\n",*extlist); + strncpy(ext_temp, *extlist, DA_MAX_STR_LEN); + /* If only one extension name is existed, don't enter here */ + while (*extlist != NULL) { + int i = 0; + /* If there are existed many extension names, + * try to search common extension name from table + * with first mime type at extension list*/ + for (i = 0; i < MAX_EXT_TABLE_INDEX; i++) + { + if (strncmp(ext_trans_table[i].standard,*extlist, + strlen(*extlist)) == 0) { + memset(ext_temp, 0x00, DA_MAX_STR_LEN); + strncpy(ext_temp,ext_trans_table[i].normal, DA_MAX_STR_LEN-1); + break; + } + } + DA_LOG_VERBOSE(Default,"index[%d]\n",i); + /* If there is a mime at extension transform table */ + if (i < MAX_EXT_TABLE_INDEX) { + break; + } + DA_SECURE_LOGD("extlist[%s]\n",*extlist); + extlist++; + } +// DA_SECURE_LOGD("extension from shared mime info[%s]",ext_temp); + } + + if (strlen(ext_temp) < 1) { + /* If there is no mime string for OMA descriptor mime type */ + if (strncmp(DD_MIME_STR, mime, strlen(DD_MIME_STR)) == 0) { + strncpy(ext_temp, DD_EXT_STR, DA_MAX_STR_LEN-1); + ret = DA_RESULT_OK; + /* If there is no extension name for "applicaion/vnd.oma.drm.messeages" + * at shared-mime-info*/ + } else if (strncmp(DRM_MIME_MSG_STR, mime, + strlen(DRM_MIME_MSG_STR)) == 0) { + strncpy(ext_temp, DRM_EXT_STR, DA_MAX_STR_LEN-1); + /* If there is extension name at extlist, the return value can have an error.*/ + ret = DA_RESULT_OK; + } else { + ret = DA_ERR_INVALID_MIME_TYPE; + DA_LOG_ERR(Default,"Invalid mime type : no extension name at list"); + } + } + if (ret != DA_RESULT_OK) + goto ERR; + + temp = strchr(ext_temp,'.'); + if (temp == NULL) + temp = ext_temp; + else + temp++; + + *ext = (char*)calloc(1, strlen(temp) + 1); + if (*ext != DA_NULL) { + strncpy(*ext, temp,strlen(temp)); + } else { + ret = DA_ERR_FAIL_TO_MEMALLOC ; + goto ERR ; + } +ERR: + return ret; +} + +da_bool_t da_get_extension_name_from_url(char *url, char **ext) +{ + da_bool_t ret = DA_TRUE; + char *buff = DA_NULL; + char *temp_str = DA_NULL; + int buf_len = 0; + + DA_LOG_FUNC_LOGV(Default); + + if (DA_NULL == url || DA_NULL == ext) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Invalid Argument"); + return ret; + } + + if ((temp_str = strrchr(url,'/'))) { + if ((buff = strrchr(temp_str,'.'))) { + char *q = DA_NULL; + buff++; + /* check to exist "?" after extension name */ + q = strrchr(buff,'?'); + if (q) { + buf_len = strlen(buff) - strlen(q); + } else { + buf_len = strlen(buff); + } + *ext = (char*) calloc(1, buf_len + 1) ; + + if (DA_NULL == *ext) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Memory Fail"); + goto ERR; + } + strncpy(*ext,buff,buf_len); + DA_SECURE_LOGD("extention name[%s]",*ext); + return ret; + } + } +ERR: + if (*ext) { + free(*ext); + *ext = DA_NULL; + } + return ret; +} + +/* FIXME move this function to another file */ +da_bool_t da_get_file_name_from_url(char *url, char **name) +{ + da_bool_t ret = DA_TRUE; + char *buff = DA_NULL; + char *Start = NULL; + char *End = NULL; + char c = 0; + int i = 0; + int j = 0; + int len_name = 0; + char name_buff[DA_MAX_FILE_PATH_LEN] = {0,}; + + DA_LOG_FUNC_LOGV(Default); + + if (DA_NULL == url || DA_NULL == name) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Invalid Argument"); + goto ERR; + } + *name = DA_NULL; + if (!strstr(url, "http") && !strstr(url, "https")) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Invalid Argument"); + goto ERR; + } + + buff = (char*) calloc(1, strlen(url) +1); + if(DA_NULL == buff) { + ret = DA_FALSE; + DA_LOG_ERR(Default,"Memory Fail"); + goto ERR; + } + + while((c = url[i++]) != 0) { + if(c == '%') { + char buffer[3] = {0,}; + buffer[0] = url[i++]; + buffer[1] = url[i++]; + buff[j++] = (char)strtol(buffer,NULL,16); + } else { + buff[j++] = c; + } + } + End = strstr(buff, "?"); + if (DA_NULL != End) { + Start = End -1; + while(*(Start) != '/') { + Start--; + } + if ((*(Start) == '/') && ((len_name = (End - Start)) > 1)) { + Start++; + if (DA_MAX_FILE_PATH_LEN <= len_name) { + strncpy(name_buff, Start, DA_MAX_FILE_PATH_LEN-1); + name_buff[DA_MAX_FILE_PATH_LEN-1] = '\0'; + } else { + strncpy(name_buff, Start, len_name); + name_buff[len_name] = '\0'; + } + } else { + ret = DA_FALSE; + goto ERR ; /*Name not found*/ + } + } else { + int urlLen = strlen (buff); + int Start_pos = 0; + Start_pos = urlLen - 1; + + while(Start_pos > 0) { + if(buff[Start_pos] == '/') + break; + Start_pos--; + } + Start_pos++; + if (Start_pos == 0 || urlLen - Start_pos <= 0) { + ret = DA_FALSE; + goto ERR; + } + while(Start_pos < urlLen) { + name_buff[len_name++] = buff[Start_pos++]; + if (DA_MAX_FILE_PATH_LEN <= len_name) { + name_buff[DA_MAX_FILE_PATH_LEN-1] ='\0'; + break; + } + } + } + + if (len_name) { + End = strrchr(name_buff, '.'); + if (End != NULL) { + *End = '\0'; + } +// DA_SECURE_LOGD("file name BEFORE removing prohibited character = %s", name_buff); + delete_prohibited_char(name_buff, strlen(name_buff)); +// DA_SECURE_LOGD("file name AFTER removing prohibited character = %s", name_buff); + len_name = strlen(name_buff); + *name = (char*) calloc(1, len_name + 1); + if (*name) { + strncpy(*name, name_buff,len_name); + } + } +// DA_SECURE_LOGD("Extracted file name : %s", *name); +ERR: + if (buff) { + free (buff); + buff = DA_NULL; + } + return ret; +} + +void delete_prohibited_char(char *szTarget, int str_len) +{ + char *chk_str = NULL; + int i = 0; + int j = 0; + int tar_len = 0; + + if(szTarget == NULL || str_len <= 0 || strlen(szTarget) != str_len) { + DA_LOG_ERR(Default,"Invaild Parameter\n"); + return; + } + + chk_str = (char *)calloc(1, str_len + 1); + if(chk_str == NULL) + return; + + while(szTarget[j] != '\0') { + if(IS_PROHIBITED_CHAR(szTarget[j]) == DA_FALSE && + IS_SPACE_CHARACTER(szTarget[j]) == DA_FALSE) { + chk_str[i] = szTarget[j]; + i++; + } + j++; + } + + chk_str[i] = '\0'; + tar_len = strlen(chk_str); + + if(tar_len <= 0) + szTarget[0] = '\0'; + else { + for(i = 0; i < tar_len; i++) + { + szTarget[i] = chk_str[i]; + } + szTarget[i] = '\0'; + } + + if(chk_str != NULL) { + free(chk_str); + } + return; +} + diff --git a/agent/download-agent-plugin-conf.c b/agent/download-agent-plugin-conf.c new file mode 100755 index 0000000..8a547f7 --- /dev/null +++ b/agent/download-agent-plugin-conf.c @@ -0,0 +1,122 @@ +/* + * 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 <string.h> +#include <stdlib.h> +#include <glib-object.h> + +#ifdef _EFL_PLATFORM +#include <vconf.h> +#include <vconf-keys.h> +#include <net_connection.h> +#endif /* _EFL_PLATFORM */ + +#include "download-agent-plugin-conf.h" +#include "download-agent-debug.h" +#include "download-agent-file.h" + +#define DEFAULT_UA_STR "Mozilla/5.0 (Linux; U; Tizen 1.0; en-us) AppleWebKit/534.46 (KHTML, like Gecko) Mobile Tizen Browser/1.0" + +da_result_t __get_conf_string(const char *key, char **out_string); + +da_result_t __get_conf_string(const char *key, char **out_string) +{ +#ifdef _EFL_PLATFORM + if (!key || !out_string) { + DA_LOG_ERR(Default,"Invalid Argument"); + return DA_ERR_INVALID_ARGUMENT; + } + + *out_string = vconf_get_str(key); + return DA_RESULT_OK; +#else + if (out_string) + *out_string = NULL; + + return DA_RESULT_OK; +#endif +} + +da_result_t get_user_agent_string(char **uagent_str) +{ + da_result_t ret = DA_RESULT_OK; +#ifdef _EFL_PLATFORM + char *key = DA_NULL; +#endif + + DA_LOG_FUNC_LOGV(Default); + + if (!uagent_str) { + DA_LOG_ERR(Default,"Invalid Argument"); + return DA_ERR_INVALID_ARGUMENT; + } + +#ifdef _EFL_PLATFORM + key = VCONFKEY_BROWSER_USER_AGENT; + ret = __get_conf_string(key, uagent_str); + if(ret == DA_RESULT_OK) { + if(*uagent_str) { + DA_SECURE_LOGD("getting uagent_str = \n%s", *uagent_str); + return ret; + } + } + DA_LOG_ERR(Default,"No UA information from vconf !!"); + *uagent_str = strdup(DEFAULT_UA_STR); + DA_LOG(Default,"Set default UA"); +#else + *uagent_str = strdup(DEFAULT_UA_STR); +#endif + return ret; +} + +char *get_proxy_address(void) +{ +#ifdef _EFL_PLATFORM + char *proxy = NULL; + char *proxyRet = NULL; + connection_h handle = NULL; + connection_address_family_e family = CONNECTION_ADDRESS_FAMILY_IPV4; + + DA_LOG_FUNC_LOGV(Default); + if (connection_create(&handle) < 0) { + DA_LOG_ERR(Default,"Fail to create connection handle"); + return NULL; + } + + if (connection_get_proxy(handle, family, &proxyRet) < 0) { + DA_LOG_ERR(Default,"Fail to get proxy address"); + connection_destroy(handle); + return NULL; + } + + if (proxyRet) { + DA_SECURE_LOGD("===== Proxy address[%s] =====", proxyRet); + proxy = strdup(proxyRet); + free(proxyRet); + proxyRet = NULL; + connection_destroy(handle); + return proxy; + } + + if (connection_destroy(handle) < 0) { + DA_LOG_ERR(Default,"Fail to desctory connection handle"); + return NULL; + } + return NULL; +#else + return NULL; +#endif +} diff --git a/agent/download-agent-plugin-libsoup.c b/agent/download-agent-plugin-libsoup.c new file mode 100755 index 0000000..6d54afd --- /dev/null +++ b/agent/download-agent-plugin-libsoup.c @@ -0,0 +1,1002 @@ +/* + * 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 "download-agent-debug.h" +#include "download-agent-plugin-libsoup.h" +#include "download-agent-http-misc.h" +#include "download-agent-utils.h" +#include "download-agent-pthread.h" + +pthread_mutex_t mutex_for_session_table = PTHREAD_MUTEX_INITIALIZER; + +pi_session_table_t pi_session_table[MAX_SESSION_COUNT] = { { 0, }, }; +da_bool_t using_content_sniffing; + +da_bool_t _pi_http_is_this_session_table_entry_using( + const int in_session_table_entry); + +da_result_t PI_http_init(void) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + using_content_sniffing = DA_TRUE; + + return DA_RESULT_OK; +} + +void PI_http_deinit(void) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + return; +} + +da_result_t _set_proxy_on_soup_session(SoupSession *session, char *proxy_addr) +{ + da_result_t ret = DA_RESULT_OK; + + + if (proxy_addr && strlen(proxy_addr) > 0) { + DA_SECURE_LOGD("received proxy = %s \n", proxy_addr); + if (!strstr(proxy_addr, "0.0.0.0")) { + if (strstr((const char *)proxy_addr, "http") == DA_NULL) { + DA_LOG_VERBOSE(Default,"There is no \"http://\" on received uri, so, add it."); + + char *tmp_str = DA_NULL; + int needed_len = 0; + + needed_len = strlen(proxy_addr) + strlen( + SCHEME_HTTP) + 1; + tmp_str = (char *) calloc(1, needed_len); + if (!tmp_str) { + DA_LOG_ERR(HTTPManager,"DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + snprintf(tmp_str, needed_len, "%s%s", + SCHEME_HTTP, proxy_addr); + + g_object_set(session, SOUP_SESSION_PROXY_URI, + soup_uri_new(tmp_str), NULL); + + free(tmp_str); + } else { + DA_LOG(HTTPManager,"There is \"http\" on uri, so, push this address to soup directly."); + g_object_set(session, SOUP_SESSION_PROXY_URI, + soup_uri_new(proxy_addr), NULL); + } + } + } else { + DA_LOG_VERBOSE(HTTPManager,"There is no proxy value"); + } +ERR: + return ret; +} + +void _fill_soup_msg_header(SoupMessage *msg, + const input_for_tranx_t *input_for_tranx) +{ + SoupMessageHeaders *headers = msg->request_headers; + + http_msg_request_t *input_http_msg_request; + http_msg_iter_t http_msg_iter; + http_msg_iter_t http_msg_iter_pre; + + char *field; + char *value; + + input_http_msg_request = input_for_tranx->http_msg_request; + + http_msg_request_get_iter(input_http_msg_request, &http_msg_iter); + http_msg_iter_pre = http_msg_iter; + while (http_msg_get_field_with_iter(&http_msg_iter, &field, &value)) { + if ((field != DA_NULL) && (value != DA_NULL)) { + DA_SECURE_LOGD("[%s] %s", field, value); + soup_message_headers_append(headers, field, value); + } + http_msg_iter_pre = http_msg_iter; + } + + if (input_http_msg_request->http_body) { + char body_len_str[16] = { 0, }; + int body_len = strlen(input_http_msg_request->http_body); + + snprintf(body_len_str, sizeof(body_len_str), "%d", body_len); + + soup_message_headers_append(headers, "Content-Length", + body_len_str); + soup_message_headers_append(headers, "Content-Type", + "text/plain"); + soup_message_body_append(msg->request_body, SOUP_MEMORY_COPY, + input_http_msg_request->http_body, body_len); + } +} + +da_result_t PI_http_start_transaction(const input_for_tranx_t *input_for_tranx, + int *out_tranx_id) +{ + da_result_t ret = DA_RESULT_OK; + int session_table_entry = -1; + pi_http_method_t pi_http_method = PI_HTTP_METHOD_GET; + queue_t *queue = DA_NULL; + char *url = DA_NULL; + SoupSession *session = DA_NULL; + SoupMessage *msg = DA_NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (DA_FALSE == _pi_http_is_valid_input_for_tranx(input_for_tranx)) { + DA_LOG_ERR(HTTPManager,"input_for_tranx is invalid"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } else { + queue = input_for_tranx->queue; + pi_http_method = input_for_tranx->http_method; + url = input_for_tranx->http_msg_request->url; + } + + session_table_entry = _pi_http_get_avaiable_session_table_entry(); + if (session_table_entry == -1) { + ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + goto ERR; + } + DA_LOG_VERBOSE(HTTPManager,"session_table_entry = %d", session_table_entry); + + if (DA_FALSE == _pi_http_register_queue_to_session_table( + session_table_entry, queue)) { + _pi_http_destroy_session_table_entry(session_table_entry); + ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + goto ERR; + } + + /* modified by keunsoon.lee 2010-09-20 use sync_new() instead of async_new() for make different soup thread from UI main thread*/ + session = soup_session_sync_new(); + /* session=soup_session_async_new(); */ + if (!session) { + DA_LOG_ERR(HTTPManager,"Fail to create session"); + return DA_ERR_INVALID_URL; + } + DA_LOG(HTTPManager,"session[%p]", session); +/* + SoupLogger* logger = soup_logger_new(SOUP_LOGGER_LOG_BODY, -1); + soup_logger_attach(logger, session); + g_object_unref(logger); +*/ + if (DA_FALSE == _pi_http_register_session_to_session_table( + session_table_entry, session)) { + _pi_http_init_session_table_entry(session_table_entry); + ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + goto ERR; + } + + g_object_set(session, SOUP_SESSION_MAX_CONNS, MAX_SESSION_COUNT, NULL); + /* Set timeout unlimited time to resume a download which has ETag when the network is re-connected + * => This is changed to 180 seconds due to limitation of max downloading items. + */ + g_object_set(session, SOUP_SESSION_TIMEOUT, MAX_TIMEOUT, NULL); + + _set_proxy_on_soup_session(session, input_for_tranx->proxy_addr); + + switch (pi_http_method) { + case PI_HTTP_METHOD_GET: + msg = soup_message_new(METHOD_GET, url); + break; + case PI_HTTP_METHOD_POST: + msg = soup_message_new(METHOD_POST, url); + break; + case PI_HTTP_METHOD_HEAD: + msg = soup_message_new(METHOD_HEAD, url); + break; + default: + DA_LOG_ERR(HTTPManager,"Cannot enter here"); + break; + } + DA_LOG_VERBOSE(HTTPManager,"msg[%p]", msg); + /* if it is failed to create a msg, the url can be invalid, becasue of the input argument of soup_message_new API */ + if (msg == NULL) { + DA_LOG_ERR(HTTPManager,"Fail to create message"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + _fill_soup_msg_header(msg, input_for_tranx); + + g_signal_connect(msg, "restarted", G_CALLBACK(_pi_http_restarted_cb), + NULL); /* for redirection case */ + g_signal_connect(msg, "got-headers", + G_CALLBACK(_pi_http_gotheaders_cb), NULL); + g_signal_connect(msg, "got-chunk", G_CALLBACK(_pi_http_gotchunk_cb), + NULL); + + if (using_content_sniffing) { + soup_session_add_feature_by_type(session, SOUP_TYPE_CONTENT_SNIFFER); + g_signal_connect(msg, "content-sniffed", + G_CALLBACK(_pi_http_contentsniffed_cb), NULL); + } else { + soup_message_disable_feature(msg, SOUP_TYPE_CONTENT_SNIFFER); + } + + soup_session_queue_message(session, msg, _pi_http_finished_cb, NULL); +// g_signal_connect(msg, "finished", G_CALLBACK(_pi_http_finished_cb), NULL); + + if (DA_FALSE == _pi_http_register_msg_to_session_table( + session_table_entry, msg)) { + _pi_http_destroy_session_table_entry(session_table_entry); + ret = DA_ERR_ALREADY_MAX_DOWNLOAD; + goto ERR; + } + + *out_tranx_id = session_table_entry; + DA_LOG(HTTPManager,"*out_tranx_id = %d", *out_tranx_id); + +ERR: + return ret; +} + +da_result_t PI_http_disconnect_transaction(int in_tranx_id) +{ + da_result_t ret = DA_RESULT_OK; + int session_table_entry = -1; + + DA_LOG_VERBOSE(HTTPManager,"in_tranx_id = %d", in_tranx_id); + + session_table_entry = in_tranx_id; + + _pi_http_destroy_session_table_entry(session_table_entry); + + return ret; +} + +da_result_t PI_http_cancel_transaction(int in_tranx_id, da_bool_t abort_option) +{ + da_result_t ret = DA_RESULT_OK; + SoupSession *session; + SoupMessage *msg; + int session_table_entry = -1; + + DA_LOG_FUNC_LOGV(HTTPManager); + + session_table_entry = in_tranx_id; + if (!_pi_http_is_this_session_table_entry_using(session_table_entry)) { + DA_LOG_CRITICAL(HTTPManager,"not using session"); + return ret; + } + session = GET_SESSION_FROM_TABLE_ENTRY(session_table_entry); + msg = GET_MSG_FROM_TABLE_ENTRY(session_table_entry); + + if (DA_NULL == session) { + DA_LOG_ERR(HTTPManager,"invalid session = %p", session); + goto ERR; + } + + if (DA_NULL == msg) { + DA_LOG_ERR(HTTPManager,"invalid message = %p", msg); + goto ERR; + } + DA_LOG(HTTPManager,"soup cancel API:abort option[%d] tranx_id[%d]", + abort_option, in_tranx_id); + if (abort_option) + soup_session_abort(session); + else + soup_session_cancel_message(session, msg, SOUP_STATUS_CANCELLED); + DA_LOG(HTTPManager,"soup cancel API-Done"); +ERR: + return ret; +} + +void PI_http_pause_transaction(int transaction_id) +{ + int session_table_entry = -1; + pthread_mutex_t *mutex; + pthread_cond_t *cond; + + DA_LOG_FUNC_LOGD(HTTPManager); + + DA_LOG(HTTPManager,"in_tranx_id = %d", transaction_id); + + session_table_entry = transaction_id; + + if (!_pi_http_is_this_session_table_entry_using(session_table_entry)) + return; + + mutex = &(pi_session_table[session_table_entry].mutex); + cond = &(pi_session_table[session_table_entry].cond); + + _da_thread_mutex_lock (mutex); + + if (pi_session_table[session_table_entry].is_paused == DA_FALSE) { + DA_LOG_CRITICAL(HTTPManager,"paused!"); + pi_session_table[session_table_entry].is_paused = DA_TRUE; + _da_thread_cond_wait(cond, mutex); + } else { + DA_LOG_CRITICAL(HTTPManager,"NOT paused!"); + } + + _da_thread_mutex_unlock (mutex); + +} + +void PI_http_unpause_transaction(int transaction_id) +{ + int session_table_entry = -1; + pthread_mutex_t *mutex; + pthread_cond_t *cond; + + DA_LOG_FUNC_LOGV(Default); + + session_table_entry = transaction_id; + + if (!_pi_http_is_this_session_table_entry_using(session_table_entry)) + return; + + mutex = &(pi_session_table[session_table_entry].mutex); + cond = &(pi_session_table[session_table_entry].cond); + + _da_thread_mutex_lock (mutex); + + if (pi_session_table[session_table_entry].is_paused == DA_TRUE) { + DA_LOG_CRITICAL(HTTPManager,"wake up!"); + pi_session_table[session_table_entry].is_paused = DA_FALSE; + _da_thread_cond_signal(cond); + } + + _da_thread_mutex_unlock (mutex); + +} + +da_bool_t _pi_http_is_valid_input_for_tranx( + const input_for_tranx_t *input_for_tranx) +{ + if (!(input_for_tranx->http_msg_request)) { + DA_LOG_ERR(HTTPManager,"http_msg_request is NULL"); + return DA_FALSE; + } + + if (!((input_for_tranx->http_method == PI_HTTP_METHOD_GET) || + (input_for_tranx->http_method == PI_HTTP_METHOD_POST) || + (input_for_tranx->http_method == PI_HTTP_METHOD_HEAD))) { + DA_LOG_ERR(HTTPManager,"http_method is neither GET or POST or HEAD"); + return DA_FALSE; + } + + return DA_TRUE; +} + +da_bool_t _pi_http_is_this_session_table_entry_using( + const int in_session_table_entry) +{ + da_bool_t is_using = DA_FALSE; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(in_session_table_entry)) + return DA_FALSE; + + _da_thread_mutex_lock (&mutex_for_session_table); + + is_using = pi_session_table[in_session_table_entry].is_using; + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return is_using; +} + +void _pi_http_init_session_table_entry(const int in_session_table_entry) +{ + int entry = in_session_table_entry; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) + return; + +// _da_thread_mutex_lock (&mutex_for_session_table); + + pi_session_table[entry].is_using = DA_TRUE; + pi_session_table[entry].msg = NULL; + pi_session_table[entry].session = NULL; + pi_session_table[entry].queue = NULL; + + _da_thread_mutex_init(&(pi_session_table[entry].mutex), DA_NULL); + _da_thread_cond_init(&(pi_session_table[entry].cond), NULL); + pi_session_table[entry].is_paused = DA_FALSE; + +// _da_thread_mutex_unlock (&mutex_for_session_table); + + return; +} + +void _pi_http_destroy_session_table_entry(const int in_session_table_entry) +{ + int entry = in_session_table_entry; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) + return; + + _da_thread_mutex_lock (&mutex_for_session_table); + + if (pi_session_table[entry].is_paused == DA_TRUE) + PI_http_unpause_transaction(entry); + + /* Warning! Do not g_object_unref(msg) here! + * soup_session_queue_message() steals msg's reference count, + * so, we don't need to do anything for memory management. + * + * But, if using soup_session_send_message(), MUST call g_object_unref(msg). */ + /* if (pi_session_table[entry].msg) + g_object_unref(pi_session_table[entry].msg); */ + + pi_session_table[entry].msg = NULL; + + /* FIXME Cannot g_object_unref(session) here, + * because msg inside this session is not destoryed yet. + * The msg's reference count is stealed by soup_session_queue_message(), + * and it will be destroyed when _pi_http_finished_cb() is returned. + * For now, this _pi_http_destroy_session_table_entry() is called inside + * _pi_http_finished_cb(), so, g_object_unref(session) is not working. + * Should find out call this function after _pi_http_finished_cb(). */ + if (pi_session_table[entry].session) + g_object_unref(pi_session_table[entry].session); + else + DA_LOG_ERR(HTTPManager,"session is NULL. Cannot unref this."); + DA_LOG(HTTPManager,"unref session [%p]",pi_session_table[entry].session); + + pi_session_table[entry].session = NULL; + + pi_session_table[entry].queue = NULL; + pi_session_table[entry].is_paused = DA_FALSE; + pi_session_table[entry].is_using = DA_FALSE; + + _da_thread_mutex_destroy(&(pi_session_table[entry].mutex)); + _da_thread_cond_destroy(&(pi_session_table[entry].cond)); + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return; +} + +int _pi_http_get_avaiable_session_table_entry(void) +{ + int i; + int avaiable_entry = -1; + + _da_thread_mutex_lock (&mutex_for_session_table); + + for (i = 0; i < MAX_SESSION_COUNT; i++) { + if (pi_session_table[i].is_using == DA_FALSE) { + /* pi_session_table[i].is_using = DA_TRUE; */ + DA_LOG_VERBOSE(HTTPManager,"available entry = %d", i); + + avaiable_entry = i; + + break; + } + } + _pi_http_init_session_table_entry(avaiable_entry); + _da_thread_mutex_unlock (&mutex_for_session_table); + + return avaiable_entry; +} + +da_bool_t _pi_http_register_queue_to_session_table( + const int in_session_table_entry, const queue_t *in_queue) +{ + int entry = in_session_table_entry; + queue_t *queue = (queue_t *) in_queue; + da_bool_t ret = DA_FALSE; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) { + DA_LOG_ERR(HTTPManager,"invalid entry = %d", entry); + return DA_FALSE; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + if (pi_session_table[entry].is_using == DA_FALSE) { + DA_LOG_ERR(HTTPManager,"this entry [%d] is not using", entry); + ret = DA_FALSE; + } else { + pi_session_table[entry].queue = queue; + DA_LOG_VERBOSE(HTTPManager,"queue = %p", pi_session_table[entry].queue); + ret = DA_TRUE; + } + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return ret; +} + +da_bool_t _pi_http_register_session_to_session_table( + const int in_session_table_entry, SoupSession *session) +{ + int entry = in_session_table_entry; + da_bool_t ret = DA_FALSE; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) { + DA_LOG_ERR(HTTPManager,"invalid entry = %d", entry); + return DA_FALSE; + } + + if (DA_NULL == session) { + DA_LOG_ERR(HTTPManager,"invalid session = %p",session); + return DA_FALSE; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + if (pi_session_table[entry].is_using == DA_FALSE) { + ret = DA_FALSE; + } else { + pi_session_table[entry].session = session; + ret = DA_TRUE; + } + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return ret; +} + +da_bool_t _pi_http_register_msg_to_session_table( + const int in_session_table_entry, SoupMessage *msg) +{ + int entry = in_session_table_entry; + da_bool_t ret = DA_FALSE; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) { + DA_LOG_ERR(HTTPManager,"invalid entry = %d", entry); + return DA_FALSE; + } + + if (DA_NULL == msg) { + DA_LOG_ERR(HTTPManager,"invalid msg = %p",msg); + return DA_FALSE; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + if (pi_session_table[entry].is_using == DA_FALSE) { + ret = DA_FALSE; + } else { + pi_session_table[entry].msg = msg; + ret = DA_TRUE; + } + + _da_thread_mutex_unlock (&mutex_for_session_table); + + return ret; +} + +queue_t *_pi_http_get_queue_from_session_table_entry( + const int in_session_table_entry) +{ + int entry = in_session_table_entry; + queue_t *out_queue = NULL; + + if (DA_FALSE == IS_VALID_SESSION_TABLE_ENTRY(entry)) { + DA_LOG_ERR(HTTPManager,"invalid entry = %d", entry); + return out_queue; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + out_queue = pi_session_table[entry].queue; + _da_thread_mutex_unlock (&mutex_for_session_table); + + return out_queue; +} + +void _pi_http_store_read_header_to_queue(SoupMessage *msg, const char *sniffedType) +{ + da_result_t ret = DA_RESULT_OK; + + queue_t *da_queue = NULL; + q_event_t *da_event = NULL; + q_event_type_data da_event_type_data; + + int session_table_entry = -1; + SoupMessageHeadersIter headers_iter; + + const char *header_name; + const char *header_value; + + http_msg_response_t *http_msg_response = NULL; + + if (msg->response_headers) { + ret = http_msg_response_create(&http_msg_response); + if (ret != DA_RESULT_OK) + return; + + http_msg_response_set_status_code(http_msg_response, + msg->status_code); + + DA_LOG_VERBOSE(HTTPManager,"\n----raw header---------------------------------------------"); + DA_LOG(HTTPManager,"status code = %d", msg->status_code); + soup_message_headers_iter_init(&headers_iter, + msg->response_headers); + while (soup_message_headers_iter_next(&headers_iter, + &header_name, &header_value)) { + if ((header_name != DA_NULL) && (header_value + != DA_NULL)) { + http_msg_response_add_field(http_msg_response, + header_name, header_value); + DA_SECURE_LOGI("[%s][%s]", header_name, header_value); + } + } + DA_LOG_VERBOSE(HTTPManager,"\n-------------------------------------------------------------\n"); + + } + + if (using_content_sniffing && sniffedType) + http_msg_response_set_content_type(http_msg_response, sniffedType); + + session_table_entry + = _pi_http_get_session_table_entry_from_message(msg); + if (session_table_entry == -1) { + DA_LOG_ERR(HTTPManager,"Fail to find matched session table entry.."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + da_event_type_data = Q_EVENT_TYPE_DATA_PACKET; + + da_queue = _pi_http_get_queue_from_session_table_entry( + session_table_entry); + + ret = Q_make_http_data_event(da_event_type_data, &da_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager,"fail to make da_event"); + goto ERR; + } else { + Q_set_status_code_on_http_data_event(da_event, msg->status_code); + da_event->type.q_event_data_http.http_response_msg + = http_msg_response; + + Q_push_event(da_queue, da_event); + } + return; + +ERR: + if (DA_RESULT_OK != ret) + http_msg_response_destroy(&http_msg_response); + + return; +} + +void _pi_http_store_read_data_to_queue(SoupMessage *msg, const char *body_data, + int received_body_len) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + + char *body_buffer = NULL; + queue_t *da_queue = NULL; + q_event_t *da_event = NULL; + q_event_type_data da_event_type_data; + int session_table_entry = -1; + int http_status = -1; + + http_status = msg->status_code; + + session_table_entry + = _pi_http_get_session_table_entry_from_message(msg); + if (session_table_entry == -1) { + DA_LOG_ERR(HTTPManager,"Fail to find matched session table entry.."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + if (received_body_len == 0) { + DA_LOG_VERBOSE(HTTPManager,"Q_EVENT_TYPE_DATA_FINAL"); + da_event_type_data = Q_EVENT_TYPE_DATA_FINAL; + } else { + da_event_type_data = Q_EVENT_TYPE_DATA_PACKET; + if (received_body_len > 0) { + body_buffer = (char*) calloc(1, received_body_len); + DA_LOG_VERBOSE(HTTPManager,"body_buffer[%p]msg[%p]",body_buffer,msg); + if (body_buffer == DA_NULL) { + DA_LOG_ERR(HTTPManager,"DA_ERR_FAIL_TO_MEMALLOC"); + goto ERR; + } + memcpy(body_buffer, body_data, received_body_len); + } + } + + da_queue = _pi_http_get_queue_from_session_table_entry( + session_table_entry); + + ret = Q_make_http_data_event(da_event_type_data, &da_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager,"fail to make da_event"); + goto ERR; + } else { + Q_set_status_code_on_http_data_event(da_event, http_status); + Q_set_http_body_on_http_data_event(da_event, received_body_len, + body_buffer); + + _da_thread_mutex_lock (&(da_queue->mutex_queue)); + b_ret = Q_push_event_without_lock(da_queue, da_event); + if (b_ret == DA_FALSE) { + DA_LOG_CRITICAL(HTTPManager,"----------------------------------------fail to push!"); + + pthread_mutex_t *session_mutex = NULL; + pthread_cond_t *session_cond = NULL; + + session_mutex + = &(pi_session_table[session_table_entry].mutex); + session_cond + = &(pi_session_table[session_table_entry].cond); + + /* MUST keep this order for these mutexes */ + _da_thread_mutex_lock (session_mutex); + _da_thread_mutex_unlock (&(da_queue->mutex_queue)); + + if (pi_session_table[session_table_entry].is_paused + == DA_FALSE) { + DA_LOG_CRITICAL(HTTPManager,"paused!"); + pi_session_table[session_table_entry].is_paused + = DA_TRUE; + _da_thread_cond_wait(session_cond, session_mutex); + } else { + DA_LOG_CRITICAL(HTTPManager,"NOT paused!"); + } + + _da_thread_mutex_unlock (session_mutex); + + DA_LOG_CRITICAL(HTTPManager,"wake up! push again"); + Q_push_event(da_queue, da_event); + } else { + _da_thread_mutex_unlock (&(da_queue->mutex_queue)); + } + + } + + return; + +ERR: + if (DA_RESULT_OK != ret) { + if (DA_NULL != body_buffer) { + free(body_buffer); + } + } + + return; +} + +int _translate_error_code(int soup_error) +{ + DA_LOG_CRITICAL(HTTPManager, "soup error code[%d]", soup_error); + switch (soup_error) { + case SOUP_STATUS_CANT_RESOLVE: + case SOUP_STATUS_CANT_RESOLVE_PROXY: + case SOUP_STATUS_CANT_CONNECT: + case SOUP_STATUS_CANT_CONNECT_PROXY: + case SOUP_STATUS_IO_ERROR: + case SOUP_STATUS_MALFORMED: + case SOUP_STATUS_TRY_AGAIN: + return DA_ERR_NETWORK_FAIL; + case SOUP_STATUS_SSL_FAILED: + return DA_ERR_SSL_FAIL; + case SOUP_STATUS_REQUEST_TIMEOUT: + return DA_ERR_HTTP_TIMEOUT; + case SOUP_STATUS_TOO_MANY_REDIRECTS: + return DA_ERR_TOO_MANY_REDIECTS; + default: + return DA_ERR_NETWORK_FAIL; + } +} + +void _pi_http_store_neterr_to_queue(SoupMessage *msg) +{ + da_result_t ret = DA_RESULT_OK; + int error_type = -1; + queue_t *da_queue = NULL; + q_event_t *da_event = NULL; + int session_table_entry = -1; + + DA_LOG_FUNC_LOGD(HTTPManager); + + error_type = _translate_error_code(msg->status_code); + + session_table_entry + = _pi_http_get_session_table_entry_from_message(msg); + if (session_table_entry == -1) { + DA_LOG_ERR(HTTPManager,"Fail to find matched session table entry.."); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } + + da_queue = _pi_http_get_queue_from_session_table_entry( + session_table_entry); + + DA_LOG_CRITICAL(HTTPManager,"Q_EVENT_TYPE_DATA_ABORT"); + ret = Q_make_http_data_event(Q_EVENT_TYPE_DATA_ABORT, &da_event); + if (ret != DA_RESULT_OK) { + DA_LOG_ERR(HTTPManager,"fail to make da_event"); + goto ERR; + } else { + Q_set_error_type_on_http_data_event(da_event, error_type); + + Q_push_event(da_queue, da_event); + } + +ERR: + return; +} + +int _pi_http_get_session_table_entry_from_message(SoupMessage *msg) +{ + + int out_entry = -1; + int i; + + if (DA_NULL == msg) { + DA_LOG_ERR(HTTPManager,"invalid message = %p", msg); + return out_entry; + } + + _da_thread_mutex_lock (&mutex_for_session_table); + + for (i = 0; i < MAX_SESSION_COUNT; i++) { + if (pi_session_table[i].is_using == DA_TRUE) { + if (pi_session_table[i].msg == msg) { + out_entry = i; + break; + } + } + } + + _da_thread_mutex_unlock (&mutex_for_session_table); + + if (i == MAX_SESSION_COUNT) { + DA_LOG_ERR(HTTPManager,"fail to find message = %p", msg); + } + + return out_entry; + +} + +void _pi_http_finished_cb(SoupSession *session, SoupMessage *msg, gpointer data) +{ + char *url = NULL; + + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!msg) { + DA_LOG_ERR(HTTPManager, "Check NULL:msg"); + return; + } + + url = soup_uri_to_string(soup_message_get_uri(msg), DA_FALSE); + + DA_SECURE_LOGI("status_code[%d], reason[%s], url[%s]",msg->status_code,msg->reason_phrase,url); + + if (url) { + free(url); + url = NULL; + } + + if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) { + if (msg->status_code == SOUP_STATUS_CANCELLED) { + _pi_http_store_read_data_to_queue(msg, DA_NULL, 0); + } else { + _pi_http_store_neterr_to_queue(msg); + } + } else { + _pi_http_store_read_data_to_queue(msg, DA_NULL, 0); + } + +} + +/* this callback is called in case of redirection */ +void _pi_http_restarted_cb(SoupMessage *msg, gpointer data) +{ + DA_LOG_FUNC_LOGD(HTTPManager); + /* Location URL is needed when extracting the file name from url. + * So, the response header should be handled by http mgr.*/ + + if (!msg) { + DA_LOG_ERR(HTTPManager, "Check NULL:msg"); + return; + } + // If there are user id and password at url, libsoup handle it automatically. + if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { + DA_LOG(HTTPManager,"Ignore:Unauthorized"); + return; + } + _pi_http_store_read_header_to_queue(msg, NULL); +} + +void _pi_http_gotheaders_cb(SoupMessage *msg, gpointer data) +{ + DA_LOG_FUNC_LOGV(HTTPManager); + + if (!msg) { + DA_LOG_ERR(HTTPManager, "Check NULL:msg"); + return; + } + + if (SOUP_STATUS_IS_REDIRECTION(msg->status_code)) { + DA_LOG(HTTPManager,"Redirection !!"); + if (SOUP_STATUS_NOT_MODIFIED != msg->status_code) + return; + } + + // If there are user id and password at url, libsoup handle it automatically. + if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { + DA_LOG(HTTPManager,"Ignore:Unauthorized"); + return; + } + + soup_message_body_set_accumulate(msg->response_body, DA_FALSE); + + if (!using_content_sniffing) + _pi_http_store_read_header_to_queue(msg, NULL); + else + DA_LOG_DEBUG(HTTPManager,"ignore because content sniffing is turned on"); +} + +void _pi_http_contentsniffed_cb(SoupMessage *msg, const char *sniffedType, + GHashTable *params, gpointer data) +{ + DA_LOG_FUNC_LOGD(HTTPManager); + + if (!msg) { + DA_LOG_ERR(HTTPManager, "Check NULL:msg"); + return; + } + + if (SOUP_STATUS_IS_REDIRECTION(msg->status_code)) { + DA_LOG(HTTPManager,"Redirection !!"); + if (SOUP_STATUS_NOT_MODIFIED != msg->status_code) + return; + } + + // If there are user id and password at url, libsoup handle it automatically. + if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { + DA_LOG(HTTPManager,"Ignore:Unauthorized"); + return; + } + + if (using_content_sniffing) + _pi_http_store_read_header_to_queue(msg, sniffedType); +} + +void _pi_http_gotchunk_cb(SoupMessage *msg, SoupBuffer *chunk, gpointer data) +{ +// DA_LOG_FUNC_LOGV(HTTPManager); + + if (!msg) { + DA_LOG_ERR(HTTPManager, "Check NULL:msg"); + return; + } + + if (!chunk) { + DA_LOG_ERR(HTTPManager, "Check NULL:chunk"); + return; + } + + if (SOUP_STATUS_IS_REDIRECTION(msg->status_code)) + return; + + if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { + DA_LOG(HTTPManager,"Ignore:Unauthorized"); + return; + } + + if (chunk->data && chunk->length > 0) { + _pi_http_store_read_data_to_queue(msg, chunk->data, + chunk->length); + } +} diff --git a/agent/download-agent-utils-dl-id-history.c b/agent/download-agent-utils-dl-id-history.c new file mode 100755 index 0000000..c6e747d --- /dev/null +++ b/agent/download-agent-utils-dl-id-history.c @@ -0,0 +1,71 @@ +/* + * 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-agent-type.h" +#include "download-agent-utils.h" +#include "download-agent-utils-dl-id-history.h" + +da_result_t init_dl_id_history(dl_id_history_t *dl_id_history) +{ + da_result_t ret = DA_RESULT_OK; + + /* Initial dl_id_history will be starting number for dl_id. + * dl_id will be sequentially increased from the dl_id_history, + * then dl_id_history will be updated. */ + _da_thread_mutex_init(&(dl_id_history->mutex), DA_NULL); + _da_thread_mutex_lock(&(dl_id_history->mutex)); + get_random_number(&(dl_id_history->starting_num)); + dl_id_history->cur_dl_id = DA_INVALID_ID; + _da_thread_mutex_unlock(&(dl_id_history->mutex)); + + DA_LOG_VERBOSE(Default,"starting num = %d", dl_id_history->starting_num); + return ret; +} + +da_result_t deinit_dl_id_history(dl_id_history_t *dl_id_history) +{ + da_result_t ret = DA_RESULT_OK; + + _da_thread_mutex_lock(&(dl_id_history->mutex)); + dl_id_history->starting_num = DA_INVALID_ID; + dl_id_history->cur_dl_id = DA_INVALID_ID; + _da_thread_mutex_unlock(&(dl_id_history->mutex)); + + _da_thread_mutex_destroy(&(dl_id_history->mutex)); + + return ret; +} + +int get_available_dl_id(dl_id_history_t *dl_id_history) +{ + int dl_id = 0; + + _da_thread_mutex_lock(&(dl_id_history->mutex)); + + if (dl_id_history->cur_dl_id == DA_INVALID_ID) + dl_id_history->cur_dl_id = dl_id_history->starting_num; + else if (dl_id_history->cur_dl_id > 254) + dl_id_history->cur_dl_id = 1; + else + dl_id_history->cur_dl_id++; + + dl_id = dl_id_history->cur_dl_id; + + _da_thread_mutex_unlock(&(dl_id_history->mutex)); + + DA_LOG_VERBOSE(Default,"dl_id = %d", dl_id); + return dl_id; +} diff --git a/agent/download-agent-utils.c b/agent/download-agent-utils.c new file mode 100755 index 0000000..3be2257 --- /dev/null +++ b/agent/download-agent-utils.c @@ -0,0 +1,248 @@ +/* + * 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 <sys/vfs.h> +#include <sys/stat.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <glib.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include "download-agent-client-mgr.h" +#include "download-agent-debug.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-file.h" +#include "download-agent-http-misc.h" +#include "download-agent-mime-util.h" +#include "download-agent-utils.h" +#include "download-agent-plugin-conf.h" +#include "download-agent-dl-info-util.h" + +#define DA_HTTP_HEADER_CONTENT_TYPE "Content-Type" +#define DA_HTTP_HEADER_CONTENT_LENGTH "Content-Length" +#define DA_FILE_NUMBER_LIMIT (1024*1024) + +typedef struct _da_descriptor_mime_table_t { + char *content_type; + da_mime_type_id_t mime_type; +} da_descriptor_mime_table_t; + +da_descriptor_mime_table_t + descriptor_mime_table[] = { + {"", DA_MIME_TYPE_NONE}, + /* DRM1.0 */ + {"application/vnd.oma.drm.message", + DA_MIME_TYPE_DRM1_MESSATE}, /* drm1.0 FL.CD*/ + {"", DA_MIME_TYPE_END}}; + +void get_random_number(int *out_num) +{ + int temp = DA_INVALID_ID; + unsigned int seed = (unsigned)time(0); + + temp = (int)(rand_r(&seed) % 100 + 1.0); + *out_num = temp; +} + +da_result_t get_extension_from_mime_type(char *mime_type, char **extension) +{ + da_result_t ret = DA_RESULT_OK; + char *ext = DA_NULL; + + DA_LOG_FUNC_LOGV(Default); + if (DA_NULL == mime_type || DA_NULL == extension) { + DA_LOG_ERR(Default,"received mime_type is null"); + ret = DA_ERR_INVALID_ARGUMENT; + goto ERR; + } +// DA_SECURE_LOGD("input mime type = %s", mime_type); + if (DA_RESULT_OK != (ret = da_mime_get_ext_name(mime_type, &ext))) { + DA_LOG_ERR(Default,"can't find proper extension!"); + goto ERR; + } + *extension = ext; +// DA_SECURE_LOGD("found extension = %s", *extension); + +ERR: + return ret; +} + +int read_data_from_file(char *file, char **out_buffer) +{ + FILE *fd; + unsigned long long file_size = -1; + char *buffer = NULL; + int buffer_len = 0; + size_t read_len = 0; + + *out_buffer = NULL; + + if (!file) + return 0; + + /* open file with "rb", because fread() handles the file as binary mode */ + fd = fopen(file, "rb"); + if (!fd) { + DA_LOG_ERR(FileManager,"File open err! received file path"); + return 0; + } + + get_file_size(file, &file_size); + if (file_size <= 0) { + DA_LOG_ERR(FileManager,"file size is [%llu]", file_size); + fclose(fd); + return 0; + } + + /* A guide from www.securecoding.cert.org + * : FIO17-C. Do not rely on an ending null character when using fread() + * + * buffer is initialized with null through calloc(), so, it is always null-terminated even if fread() failed. + * allocate memory one more byte to ensure null-terminated even if the file is not null-terminated. + */ + buffer_len = sizeof(char) * file_size; + buffer = (char *)calloc(1, buffer_len + 1); + if (buffer) { + read_len = fread(buffer, sizeof(char), file_size, fd); + if (read_len == file_size) { + *out_buffer = buffer; + } else { + DA_LOG_ERR(FileManager,"File Read Not Complete read length = %d", read_len); + free(buffer); + buffer = NULL; + buffer_len = 0; + } + } else { + buffer_len = 0; + } + + fclose(fd); + + return buffer_len; +} + +da_mime_type_id_t get_mime_type_id(char *content_type) +{ + int i = 0; + + if (content_type == NULL) { + DA_LOG_CRITICAL(Default, "No Mime Type Id"); + return DA_MIME_TYPE_NONE; + } + + while(descriptor_mime_table[i].mime_type != DA_MIME_TYPE_END) + { + if (!strcmp(descriptor_mime_table[i].content_type, content_type)) { + break; + } + i++; + } + //DA_LOG_VERBOSE(Default, "dd mime type check: index[%d] type[%d]", i, descriptor_mime_table[i].mime_type); + return descriptor_mime_table[i].mime_type; +} + + + +da_bool_t is_valid_url(const char *url, da_result_t *err_code) +{ + da_result_t ret = DA_RESULT_OK; + da_bool_t b_ret = DA_FALSE; + + int wanted_str_len = 0; + char *wanted_str = NULL; + char *wanted_str_start = NULL; + char *wanted_str_end = NULL; + + if ((DA_NULL == url) || (1 > strlen(url))) { + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + wanted_str_start = (char*)url; + wanted_str_end = strstr(url, "://"); + if (!wanted_str_end) { + DA_LOG_ERR(Default,"No protocol on this url"); + ret = DA_ERR_INVALID_URL; + goto ERR; + } + + wanted_str_len = wanted_str_end - wanted_str_start; + wanted_str = (char*)calloc(1, wanted_str_len + 1); + if (!wanted_str) { + DA_LOG_ERR(Default,"DA_ERR_FAIL_TO_MEMALLOC"); + ret = DA_ERR_FAIL_TO_MEMALLOC; + goto ERR; + } + strncpy(wanted_str, wanted_str_start, wanted_str_len); + + b_ret = is_supporting_protocol(wanted_str); + if (!b_ret) { + ret = DA_ERR_UNSUPPORTED_PROTOCAL; + goto ERR; + } + +ERR: + if (wanted_str) { + free(wanted_str); + wanted_str = NULL; + } + + if (err_code) + *err_code = ret; + + return b_ret; +} + +da_result_t move_file(const char *from_path, const char *to_path) +{ + da_result_t ret = DA_RESULT_OK; + + if (!from_path || !to_path) + return DA_ERR_INVALID_ARGUMENT; + + if (rename(from_path, to_path) != 0) { + DA_LOG_CRITICAL(FileManager,"rename failed : syserr[%d]",errno); + if (errno == EXDEV) { + DA_LOG_CRITICAL(FileManager,"File system is diffrent. Try to copy a file"); + ret = copy_file(from_path, to_path); + if (ret == DA_RESULT_OK) { + remove_file(from_path); + } else { + if (is_file_exist(to_path)) + remove_file(to_path); + ret = DA_ERR_FAIL_TO_INSTALL_FILE; + } + } else { + ret = DA_ERR_FAIL_TO_INSTALL_FILE; + } + } + return ret; +} + +void remove_file(const char *file_path) +{ + DA_LOG_FUNC_LOGD(FileManager); + + if (file_path && is_file_exist(file_path)) { + DA_SECURE_LOGD("remove file [%s]", file_path); + if (unlink(file_path) < 0) { + DA_LOG_ERR(FileManager,"file removing failed."); + } + } +} diff --git a/agent/include/download-agent-basic.h b/agent/include/download-agent-basic.h new file mode 100755 index 0000000..340a34a --- /dev/null +++ b/agent/include/download-agent-basic.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_Agent_Basic_H +#define _Download_Agent_Basic_H + +#include <string.h> + +#include "download-agent-type.h" +#include "download-agent-interface.h" +#include "download-agent-dl-mgr.h" + +da_result_t start_download(const char *url, int *dl_id); +da_result_t start_download_with_extension(const char *url , int *dl_id, extension_data_t *extension_data); + +#endif diff --git a/agent/include/download-agent-client-mgr.h b/agent/include/download-agent-client-mgr.h new file mode 100755 index 0000000..f4c6234 --- /dev/null +++ b/agent/include/download-agent-client-mgr.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_Agent_Client_Mgr_H +#define _Download_Agent_Client_Mgr_H + +#include <string.h> + +#include "download-agent-type.h" +#include "download-agent-interface.h" + +#include "download-agent-pthread.h" + +typedef enum { + Q_CLIENT_NOTI_TYPE_STARTED_INFO = 0, + Q_CLIENT_NOTI_TYPE_PROGRESS_INFO, + Q_CLIENT_NOTI_TYPE_PAUSED_INFO, + Q_CLIENT_NOTI_TYPE_FINISHED_INFO, + Q_CLIENT_NOTI_TYPE_TERMINATE, +} client_noti_type; + +typedef struct _client_noti_t client_noti_t; +struct _client_noti_t { + int slot_id; + void *user_data; + client_noti_type noti_type; + union _client_type { + user_download_info_t update_dl_info; + user_progress_info_t update_progress_info; + user_paused_info_t paused_info; + user_finished_info_t finished_info; + } type; + + client_noti_t *next; +}; + +typedef struct _client_queue_t { + da_bool_t having_data; + client_noti_t *client_q_head; + pthread_mutex_t mutex_client_queue; + pthread_cond_t cond_client_queue; +} client_queue_t; + +typedef struct _client_app_info_t { + da_client_cb_t client_callback; + char *client_user_agent; +} client_app_info_t; + +typedef struct _client_app_mgr_t { + da_bool_t is_init; + client_queue_t client_queue; + client_app_info_t client_app_info; + pthread_t thread_id; + da_bool_t is_thread_init; + pthread_mutex_t mutex_client_mgr; +} client_app_mgr_t; + +da_result_t init_client_app_mgr(void); +da_bool_t is_client_app_mgr_init(void); + +da_result_t reg_client_app(da_client_cb_t *da_client_callback); +da_result_t dereg_client_app(void); + +da_result_t send_client_paused_info (int slot_id); +da_result_t send_client_update_dl_info (int slot_id, int dl_id, + char *file_type, unsigned long int file_size, char *tmp_saved_path, + char *pure_file_name, char *etag, char *extension); +da_result_t send_client_update_progress_info (int slot_id, int dl_id, + unsigned long int received_size); +da_result_t send_client_finished_info (int slot_id, int dl_id, + char *saved_path, char *etag, int error, int http_status); + +char *get_client_user_agent_string(void); + +void push_client_noti(client_noti_t *client_noti); + +#endif diff --git a/agent/include/download-agent-debug.h b/agent/include/download-agent-debug.h new file mode 100755 index 0000000..24ac67e --- /dev/null +++ b/agent/include/download-agent-debug.h @@ -0,0 +1,107 @@ +/* + * 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_Agent_Debug_H +#define _Download_Agent_Debug_H + +#include "download-agent-type.h" + +#define DA_DEBUG_ENV_KEY "DOWNLOAD_AGENT_DEBUG" +#define DA_DEBUG_CONFIG_FILE_PATH "/tmp/.download_agent.conf" + +#define IS_LOG_ON(channel) (DALogBitMap & (0x1<<(channel))) + +typedef enum { + Soup, + HTTPManager, + FileManager, + DRMManager, + DownloadManager, + ClientNoti, + HTTPMessageHandler, + Encoding, + QueueManager, + Parsing, + Thread, + Default, + DA_LOG_CHANNEL_MAX +} da_log_channel; + +extern int DALogBitMap; + +da_result_t init_log_mgr(void); + +#ifdef NODEBUG + #define DA_LOG(channel, format, ...) ((void)0) + #define DA_LOG_CRITICAL(channel, format, ...) ((void)0) + #define DA_LOG_VERBOSE(channel, format, ...) ((void)0) + #define DA_LOG_ERR(channel, format, ...) ((void)0) + #define DA_LOG_FUNC_LOGD(channel, ...) ((void)0) + #define DA_LOG_FUNC_LOGV(channel, ...) ((void)0) + #define DA_SECURE_LOGD(format, ...) ((void)0) + #define DA_SECURE_LOGI(format, ...) ((void)0) + #define DA_SECURE_LOGE(format, ...) ((void)0) + +#else /* NODEBUG */ +#include <stdio.h> +#include <stdarg.h> +#include <pthread.h> + +#ifdef DA_DEBUG_USING_DLOG + #include <dlog.h> + #ifdef LOG_TAG + #undef LOG_TAG + #endif /* LOG_TAG */ + #define LOG_TAG "DOWNLOAD_AGENT" + + #define DA_LOG(channel, format, ...) LOGI_IF(IS_LOG_ON(channel), format, ##__VA_ARGS__) + #define DA_LOG_DEBUG(channel, format, ...) LOGD_IF(IS_LOG_ON(channel), format, ##__VA_ARGS__) + #define DA_LOG_CRITICAL(channel, format, ...) LOGI_IF(IS_LOG_ON(channel), format, ##__VA_ARGS__) + #define DA_LOG_VERBOSE(channel, format, ...) ((void)0)//LOGD_IF(IS_LOG_ON(channel), format, ##__VA_ARGS__) + #define DA_LOG_ERR(channel, format, ...) LOGE_IF(IS_LOG_ON(channel), "ERR! "format, ##__VA_ARGS__) + #define DA_LOG_FUNC_LOGD(channel, ...) LOGD_IF(IS_LOG_ON(channel), "starting...") + #define DA_LOG_FUNC_LOGV(channel, ...) ((void)0)//LOGD_IF(IS_LOG_ON(channel), "starting...") + #define DA_SECURE_LOGD(format, ...) SECURE_LOGD(format, ##__VA_ARGS__) + #define DA_SECURE_LOGI(format, ...) SECURE_LOGI(format, ##__VA_ARGS__) + #define DA_SECURE_LOGE(format, ...) SECURE_LOGE(format, ##__VA_ARGS__) +#else /* DA_DEBUG_USING_DLOG */ + #include <unistd.h> + #include <syscall.h> + + #define DA_LOG(channel, format, ...) do {\ + IS_LOG_ON(channel) \ + ? fprintf(stderr, "[DA][%u][%s(): %d] "format"\n",(unsigned int)syscall(__NR_gettid), __FUNCTION__,__LINE__, ##__VA_ARGS__) \ + : ((void)0);\ + }while(0) + #define DA_LOG_ERR(channel, format, ...) do {\ + IS_LOG_ON(channel) \ + ? fprintf(stderr, "[DA][%u][ERR][%s(): %d]\n",(unsigned int)syscall(__NR_gettid), __FUNCTION__,__LINE__, ##__VA_ARGS__) \ + : ((void)0); \ + }while(0) + #define DA_LOG_FUNC_LOGD(channel, ...) do {\ + IS_LOG_ON(channel) \ + ? fprintf(stderr, "[DA][%u][%s(): %d] starting\n",(unsigned int)syscall(__NR_gettid), __FUNCTION__,__LINE__) \ + : ((void)0); \ + }while(0) + #define DA_LOG_CRITICAL DA_LOG + #define DA_LOG_VERBOSE DA_LOG + #define DA_LOG_FUNC_LOGV ((void)0) + #define DA_SECURE_LOGD(format, ...) ((void)0) + #define DA_SECURE_LOGI(format, ...) ((void)0) + #define DA_SECURE_LOGE(format, ...) ((void)0) +#endif /* DA_DEBUG_USING_DLOG */ +#endif /* NDEBUG */ +#endif /* _Download_Agent_Debug_H */ diff --git a/agent/include/download-agent-defs.h b/agent/include/download-agent-defs.h new file mode 100755 index 0000000..b69690a --- /dev/null +++ b/agent/include/download-agent-defs.h @@ -0,0 +1,89 @@ +/* + * 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_Agent_Defs_H +#define _Download_Agent_Defs_H + +#ifndef DEPRECATED +#define DEPRECATED __attribute__((deprecated)) +#endif + +/** + * Max count to download files simultaneously. \n + * Main reason for this restriction is because of Network bandwidth. + */ +#define DA_MAX_DOWNLOAD_REQ_AT_ONCE 50 + +#define DA_RESULT_OK 0 + +#define DA_TRUE 1 +#define DA_FALSE 0 +#define DA_NULL (void *)0 +#define DA_INVALID_ID -1 + +#define DA_RESULT_USER_CANCELED -10 + +// InputError Input error (-100 ~ -199) +// Client passed wrong parameter +#define DA_ERR_INVALID_ARGUMENT -100 +#define DA_ERR_INVALID_DL_REQ_ID -101 +#define DA_ERR_INVALID_URL -103 +#define DA_ERR_INVALID_INSTALL_PATH -104 +#define DA_ERR_INVALID_MIME_TYPE -105 + +// Client passed correct parameter, but Download Agent rejects the request because of internal policy. +#define DA_ERR_ALREADY_CANCELED -160 +#define DA_ERR_ALREADY_SUSPENDED -161 +#define DA_ERR_ALREADY_RESUMED -162 +#define DA_ERR_CANNOT_SUSPEND -170 +#define DA_ERR_CANNOT_RESUME -171 +#define DA_ERR_INVALID_STATE -190 +#define DA_ERR_ALREADY_MAX_DOWNLOAD -191 +#define DA_ERR_UNSUPPORTED_PROTOCAL -192 + +// System error (-200 ~ -299) +#define DA_ERR_FAIL_TO_MEMALLOC -200 +#define DA_ERR_FAIL_TO_CREATE_THREAD -210 +#define DA_ERR_FAIL_TO_OBTAIN_MUTEX -220 +#define DA_ERR_FAIL_TO_ACCESS_FILE -230 +#define DA_ERR_DISK_FULL -240 + +// Platform error (-300 ~ -399) +#define DA_ERR_FAIL_TO_GET_CONF_VALUE -300 +#define DA_ERR_FAIL_TO_ACCESS_STORAGE -310 +#define DA_ERR_DLOPEN_FAIL -330 + +// Network error (-400 ~ -499) +#define DA_ERR_NETWORK_FAIL -400 +#define DA_ERR_UNREACHABLE_SERVER -410 +#define DA_ERR_HTTP_TIMEOUT -420 +#define DA_ERR_SSL_FAIL -430 +#define DA_ERR_TOO_MANY_REDIECTS -440 + +// HTTP error - not conforming with HTTP spec (-500 ~ -599) +#define DA_ERR_MISMATCH_CONTENT_TYPE -500 +#define DA_ERR_MISMATCH_CONTENT_SIZE -501 +#define DA_ERR_SERVER_RESPOND_BUT_SEND_NO_CONTENT -502 + +// DRM error - not conforming with DRM spec (-700 ~ -799) +#define DA_ERR_DRM_FAIL -700 +#define DA_ERR_DRM_FILE_FAIL -710 + +// install error (-800 ~ -899) +#define DA_ERR_FAIL_TO_INSTALL_FILE -800 + +#endif + diff --git a/agent/include/download-agent-dl-info-util.h b/agent/include/download-agent-dl-info-util.h new file mode 100755 index 0000000..d6e20a8 --- /dev/null +++ b/agent/include/download-agent-dl-info-util.h @@ -0,0 +1,257 @@ +/* + * 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_Agent_Dl_Info_Util_H +#define _Download_Agent_Dl_Info_Util_H + +#include "download-agent-type.h" +#include "download-agent-http-queue.h" +#include "download-agent-utils-dl-id-history.h" + +#define DA_MAX_DOWNLOAD_ID DA_MAX_DOWNLOAD_REQ_AT_ONCE +#define DA_MAX_TYPE_COUNT 10 + +#define DOWNLOAD_NOTIFY_LIMIT (1024*32) //bytes +extern pthread_mutex_t mutex_download_state[]; + +typedef enum { + DOWNLOAD_STATE_IDLE = 0, + DOWNLOAD_STATE_NEW_DOWNLOAD = 10, /* stage */ + DOWNLOAD_STATE_FINISH = 50, /* stage */ + DOWNLOAD_STATE_PAUSED = 60, /* http */ + DOWNLOAD_STATE_CANCELED = 70, /* http */ + DOWNLOAD_STATE_ABORTED = 80 /* satge */ +} download_state_t; + +typedef enum { + HTTP_STATE_READY_TO_DOWNLOAD = 0, + HTTP_STATE_REDIRECTED = 1, + HTTP_STATE_DOWNLOAD_REQUESTED = 2, + HTTP_STATE_DOWNLOAD_STARTED = 3, + HTTP_STATE_DOWNLOADING = 4, + HTTP_STATE_DOWNLOAD_FINISH = 5, + HTTP_STATE_REQUEST_CANCEL = 6, + HTTP_STATE_REQUEST_PAUSE = 7, + HTTP_STATE_REQUEST_RESUME = 8, + HTTP_STATE_CANCELED = 9, + HTTP_STATE_PAUSED = 10, + HTTP_STATE_RESUMED = 11, + HTTP_STATE_ABORTED = 12, +} http_state_t; + +typedef struct _client_input_basic_t { + char *req_url; + char **user_request_header; + int user_request_header_count; +} client_input_basic_t; + +typedef struct _client_input_t { + void *user_data; + char *install_path; + char *file_name; + char *etag; + char *temp_file_path; + char *pkg_name; + client_input_basic_t client_input_basic; +} client_input_t; + +typedef struct _download_thread_input { + int slot_id; + client_input_t *client_input; +} download_thread_input; + +typedef struct _source_info_basic_t { + int dl_id; + char *url; + char **user_request_header; + int user_request_header_count; +} source_info_basic_t; + +typedef struct _source_info_t { + union _source_info_type { + source_info_basic_t *source_info_basic; + } source_info_type; +} source_info_t; + +#define GET_SOURCE_TYPE(SOURCE) ((SOURCE)->source_type) +#define GET_SOURCE_BASIC(SOURCE) ((SOURCE)->source_info_type.source_info_basic) +#define GET_SOURCE_BASIC_URL(SOURCE) (GET_SOURCE_BASIC(SOURCE)->url) + +typedef struct _req_dl_info { + http_info_t http_info; + + /* This is just pointer assignment from stage source info. */ + char *destination_url; + /* The location url is assigned here in case of redirection. + * At this time, the pointer should be freed. */ + char *location_url; + char **user_request_header; + int user_request_header_count; + char *user_request_etag; + char *user_request_temp_file_path; + + http_state_t http_state; + pthread_mutex_t mutex_http_state; + + da_result_t result; + /*************** will be depreciated ***********************/ + /* ToDo : previous http_info should be saved in case of pause */ + char *content_type_from_header; /* calloced in set hdr fiels on download info */ + unsigned long long content_len_from_header; + char *etag_from_header; + + unsigned long int downloaded_data_size; + + int invloved_transaction_id; +} req_dl_info; + +#define GET_REQUEST_HTTP_RESULT(REQUEST) (REQUEST->result) +#define GET_REQUEST_HTTP_TRANS_ID(REQUEST) (REQUEST->invloved_transaction_id) +#define GET_REQUEST_HTTP_REQ_URL(REQUEST) (REQUEST->destination_url) +#define GET_REQUEST_HTTP_REQ_LOCATION(REQUEST) (REQUEST->location_url) +#define GET_REQUEST_HTTP_USER_REQUEST_HEADER(REQUEST) (REQUEST->user_request_header) +#define GET_REQUEST_HTTP_USER_REQUEST_HEADER_COUNT(REQUEST) (REQUEST->user_request_header_count) +#define GET_REQUEST_HTTP_USER_REQUEST_ETAG(REQUEST) (REQUEST->user_request_etag) +#define GET_REQUEST_HTTP_USER_REQUEST_TEMP_FILE_PATH(REQUEST) (REQUEST->user_request_temp_file_path) +#define GET_REQUEST_HTTP_HDR_ETAG(REQUEST) (REQUEST->etag_from_header) +#define GET_REQUEST_HTTP_HDR_CONT_TYPE(REQUEST) (REQUEST->content_type_from_header) +#define GET_REQUEST_HTTP_HDR_CONT_LEN(REQUEST) (REQUEST->content_len_from_header) +#define GET_REQUEST_HTTP_CONTENT_OFFSET(REQUEST)(REQUEST->downloaded_data_size) +#define GET_REQUEST_HTTP_MUTEX_HTTP_STATE(STAGE) (GET_STAGE_TRANSACTION_INFO(STAGE)->mutex_http_state) +#define GET_HTTP_STATE_ON_STAGE(STAGE) (GET_STAGE_TRANSACTION_INFO(STAGE)->http_state) +#define CHANGE_HTTP_STATE(STATE,STAGE) {\ + _da_thread_mutex_lock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(STAGE)));\ + GET_HTTP_STATE_ON_STAGE(STAGE) = STATE;\ + DA_LOG_DEBUG(Default, "Changed http_state to - [%d] ", GET_HTTP_STATE_ON_STAGE(STAGE));\ + _da_thread_mutex_unlock(&(GET_REQUEST_HTTP_MUTEX_HTTP_STATE(STAGE)));\ +} + +typedef struct _file_info { + void *file_handle; + char *pure_file_name; + char *extension; + char *file_name_final; /* malloced in set_file_path_for_final_saving */ + char *content_type; /* malloced in make file info. */ + char *add_to_buffer; + unsigned int file_size; /* http header's Content-Length has higher priority than DD's <size> */ + unsigned int total_bytes_written_to_file; /* current written file size */ + unsigned int bytes_written_to_file; + unsigned int current_buffer_len; +} file_info; + +#define GET_CONTENT_STORE_PURE_FILE_NAME(FILE_CNTXT) (FILE_CNTXT)->pure_file_name +#define GET_CONTENT_STORE_EXTENSION(FILE_CNTXT) (FILE_CNTXT)->extension +#define GET_CONTENT_STORE_ACTUAL_FILE_NAME(FILE_CNTXT) (FILE_CNTXT)->file_name_final +#define GET_CONTENT_STORE_FILE_HANDLE(FILE_CNTXT) (FILE_CNTXT)->file_handle +#define GET_CONTENT_STORE_FILE_SIZE(FILE_CNTXT) (FILE_CNTXT)->file_size +#define GET_CONTENT_STORE_CURRENT_FILE_SIZE(FILE_CNTXT) (FILE_CNTXT)->total_bytes_written_to_file +#define IS_CONTENT_STORE_FILE_BYTES_WRITTEN_TO_FILE(FILE_CNTXT) (FILE_CNTXT)->bytes_written_to_file +#define GET_CONTENT_STORE_FILE_BUFFER(FILE_CNTXT) (FILE_CNTXT)->add_to_buffer +#define GET_CONTENT_STORE_FILE_BUFF_LEN(FILE_CNTXT) ((FILE_CNTXT)->current_buffer_len) +#define GET_CONTENT_STORE_CONTENT_TYPE(FILE_CNTXT) (FILE_CNTXT)->content_type + +typedef struct _stage_info { + int dl_id; + int thread_id; + source_info_t dl_request; + req_dl_info dl_tansaction_context; + file_info dl_content_storage; + struct _stage_info *next_stage_info; +} stage_info; + +#define GET_STAGE_DL_ID(STAGE) ((STAGE)->dl_id) +#define GET_STAGE_THREAD_ID(STAGE) ((STAGE)->thread_id) +#define GET_STAGE_SOURCE_INFO(STAGE) (&((STAGE)->dl_request)) +#define GET_STAGE_TRANSACTION_INFO(STAGE) (&((STAGE)->dl_tansaction_context)) +#define GET_STAGE_CONTENT_STORE_INFO(STAGE) (&((STAGE)->dl_content_storage)) +#define GET_STAGE_INSTALLATION_INFO(STAGE) (&((STAGE)->post_dl_context)) + +typedef struct { + da_bool_t is_using; + int slot_id; + int dl_id; + pthread_t active_dl_thread_id; + download_state_t state; + stage_info *download_stage_data; + queue_t queue; + int http_status; + da_bool_t enable_pause_update; + // FIXME have client_input itself, not to have each of them + char *user_install_path; + char *user_file_name; + char *user_etag; + char *user_temp_file_path; + void *user_data; +} dl_info_t; + +#define GET_DL_THREAD_ID(ID) (download_mgr.dl_info[ID].active_dl_thread_id) +#define GET_DL_STATE_ON_ID(ID) (download_mgr.dl_info[ID].state) +#define GET_DL_STATE_ON_STAGE(STAGE) (GET_DL_STATE_ON_ID(GET_STAGE_DL_ID(STAGE))) +#define GET_DL_CURRENT_STAGE(ID) (download_mgr.dl_info[ID].download_stage_data) +#define GET_DL_ID(ID) (download_mgr.dl_info[ID].dl_id) +#define GET_DL_QUEUE(ID) &(download_mgr.dl_info[ID].queue) +#define GET_DL_ENABLE_PAUSE_UPDATE(ID) (download_mgr.dl_info[ID].enable_pause_update) +#define GET_DL_USER_INSTALL_PATH(ID) (download_mgr.dl_info[ID].user_install_path) +#define GET_DL_USER_FILE_NAME(ID) (download_mgr.dl_info[ID].user_file_name) +#define GET_DL_USER_ETAG(ID) (download_mgr.dl_info[ID].user_etag) +#define GET_DL_USER_TEMP_FILE_PATH(ID) (download_mgr.dl_info[ID].user_temp_file_path) +#define GET_DL_USER_DATA(ID) (download_mgr.dl_info[ID].user_data) +#define IS_THIS_DL_ID_USING(ID) (download_mgr.dl_info[ID].is_using) + +#define CHANGE_DOWNLOAD_STATE(STATE,STAGE) {\ + _da_thread_mutex_lock (&mutex_download_state[GET_STAGE_DL_ID(STAGE)]);\ + GET_DL_STATE_ON_STAGE(STAGE) = STATE;\ + DA_LOG_DEBUG(Default, "Changed download_state to - [%d] ", GET_DL_STATE_ON_STAGE(STAGE));\ + _da_thread_mutex_unlock (&mutex_download_state[GET_STAGE_DL_ID(STAGE)]);\ + } + +typedef struct _download_mgr_t { + da_bool_t is_init; + dl_info_t dl_info[DA_MAX_DOWNLOAD_ID]; + dl_id_history_t dl_id_history; + /* FIXME: This is temporary solution to prevent crash on following case; + * 1) OMA download(that is, DA's libsoup is using) is on progressing on Browser + * 2) User push END hard key + * 3) da_deinit() is called. - on UI thread + * 4) cancel_download(all) is called. + * 5) plugin-libsoup.c calls soup_session_cancel_message(). + * 6) da_deinit() is finished and process is over. + * 7) soup's callback for soup_session_cancel_message() is trying to be called - on UI thread + * 8) Browser crashed because the callback address is no longer exist. + * + * Here is a temporary solution; + * If cancel is from da_deinit(), plugin-libsoup.c will not call soup_session_cancel_message(). + * So, append following variable to recognize this. + **/ + //da_bool_t is_progressing_deinit; +} download_mgr_t; + +extern download_mgr_t download_mgr; + +da_result_t init_download_mgr(); +da_result_t deinit_download_mgr(void); +void init_download_info(int slot_id); +void destroy_download_info(int slot_id); +void *Add_new_download_stage(int slot_id); +void remove_download_stage(int slot_id, stage_info *in_stage); +void empty_stage_info(stage_info *in_stage); +void clean_up_client_input_info(client_input_t *client_input); +da_result_t get_available_slot_id(int *available_id); +da_result_t get_slot_id_for_dl_id(int dl_id , int* slot_id); +da_bool_t is_valid_slot_id(int slot_id); +void store_http_status(int dl_id, int status); +int get_http_status(int dl_id); +#endif /* _Download_Agent_Dl_Info_Util_H */ diff --git a/agent/include/download-agent-dl-mgr.h b/agent/include/download-agent-dl-mgr.h new file mode 100755 index 0000000..b273fd8 --- /dev/null +++ b/agent/include/download-agent-dl-mgr.h @@ -0,0 +1,33 @@ +/* + * 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_Agent_Dl_Mgr_H +#define _Download_Agent_Dl_Mgr_H + +#include "download-agent-type.h" +#include "download-agent-dl-info-util.h" + +da_result_t cancel_download(int dl_id); +da_result_t suspend_download(int dl_id, da_bool_t is_enable_cb); +da_result_t resume_download (int dl_id); + +da_result_t requesting_download(stage_info *stage); +da_result_t handle_after_download(stage_info *stage); +da_result_t send_user_noti_and_finish_download_flow( + int slot_id, char *installed_path, char *etag); + +da_bool_t is_valid_download_id(int dl_id); +#endif diff --git a/agent/include/download-agent-encoding.h b/agent/include/download-agent-encoding.h new file mode 100755 index 0000000..c5c7fe5 --- /dev/null +++ b/agent/include/download-agent-encoding.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_Agent_Encoding_H +#define _Download_Agent_Encoding_H + +#include "download-agent-type.h" + +da_bool_t is_base64_encoded_word(const char *in_str); +da_result_t decode_base64_encoded_str(const char *in_encoded_str, + char **out_decoded_ascii_str); +void decode_url_encoded_str(const char *in_encoded_str, char **out_str); + +#endif // _Download_Agent_Encoding_H diff --git a/agent/include/download-agent-file.h b/agent/include/download-agent-file.h new file mode 100755 index 0000000..e9ff05f --- /dev/null +++ b/agent/include/download-agent-file.h @@ -0,0 +1,47 @@ +/* + * 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_Agent_File_H +#define _Download_Agent_File_H + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "download-agent-type.h" +#include "download-agent-dl-mgr.h" + +da_bool_t is_file_exist(const char *file_path); +da_bool_t is_dir_exist(const char *dir_path); + +void get_file_size(char *file_path, unsigned long long *out_file_size); + +da_result_t clean_files_from_dir(char *dir_path); +da_result_t file_write_ongoing(stage_info *stage, char *body, int body_len); +da_result_t file_write_complete(stage_info *stage); +da_result_t start_file_writing(stage_info *stage); +da_result_t start_file_writing_append(stage_info *stage); +da_result_t start_file_writing_append_with_new_download(stage_info *stage); + +da_result_t get_mime_type(stage_info *stage, char **out_mime_type); +da_result_t discard_download(stage_info *stage) ; +void clean_paused_file(stage_info *stage); +da_result_t replace_content_file_in_stage(stage_info *stage, const char *dest_dd_file_path); +char *get_full_path_avoided_duplication(char *in_dir, char *in_candidate_file_name, char *in_extension); + +da_result_t copy_file(const char *src, const char *dest); + +#endif diff --git a/agent/include/download-agent-http-mgr.h b/agent/include/download-agent-http-mgr.h new file mode 100755 index 0000000..6653af0 --- /dev/null +++ b/agent/include/download-agent-http-mgr.h @@ -0,0 +1,47 @@ +/* + * 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_Agent_Http_Mgr_H +#define _Download_Agent_Http_Mgr_H + +#include <string.h> + +#include "download-agent-type.h" +#include "download-agent-dl-mgr.h" +#include "download-agent-http-queue.h" + +#define DA_MAX_SESSION_INFO DA_MAX_DOWNLOAD_ID +#define DA_MAX_TRANSACTION_INFO 10 +#define DA_MAX_TRANSACTION_MUTEX DA_MAX_SESSION_INFO*DA_MAX_TRANSACTION_INFO + +typedef struct _http_mgr_t +{ + da_bool_t is_init; + da_bool_t is_http_init; +}http_mgr_t; + +extern http_mgr_t http_mgr; + +da_result_t init_http_mgr(void); +void deinit_http_mgr(void); +da_result_t make_req_dl_info_http(stage_info *stage, req_dl_info *out_info); +da_result_t request_http_download(stage_info *stage); +da_result_t request_to_cancel_http_download(stage_info *stage); +da_result_t request_to_abort_http_download(stage_info *stage); +da_result_t request_to_suspend_http_download(stage_info *stage); +da_result_t request_to_resume_http_download(stage_info *stage); + +#endif diff --git a/agent/include/download-agent-http-misc.h b/agent/include/download-agent-http-misc.h new file mode 100755 index 0000000..3bf137b --- /dev/null +++ b/agent/include/download-agent-http-misc.h @@ -0,0 +1,47 @@ +/* + * 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_Agent_Http_Misc_H +#define _Download_Agent_Http_Misc_H + +#include <string.h> + +#include "download-agent-type.h" + +#define SCHEME_HTTP "http://" +#define SCHEME_HTTPS "https://" +#define SCHEME_CID "cid:" + +#define METHOD_GET "GET" +#define METHOD_POST "POST" +#define METHOD_HEAD "HEAD" + +#define HTTP_TAG_UAGENT "User-Agent: " +#define HTTP_TAG_HOST "Host: " +#define HTTP_TAG_UAPROF "X-Wap-Profile: " +#define HTTP_TAG_CONTENT_LENGTH "Content-Length: " +#define HTTP_TAG_CONTENT_TYPE "Content-Type: " +#define HTTP_TAG_IF_MATCH "If-Match: " +#define HTTP_TAG_RANGE "Range: " +#define HTTP_TAG_IF_RANGE "If-Range: " + +#define END_OF_FIELD "\r\n" + +char *get_user_agent(); + +da_bool_t is_supporting_protocol(const char *protocol); + +#endif diff --git a/agent/include/download-agent-http-msg-handler.h b/agent/include/download-agent-http-msg-handler.h new file mode 100755 index 0000000..29d0ae4 --- /dev/null +++ b/agent/include/download-agent-http-msg-handler.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_Agent_Http_Msg_Handler_H +#define _Download_Agent_Http_Msg_Handler_H + +#include "download-agent-type.h" + +#define HTTP_FIELD_UAGENT "User-Agent" +#define HTTP_FIELD_HOST "Host" +#define HTTP_FIELD_UAPROF "X-Wap-Profile" +#define HTTP_FIELD_CONTENT_LENGTH "Content-Length" +#define HTTP_FIELD_CONTENT_TYPE "Content-Type" +#define HTTP_FIELD_IF_MATCH "If-Match" +#define HTTP_FIELD_RANGE "Range" +#define HTTP_FIELD_IF_RANGE "If-Range" +#define HTTP_FIELD_ACCEPT_LANGUAGE "Accept-Language" +#define HTTP_FIELD_ACCEPT_CHARSET "Accept-Charset" + +typedef struct _http_header_options_t http_header_options_t; +struct _http_header_options_t{ + char *field; + char *value; + + http_header_options_t *next; +}; + +typedef struct _http_header_t http_header_t; +struct _http_header_t{ + char *field; + char *value; + http_header_options_t *options; + + char *raw_value; // raw string including options + + http_header_t *next; +}; + +typedef struct{ + char *http_method; + char *url; + http_header_t *head; + char *http_body; +}http_msg_request_t; + + +typedef struct{ + int status_code; + http_header_t *head; +}http_msg_response_t; + +typedef http_header_t *http_msg_iter_t; + + +typedef struct{ + http_msg_request_t *http_msg_request; + http_msg_response_t *http_msg_response; +}http_info_t; + + +da_result_t http_msg_request_create(http_msg_request_t **http_msg_request); +void http_msg_request_destroy(http_msg_request_t **http_msg_request); + +da_result_t http_msg_request_set_method(http_msg_request_t *http_msg_request, const char *method); +da_result_t http_msg_request_get_method(http_msg_request_t *http_msg_request, const char **method); + +da_result_t http_msg_request_set_url(http_msg_request_t *http_msg_request, const char *url); +da_result_t http_msg_request_get_url(http_msg_request_t *http_msg_request, const char **url); + +da_result_t http_msg_request_set_body(http_msg_request_t *http_msg_request, const char *body); +da_result_t http_msg_request_get_body(http_msg_request_t *http_msg_request, const char **body); + +da_result_t http_msg_request_add_field(http_msg_request_t *http_msg_request, const char *field, const char *value); + + +da_result_t http_msg_response_create(http_msg_response_t **http_msg_response); +void http_msg_response_destroy(http_msg_response_t **http_msg_response); + +da_result_t http_msg_response_set_status_code(http_msg_response_t *http_msg_response, int status_code); +da_result_t http_msg_response_get_status_code(http_msg_response_t *http_msg_response, int *status_code); + +da_result_t http_msg_response_add_field(http_msg_response_t *http_msg_response, const char *field, const char *value); + +/* Caution! Caller must free memory for every "char** out_xxx" for followings */ +da_bool_t http_msg_response_get_content_type(http_msg_response_t *http_msg_response, char **out_type); +void http_msg_response_set_content_type(http_msg_response_t *http_msg_response, const char *in_type); + +da_bool_t http_msg_response_get_content_length(http_msg_response_t *http_msg_response, unsigned long long *out_length); +da_bool_t http_msg_response_get_content_disposition(http_msg_response_t *http_msg_response, char **out_disposition, char **out_file_name); +da_bool_t http_msg_response_get_ETag(http_msg_response_t *http_msg_response, char **out_value); +da_bool_t http_msg_response_get_date(http_msg_response_t *http_msg_response, char **out_value); +da_bool_t http_msg_response_get_location(http_msg_response_t *http_msg_response, char **out_value); +// should be refactored later +da_result_t http_msg_response_get_boundary(http_msg_response_t *http_msg_response, char **out_val); + + +da_result_t http_msg_request_get_iter(http_msg_request_t *http_msg_request, http_msg_iter_t *http_msg_iter); +da_result_t http_msg_response_get_iter(http_msg_response_t *http_msg_response, http_msg_iter_t *http_msg_iter); + +// should remove later +da_bool_t http_msg_get_field_with_iter(http_msg_iter_t *http_msg_iter, char **field, char **value); +da_bool_t http_msg_get_header_with_iter(http_msg_iter_t *http_msg_iter, char **out_field, http_header_t **out_header); + +char *get_http_response_header_raw(http_msg_response_t *http_msg_response); + +da_bool_t extract_attribute_from_header(char *szHeadStr, const char *szFindStr, char **ppRtnValue); +#endif // _Download_Agent_Http_Msg_Handler_H diff --git a/agent/include/download-agent-http-queue.h b/agent/include/download-agent-http-queue.h new file mode 100755 index 0000000..9973698 --- /dev/null +++ b/agent/include/download-agent-http-queue.h @@ -0,0 +1,126 @@ +/* + * 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_Agent_Http_Queue_H +#define _Download_Agent_Http_Queue_H + + +#include "download-agent-type.h" +#include "download-agent-http-msg-handler.h" + +#include <pthread.h> +#include <stdlib.h> + +#define MAX_QUEUE_SIZE 1024*64 + +typedef enum +{ + Q_EVENT_TYPE_DATA_HTTP, + Q_EVENT_TYPE_DATA_DRM, + Q_EVENT_TYPE_CONTROL, +}q_event_type; + +typedef enum +{ + Q_EVENT_TYPE_CONTROL_NONE = 0, + Q_EVENT_TYPE_CONTROL_CANCEL, + Q_EVENT_TYPE_CONTROL_SUSPEND, + Q_EVENT_TYPE_CONTROL_RESUME, + Q_EVENT_TYPE_CONTROL_NET_DISCONNECTED, + Q_EVENT_TYPE_CONTROL_ABORT, +// [090205][jungki]not used yet. +// Q_EVENT_TYPE_CONTROL_USER_CONFIRM_RESULT, +// Q_EVENT_TYPE_CONTROL_INSTALL_RESULT, +}q_event_type_control; + +typedef enum +{ + Q_EVENT_TYPE_DATA_PACKET, + Q_EVENT_TYPE_DATA_FINAL, + Q_EVENT_TYPE_DATA_ABORT, +}q_event_type_data; + +typedef struct _q_event_data_http_t +{ + q_event_type_data data_type; + + int status_code; + + http_msg_response_t* http_response_msg; + + int body_len; + char *body_data; + + da_result_t error_type; +}q_event_data_http_t; + +typedef struct _q_event_control_t +{ + q_event_type_control control_type; +}q_event_control_t; + +typedef struct _q_event_t q_event_t; +struct _q_event_t +{ + int size; + q_event_type event_type; + union _type + { + q_event_data_http_t q_event_data_http; + q_event_control_t q_event_control; + } type; + + q_event_t *next; +}; + +typedef struct _queue_t +{ + da_bool_t having_data; + + q_event_t *control_head; + q_event_t *data_head; + + pthread_mutex_t mutex_queue; + pthread_cond_t cond_queue; + + int queue_size; +}queue_t; + +void Q_init_queue(queue_t *queue); +void Q_destroy_queue(queue_t *queue); + +void Q_init_q_event(q_event_t *q_event); +void Q_destroy_q_event(q_event_t **q_event); + +da_result_t Q_make_control_event(q_event_type_control control_type, q_event_t **out_event); + +da_result_t Q_make_http_data_event(q_event_type_data data_type, q_event_t **out_event); +da_result_t Q_set_status_code_on_http_data_event(q_event_t *q_event, int status_code); +da_result_t Q_set_http_body_on_http_data_event(q_event_t *q_event, int body_len, char *body_data); +da_result_t Q_set_error_type_on_http_data_event(q_event_t *q_event, int error_type); + + +da_bool_t Q_push_event(const queue_t *in_queue, const q_event_t *in_event); +da_bool_t Q_push_event_without_lock(const queue_t *in_queue, const q_event_t *in_event); +void Q_pop_event(const queue_t *in_queue, q_event_t **out_event); + +#define GET_IS_Q_HAVING_DATA(QUEUE) (QUEUE->having_data) + +void Q_goto_sleep(const queue_t *in_queue); +void Q_wake_up(const queue_t *in_queue); + + +#endif diff --git a/agent/include/download-agent-interface.h b/agent/include/download-agent-interface.h new file mode 100755 index 0000000..44081bd --- /dev/null +++ b/agent/include/download-agent-interface.h @@ -0,0 +1,478 @@ +/* + * 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_Agent_Interface_H +#define _Download_Agent_Interface_H + +#ifndef EXPORT_API +#define EXPORT_API __attribute__((visibility("default"))) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "download-agent-defs.h" +#include <stdarg.h> + +/** + * @struct user_paused_info_t + * @brief Download Agent will send its state through this structure. + * @see da_paused_info_cb + * @par + * This is used only by callback /a user_paused_info_t. \n + */ +typedef struct { + /// download request id for this notification + int download_id; +} user_paused_info_t; + +/** + * @struct user_progress_info_t + * @brief Download Agent will send current downloading file's information through this structure. + * @see da_progress_info_cb + * @par + * This is used only by callback /a da_progress_info_cb. \n + */ +typedef struct { + /// download request id for this updated download information + int download_id; + /// received size of chunked data. + unsigned long int received_size; +} user_progress_info_t; + +/** + * @struct user_download_info_t + * @brief Download Agent will send current download's information through this structure. + * @see da_started_info_cb + * @par + * This is used only by callback /a da_started_info_cb. \n + */ +typedef struct { + /// download request id for this updated download information + int download_id; + /// file's mime type from http header. + char *file_type; + /// file size from http header. + unsigned long int file_size; + /// This is temporary file path. + char *tmp_saved_path; + /// This is the file name for showing to user. + char *content_name; + /// etag string value for resume download, + char *etag; +} user_download_info_t; + +typedef struct { + /// download request id for this updated download information + int download_id; + /// This has only file name for now. + char *saved_path; + /// etag string value for resume download, + /// This is returned when the download is failed and the etag is received from content server + char *etag; + /// convey error code if necessary, or it is zero. + int err; + /// http status code if necessary, or it is zero. + int http_status; +} user_finished_info_t; + +typedef struct { + const char **request_header; + int request_header_count; + const char *install_path; + const char *file_name; + const char *temp_file_path; /* For resume download, the "etag" value should be existed together */ + const char *etag; /* For resume download */ + const char *pkg_name; /* For system resource */ + void *user_data; +} extension_data_t; + +/** + * @typedef da_paused_cb + * @brief Download Agent will call this function to paused its state. + * + * This is user callback function registered on \a da_init. \n + * + * @remarks For the most of time, this state is just informative, so, user doesn't need to do any action back to Download Agent. + * + * @warning Download will be holding until getting user confirmation result through the function. + * + * @param[in] state state from Download Agent + * @param[in] user_param user parameter which is set with \a DA_FEATURE_USER_DATA + * + * @see da_init + * @see da_client_cb_t + */ +typedef void (*da_paused_info_cb) (user_paused_info_t *paused_info, void *user_param); + +/** + * @brief Download Agent will call this function to update received size of download-requested file. + * + * This is user callback function registered on \a da_init. \n + * This is informative, so, user doesn't need to do any action back to Download Agent.\n + * + * @param[in] progress_info updated downloading information + * @param[in] user_param user parameter which is set with \a DA_FEATURE_USER_DATA + * + * @see da_init + * @see da_client_cb_t + */ +typedef void (*da_progress_info_cb) (user_progress_info_t *progress_info, void *user_param); + +/** + * @brief Download Agent will call this function to update mime type, temp file name, total file sizeand installed path. + * + * This is user callback function registered on \a da_init. \n + * This is informative, so, user doesn't need to do any action back to Download Agent.\n + * + * @param[in] download_info updated download information + * @param[in] user_param user parameter which is set with \a DA_FEATURE_USER_DATA + * + * @see da_init + * @see da_client_cb_t + */ +typedef void (*da_started_info_cb) (user_download_info_t *download_info, void *user_param); + +typedef void (*da_finished_info_cb) (user_finished_info_t *finished_info, void *user_param); + /** + * @struct da_client_cb_t + * @brief This structure convey User's callback functions for \a da_init + * @see da_init + */ +typedef struct { + /// callback to convey download information + da_started_info_cb update_dl_info_cb; + /// callback to convey downloading information while downloading including received file size + da_progress_info_cb update_progress_info_cb; + /// callback to convey saved path + da_finished_info_cb finished_info_cb; + /// callback to convey etag value + da_paused_info_cb paused_info_cb; +} da_client_cb_t; + +/** + * @fn int da_init (da_client_cb_t *da_client_callback) + * @brief This function initiates Download Agent and registers user callback functions. + * @warning This should be called at once when client application is initialized before using other Download Agent APIs + * @warning This function is paired with da_deinit function. + * + * @pre None. + * @post None. + * + * @param[in] da_client_callback User callback function structure. The type is struct data pointer. + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail. DA_ERR_XXX is defined at download-agent-def.h. + * @remarks User MUST call this function first rather than any other DA APIs. \n + * Please do not call UI code at callback function in direct. \n + * It is better that it returns as soon as copying the data of callback functon. \n + * @see da_deinit + * @par Example + * @code + * #include <download-agent-interface.h> + * + * void da_started_info_cb(user_download_info_t *download_info,void *user_param); + * void da_progress_info_cb(user_downloading_info_t *downloading_info,void *user_param); + * void da_finished_cb(user_finished_info_t *complted_info, void *user_param); + * void da_paused_info_cb(user_paused_info_t *paused_info, void *user_param); + * + * int download_initialize() + * { + * int da_ret; + * da_client_cb_t da_cb = {0}; + * + * da_cb.update_dl_info_cb = &update_download_info_cb; + * da_cb.update_progress_info_cb = &progress_info_cb; + * da_cb.finished_info_cb = &finished_info_cb; + * da_cb.paused_info_cb = &paused_cb; + * + * da_ret = da_init (&da_cb, 0); + * if (da_ret == DA_RESULT_OK) { + * // printf("successed\n"); + * return true; + * } else { + * // printf("failed with error code %d\n", da_ret); + * return fail; + * } + * } + * @endcode + */ +EXPORT_API int da_init(da_client_cb_t *da_client_callback); + + /** + * @fn int da_deinit () + * @brief This function deinitiates Download Agent. + * + * This function destroys all infomation for client manager. + * When Download Agent is not used any more, please call this function. + * Usually when client application is destructed, this is needed. + * + * @remarks This is paired with da_init. \n + * The client Id should be the one from /a da_init(). \n + * Otherwise, it cannot excute to deinitialize. \n + * + * @pre da_init() must be called in advance. + * @post None. + * + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail. DA_ERR_XXX is defined at download-agent-def.h. + * @see da_init + * @par Example + * @code + * #include <download-agent-interface.h> + * + * + * int download_deinitialize() + * { + * int da_ret; + * da_ret = da_deinit(); + * if(da_ret == DA_RESULT_OK) { + * // printf("successed\n"); + * return true; + * } else { + * // printf("failed with error code %d\n", da_ret); + * return fail; + * } + * } + @endcode + */ +EXPORT_API int da_deinit(); + + /** + * @fn int da_start_download(const char *url, int *download_id) + * @brief This function starts to download a content on passed URL. + * + * Useful information and result are conveyed through following callbacks. + * @li da_started_info_cb + * @li da_progress_cb + * + * @pre da_init() must be called in advance. + * @post None. + * @remarks + * Downloaded file is automatically registered to system. (e.g. File DB) \n + * If there is another file has same name on registering directory, new one's name would have numbering postfix. \n + * (e.g. abc.mp3 to abc_1.mp3) + * + * @param[in] url url to start download + * @param[out] download_id assigned download request id for this URL + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail. DA_ERR_XXX is defined at download-agent-def.h. + * + * @see None. + * + * @par Example + * @code + * #include <download-agent-interface.h> + * + * int da_ret; + * int download_id; + * char *url = "http://www.test.com/sample.mp3"; + * + * da_ret = da_start_download(url,&download_id); + * if (da_ret == DA_RESULT_OK) + * printf("download requesting is successed\n"); + * else + * printf("download requesting is failed with error code %d\n", da_ret); + * @endcode + */ +EXPORT_API int da_start_download(const char *url, int *download_id); + +/** +* @fn int da_start_download_with_extension(const char *url, extension_data_t ext_data, int *download_id) +* @brief This function starts to download a content on passed URL with passed extension. +* +* Useful information and result are conveyed through following callbacks. +* @li da_started_info_cb +* @li da_progress_cb +* +* @pre da_init() must be called in advance. +* @post None. +* @remarks This API operation is exactly same with da_start_download(), except for input properties. \n +* +* @param[in] url url to start download +* @param[in] ext_data extension data +* @param[out] download_id assigned download request id for this URL +* @return DA_RESULT_OK for success, or DA_ERR_XXX for fail. DA_ERR_XXX is defined at download-agent-def.h. +* +* +* @par Example +* @code + #include <download-agent-interface.h> + + int da_ret; + int download_id; + extension_data_t ext_data = {0,}; + const char *url = "https://www.test.com/sample.mp3"; + const char *install_path = "/myFiles/music"; + const char *my_data = strdup("data"); + ext_data.install_path = install_path; + ext_data.user_data = (void *)my_data; + + da_ret = da_start_download_with_extension(url, &download_id, &ext_data); + if (da_ret == DA_RESULT_OK) + printf("download requesting is successed\n"); + else + printf("download requesting is failed with error code %d\n", da_ret); + @endcode +*/ +EXPORT_API int da_start_download_with_extension(const char *url, + extension_data_t *ext_data, + int *download_id +); + + +/** + * @fn int da_cancel_download(int download_id) + * @brief This function cancels a download for passed download_id. + * + * Client can use this function if user wants to cancel already requested download. + * + * @remarks Should check return value. \n + * If return value is not DA_RESULT_OK, then previous requested download can be keep downloading. + * @remarks After calling this function, all information for the download_id will be deleted. So, client cannot request anything for the download_id. + * + * @pre There should be exist ongoing or suspended download for download_id. + * @post None. + * + * @param[in] download_id download request id + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail + * + * @see None. + * + * @par Example + * @code + #include <download-agent-interface.h> + + int da_ret; + int download_id; + + da_ret = da_cancel_download(download_id); + if(da_ret == DA_RESULT_OK) { + // printf("download with [%d] is successfully canceled.\n", download_id); + } + else { + // in this case, downloading with download_id is keep ongoing. + printf("failed to cancel with error code %d\n", da_ret); + } + @endcode + */ +EXPORT_API int da_cancel_download(int download_id); + + +/** + * @fn int da_suspend_download(int download_id) + * @brief This function suspends downloading for passed download_id. + * + * Client can use this function if user wants to suspend already requested download. + * + * @remarks Should check return value. \n + * If return value is not DA_RESULT_OK, then previous requested download can be keep downloading. + * @remarks After calling this function, all information for the download_id will be remained. So, client can request resume for the download_id. + * @remarks Client should cancel or resume for this download_id, or all information for the download_id will be leaved forever. + * + * @pre There should be exist ongoing download for download_id. + * @post None. + * + * @param[in] download_id download request id + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail + * + * @see da_resume_download() + * @see da_cancel_download() + * + * @par Example + * @code + #include <download-agent-interface.h> + + int da_ret; + int download_id; + + da_ret = da_suspend_download(download_id); + if(da_ret == DA_RESULT_OK) { + // printf("download with [%d] is successfully suspended.\n", download_id); + } + else { + // in this case, downloading with download_id is keep ongoing. + printf("failed to suspend with error code %d\n", da_ret); + } + @endcode + */ +EXPORT_API int da_suspend_download(int download_id); + +EXPORT_API int da_suspend_download_without_update(int download_id); +/** + * @fn int da_resume_download(int download_id) + * @brief This function resumes downloading for passed download_id. + * + * Client can use this function if user wants to resume suspended download. + * + * @remarks Should check return value. \n + * If return value is not DA_RESULT_OK, then requested download can be not to resume. + * + * @pre There should be exist suspended download for download_id. + * @post None. + * + * @param[in] download_id download request id + * @return DA_RESULT_OK for success, or DA_ERR_XXX for fail + * + * @see da_suspend_download() + * + * @par Example + * @code + #include <download-agent-interface.h> + + int da_ret; + int download_id; + + da_ret = da_resume_download(download_id); + if(da_ret == DA_RESULT_OK) { + // printf("download with [%d] is successfully resumed.\n", download_id); + } + else { + // in this case, downloading with download_id is keep suspended. + printf("failed to resume with error code %d\n", da_ret); + } + @endcode + */ +EXPORT_API int da_resume_download(int download_id); + +/** + * @fn int da_is_valid_download_id(int download_id) + * @brief This function return the download id is valid and the download thread is still alive. + * + * Client can use this function if user wants to resume download. + * If the download id is vaild and the download thread is alive, it can resume download with using da_resume_download() + * If the the download thread was already terminated due to restarting the process, + * it can resume download with using da_start_download_with_extension() + * + * + * + * @remarks Should check return value. \n + * If return value is not DA_RESULT_OK, then requested download can be not to resume. + * + * @pre There should be exist suspended download for download_id. + * @post None. + * + * @param[in] download_id download request id + * @return 1 for success, or 0 for fail + * + */ +EXPORT_API int da_is_valid_download_id(int download_id); + +#ifdef __cplusplus +} +#endif + +#endif //_Download_Agent_Interface_H + + diff --git a/agent/include/download-agent-mime-util.h b/agent/include/download-agent-mime-util.h new file mode 100755 index 0000000..5f143e9 --- /dev/null +++ b/agent/include/download-agent-mime-util.h @@ -0,0 +1,40 @@ +/* + * 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_Agent_Mime_Table_H +#define _Download_Agent_Mime_Table_H + +#include "download-agent-type.h" + +#define NO_EXTENSION_NAME_STR "dat" + +#define DD_MIME_STR "application/vnd.oma.dd+xml" +#define DD_EXT_STR "*.dd" +#define DRM_MIME_MSG_STR "application/vnd.oma.drm.message" +#define DRM_EXT_STR "*.dm" +#define DRM_MIME_CONTENT_STR "application/vnd.oma.drm.content" + +typedef struct { + char *standard; + char *normal; +} Ext_translation_table; + +da_bool_t is_ambiguous_MIME_Type(const char *in_mime_type); +da_bool_t da_get_extension_name_from_url(char *url, char **ext); +da_result_t da_mime_get_ext_name(char *mime, char **ext); +da_bool_t da_get_file_name_from_url(char *url, char **name) ; +void delete_prohibited_char(char *szTarget, int str_len); +#endif diff --git a/agent/include/download-agent-plugin-conf.h b/agent/include/download-agent-plugin-conf.h new file mode 100755 index 0000000..2c4f55f --- /dev/null +++ b/agent/include/download-agent-plugin-conf.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_Agent_Plugin_Conf_H +#define _Download_Agent_Plugin_Conf_H + +#include "download-agent-type.h" +#include "download-agent-interface.h" +#include "download-agent-utils.h" + +da_result_t get_user_agent_string(char **uagent_str); +char *get_proxy_address(void); + +#endif diff --git a/agent/include/download-agent-plugin-http-interface.h b/agent/include/download-agent-plugin-http-interface.h new file mode 100755 index 0000000..b9698bb --- /dev/null +++ b/agent/include/download-agent-plugin-http-interface.h @@ -0,0 +1,51 @@ +/* + * 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_Agent_Plugin_Http_Interface_H +#define _Download_Agent_Plugin_Http_Interface_H + +#include "download-agent-type.h" +#include "download-agent-http-msg-handler.h" + +typedef enum { + PI_HTTP_METHOD_GET = 1, + PI_HTTP_METHOD_POST = 2, + PI_HTTP_METHOD_HEAD = 3 +} pi_http_method_t; + + +typedef struct _input_for_tranx_t { + pi_http_method_t http_method; + + char *proxy_addr; + queue_t *queue; + + http_msg_request_t* http_msg_request; +} input_for_tranx_t; + + + +da_result_t PI_http_init(void); +void PI_http_deinit(void); + +da_result_t PI_http_start_transaction(const input_for_tranx_t *input_for_tranx, int *out_tranx_id); +da_result_t PI_http_cancel_transaction(int in_tranx_id, da_bool_t abort_option); +da_result_t PI_http_disconnect_transaction(int in_tranx_id); +void PI_http_pause_transaction(int transaction_id); +void PI_http_unpause_transaction(int transaction_id); + +#endif + diff --git a/agent/include/download-agent-plugin-libsoup.h b/agent/include/download-agent-plugin-libsoup.h new file mode 100755 index 0000000..8160042 --- /dev/null +++ b/agent/include/download-agent-plugin-libsoup.h @@ -0,0 +1,75 @@ +/* + * 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_Agent_Plugin_Libsoup_H +#define _Download_Agent_Plugin_Libsoup_H + +#include <string.h> +#include <libsoup/soup.h> + +#include "download-agent-http-queue.h" +#include "download-agent-pthread.h" +#include "download-agent-plugin-http-interface.h" + +typedef struct _pi_session_table_t { + da_bool_t is_using; + SoupSession *session; + SoupMessage *msg; + queue_t *queue; + pthread_mutex_t mutex; + pthread_cond_t cond; + da_bool_t is_paused; +} pi_session_table_t; + +extern pi_session_table_t pi_session_table[]; + +#define MAX_SESSION_COUNT DA_MAX_DOWNLOAD_REQ_AT_ONCE +#define MAX_TIMEOUT 60 // second + +#define IS_VALID_SESSION_TABLE_ENTRY(ENTRY) ((((ENTRY) < 0) || ((ENTRY) > MAX_SESSION_COUNT-1)) ? 0 : 1) + + +#define GET_SESSION_FROM_TABLE_ENTRY(ENTRY) (pi_session_table[ENTRY].session) +#define GET_MSG_FROM_TABLE_ENTRY(ENTRY) (pi_session_table[ENTRY].msg) +#define GET_QUEUE_FROM_TABLE_ENTRY(ENTRY) (pi_session_table[ENTRY].queue) + + +da_bool_t _pi_http_is_valid_input_for_tranx(const input_for_tranx_t *input_for_tranx); + +void _pi_http_init_session_table_entry(const int in_session_table_entry); +void _pi_http_destroy_session_table_entry(const int in_session_table_entry); +int _pi_http_get_avaiable_session_table_entry(void); + +da_bool_t _pi_http_register_queue_to_session_table(const int session_table_entry, const queue_t *in_queue); +da_bool_t _pi_http_register_session_to_session_table(const int in_session_table_entry, SoupSession *session); +da_bool_t _pi_http_register_msg_to_session_table(const int in_session_table_entry, SoupMessage *msg); + +queue_t *_pi_http_get_queue_from_session_table_entry(const int in_session_table_entry); +int _pi_http_get_session_table_entry_from_message(SoupMessage *msg); + +void _pi_http_store_read_data_to_queue(SoupMessage *msg, const char *body_data, int received_body_len); +void _pi_http_store_read_header_to_queue(SoupMessage *msg, const char *sniffedType); +void _pi_http_store_neterr_to_queue(SoupMessage *msg); + + +void _pi_http_finished_cb(SoupSession *session, SoupMessage *msg, gpointer data); +void _pi_http_restarted_cb(SoupMessage *msg, gpointer data); +void _pi_http_gotheaders_cb(SoupMessage *msg, gpointer data); +void _pi_http_contentsniffed_cb(SoupMessage *msg, const char *sniffedType, GHashTable *params, gpointer data); +void _pi_http_gotchunk_cb(SoupMessage *msg, SoupBuffer *chunk, gpointer data); + + +#endif diff --git a/agent/include/download-agent-pthread.h b/agent/include/download-agent-pthread.h new file mode 100755 index 0000000..8523567 --- /dev/null +++ b/agent/include/download-agent-pthread.h @@ -0,0 +1,147 @@ +/* + * 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_Agent_Pthread_H +#define _Download_Agent_Pthread_H + +#include <pthread.h> +#include <errno.h> +#include <time.h> + +#include "download-agent-type.h" +#include "download-agent-debug.h" + +#define _da_thread_mutex_init(mutex_add, attr) { \ + int ret = 0; \ + do{ \ + ret = pthread_mutex_init(mutex_add, attr); \ + if (0 == ret){ \ + break; \ + } \ + else if(EINVAL == ret){ \ + DA_LOG_ERR(Default, "pthread_mutex_init FAIL with EINVAL."); \ + break; \ + } \ + else if(ENOMEM == ret){ \ + DA_LOG_ERR(Default, "pthread_mutex_init FAIL with ENOMEM."); \ + break; \ + } \ + else{ \ + DA_LOG_ERR(Default, "pthread_mutex_init FAIL with %d.", ret); \ + break; \ + } \ + }while(1); \ + } + +#define _da_thread_cond_init(cond_add, attr) do{ \ + if (0 != pthread_cond_init(cond_add, attr)){\ + DA_LOG_ERR(Default, "pthread_cond_init FAIL");} \ + }while(0) + + + +#define _da_thread_mutex_lock(mutex_add) {\ + int ret = 0;\ + do{\ + ret = pthread_mutex_lock(mutex_add);\ + if (0 == ret){\ + break;\ + }\ + else if(EINVAL == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_lock FAIL with EINVAL.");\ + break;\ + }\ + else if(EDEADLK == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_lock FAIL with EDEADLK.");\ + break;\ + }\ + else{\ + DA_LOG_ERR(Default, "pthread_mutex_lock FAIL with %d.", ret);\ + break;\ + }\ + }while(1);\ + } + + +#define _da_thread_mutex_unlock(mutex_add) {\ + int ret = 0;\ + do{\ + ret = pthread_mutex_unlock(mutex_add);\ + if (0 == ret){\ + break;\ + }\ + else if(EINVAL == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_unlock FAIL with EINVAL.");\ + break;\ + }\ + else if(EPERM == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_unlock FAIL with EPERM.");\ + break;\ + }\ + else{\ + DA_LOG_ERR(Default, "pthread_mutex_unlock FAIL with %d.", ret);\ + break;\ + }\ + }while(1);\ + } + + +#define _da_thread_cond_signal(cond_add) do{ \ + if (0 != pthread_cond_signal(cond_add)){\ + DA_LOG_ERR(Default, "pthread_cond_signal FAIL");} \ + }while(0) + + + +#define _da_thread_cond_wait(cond_add, mutex_add) do{ \ + if (0 != pthread_cond_wait(cond_add, mutex_add)){\ + DA_LOG_ERR(Default, "pthread_cond_wait FAIL");} \ + }while(0) + +#define _da_thread_cond_timed_wait(cond_add, mutex_add, time) do{ \ + if (0 != pthread_cond_timedwait(cond_add, mutex_add, time)){\ + DA_LOG_ERR(Default, "pthread_cond_wait FAIL");} \ + }while(0) + + +#define _da_thread_cond_destroy(cond_add) do{ \ + if (0 != pthread_cond_destroy(cond_add)){\ + DA_LOG_ERR(Default, "pthread_cond_destroy FAIL");} \ + }while(0) + +#define _da_thread_mutex_destroy(mutex_add) {\ + int ret = 0;\ + do{\ + ret = pthread_mutex_destroy(mutex_add);\ + if (0 == ret){\ + break;\ + }\ + else if(EINVAL == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_destroy FAIL with EINVAL.");\ + break;\ + }\ + else if(EBUSY == ret){\ + DA_LOG_ERR(Default, "pthread_mutex_destroy FAIL with EBUSY.");\ + break;\ + }\ + else{\ + DA_LOG_ERR(Default, "pthread_mutex_destroy FAIL with %d.", ret);\ + break;\ + }\ + }while(1);\ + } + +#endif diff --git a/agent/include/download-agent-type.h b/agent/include/download-agent-type.h new file mode 100755 index 0000000..86fa5ca --- /dev/null +++ b/agent/include/download-agent-type.h @@ -0,0 +1,35 @@ +/* + * 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_Agent_Types_H +#define _Download_Agent_Types_H + +#include "download-agent-defs.h" + +typedef int da_result_t; +typedef int da_bool_t; + +#define IS_NOT_VALID_ID(x) (x <= DA_INVALID_ID) + +#define DA_MAX_URI_LEN 1024 +#define DA_MAX_FULL_PATH_LEN 356 // need configuration +#define DA_MAX_FILE_PATH_LEN 256 // need configuration +#define DA_MAX_STR_LEN 256 +#define DA_MAX_MIME_STR_LEN 256 +#define DA_MAX_PROXY_ADDR_LEN 64 // e.g. 100.200.300.400:10000 + +#endif + diff --git a/agent/include/download-agent-utils-dl-id-history.h b/agent/include/download-agent-utils-dl-id-history.h new file mode 100755 index 0000000..3e32048 --- /dev/null +++ b/agent/include/download-agent-utils-dl-id-history.h @@ -0,0 +1,35 @@ +/* + * 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_Agent_Utils_Hash_Table_H +#define _Download_Agent_Utils_Hash_Table_H + +#include "download-agent-pthread.h" + +typedef struct _dl_id_history_t dl_id_history_t; +struct _dl_id_history_t { + int starting_num; + int cur_dl_id; + pthread_mutex_t mutex; +}; + +da_result_t init_dl_id_history(dl_id_history_t *dl_id_history); +da_result_t deinit_dl_id_history(dl_id_history_t *dl_id_history); + +int get_available_dl_id(dl_id_history_t *dl_id_history); + + +#endif /* _Download_Agent_Utils_Hash_Table_H */ diff --git a/agent/include/download-agent-utils.h b/agent/include/download-agent-utils.h new file mode 100755 index 0000000..488cf3c --- /dev/null +++ b/agent/include/download-agent-utils.h @@ -0,0 +1,54 @@ +/* + * 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_Agent_Utils_H +#define _Download_Agent_Utils_H + +#include <time.h> +#include "download-agent-defs.h" +#include "download-agent-interface.h" +#include "download-agent-dl-mgr.h" + +#define SAVE_FILE_BUFFERING_SIZE_50KB (50*1024) +#define SAVE_FILE_BUFFERING_SIZE_5MB (5*1024*1024) + +#define DA_SLEEP(x) \ + do \ + { \ + struct timespec interval,remainder; \ + interval.tv_sec = (unsigned int)((x)/1000); \ + interval.tv_nsec = (((x)-(interval.tv_sec*1000))*1000000); \ + nanosleep(&interval,&remainder); \ + } while(0) + +typedef enum { + DA_MIME_TYPE_NONE, + DA_MIME_TYPE_DRM1_MESSATE, + DA_MIME_TYPE_END +} da_mime_type_id_t; + +void get_random_number(int *out_num); +da_result_t get_available_dd_id(int *available_id); +da_result_t get_extension_from_mime_type(char *mime_type, char **extension); +da_mime_type_id_t get_mime_type_id(char *content_type); +da_bool_t is_valid_url(const char *url, da_result_t *err_code); + +int read_data_from_file(char *file, char**out_buffer); +da_result_t move_file(const char *from_path, const char *to_path); +void remove_file(const char *file_path); +char *_stristr(const char *long_str, const char *find_str); + +#endif |