diff options
author | gloryj.kim <gloryj.kim@samsung.com> | 2015-03-03 21:14:49 +0900 |
---|---|---|
committer | gloryj.kim <gloryj.kim@samsung.com> | 2015-03-03 21:14:49 +0900 |
commit | d95c159060dd20d6e6308c69017127561ae45551 (patch) | |
tree | c14e32f9915ef4785edfed448642dcf020c347d9 | |
parent | a6d45b6f285e5caa1537992e6c42dd3ddb80a430 (diff) | |
download | download-provider-accepted/tizen_tv.tar.gz download-provider-accepted/tizen_tv.tar.bz2 download-provider-accepted/tizen_tv.zip |
Initialize Tizen 3.0submit/tizen/20150304.023549accepted/tizen/wearable/20150304.124835accepted/tizen/tv/20150304.123044accepted/tizen/mobile/20150304.131032accepted/tizen/common/20150304.105331accepted/tizen_wearableaccepted/tizen_tvaccepted/tizen_mobileaccepted/tizen_common
Change-Id: I3dab60faaf5185e55536626a3cc4293d5330f234
135 files changed, 23911 insertions, 0 deletions
@@ -0,0 +1,2 @@ +Kwangmin Bang <justine.bang@samsung.com>
+Jungki Kwak <jungki.kwak@samsung.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..7244ff7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,64 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +MESSAGE("PROJECT : ${PKG_NAME}") + +# DEFINES + +SET(VERSION ${PKG_VERSION}-${PKG_RELEASE}) +MESSAGE("VERSION : ${VERSION}") + +IF(DEFINED SUPPORT_WIFI_DIRECT) + MESSAGE("SUPPORT_WIFI_DIRECT: ${SUPPORT_WIFI_DIRECT}") + IF(SUPPORT_WIFI_DIRECT) + ADD_DEFINITIONS(-DSUPPORT_WIFI_DIRECT) + ENDIF(SUPPORT_WIFI_DIRECT) +ENDIF(DEFINED SUPPORT_WIFI_DIRECT) + +IF(DEFINED SUPPORT_LOG_MESSAGE) + MESSAGE("SUPPORT_LOG_MESSAGE: ${SUPPORT_LOG_MESSAGE}") + IF(SUPPORT_LOG_MESSAGE) + ADD_DEFINITIONS(-DSUPPORT_LOG_MESSAGE) + ENDIF(SUPPORT_LOG_MESSAGE) +ENDIF(DEFINED SUPPORT_LOG_MESSAGE) + +IF(DEFINED SUPPORT_CHECK_IPC) + MESSAGE("SUPPORT_CHECK_IPC: ${SUPPORT_CHECK_IPC}") + IF(SUPPORT_CHECK_IPC) + ADD_DEFINITIONS(-DSUPPORT_CHECK_IPC) + ENDIF(SUPPORT_CHECK_IPC) +ENDIF(DEFINED SUPPORT_CHECK_IPC) + +# BUILD + +ADD_SUBDIRECTORY(agent) +ADD_SUBDIRECTORY(provider-interface) +ADD_SUBDIRECTORY(provider) +# i18n +ADD_SUBDIRECTORY(po) + +# INSTALL + +PROJECT(${PKG_NAME} C) +SET(PACKAGE_DESCRIPTION "Defines for ${PROJECT_NAME}") + +CONFIGURE_FILE(download-provider.pc.in download-provider.pc @ONLY) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/download-provider.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) + +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/systemd/download-provider.service DESTINATION ${SYSTEMD_DIR}) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/systemd/download-provider.socket DESTINATION ${SYSTEMD_DIR}) + +INSTALL(FILES ${CMAKE_SOURCE_DIR}/download-provider-schema.sql DESTINATION ${DATABASE_SCHEMA_DIR}) + +# install images +INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/res/images/ DESTINATION ${IMAGE_DIR} + FILES_MATCHING + PATTERN "*.png" + PATTERN "*.gif" + ) + +IF(DEFINED LICENSE_DIR) + CONFIGURE_FILE(LICENSE.APLv2 share/license/${PROJECT_NAME}) + INSTALL(FILES share/license/${PROJECT_NAME} DESTINATION ${LICENSE_DIR}) +ENDIF(DEFINED LICENSE_DIR) + + diff --git a/LICENSE.APLv2 b/LICENSE.APLv2 new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.APLv2 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. @@ -0,0 +1,3 @@ +Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Apache License, Version 2. +Please, see the LICENSE file for Apache License terms and conditions. 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 diff --git a/download-provider-schema.sql b/download-provider-schema.sql new file mode 100644 index 0000000..44beda2 --- /dev/null +++ b/download-provider-schema.sql @@ -0,0 +1,74 @@ +CREATE TABLE IF NOT EXISTS groups +( +id INTEGER UNIQUE PRIMARY KEY, +uid INTEGER DEFAULT 0, +gid INTEGER DEFAULT 0, +extra_int INTEGER DEFAULT 0, +packagename TEXT DEFAULT NULL, +smack_label TEXT DEFAULT NULL, +extra TEXT DEFAULT NULL, +date_first_connected DATE, +date_last_connected DATE +); + +CREATE TABLE IF NOT EXISTS logging +( +id INTEGER UNIQUE PRIMARY KEY, +state INTEGER DEFAULT 0, +errorcode INTEGER DEFAULT 0, +startcount INTEGER DEFAULT 0, +packagename TEXT DEFAULT NULL, +createtime DATE, +accesstime DATE +); + +CREATE TABLE IF NOT EXISTS requestinfo +( +id INTEGER UNIQUE PRIMARY KEY, +auto_download BOOLEAN DEFAULT 0, +state_event BOOLEAN DEFAULT 0, +progress_event BOOLEAN DEFAULT 0, +noti_enable BOOLEAN DEFAULT 0, +network_type TINYINT DEFAULT 0, +filename TEXT DEFAULT NULL, +destination TEXT DEFAULT NULL, +url TEXT DEFAULT NULL, +FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS downloadinfo +( +id INTEGER UNIQUE PRIMARY KEY, +http_status INTEGER DEFAULT 0, +content_size UNSIGNED BIG INT DEFAULT 0, +mimetype VARCHAR(64) DEFAULT NULL, +content_name TEXT DEFAULT NULL, +saved_path TEXT DEFAULT NULL, +tmp_saved_path TEXT DEFAULT NULL, +etag TEXT DEFAULT NULL, +FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS httpheaders +( +id INTEGER NOT NULL, +header_field TEXT DEFAULT NULL, +header_data TEXT DEFAULT NULL, +FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS notification +( +id INTEGER NOT NULL, +extra_key TEXT DEFAULT NULL, +extra_data TEXT DEFAULT NULL, +raw_bundle_data_complete_state BLOB DEFAULT NULL, +raw_bundle_data_fail_state BLOB DEFAULT NULL, +raw_bundle_data_ongoing_state BLOB DEFAULT NULL, +title TEXT DEFAULT NULL, +description TEXT DEFAULT NULL, +noti_type INTEGER DEFAULT 0, +FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX IF NOT EXISTS requests_index ON logging (id, state, errorcode, packagename, createtime, accesstime); diff --git a/download-provider.manifest b/download-provider.manifest new file mode 100644 index 0000000..09fdd97 --- /dev/null +++ b/download-provider.manifest @@ -0,0 +1,5 @@ +<manifest> + <request> + <domain name="_"/> + </request> +</manifest>
\ No newline at end of file diff --git a/download-provider.pc.in b/download-provider.pc.in new file mode 100644 index 0000000..72ea9e5 --- /dev/null +++ b/download-provider.pc.in @@ -0,0 +1,6 @@ +# Package Information + +Name: @PROJECT_NAME@ +Description: @PACKAGE_DESCRIPTION@ +Version: @VERSION@ +Cflags: -I/usr/include/download-provider diff --git a/packaging/download-provider.spec b/packaging/download-provider.spec new file mode 100755 index 0000000..890ed32 --- /dev/null +++ b/packaging/download-provider.spec @@ -0,0 +1,136 @@ +Name: download-provider +Summary: Download the contents in background +Version: 1.1.6 +Release: 0 +Group: System/Libraries +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +BuildRequires: cmake +BuildRequires: fdupes +BuildRequires: libprivilege-control-conf +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(gobject-2.0) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(libsoup-2.4) +BuildRequires: pkgconfig(xdgmime) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(sqlite3) +BuildRequires: pkgconfig(bundle) +BuildRequires: pkgconfig(capi-base-common) +BuildRequires: pkgconfig(capi-appfw-app-manager) +BuildRequires: pkgconfig(capi-network-connection) +BuildRequires: pkgconfig(notification) +BuildRequires: pkgconfig(appsvc) +BuildRequires: pkgconfig(wifi-direct) +BuildRequires: pkgconfig(libsmack) +BuildRequires: gettext-devel +BuildRequires: pkgconfig(libsystemd-daemon) +BuildRequires: pkgconfig(libtzplatform-config) +Requires(post): /sbin/ldconfig +Requires(post): /usr/bin/vconftool +Requires: sqlite +Requires: connman +Requires: net-config + +%description +Description: download the contents in background + +%package devel +Summary: Download-provider +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +Description: download the contents in background (development files) + +%prep +%setup -q + +%define _data_install_path %{_datadir}/%{name} +%define _imagedir %{_data_install_path}/images +%define _localedir %{_data_install_path}/locales +%define _sqlschemadir %{_data_install_path}/sql +%define _sqlschemafile %{_sqlschemadir}/download-provider-schema.sql + +%define cmake \ + CFLAGS="${CFLAGS:-%optflags} -fPIC -D_REENTRANT -fvisibility=hidden"; export CFLAGS \ + FFLAGS="${FFLAGS:-%optflags} -fPIC -fvisibility=hidden"; export FFLAGS \ + LDFLAGS+=" -Wl,--as-needed -Wl,--hash-style=both"; export LDFLAGS \ + %__cmake \\\ + -DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \\\ + -DBIN_INSTALL_DIR:PATH=%{_bindir} \\\ + -DLIB_INSTALL_DIR:PATH=%{_libdir} \\\ + -DINCLUDE_INSTALL_DIR:PATH=%{_includedir} \\\ + -DPKG_NAME=%{name} \\\ + -DPKG_VERSION=%{version} \\\ + -DPKG_RELEASE=%{release} \\\ + -DIMAGE_DIR:PATH=%{_imagedir} \\\ + -DLOCALE_DIR:PATH=%{_localedir} \\\ + -DSYSTEMD_DIR:PATH=%{_unitdir} \\\ + -DDATABASE_SCHEMA_DIR=%{_sqlschemadir} \\\ + -DDATABASE_SCHEMA_FILE=%{_sqlschemafile} \\\ + -DSUPPORT_WIFI_DIRECT:BOOL=OFF \\\ + -DSUPPORT_LOG_MESSAGE:BOOL=ON \\\ + -DSUPPORT_CHECK_IPC:BOOL=ON \\\ + %if "%{?_lib}" == "lib64" \ + %{?_cmake_lib_suffix64} \\\ + %endif \ + %{?_cmake_skip_rpath} \\\ + -DBUILD_SHARED_LIBS:BOOL=ON + +%build +%if 0%{?tizen_build_binary_release_type_eng} +export CFLAGS="$CFLAGS -DTIZEN_ENGINEER_MODE" +export CXXFLAGS="$CXXFLAGS -DTIZEN_ENGINEER_MODE" +export FFLAGS="$FFLAGS -DTIZEN_ENGINEER_MODE" +%endif +%cmake . +make %{?jobs:-j%jobs} + +%install +%make_install +mkdir -p %{buildroot}/%{_data_install_path} +mkdir -p %{buildroot}%{_unitdir}/default.target.wants +mkdir -p %{buildroot}%{_unitdir}/sockets.target.wants +ln -s ../download-provider.service %{buildroot}%{_unitdir}/default.target.wants/ +ln -s ../download-provider.socket %{buildroot}%{_unitdir}/sockets.target.wants/ +%fdupes %{buildroot}%{_localedir} + +%post +/sbin/ldconfig +%systemd_post %{name}.service +vconftool set -t int db/setting/default_memory/wap 0 + +%preun +%systemd_preun %{name}.service + +%postun +/sbin/ldconfig +%systemd_postun %{name}.service + +%files +%defattr(-,root,root,-) +%manifest %{name}.manifest +%license LICENSE.APLv2 +%{_bindir}/%{name} +%{_imagedir}/*.png +%{_imagedir}/*.gif +%{_libdir}/libdownloadagent2.so* +%{_libdir}/libdownload-provider-interface.so* +%{_unitdir}/download-provider.service +%{_unitdir}/default.target.wants/download-provider.service +%{_unitdir}/download-provider.socket +%{_unitdir}/sockets.target.wants/download-provider.socket +%{_sqlschemafile} +%{_localedir}/* + +%files devel +%defattr(-,root,root,-) +%manifest %{name}.manifest +%{_libdir}/libdownloadagent2.so +%{_libdir}/libdownload-provider-interface.so +%{_includedir}/download-provider/download-provider-defs.h +%{_includedir}/download-provider/download-provider-interface.h +%{_libdir}/pkgconfig/download-provider.pc +%{_libdir}/pkgconfig/download-provider-interface.pc + diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt new file mode 100755 index 0000000..9e2d8c6 --- /dev/null +++ b/po/CMakeLists.txt @@ -0,0 +1,28 @@ +# for i18n + +SET(POFILES ar.po az.po bg.po ca.po cs.po da.po de_DE.po el_GR.po en.po en_PH.po + en_US.po es_ES.po es_MX.po et.po eu.po fi.po fr_CA.po fr_FR.po ga.po gl.po hi.po + hr.po hu.po hy.po is.po it_IT.po ja_JP.po ka.po kk.po ko_KR.po lt.po lv.po mk.po + nb.po nl_NL.po pl.po pt_BR.po pt_PT.po ro.po ru_RU.po sk.po sl.po sr.po sv.po + tr_TR.po uk.po uz.po zh_CN.po zh_HK.po zh_SG.po zh_TW.po) + +SET(MSGFMT "/usr/bin/msgfmt") + +FOREACH(pofile ${POFILES}) + SET(pofile ${CMAKE_CURRENT_SOURCE_DIR}/${pofile}) + MESSAGE("PO: ${pofile}") + GET_FILENAME_COMPONENT(absPofile ${pofile} ABSOLUTE) + GET_FILENAME_COMPONENT(lang ${absPofile} NAME_WE) + SET(moFile ${CMAKE_CURRENT_BINARY_DIR}/${lang}.mo) + ADD_CUSTOM_COMMAND( + OUTPUT ${moFile} + COMMAND ${MSGFMT} -o ${moFile} ${absPofile} + DEPENDS ${absPofile} + ) + INSTALL(FILES ${moFile} + DESTINATION ${LOCALE_DIR}/${lang}/LC_MESSAGES RENAME ${PKG_NAME}.mo) + SET(moFiles ${moFiles} ${moFile}) +ENDFOREACH(pofile) + +MESSAGE(".mo files: ${moFiles}") +ADD_CUSTOM_TARGET(po ALL DEPENDS ${moFiles}) diff --git a/po/POTFILES.in.in b/po/POTFILES.in.in new file mode 100755 index 0000000..bcb7f1f --- /dev/null +++ b/po/POTFILES.in.in @@ -0,0 +1,2 @@ +# List of source files containing translatable strings. +@@APPNAME@@.c diff --git a/po/ar.po b/po/ar.po new file mode 100755 index 0000000..db4e25e --- /dev/null +++ b/po/ar.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "تم التنزيل" + diff --git a/po/az.po b/po/az.po new file mode 100755 index 0000000..8120d37 --- /dev/null +++ b/po/az.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Yükləmə tamamlandı" + diff --git a/po/bg.po b/po/bg.po new file mode 100755 index 0000000..b8b9584 --- /dev/null +++ b/po/bg.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Изтеглянето завършено" + diff --git a/po/ca.po b/po/ca.po new file mode 100755 index 0000000..291f7a7 --- /dev/null +++ b/po/ca.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Descàrrega completa" + diff --git a/po/cs.po b/po/cs.po new file mode 100755 index 0000000..cf228be --- /dev/null +++ b/po/cs.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Stahování dokončeno" + diff --git a/po/da.po b/po/da.po new file mode 100755 index 0000000..ffba089 --- /dev/null +++ b/po/da.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Overførsel fuldført" + diff --git a/po/de_DE.po b/po/de_DE.po new file mode 100755 index 0000000..1c9c61a --- /dev/null +++ b/po/de_DE.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Download beendet" + diff --git a/po/el_GR.po b/po/el_GR.po new file mode 100755 index 0000000..29efbc7 --- /dev/null +++ b/po/el_GR.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Η λήψη ολοκληρώθηκε" + diff --git a/po/en.po b/po/en.po new file mode 100755 index 0000000..03ffa61 --- /dev/null +++ b/po/en.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Download complete" + diff --git a/po/en_PH.po b/po/en_PH.po new file mode 100755 index 0000000..03ffa61 --- /dev/null +++ b/po/en_PH.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Download complete" + diff --git a/po/en_US.po b/po/en_US.po new file mode 100755 index 0000000..03ffa61 --- /dev/null +++ b/po/en_US.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Download complete" + diff --git a/po/es_ES.po b/po/es_ES.po new file mode 100755 index 0000000..3cd494a --- /dev/null +++ b/po/es_ES.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Descarga completa" + diff --git a/po/es_MX.po b/po/es_MX.po new file mode 100755 index 0000000..3cd494a --- /dev/null +++ b/po/es_MX.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Descarga completa" + diff --git a/po/et.po b/po/et.po new file mode 100755 index 0000000..4ad6fad --- /dev/null +++ b/po/et.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Allalaadimine on lõpule viidud" + diff --git a/po/eu.po b/po/eu.po new file mode 100755 index 0000000..b1e418a --- /dev/null +++ b/po/eu.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Deskarga osatuta" + diff --git a/po/fi.po b/po/fi.po new file mode 100755 index 0000000..058a8d6 --- /dev/null +++ b/po/fi.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Lataus valmis" + diff --git a/po/fr_CA.po b/po/fr_CA.po new file mode 100755 index 0000000..64e4156 --- /dev/null +++ b/po/fr_CA.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Téléchargement terminé" + diff --git a/po/fr_FR.po b/po/fr_FR.po new file mode 100755 index 0000000..64e4156 --- /dev/null +++ b/po/fr_FR.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Téléchargement terminé" + diff --git a/po/ga.po b/po/ga.po new file mode 100755 index 0000000..5e9474a --- /dev/null +++ b/po/ga.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Íoslódáilte" + diff --git a/po/gl.po b/po/gl.po new file mode 100755 index 0000000..3cd494a --- /dev/null +++ b/po/gl.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Descarga completa" + diff --git a/po/hi.po b/po/hi.po new file mode 100755 index 0000000..5ece567 --- /dev/null +++ b/po/hi.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "डाउनलोड पूर्ण" + diff --git a/po/hr.po b/po/hr.po new file mode 100755 index 0000000..b4ae9f2 --- /dev/null +++ b/po/hr.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Skidanje dovršeno" + diff --git a/po/hu.po b/po/hu.po new file mode 100755 index 0000000..239c6b9 --- /dev/null +++ b/po/hu.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "A letöltés befejeződött" + diff --git a/po/hy.po b/po/hy.po new file mode 100755 index 0000000..12ea497 --- /dev/null +++ b/po/hy.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Ներբեռնումն ավարտվեց" + diff --git a/po/is.po b/po/is.po new file mode 100755 index 0000000..470a453 --- /dev/null +++ b/po/is.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Niðurhali lokið" + diff --git a/po/it_IT.po b/po/it_IT.po new file mode 100755 index 0000000..e96495c --- /dev/null +++ b/po/it_IT.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Download completato" + diff --git a/po/ja_JP.po b/po/ja_JP.po new file mode 100755 index 0000000..41ea60e --- /dev/null +++ b/po/ja_JP.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "ダウンロード完了" + diff --git a/po/ka.po b/po/ka.po new file mode 100755 index 0000000..10a084d --- /dev/null +++ b/po/ka.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "ჩამოტვირთვა დასრულდა" + diff --git a/po/kk.po b/po/kk.po new file mode 100755 index 0000000..40ff8b2 --- /dev/null +++ b/po/kk.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Жүктеу аяқталды" + diff --git a/po/ko_KR.po b/po/ko_KR.po new file mode 100755 index 0000000..1f47a18 --- /dev/null +++ b/po/ko_KR.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "다운로드 완료" + diff --git a/po/lt.po b/po/lt.po new file mode 100755 index 0000000..ea1075f --- /dev/null +++ b/po/lt.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Atsisiuntimas baigtas" + diff --git a/po/lv.po b/po/lv.po new file mode 100755 index 0000000..d3bf10c --- /dev/null +++ b/po/lv.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Lejupielāde pabeigta" + diff --git a/po/mk.po b/po/mk.po new file mode 100755 index 0000000..8ae0de2 --- /dev/null +++ b/po/mk.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Преземањето заврши" + diff --git a/po/nb.po b/po/nb.po new file mode 100755 index 0000000..33af7b7 --- /dev/null +++ b/po/nb.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Nedlasting fullført" + diff --git a/po/nl_NL.po b/po/nl_NL.po new file mode 100755 index 0000000..2273390 --- /dev/null +++ b/po/nl_NL.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Downloaden voltooid" + diff --git a/po/pl.po b/po/pl.po new file mode 100755 index 0000000..8a48b82 --- /dev/null +++ b/po/pl.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Pobieranie zakończone" + diff --git a/po/pt_BR.po b/po/pt_BR.po new file mode 100755 index 0000000..f97065e --- /dev/null +++ b/po/pt_BR.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Download concluído" + diff --git a/po/pt_PT.po b/po/pt_PT.po new file mode 100755 index 0000000..cbe7377 --- /dev/null +++ b/po/pt_PT.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Transferência concluída" + diff --git a/po/ro.po b/po/ro.po new file mode 100755 index 0000000..d82366c --- /dev/null +++ b/po/ro.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Descărcare finalizată" + diff --git a/po/ru_RU.po b/po/ru_RU.po new file mode 100755 index 0000000..27aa56c --- /dev/null +++ b/po/ru_RU.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Загрузка завершена" + diff --git a/po/sk.po b/po/sk.po new file mode 100755 index 0000000..178001d --- /dev/null +++ b/po/sk.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Sťahovanie je dokončené" + diff --git a/po/sl.po b/po/sl.po new file mode 100755 index 0000000..d031ce5 --- /dev/null +++ b/po/sl.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Prenos je končan" + diff --git a/po/sr.po b/po/sr.po new file mode 100755 index 0000000..e21e6d3 --- /dev/null +++ b/po/sr.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Preuzimanje završeno" + diff --git a/po/sv.po b/po/sv.po new file mode 100755 index 0000000..8f0dce9 --- /dev/null +++ b/po/sv.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Hämtningen är klar" + diff --git a/po/tr_TR.po b/po/tr_TR.po new file mode 100755 index 0000000..ec760e5 --- /dev/null +++ b/po/tr_TR.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "İndirme tamamlandı" + diff --git a/po/uk.po b/po/uk.po new file mode 100755 index 0000000..504e5f1 --- /dev/null +++ b/po/uk.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Завантаження завершено" + diff --git a/po/uz.po b/po/uz.po new file mode 100755 index 0000000..c97fec7 --- /dev/null +++ b/po/uz.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "Yuklab olish yakunlandi" + diff --git a/po/zh_CN.po b/po/zh_CN.po new file mode 100755 index 0000000..3238acd --- /dev/null +++ b/po/zh_CN.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "下载完成" + diff --git a/po/zh_HK.po b/po/zh_HK.po new file mode 100755 index 0000000..51101ba --- /dev/null +++ b/po/zh_HK.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "下載完成" + diff --git a/po/zh_SG.po b/po/zh_SG.po new file mode 100755 index 0000000..3238acd --- /dev/null +++ b/po/zh_SG.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "下载完成" + diff --git a/po/zh_TW.po b/po/zh_TW.po new file mode 100755 index 0000000..51101ba --- /dev/null +++ b/po/zh_TW.po @@ -0,0 +1,3 @@ +msgid "IDS_DM_HEADER_DOWNLOAD_COMPLETE" +msgstr "下載完成" + diff --git a/provider-interface/CMakeLists.txt b/provider-interface/CMakeLists.txt new file mode 100755 index 0000000..eb523c4 --- /dev/null +++ b/provider-interface/CMakeLists.txt @@ -0,0 +1,44 @@ + +## PROJECT NAME +PROJECT(download-provider-interface C) +SET(PACKAGE_DESCRIPTION "Interface supported by download-provider") + +IF("${CMAKE_BUILD_TYPE}" STREQUAL "") + SET(CMAKE_BUILD_TYPE "Debug") +ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "") +MESSAGE("Build type: ${CMAKE_BUILD_TYPE}") + +SET(PC_REQUIRED "glib-2.0 gobject-2.0 dlog capi-base-common capi-appfw-app-manager bundle") + +INCLUDE(FindPkgConfig) + +pkg_check_modules(provider_interface_pkgs REQUIRED ${PC_REQUIRED}) + +FOREACH(flag ${provider_interface_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +## INCLUDES +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/provider/include) + +SET(PROVIDER_INTERFACE_LINK_LIBRARIES + ${GLIB-2_LIBRARIES} + ${GOBJECT-2_LIBRARIES} + pthread + ) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -Wall") + +ADD_LIBRARY(${PROJECT_NAME} SHARED + ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.c ) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${provider_interface_pkgs_LDFLAGS} ${PROVIDER_INTERFACE_LINK_LIBRARIES}) +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES VERSION ${PKG_VERSION}) +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION 0) + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR} COMPONENT RuntimeLibraries) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME}.h DESTINATION ${INCLUDE_INSTALL_DIR}/${PKG_NAME}) +CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) diff --git a/provider-interface/download-provider-interface.c b/provider-interface/download-provider-interface.c new file mode 100755 index 0000000..388a6f2 --- /dev/null +++ b/provider-interface/download-provider-interface.c @@ -0,0 +1,2200 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> + +#include <pthread.h> +#include <signal.h> + +#include <dlog.h> +#include <download-provider-interface.h> +#include <download-provider.h> +#include <tzplatform_config.h> + +#define DEFAULT_INSTALL_PATH tzplatform_getenv(TZ_USER_DOWNLOADS) + +#ifdef SUPPORT_CHECK_IPC +#include <sys/ioctl.h> +#endif + +#define DP_CHECK_CONNECTION do {\ + if (__check_connections() != DP_ERROR_NONE) {\ + pthread_mutex_unlock(&g_function_mutex);\ + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR;\ + }\ +} while(0) + +#define DP_CHECK_PROVIDER_STATUS do {\ + dp_error_type errorcode = DP_ERROR_NONE;\ + errorcode = __ipc_check_ready_status(g_interface_info->cmd_socket);\ + if (errorcode != DP_ERROR_NONE) {\ + pthread_mutex_unlock(&g_interface_info->mutex);\ + if (errorcode == DP_ERROR_IO_ERROR)\ + __disconnect_from_provider();\ + pthread_mutex_unlock(&g_function_mutex);\ + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR;\ + }\ +} while(0) + +#define DP_PRE_CHECK_ID do {\ + if (id <= 0) {\ + TRACE_ERROR("[CHECK ID] (%d)", id);\ + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER;\ + }\ +} while(0) + +#define MAX_DOWNLOAD_HANDLE 32 + +#ifdef SUPPORT_LOG_MESSAGE +#include <dlog.h> +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "DOWNLOAD_PROVIDER_INTERFACE" +#define TRACE_DEBUG(format, ARG...) LOGD(format, ##ARG) +#define TRACE_ERROR(format, ARG...) LOGE(format, ##ARG) +#define TRACE_STRERROR(format, ARG...) LOGE(format" [%s]", ##ARG, strerror(errno)) +#define TRACE_INFO(format, ARG...) LOGI(format, ##ARG) + +#ifdef SECURE_LOGD +#define TRACE_SECURE_DEBUG(format, ARG...) SECURE_LOGD(format, ##ARG) +#else +#define TRACE_SECURE_DEBUG(...) do { } while(0) +#endif +#ifdef SECURE_LOGI +#define TRACE_SECURE_INFO(format, ARG...) SECURE_LOGI(format, ##ARG) +#else +#define TRACE_SECURE_INFO(...) do { } while(0) +#endif +#ifdef SECURE_LOGE +#define TRACE_SECURE_ERROR(format, ARG...) SECURE_LOGE(format, ##ARG) +#else +#define TRACE_SECURE_ERROR(...) do { } while(0) +#endif + +#else +#define TRACE_DEBUG(...) do { } while(0) +#define TRACE_ERROR(...) do { } while(0) +#define TRACE_STRERROR(...) do { } while(0) +#define TRACE_INFO(...) do { } while(0) +#define TRACE_SECURE_DEBUG(...) do { } while(0) +#define TRACE_SECURE_INFO(...) do { } while(0) +#define TRACE_SECURE_ERROR(...) do { } while(0) +#endif + +// define type +typedef struct { + // send command * get return value. + int cmd_socket; + // getting event from download-provider + int event_socket; + pthread_mutex_t mutex; // lock before using, unlock after using +} dp_interface_info; + +typedef struct { + dp_interface_state_changed_cb state; + void *state_data; + dp_interface_progress_cb progress; + void *progress_data; +} dp_interface_callback; + +typedef struct { + int id; + dp_interface_callback callback; +} dp_interface_slot; + +// declare the variables +dp_interface_info *g_interface_info = NULL; +dp_interface_slot g_interface_slots[MAX_DOWNLOAD_HANDLE]; +static pthread_mutex_t g_function_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t g_clear_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_t g_interface_event_thread_id = 0; + +//////////// defines functions ///////////////// + + + +static int __dp_interface_convert_network_adaptor(int type) +{ + switch (type) { + case DOWNLOAD_ADAPTOR_NETWORK_WIFI: + return DP_NETWORK_TYPE_WIFI; + case DOWNLOAD_ADAPTOR_NETWORK_DATA_NETWORK: + return DP_NETWORK_TYPE_DATA_NETWORK; + case DOWNLOAD_ADAPTOR_NETWORK_WIFI_DIRECT: + return DP_NETWORK_TYPE_WIFI_DIRECT; + case DOWNLOAD_ADAPTOR_NETWORK_ALL: + return DP_NETWORK_TYPE_ALL; + default: + break; + } + return type; +} + +static int __dp_interface_convert_network_provider(int type) +{ + switch (type) { + case DP_NETWORK_TYPE_WIFI: + return DOWNLOAD_ADAPTOR_NETWORK_WIFI; + case DP_NETWORK_TYPE_DATA_NETWORK: + return DOWNLOAD_ADAPTOR_NETWORK_DATA_NETWORK; + case DP_NETWORK_TYPE_WIFI_DIRECT: + return DOWNLOAD_ADAPTOR_NETWORK_WIFI_DIRECT; + case DOWNLOAD_ADAPTOR_NETWORK_ALL: + return DP_NETWORK_TYPE_ALL; + default: + break; + } + return type; +} + +static int __dp_interface_convert_state(int state) +{ + switch (state) { + case DP_STATE_READY: + TRACE_DEBUG("READY"); + return DOWNLOAD_ADPATOR_STATE_READY; + case DP_STATE_CONNECTING: + TRACE_DEBUG("CONNECTING/QUEUED"); + return DOWNLOAD_ADPATOR_STATE_QUEUED; + case DP_STATE_QUEUED: + TRACE_DEBUG("QUEUED"); + return DOWNLOAD_ADPATOR_STATE_QUEUED; + case DP_STATE_DOWNLOADING: + TRACE_DEBUG("DOWNLOADING"); + return DOWNLOAD_ADPATOR_STATE_DOWNLOADING; + case DP_STATE_PAUSE_REQUESTED: + TRACE_DEBUG("PAUSE_REQUESTED/DOWNLOADING"); + return DOWNLOAD_ADPATOR_STATE_DOWNLOADING; + case DP_STATE_PAUSED: + TRACE_DEBUG("PAUSED"); + return DOWNLOAD_ADPATOR_STATE_PAUSED; + case DP_STATE_COMPLETED: + TRACE_DEBUG("COMPLETED"); + return DOWNLOAD_ADPATOR_STATE_COMPLETED; + case DP_STATE_CANCELED: + TRACE_DEBUG("CANCELED"); + return DOWNLOAD_ADPATOR_STATE_CANCELED; + case DP_STATE_FAILED: + TRACE_DEBUG("FAILED"); + return DOWNLOAD_ADPATOR_STATE_FAILED; + default: + break; + } + return DOWNLOAD_ADPATOR_STATE_NONE; +} + +static int __dp_interface_convert_errorcode(int errorcode) +{ + switch (errorcode) { + case DP_ERROR_NONE: + return DOWNLOAD_ADAPTOR_ERROR_NONE; + case DP_ERROR_INVALID_PARAMETER: + TRACE_DEBUG("ERROR_INVALID_PARAMETER"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + case DP_ERROR_OUT_OF_MEMORY: + TRACE_DEBUG("ERROR_OUT_OF_MEMORY"); + return DOWNLOAD_ADAPTOR_ERROR_OUT_OF_MEMORY; + case DP_ERROR_IO_EAGAIN: + TRACE_DEBUG("ERROR_IO_ERROR(EAGAIN)"); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + case DP_ERROR_IO_EINTR: + TRACE_DEBUG("ERROR_IO_ERROR(EINTR)"); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + case DP_ERROR_IO_ERROR: + TRACE_DEBUG("ERROR_IO_ERROR"); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + case DP_ERROR_NETWORK_UNREACHABLE: + TRACE_DEBUG("ERROR_NETWORK_UNREACHABLE"); + return DOWNLOAD_ADAPTOR_ERROR_NETWORK_UNREACHABLE; + case DP_ERROR_NO_SPACE: + TRACE_DEBUG("ERROR_NO_SPACE"); + return DOWNLOAD_ADAPTOR_ERROR_NO_SPACE; + case DP_ERROR_FIELD_NOT_FOUND: + TRACE_DEBUG("ERROR_FIELD_NOT_FOUND"); + return DOWNLOAD_ADAPTOR_ERROR_FIELD_NOT_FOUND; + case DP_ERROR_INVALID_STATE: + TRACE_DEBUG("ERROR_INVALID_STATE"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_STATE; + case DP_ERROR_CONNECTION_FAILED: + TRACE_DEBUG("ERROR_CONNECTION_TIMED_OUT/CONNECTION_FAILED"); + return DOWNLOAD_ADAPTOR_ERROR_CONNECTION_TIMED_OUT; + case DP_ERROR_INVALID_URL: + TRACE_DEBUG("ERROR_INVALID_URL"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_URL; + case DP_ERROR_INVALID_DESTINATION: + TRACE_DEBUG("ERROR_INVALID_DESTINATION"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_DESTINATION; + case DP_ERROR_PERMISSION_DENIED: + TRACE_DEBUG("ERROR_PERMISSION_DENIED"); + return DOWNLOAD_ADAPTOR_ERROR_PERMISSION_DENIED; + case DP_ERROR_QUEUE_FULL: + TRACE_DEBUG("ERROR_QUEUE_FULL"); + return DOWNLOAD_ADAPTOR_ERROR_QUEUE_FULL; + case DP_ERROR_ALREADY_COMPLETED: + TRACE_DEBUG("ERROR_ALREADY_COMPLETED"); + return DOWNLOAD_ADAPTOR_ERROR_ALREADY_COMPLETED; + case DP_ERROR_FILE_ALREADY_EXISTS: + TRACE_DEBUG("ERROR_FILE_ALREADY_EXISTS"); + return DOWNLOAD_ADAPTOR_ERROR_FILE_ALREADY_EXISTS; + case DP_ERROR_TOO_MANY_DOWNLOADS: + TRACE_DEBUG("ERROR_TOO_MANY_DOWNLOADS"); + return DOWNLOAD_ADAPTOR_ERROR_TOO_MANY_DOWNLOADS; + case DP_ERROR_NO_DATA: + TRACE_DEBUG("ERROR_NO_DATA"); + return DOWNLOAD_ADAPTOR_ERROR_NO_DATA; + case DP_ERROR_UNHANDLED_HTTP_CODE: + TRACE_DEBUG("ERROR_UNHANDLED_HTTP_CODE"); + return DOWNLOAD_ADAPTOR_ERROR_UNHANDLED_HTTP_CODE; + case DP_ERROR_CANNOT_RESUME: + TRACE_DEBUG("ERROR_CANNOT_RESUME"); + return DOWNLOAD_ADAPTOR_ERROR_CANNOT_RESUME; + case DP_ERROR_ID_NOT_FOUND: + TRACE_DEBUG("ERROR_ID_NOT_FOUND"); + return DOWNLOAD_ADAPTOR_ERROR_ID_NOT_FOUND; + case DP_ERROR_UNKNOWN: + TRACE_DEBUG("ERROR_INVALID_STATE/UNKNOWN"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_STATE; + case DP_ERROR_INVALID_NETWORK_TYPE: + TRACE_DEBUG("ERROR_INVALID_NETWORK_TYPE"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_NETWORK_TYPE; + default: + break; + } + return DOWNLOAD_ADAPTOR_ERROR_NONE; +} + +static int __get_my_slot_index(int id) +{ + int i = 0; + // search same info in array. + for (; i < MAX_DOWNLOAD_HANDLE; i++) + if (g_interface_slots[i].id == id) + return i; + return -1; +} + +static int __get_empty_slot_index() +{ + int i = 0; + for (; i < MAX_DOWNLOAD_HANDLE; i++) + if (g_interface_slots[i].id <= 0) + return i; + return -1; +} + +static dp_error_type __get_standard_errorcode(dp_error_type basecode) +{ + dp_error_type errorcode = basecode; + if (errno == EPIPE) { + TRACE_ERROR("[EPIPE] Broken Pipe [%d]", errno); + errorcode = DP_ERROR_IO_ERROR; + } else if (errno == EAGAIN) { + TRACE_ERROR + ("[EAGAIN] Resource temporarily unavailable [%d]", + errno); + errorcode = DP_ERROR_IO_EAGAIN; + } else if (errno == EINTR) { + TRACE_ERROR("[EINTR] Interrupted System Call [%d]", errno); + errorcode = DP_ERROR_IO_ERROR; + } + return errorcode; +} + +static int __ipc_read_custom_type(int fd, void *value, size_t type_size) +{ + if (fd < 0) { + TRACE_ERROR("[CHECK SOCKET]"); + return -1; + } + if (value == NULL) { + TRACE_ERROR("[CHECK value]"); + return -1; + } + if (type_size <= 0) { + TRACE_ERROR("[CHECK size]"); + return -1; + } + + ssize_t recv_bytes = read(fd, value, type_size); + if (recv_bytes < 0) { + TRACE_STRERROR("[CRITICAL] read"); + return -1; + } + return 0; +} + +static int __ipc_read_int(int fd) +{ + int value = -1; + + if (fd < 0) { + TRACE_ERROR("[CHECK SOCKET]"); + return -1; + } + ssize_t recv_bytes = read(fd, &value, sizeof(int)); + if (recv_bytes < 0) { + TRACE_STRERROR("[CRITICAL] read"); + return -1; + } + return value; +} + +static int __ipc_read_download_id(int fd) +{ + int value = -1; + int read_len = 0; + int try_count = 5; + + if (fd < 0) { + TRACE_ERROR("[CHECK SOCKET]"); + return -1; + } + do { + read_len = read(fd, &value, sizeof(int)); + if (read_len < 0) { + TRACE_STRERROR("[CRITICAL] read"); + return -1; + } + try_count--; + } while (read_len == 0 && value == 0 && try_count > 0); + return value; +} + +// keep the order/ unsigned , str +static char *__ipc_read_string(int fd) +{ + unsigned length = 0; + size_t recv_size = 0; + unsigned remain_size = 0; + size_t buffer_size = 0; + char *str = NULL; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return NULL; + } + + // read flexible URL from client. + ssize_t recv_bytes = read(fd, &length, sizeof(unsigned)); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return NULL; + } + if (length < 1 || length > DP_MAX_URL_LEN) { + TRACE_ERROR("[STRING LEGNTH] [%d]", length); + return NULL; + } + str = (char *)calloc((length + 1), sizeof(char)); + if (str == NULL) { + TRACE_STRERROR("[ERROR] calloc length:%d FD[%d]", length, fd); + return NULL; + } + remain_size = length; + do { + buffer_size = 0; + if (remain_size > DP_DEFAULT_BUFFER_SIZE) + buffer_size = DP_DEFAULT_BUFFER_SIZE; + else + buffer_size = remain_size; + recv_size = (size_t)read(fd, str + (int)(length - remain_size), + buffer_size * sizeof(char)); + if (recv_size > DP_DEFAULT_BUFFER_SIZE) { + recv_size = -1; + break; + } + if (recv_size > 0) + remain_size = remain_size - (unsigned)recv_size; + } while (recv_size > 0 && remain_size > 0); + + if (recv_size == 0) { + TRACE_STRERROR("[ERROR] closed peer:%d", fd); + free(str); + return NULL; + } + str[length] = '\0'; + return str; +} + +int __ipc_read_bundle(int fd, bundle_raw **b) +{ + unsigned length = 0; + size_t recv_size = 0; + unsigned remain_size = 0; + size_t buffer_size = 0; + bundle_raw *b_raw = NULL; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return 0; + } + + // read bundle data from client. + ssize_t recv_bytes = read(fd, &length, sizeof(unsigned)); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return 0; + } + if (length < 1 || length > DP_MAX_URL_LEN) { + TRACE_ERROR("[STRING LEGNTH] [%d]", length); + return 0; + } + b_raw = (bundle_raw *)calloc(length, 1); + if (b_raw == NULL) { + TRACE_STRERROR("[ERROR] calloc length:%d FD[%d]", length, fd); + return 0; + } + remain_size = length; + do { + buffer_size = 0; + if (remain_size > DP_DEFAULT_BUFFER_SIZE) + buffer_size = DP_DEFAULT_BUFFER_SIZE; + else + buffer_size = remain_size; + recv_size = (size_t)read(fd, b_raw + (int)(length - remain_size), + buffer_size); + if (recv_size > DP_DEFAULT_BUFFER_SIZE) { + recv_size = -1; + break; + } + if (recv_size > 0) + remain_size = remain_size - (unsigned)recv_size; + } while (recv_size > 0 && remain_size > 0); + + if (recv_size == 0) { + TRACE_STRERROR("[ERROR] closed peer:%d", fd); + bundle_free_encoded_rawdata(&b_raw); + return 0; + } + *b = b_raw; + return (int)length; +} + +static dp_error_type __ipc_return(int fd) +{ + dp_error_type errorcode = DP_ERROR_NONE; + + if (fd < 0) { + TRACE_ERROR("[CHECK SOCKET]"); + return DP_ERROR_IO_ERROR; + } + ssize_t recv_bytes = read(fd, &errorcode, sizeof(dp_error_type)); + if (recv_bytes < 0) { + TRACE_STRERROR("[CRITICAL] read"); + return __get_standard_errorcode(DP_ERROR_IO_ERROR); + } + if (errorcode != DP_ERROR_NONE) + TRACE_ERROR("return : %d", errorcode); + return errorcode; +} + +static dp_event_info* __ipc_event(int fd) +{ + dp_event_info *event = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK SOCKET]"); + return NULL; + } + + event = (dp_event_info *) calloc(1, sizeof(dp_event_info)); + if (event == NULL) { + TRACE_ERROR("[CHECK ALLOCATION]"); + return NULL; + } + ssize_t recv_bytes = read(fd, event, sizeof(dp_event_info)); + if (recv_bytes < 0) { + TRACE_STRERROR("[CRITICAL] read"); + free(event); + return NULL; + } + return event; +} + +static int __ipc_send_int(int fd, int value) +{ + if (fd < 0) { + TRACE_ERROR("[CHECK FD] [%d]", fd); + return -1; + } + + if (fd >= 0 && write(fd, &value, sizeof(int)) < 0) { + TRACE_STRERROR("[CRITICAL] send"); + return -1; + } + return 0; +} + +// keep the order/ unsigned , str +static dp_error_type __ipc_send_string(int fd, const char *str) +{ + unsigned length = 0; + + if (fd < 0) { + TRACE_ERROR("[CHECK FD]"); + return DP_ERROR_IO_ERROR; + } + if (str == NULL || (length = strlen(str)) <= 0) { + TRACE_ERROR("[CHECK STRING]"); + return DP_ERROR_INVALID_PARAMETER; + } + + if (fd >= 0 && write(fd, &length, sizeof(unsigned)) < 0) { + TRACE_STRERROR("[CRITICAL] send"); + return DP_ERROR_IO_ERROR; + } + if (fd >= 0 && write(fd, str, length * sizeof(char)) < 0) { + TRACE_STRERROR("[CRITICAL] send"); + return DP_ERROR_IO_ERROR; + } + return DP_ERROR_NONE; +} + +static dp_error_type __ipc_send_raw_bundle(int fd, int type, const bundle_raw *b, int len) +{ + if (fd < 0) { + TRACE_ERROR("[CHECK FD]"); + return DP_ERROR_IO_ERROR; + } + if (b == NULL || len <= 0) { + TRACE_ERROR("[CHECK STRING]"); + return DP_ERROR_INVALID_PARAMETER; + } + if (fd >= 0 && write(fd, &type, sizeof(unsigned)) < 0) { + TRACE_STRERROR("[CRITICAL] send"); + return DP_ERROR_IO_ERROR; + } + if (fd >= 0 && write(fd, &len, sizeof(unsigned)) < 0) { + TRACE_STRERROR("[CRITICAL] send"); + return DP_ERROR_IO_ERROR; + } + if (fd >= 0 && write(fd, b, len) < 0) { + TRACE_STRERROR("[CRITICAL] send"); + return DP_ERROR_IO_ERROR; + } + return DP_ERROR_NONE; +} + +static dp_error_type __ipc_send_command + (int fd, int id, dp_command_type cmd) +{ + if (fd < 0) { + TRACE_ERROR("[CHECK SOCKET]"); + return DP_ERROR_IO_ERROR; + } + + dp_command command; + command.id = id; + command.cmd = cmd; + if (fd >= 0 && write(fd, &command, sizeof(dp_command)) < 0) { + TRACE_STRERROR("[CRITICAL] send"); + return DP_ERROR_IO_ERROR; + } + return DP_ERROR_NONE; +} + +static dp_error_type __ipc_send_command_return + (int id, dp_command_type cmd) +{ + if (cmd <= DP_CMD_NONE) { + TRACE_ERROR("[CHECK COMMAND] (%d)", cmd); + return DP_ERROR_INVALID_PARAMETER; + } + // send commnad with ID + if (__ipc_send_command(g_interface_info->cmd_socket, id, cmd) != + DP_ERROR_NONE) + return DP_ERROR_IO_ERROR; + // return from provider. + return __ipc_return(g_interface_info->cmd_socket); +} + +static int __create_socket() +{ + int sockfd = -1; + struct timeval tv_timeo = { 2, 500000 }; //2.5 second + struct sockaddr_un clientaddr; + + if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + TRACE_STRERROR("[CRITICAL] socket system error"); + return -1; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv_timeo, + sizeof( tv_timeo ) ) < 0) { + TRACE_STRERROR("[CRITICAL] setsockopt SO_RCVTIMEO"); + close(sockfd); + return -1; + } + + bzero(&clientaddr, sizeof clientaddr); + clientaddr.sun_family = AF_UNIX; + memset(clientaddr.sun_path, 0x00, sizeof(clientaddr.sun_path)); + strncpy(clientaddr.sun_path, DP_IPC, strlen(DP_IPC)); + clientaddr.sun_path[strlen(DP_IPC)] = '\0'; + if (connect(sockfd, + (struct sockaddr*)&clientaddr, sizeof(clientaddr)) < 0) { + close(sockfd); + return -1; + } + TRACE_DEBUG("sockfd [%d]", sockfd); + return sockfd; +} + +static void __clear_interface() +{ + TRACE_DEBUG(""); + pthread_mutex_lock(&g_clear_mutex); + if (g_interface_info != NULL) { + shutdown(g_interface_info->cmd_socket, 0); + close(g_interface_info->cmd_socket); + g_interface_info->cmd_socket= -1; + shutdown(g_interface_info->event_socket, 0); + close(g_interface_info->event_socket); + g_interface_info->event_socket = -1; + pthread_mutex_destroy(&g_interface_info->mutex); + free(g_interface_info); + g_interface_info = NULL; + } + pthread_mutex_unlock(&g_clear_mutex); +} + +static int __disconnect_from_provider() +{ + TRACE_DEBUG(""); + if (g_interface_event_thread_id > 0 && + pthread_kill(g_interface_event_thread_id, 0) != ESRCH) { + if (pthread_cancel(g_interface_event_thread_id) != 0) { + TRACE_STRERROR("pthread:%d", (int)g_interface_event_thread_id); + } + g_interface_event_thread_id = 0; + } + __clear_interface(); + return DP_ERROR_NONE; +} + +#ifdef SUPPORT_CHECK_IPC +// clear read buffer. call in head of API before calling IPC_SEND +static void __clear_read_buffer(int fd) +{ + int i; + int unread_count; + char tmp_char; + + // FIONREAD : Returns the number of bytes immediately readable + if (ioctl(fd, FIONREAD, &unread_count) >= 0) { + if (unread_count > 0) { + TRACE_DEBUG("[CLEAN] garbage packet[%ld]", unread_count); + for ( i = 0; i < unread_count; i++) { + if (read(fd, &tmp_char, sizeof(char)) < 0) { + TRACE_STRERROR("[CHECK] read"); + break; + } + } + } + } +} +#endif + +// ask to provider before sending a command. +// if provider wait in some commnad, can not response immediately +// capi will wait in read block. +// after asking, call clear_read_buffer. +static dp_error_type __ipc_check_ready_status(int fd) +{ + dp_error_type errorcode = DP_ERROR_NONE; + +#ifdef SUPPORT_CHECK_IPC + // echo from provider + errorcode = __ipc_send_command_return(-1, DP_CMD_ECHO); + if (errorcode == DP_ERROR_NONE) + __clear_read_buffer(fd); +#endif + return errorcode; +} + +// listen ASYNC state event, no timeout +static void *__dp_interface_event_manager(void *arg) +{ + int maxfd, index; + fd_set rset, read_fdset; + dp_event_info *eventinfo = NULL; + + if (g_interface_info == NULL) { + TRACE_STRERROR("[CRITICAL] INTERFACE null"); + return 0; + } + if (g_interface_info->event_socket < 0) { + TRACE_STRERROR("[CRITICAL] IPC NOT ESTABILISH"); + return 0; + } + + // deferred wait to cancal until next function called. + // ex) function : select, read in this thread + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); + + maxfd = g_interface_info->event_socket; + FD_ZERO(&read_fdset); + FD_SET(g_interface_info->event_socket, &read_fdset); + + int sock = g_interface_info->event_socket; + + while(g_interface_info != NULL + && g_interface_info->event_socket >= 0) { + rset = read_fdset; + if (select((maxfd + 1), &rset, 0, 0, 0) < 0) { + TRACE_STRERROR("[CRITICAL] select"); + break; + } + + if (g_interface_event_thread_id <=0 + || pthread_self() != g_interface_event_thread_id) { + TRACE_ERROR + ("[CRITICAL] [CHECK TID] SELF ID [%d] Global ID (%d)", + pthread_self(), g_interface_event_thread_id); + // another thread may work. just terminate + break; + } + + if (FD_ISSET(sock, &rset) > 0) { + // read state info from socket + eventinfo = __ipc_event(sock); + if (eventinfo == NULL || eventinfo->id <= 0) { + // failed to read from socket // ignore this status + free(eventinfo); + TRACE_STRERROR("[CRITICAL] Can not read Event packet"); + g_interface_event_thread_id = 0; + __clear_interface(); + return 0; + } + + if ((index = __get_my_slot_index(eventinfo->id)) < 0) { + TRACE_ERROR("[CRITICAL] not found slot for [%d]", + eventinfo->id); + free(eventinfo); + continue; + } + + // begin protect callback sections & thread safe + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + dp_interface_callback *callback = + &g_interface_slots[index].callback; + + if (eventinfo->state == DP_STATE_DOWNLOADING + && eventinfo->received_size > 0) { + if (eventinfo->id == g_interface_slots[index].id && + callback->progress != NULL) { + // progress event + callback->progress(eventinfo->id, + eventinfo->received_size, + callback->progress_data); + } + } else { + if (eventinfo->id == g_interface_slots[index].id && + callback->state != NULL) { + // state event + callback->state(eventinfo->id, + __dp_interface_convert_state(eventinfo->state), + callback->state_data); + } + } + free(eventinfo); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + } + } // while + + FD_CLR(sock, &read_fdset); + g_interface_event_thread_id = 0; // set 0 to not call pthread_cancel + TRACE_DEBUG("thread end by itself"); + return 0; +} + +static int __connect_to_provider() +{ + pthread_mutex_lock(&g_clear_mutex); + if (g_interface_info == NULL) { + + g_interface_info = + (dp_interface_info *) calloc(1, sizeof(dp_interface_info)); + } + + if (g_interface_info != NULL) { + + int connect_retry = 3; + g_interface_info->cmd_socket = -1; + while(g_interface_info->cmd_socket < 0 && connect_retry-- > 0) { + g_interface_info->cmd_socket = __create_socket(); + if (g_interface_info->cmd_socket < 0) + usleep(50000); + } + if (g_interface_info->cmd_socket < 0) { + TRACE_STRERROR("[CRITICAL] connect system error"); + free(g_interface_info); + g_interface_info = NULL; + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } + // send a command + if (__ipc_send_int(g_interface_info->cmd_socket, + DP_CMD_SET_COMMAND_SOCKET) < 0) { + close(g_interface_info->cmd_socket); + free(g_interface_info); + g_interface_info = NULL; + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } +#ifndef SO_PEERCRED + // send PID. Not support SO_PEERCRED + if (__ipc_send_int(g_interface_info->cmd_socket, getpid()) < 0) { + close(g_interface_info->cmd_socket); + free(g_interface_info); + g_interface_info = NULL; + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } + if (__ipc_send_int(g_interface_info->cmd_socket, getuid()) < 0) { + close(g_interface_info->cmd_socket); + free(g_interface_info); + g_interface_info = NULL; + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } + if (__ipc_send_int(g_interface_info->cmd_socket, getgid()) < 0) { + close(g_interface_info->cmd_socket); + free(g_interface_info); + g_interface_info = NULL; + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } +#endif + g_interface_info->event_socket = __create_socket(); + if (g_interface_info->event_socket < 0) { + TRACE_STRERROR("[CRITICAL] connect system error"); + close(g_interface_info->cmd_socket); + free(g_interface_info); + g_interface_info = NULL; + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } + // send a command + if (__ipc_send_int(g_interface_info->event_socket, + DP_CMD_SET_EVENT_SOCKET) < 0) { + close(g_interface_info->cmd_socket); + close(g_interface_info->event_socket); + free(g_interface_info); + g_interface_info = NULL; + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } +#ifndef SO_PEERCRED + // send PID. Not support SO_PEERCRED + if (__ipc_send_int + (g_interface_info->event_socket, getpid()) < 0) { + close(g_interface_info->cmd_socket); + close(g_interface_info->event_socket); + free(g_interface_info); + g_interface_info = NULL; + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } +#endif + + int ret = pthread_mutex_init(&g_interface_info->mutex, NULL); + if (ret != 0) { + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with %d.", ret); + __clear_interface(); + pthread_mutex_unlock(&g_clear_mutex); + return DP_ERROR_IO_ERROR; + } + + } + pthread_mutex_unlock(&g_clear_mutex); + if (g_interface_event_thread_id <= 0) { + // create thread here ( getting event_socket ) + pthread_attr_t thread_attr; + if (pthread_attr_init(&thread_attr) != 0) { + TRACE_STRERROR("[CRITICAL] pthread_attr_init"); + __disconnect_from_provider(); + return DP_ERROR_IO_ERROR; + } + if (pthread_attr_setdetachstate(&thread_attr, + PTHREAD_CREATE_DETACHED) != 0) { + TRACE_STRERROR + ("[CRITICAL] pthread_attr_setdetachstate"); + __disconnect_from_provider(); + return DP_ERROR_IO_ERROR; + } + if (pthread_create(&g_interface_event_thread_id, + &thread_attr, __dp_interface_event_manager, + g_interface_info) != 0) { + TRACE_STRERROR("[CRITICAL] pthread_create"); + __clear_interface(); + g_interface_event_thread_id = 0; + return DP_ERROR_IO_ERROR; + } + } + return DP_ERROR_NONE; +} + +static dp_error_type __check_connections() +{ + int ret = 0; + + if (g_interface_info == NULL) + if ((ret = __connect_to_provider()) != DP_ERROR_NONE) + return ret; + + if (g_interface_info == NULL || g_interface_info->cmd_socket < 0) { + TRACE_ERROR("[CHECK IPC]"); + return DP_ERROR_IO_ERROR; + } + return DP_ERROR_NONE; +} + +// used frequently +static dp_error_type __dp_interface_set_string + (const int id, const dp_command_type cmd, const char *value) +{ + dp_error_type errorcode = DP_ERROR_NONE; + if (value == NULL || strlen(value) <= 0) { + TRACE_ERROR("[CHECK url]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + int fd = g_interface_info->cmd_socket; + + // send commnad with ID + errorcode = __ipc_send_command_return(id, cmd); + if (errorcode == DP_ERROR_NONE) { + // send string + errorcode = __ipc_send_string(fd, value); + if (errorcode == DP_ERROR_NONE) { + // return from provider. + errorcode = __ipc_return(fd); + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +static dp_error_type __dp_interface_set_strings + (const int id, const dp_command_type cmd, const char **strings, + const unsigned count) +{ + dp_error_type errorcode = DP_ERROR_NONE; + if (strings == NULL || count == 0) { + TRACE_ERROR("[CHECK strings]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + int fd = g_interface_info->cmd_socket; + + // send commnad with ID + errorcode = __ipc_send_command_return(id, cmd); + if (errorcode == DP_ERROR_NONE) { + if (__ipc_send_int(fd, (int)count) == 0) { + int i = 0; + for (; i < count; i++) { + // send string + errorcode = __ipc_send_string(fd, strings[i]); + if (errorcode != DP_ERROR_NONE) + break; + } + } else { + errorcode = DP_ERROR_IO_ERROR; + } + if (errorcode == DP_ERROR_NONE) { + // return from provider. + errorcode = __ipc_return(fd); + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +static dp_error_type __dp_interface_get_string + (const int id, const dp_command_type cmd, char **value) +{ + int errorcode = DP_ERROR_NONE; + char *recv_str = NULL; + + if (value == NULL) { + TRACE_ERROR("[CHECK buffer]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + int fd = g_interface_info->cmd_socket; + + errorcode = __ipc_send_command_return(id, cmd); + if (errorcode == DP_ERROR_NONE) { + // getting state with ID from provider. + recv_str = __ipc_read_string(fd); + if (recv_str != NULL) + *value = recv_str; + else + errorcode = __get_standard_errorcode(DP_ERROR_IO_ERROR); + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +static dp_error_type __dp_interface_get_strings + (const int id, const dp_command_type cmd, const char **strings, + const unsigned length, char ***values, unsigned *count) +{ + int errorcode = DP_ERROR_NONE; + int i = 0; + int recv_str_index = 0; + char **recv_strings = NULL; + + if (values == NULL || count == NULL) { + TRACE_ERROR("[CHECK buffer]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + int fd = g_interface_info->cmd_socket; + + errorcode = __ipc_send_command_return(id, cmd); + if (errorcode == DP_ERROR_NONE) { + if (length > 0 && strings != NULL) { + if (__ipc_send_int(fd, (int)length) == 0) { + for (i = 0; i < length; i++) { + // send string + errorcode = __ipc_send_string(fd, strings[i]); + if (errorcode != DP_ERROR_NONE) + break; + } + } else { + errorcode = DP_ERROR_IO_ERROR; + } + } + if (errorcode == DP_ERROR_NONE) { + // return from provider. + errorcode = __ipc_return(fd); + } + } + if (errorcode == DP_ERROR_NONE) { + int recv_int = __ipc_read_int(fd); + if (recv_int < 0) { + errorcode = DP_ERROR_IO_ERROR; + } else if (recv_int > 0) { + recv_strings = (char **)calloc(recv_int, sizeof(char *)); + if (recv_strings == NULL) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + for (i = 0; i < recv_int; i++) { + char *recv_str = __ipc_read_string(fd); + if (recv_str == NULL) { + errorcode = + __get_standard_errorcode(DP_ERROR_IO_ERROR); + break; + } else { + recv_strings[recv_str_index++] = recv_str; + } + } + } + } + if (errorcode == DP_ERROR_NONE) { + *count = recv_str_index; + *values = recv_strings; + } else { + *count = 0; + for (i = 0; i < recv_str_index; i++) + free(recv_strings[i]); + free(recv_strings); + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +static dp_error_type __dp_interface_get_int + (const int id, dp_command_type cmd, int *value) +{ + int errorcode = DP_ERROR_NONE; + int recv_int = -1; + + if (value == NULL) { + TRACE_ERROR("[CHECK buffer]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + int fd = g_interface_info->cmd_socket; + + errorcode = __ipc_send_command_return(id, cmd); + if (errorcode == DP_ERROR_NONE) { + recv_int = __ipc_read_int(fd); + if (recv_int >= 0) { + *value = recv_int; + TRACE_DEBUG("ID : %d recv_int : %d", id, *value); + } else { + errorcode = __get_standard_errorcode(DP_ERROR_IO_ERROR); + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +static dp_error_type __dp_interface_set_int + (const int id, dp_command_type cmd, const int value) +{ + int errorcode = DP_ERROR_NONE; + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + int fd = g_interface_info->cmd_socket; + + // send commnad with ID + errorcode = __ipc_send_command_return(id, cmd); + if (errorcode == DP_ERROR_NONE) { + // send string + if (__ipc_send_int(fd, value) == 0) { + // return from provider. + errorcode = __ipc_return(fd); + } else { + errorcode = DP_ERROR_IO_ERROR; + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +static dp_error_type __dp_interface_set_raw_bundle + (const int id, const dp_command_type cmd, int type, const bundle_raw *b, int len) +{ + dp_error_type errorcode = DP_ERROR_NONE; + if (b == NULL) { + TRACE_ERROR("[CHECK bundle]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + int fd = g_interface_info->cmd_socket; + + // send commnad with ID + errorcode = __ipc_send_command_return(id, cmd); + if (errorcode == DP_ERROR_NONE) { + // send raw bundle + errorcode = __ipc_send_raw_bundle(fd, type, b, len); + if (errorcode == DP_ERROR_NONE) { + // return from provider. + errorcode = __ipc_return(fd); + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +static dp_error_type __dp_interface_get_raw_bundle + (const int id, const dp_command_type cmd, int type, bundle_raw **value, int *len) +{ + int errorcode = DP_ERROR_NONE; + + if (value == NULL) { + TRACE_ERROR("[CHECK buffer]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + int fd = g_interface_info->cmd_socket; + + errorcode = __ipc_send_command_return(id, cmd); + if (errorcode == DP_ERROR_NONE) { + errorcode = __ipc_send_int(fd, type); + if(errorcode == 0) { + errorcode = __ipc_return(g_interface_info->cmd_socket); + if (errorcode == DP_ERROR_NONE) { + *len = __ipc_read_bundle(fd, value); + if (*len <= 0) + errorcode = __get_standard_errorcode(DP_ERROR_IO_ERROR); + } else { + TRACE_ERROR("[ERROR] Fail to get result for sending type value]"); + errorcode = DP_ERROR_IO_ERROR; + } + } else { + TRACE_ERROR("[ERROR] Fail to send type]"); + errorcode = DP_ERROR_IO_ERROR; + } + } else { + TRACE_ERROR("[ERROR] Fail to send command]"); + errorcode = DP_ERROR_IO_ERROR; + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + + +static int _mkdir(const char *dir, mode_t mode) { + char tmp[256]; + char *p = NULL; + size_t len; + + snprintf(tmp, sizeof(tmp),"%s",dir); + len = strlen(tmp); + if(tmp[len - 1] == '/') + tmp[len - 1] = 0; + for(p = tmp + 1; *p; p++) + if(*p == '/') { + *p = 0; + mkdir(tmp, mode); + *p = '/'; + } + return mkdir(tmp, mode); +} + +static dp_error_type __create_dir(const char *install_dir) +{ + dp_error_type ret = DP_ERROR_NONE; + /* read/write/search permissions for owner and group, + * and with read/search permissions for others as fas as + * umask allows. */ + if (_mkdir(install_dir, S_IRWXU | S_IRWXG | S_IRWXO)) { + ret = DP_ERROR_PERMISSION_DENIED; + } + return ret; +} + + +/////////////////////// APIs ///////////////////////////////// + +int dp_interface_create(int *id) +{ + int errorcode = DP_ERROR_NONE; + int t_id = 0; + int index = -1; + + if (id == NULL) { + TRACE_ERROR("[CHECK id variable]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + pthread_mutex_lock(&g_function_mutex); + + if ((index = __get_empty_slot_index()) < 0) { + TRACE_ERROR + ("[ERROR] TOO_MANY_DOWNLOADS[%d]", MAX_DOWNLOAD_HANDLE); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_TOO_MANY_DOWNLOADS; + } + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + errorcode = __ipc_send_command_return(-1, DP_CMD_CREATE); + if (errorcode == DP_ERROR_NONE) { + // getting state with ID from provider. + t_id = __ipc_read_download_id(g_interface_info->cmd_socket); + if (t_id > 0) { + *id = t_id; + g_interface_slots[index].id = t_id; + g_interface_slots[index].callback.state = NULL; + g_interface_slots[index].callback.state_data = NULL; + g_interface_slots[index].callback.progress = NULL; + g_interface_slots[index].callback.progress_data = NULL; + errorcode = DP_ERROR_NONE; + } else { + errorcode = __get_standard_errorcode(DP_ERROR_IO_ERROR); + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + if (errorcode == DP_ERROR_NONE){ + // safely set the default path, maybe overwritten by the user later + const char* dest_path = DEFAULT_INSTALL_PATH; + if (dest_path){ + struct stat dir_state; + int stat_ret = stat(dest_path, &dir_state); + if (stat_ret != 0) { + __create_dir(dest_path); + } + dp_interface_set_destination(t_id, dest_path); + } + } + return __dp_interface_convert_errorcode(errorcode); +} + +int dp_interface_destroy(const int id) +{ + int index = -1; + int errorcode = DP_ERROR_NONE; + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + if ((index = __get_my_slot_index(id)) >= 0) { + g_interface_slots[index].id = 0; + g_interface_slots[index].callback.state = NULL; + g_interface_slots[index].callback.state_data = NULL; + g_interface_slots[index].callback.progress = NULL; + g_interface_slots[index].callback.progress_data = NULL; + } + errorcode = __ipc_send_command_return(id, DP_CMD_DESTROY); + if (errorcode == DP_ERROR_NONE) { + // after getting errorcode, send FREE to provider. + // send again DP_CMD_FREE with ID. + errorcode = __ipc_send_command + (g_interface_info->cmd_socket, id, DP_CMD_FREE); + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + + +int dp_interface_start(const int id) +{ + + int errorcode = DP_ERROR_NONE; + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + errorcode = __ipc_send_command_return(id, DP_CMD_START); + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +int dp_interface_pause(const int id) +{ + int errorcode = DP_ERROR_NONE; + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + errorcode = __ipc_send_command_return(id, DP_CMD_PAUSE); + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +int dp_interface_cancel(const int id) +{ + int errorcode = DP_ERROR_NONE; + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + errorcode = __ipc_send_command_return(id, DP_CMD_CANCEL); + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +int dp_interface_set_url(const int id, const char *url) +{ + return __dp_interface_set_string(id, DP_CMD_SET_URL, url); +} + +int dp_interface_get_url(const int id, char **url) +{ + return __dp_interface_get_string(id, DP_CMD_GET_URL, url); +} + +int dp_interface_set_network_type(const int id, int net_type) +{ + return __dp_interface_set_int(id, DP_CMD_SET_NETWORK_TYPE, + __dp_interface_convert_network_adaptor(net_type)); +} + +int dp_interface_get_network_type(const int id, int *net_type) +{ + if (net_type == NULL) { + TRACE_ERROR("[CHECK buffer]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + int network_type = DP_NETWORK_TYPE_ALL; + int ret = __dp_interface_get_int + (id, DP_CMD_GET_NETWORK_TYPE, &network_type); + if (ret == DOWNLOAD_ADAPTOR_ERROR_NONE) + *net_type = + __dp_interface_convert_network_provider(network_type); + return ret; +} + +int dp_interface_set_destination(const int id, const char *path) +{ + if (path && strlen(path)>0) { + return __dp_interface_set_string(id, DP_CMD_SET_DESTINATION, path); + } else { + return __dp_interface_convert_errorcode(DP_ERROR_NONE); + } +} + + +int dp_interface_get_destination(const int id, char **path) +{ + return __dp_interface_get_string(id, DP_CMD_GET_DESTINATION, path); +} + +int dp_interface_set_file_name(const int id, const char *file_name) +{ + return __dp_interface_set_string(id, DP_CMD_SET_FILENAME, file_name); +} + +int dp_interface_get_file_name(const int id, char **file_name) +{ + return __dp_interface_get_string(id, DP_CMD_GET_FILENAME, file_name); +} + +int dp_interface_set_ongoing_notification(const int id, int enable) +{ + return dp_interface_set_notification(id, enable); +} + +int dp_interface_set_notification(const int id, int enable) +{ + return __dp_interface_set_int(id, DP_CMD_SET_NOTIFICATION, enable); +} + +int dp_interface_get_ongoing_notification(const int id, int *enable) +{ + return dp_interface_get_notification(id, enable); +} + +int dp_interface_get_notification(const int id, int *enable) +{ + return __dp_interface_get_int(id, DP_CMD_GET_NOTIFICATION, enable); +} + +int dp_interface_get_downloaded_file_path(const int id, char **path) +{ + return __dp_interface_get_string(id, DP_CMD_GET_SAVED_PATH, path); +} + +int dp_interface_set_notification_extra_param(const int id, char *key, + char *value) +{ +#if 0 + DP_PRE_CHECK_ID; + + if (key == NULL || value == NULL) { + TRACE_ERROR("[CHECK param]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + // send commnad with ID + if (__ipc_send_command + (g_interface_info->cmd_socket, id, DP_CMD_SET_EXTRA_PARAM) + != DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + if (__ipc_send_string(g_interface_info->cmd_socket, key) + != DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + + if (__ipc_send_string(g_interface_info->cmd_socket, value) + != DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + + int errorcode = + __ipc_return(g_interface_info->cmd_socket); + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) { + TRACE_ERROR("[CHECK IO] (%d)", id); + __disconnect_from_provider(); + } + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +#endif + return DOWNLOAD_ADAPTOR_ERROR_NONE; +} + +int dp_interface_get_notification_extra_param(const int id, char **key, + char **value) +{ +#if 0 + int errorcode = DP_ERROR_NONE; + char *key_str = NULL; + char *value_str = NULL; + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + errorcode = __ipc_send_command_return(id, DP_CMD_GET_EXTRA_PARAM); + if (errorcode != DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); + } + // getting state with ID from provider. + key_str = __ipc_read_string(g_interface_info->cmd_socket); + if (key_str == NULL) { + pthread_mutex_unlock(&g_interface_info->mutex); + errorcode = __get_standard_errorcode(DP_ERROR_IO_ERROR); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); + } + + value_str = __ipc_read_string(g_interface_info->cmd_socket); + pthread_mutex_unlock(&g_interface_info->mutex); + if (value_str == NULL) { + free(key_str); + errorcode = __get_standard_errorcode(DP_ERROR_IO_ERROR); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); + } + + *key = key_str; + *value = value_str; + pthread_mutex_unlock(&g_function_mutex); +#endif + return DOWNLOAD_ADAPTOR_ERROR_NONE; +} + +int dp_interface_add_http_header_field(const int id, const char *field, + const char *value) +{ + int errorcode = DP_ERROR_NONE; + + DP_PRE_CHECK_ID; + + if (field == NULL || value == NULL) { + TRACE_ERROR("[CHECK field or value]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + // send commnad with ID + if (__ipc_send_command_return(id, DP_CMD_SET_HTTP_HEADER) != + DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + + if (__ipc_send_string(g_interface_info->cmd_socket, field) + != DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + + if (__ipc_send_string(g_interface_info->cmd_socket, value) + != DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + // return from provider. + errorcode = __ipc_return(g_interface_info->cmd_socket); + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) { + TRACE_ERROR("[CHECK IO] (%d)", id); + __disconnect_from_provider(); + } + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +int dp_interface_get_http_header_field(const int id, const char *field, + char **value) +{ + int errorcode = DP_ERROR_NONE; + char *str = NULL; + + DP_PRE_CHECK_ID; + + if (field == NULL || value == NULL) { + TRACE_ERROR("[CHECK field or value]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + if (__ipc_send_command_return(id, DP_CMD_GET_HTTP_HEADER) != + DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + + if (__ipc_send_string(g_interface_info->cmd_socket, field) + != DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + + errorcode = __ipc_return(g_interface_info->cmd_socket); + if (errorcode == DP_ERROR_NONE) { + // getting string with ID from provider. + str = __ipc_read_string(g_interface_info->cmd_socket); + if (str != NULL) { + *value = str; + } else { + errorcode = __get_standard_errorcode(DP_ERROR_IO_ERROR); + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR || str == NULL) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +int dp_interface_get_http_header_field_list(const int id, char ***fields, + int *length) +{ + return __dp_interface_get_strings(id, DP_CMD_GET_HTTP_HEADER_LIST, + NULL, 0, fields, (unsigned *)length); +} + +int dp_interface_remove_http_header_field(const int id, + const char *field) +{ + int errorcode = DP_ERROR_NONE; + + DP_PRE_CHECK_ID; + + if (field == NULL) { + TRACE_ERROR("[CHECK field]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + // send commnad with ID + if (__ipc_send_command_return(id, DP_CMD_DEL_HTTP_HEADER) != + DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + if (__ipc_send_string(g_interface_info->cmd_socket, field) + != DP_ERROR_NONE) { + pthread_mutex_unlock(&g_interface_info->mutex); + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return DOWNLOAD_ADAPTOR_ERROR_IO_ERROR; + } + // return from provider. + errorcode = __ipc_return(g_interface_info->cmd_socket); + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) { + TRACE_ERROR("[CHECK IO] (%d)", id); + __disconnect_from_provider(); + } + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +int dp_interface_set_state_changed_cb(const int id, + dp_interface_state_changed_cb callback, void *user_data) +{ + int errorcode = DP_ERROR_NONE; + int index = -1; + + if (callback == NULL) { + dp_interface_unset_state_changed_cb(id); + return DOWNLOAD_ADAPTOR_ERROR_NONE; + } + + errorcode = + __dp_interface_set_int(id, DP_CMD_SET_STATE_CALLBACK, 1); + if (errorcode == DOWNLOAD_ADAPTOR_ERROR_NONE) { + pthread_mutex_lock(&g_function_mutex); + // search same info in array. + index = __get_my_slot_index(id); + if (index < 0) { + index = __get_empty_slot_index(); + if (index >= 0) { + g_interface_slots[index].id = id; + } else { + TRACE_ERROR("[ERROR] TOO_MANY_DOWNLOADS [%d]", + MAX_DOWNLOAD_HANDLE); + errorcode = DOWNLOAD_ADAPTOR_ERROR_TOO_MANY_DOWNLOADS; + } + } + if (index >= 0) { + g_interface_slots[index].callback.state = callback; + g_interface_slots[index].callback.state_data = user_data; + } + pthread_mutex_unlock(&g_function_mutex); + } + return errorcode; +} + +int dp_interface_unset_state_changed_cb(const int id) +{ + int errorcode = DP_ERROR_NONE; + int index = -1; + + errorcode = + __dp_interface_set_int(id, DP_CMD_SET_STATE_CALLBACK, 0); + // clear by force although failed to clear in provider + pthread_mutex_lock(&g_function_mutex); + if ((index = __get_my_slot_index(id)) >= 0) { + g_interface_slots[index].callback.state = NULL; + g_interface_slots[index].callback.state_data = NULL; + } + pthread_mutex_unlock(&g_function_mutex); + return errorcode; +} + +int dp_interface_set_progress_cb(const int id, + dp_interface_progress_cb callback, void *user_data) +{ + int errorcode = DP_ERROR_NONE; + int index = -1; + + if (callback == NULL) { + dp_interface_unset_progress_cb(id); + return DOWNLOAD_ADAPTOR_ERROR_NONE; + } + + errorcode = + __dp_interface_set_int(id, DP_CMD_SET_PROGRESS_CALLBACK, 1); + if (errorcode == DOWNLOAD_ADAPTOR_ERROR_NONE) { + pthread_mutex_lock(&g_function_mutex); + // search same info in array. + index = __get_my_slot_index(id); + if (index < 0) { + index = __get_empty_slot_index(); + if (index >= 0) { + g_interface_slots[index].id = id; + } else { + TRACE_ERROR("[ERROR] TOO_MANY_DOWNLOADS [%d]", + MAX_DOWNLOAD_HANDLE); + errorcode = DOWNLOAD_ADAPTOR_ERROR_TOO_MANY_DOWNLOADS; + } + } + if (index >= 0) { + g_interface_slots[index].callback.progress = callback; + g_interface_slots[index].callback.progress_data = user_data; + } + pthread_mutex_unlock(&g_function_mutex); + } + return errorcode; +} + +int dp_interface_unset_progress_cb(const int id) +{ + int errorcode = DP_ERROR_NONE; + int index = -1; + + errorcode = + __dp_interface_set_int(id, DP_CMD_SET_PROGRESS_CALLBACK, 0); + // clear by force although failed to clear in provider + pthread_mutex_lock(&g_function_mutex); + if ((index = __get_my_slot_index(id)) >= 0) { + g_interface_slots[index].callback.progress = NULL; + g_interface_slots[index].callback.progress_data = NULL; + } + pthread_mutex_unlock(&g_function_mutex); + return errorcode; +} + +int dp_interface_get_state(const int id, int *state) +{ + if (state == NULL) { + TRACE_ERROR("[CHECK buffer]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + int statecode = DOWNLOAD_ADPATOR_STATE_NONE; + int ret = __dp_interface_get_int(id, DP_CMD_GET_STATE, &statecode); + if (ret == DOWNLOAD_ADAPTOR_ERROR_NONE) + *state = __dp_interface_convert_state(statecode); + return ret; +} + +int dp_interface_get_temp_path(const int id, char **temp_path) +{ + return __dp_interface_get_string + (id, DP_CMD_GET_TEMP_SAVED_PATH, temp_path); +} + +int dp_interface_get_content_name(const int id, char **content_name) +{ + return __dp_interface_get_string + (id, DP_CMD_GET_CONTENT_NAME, content_name); +} + +int dp_interface_get_content_size(const int id, + unsigned long long *content_size) +{ + int errorcode = DP_ERROR_NONE; + + if (content_size == NULL) { + TRACE_ERROR("[CHECK buffer content_size]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + DP_PRE_CHECK_ID; + + pthread_mutex_lock(&g_function_mutex); + + DP_CHECK_CONNECTION; + + pthread_mutex_lock(&g_interface_info->mutex); + + DP_CHECK_PROVIDER_STATUS; + + errorcode = + __ipc_send_command_return(id, DP_CMD_GET_TOTAL_FILE_SIZE); + if (errorcode == DP_ERROR_NONE) { + // getting content_size from provider. + if (__ipc_read_custom_type(g_interface_info->cmd_socket, + content_size, sizeof(unsigned long long)) < 0) { + errorcode = __get_standard_errorcode(DP_ERROR_IO_ERROR); + } + } + pthread_mutex_unlock(&g_interface_info->mutex); + if (errorcode == DP_ERROR_IO_ERROR) + __disconnect_from_provider(); + pthread_mutex_unlock(&g_function_mutex); + return __dp_interface_convert_errorcode(errorcode); +} + +int dp_interface_get_mime_type(const int id, char **mime_type) +{ + return __dp_interface_get_string + (id, DP_CMD_GET_MIME_TYPE, mime_type); +} + +int dp_interface_set_auto_download(const int id, int enable) +{ + return __dp_interface_set_int(id, DP_CMD_SET_AUTO_DOWNLOAD, enable); +} + +int dp_interface_get_auto_download(const int id, int *enable) +{ + return __dp_interface_get_int(id, DP_CMD_GET_AUTO_DOWNLOAD, enable); +} + +int dp_interface_get_error(const int id, int *error) +{ + if (error == NULL) { + TRACE_ERROR("[CHECK buffer error]"); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + int errorcode = DP_ERROR_NONE; + int ret = __dp_interface_get_int(id, DP_CMD_GET_ERROR, &errorcode); + if (ret == DOWNLOAD_ADAPTOR_ERROR_NONE) + *error = __dp_interface_convert_errorcode(errorcode); + return ret; +} + +int dp_interface_get_http_status(const int id, int *http_status) +{ + return __dp_interface_get_int + (id, DP_CMD_GET_HTTP_STATUS, http_status); +} + +int dp_interface_add_noti_extra(const int id, const char *key, + const char **values, const unsigned length) +{ + int i = 0; + + if (key == NULL || values == NULL) { + TRACE_ERROR("[CHECK key/values] (%d)", id); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + if (length <= 0) { + TRACE_ERROR("[CHECK legnth] (%d)", id); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + char *strings[length + 1]; + strings[0] = (char *)key; + for (i = 0; i < length; i++) { + strings[i + 1] = (char *)values[i]; + } + return __dp_interface_set_strings(id, DP_CMD_ADD_EXTRA_PARAM, + (const char **)strings, length + 1); +} + +int dp_interface_get_noti_extra_values(const int id, const char *key, + char ***values, unsigned *length) +{ + return __dp_interface_get_strings(id, DP_CMD_GET_EXTRA_PARAM, + &key, 1, values, length); +} + +int dp_interface_remove_noti_extra_key(const int id, const char *key) +{ + return __dp_interface_set_string + (id, DP_CMD_REMOVE_EXTRA_PARAM, key); +} + +int dp_interface_set_notification_bundle(const int id, int type, bundle *b) +{ + bundle_raw *r = NULL; + int len = 0; + int retval = -1; + retval = bundle_encode_raw(b, &r, &len); + if (retval == 0) + retval = __dp_interface_set_raw_bundle(id, DP_CMD_SET_NOTIFICATION_BUNDLE, type, r, len); + else { + bundle_free_encoded_rawdata(&r); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + bundle_free_encoded_rawdata(&r); + return retval; +} + +int dp_interface_get_notification_bundle(const int id, int type, bundle **b) +{ + bundle_raw *r = NULL; + int len = 0; + download_adaptor_error_e error = DOWNLOAD_ADAPTOR_ERROR_NONE; + + if (b == NULL) { + TRACE_ERROR("[CHECK bundle] (%d)", id); + return DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER; + } + + error = __dp_interface_get_raw_bundle(id, DP_CMD_GET_NOTIFICATION_BUNDLE, type, &r, &len); + if (error == DOWNLOAD_ADAPTOR_ERROR_NONE) { + *b = bundle_decode_raw(r, len); + } + bundle_free_encoded_rawdata(&r); + return error; +} + +int dp_interface_set_notification_title(const int id, const char *title) +{ + return __dp_interface_set_string(id, DP_CMD_SET_NOTIFICATION_TITLE, title); +} + +int dp_interface_get_notification_title(const int id, char **title) +{ + return __dp_interface_get_string(id, DP_CMD_GET_NOTIFICATION_TITLE, title); +} + +int dp_interface_set_notification_description(const int id, const char *description) +{ + return __dp_interface_set_string(id, DP_CMD_SET_NOTIFICATION_DESCRIPTION, description); +} + +int dp_interface_get_notification_description(const int id, char **description) +{ + return __dp_interface_get_string(id, DP_CMD_GET_NOTIFICATION_DESCRIPTION, description); +} + +int dp_interface_set_notification_type(const int id, int type) +{ + return __dp_interface_set_int(id, DP_CMD_SET_NOTIFICATION_TYPE, type); +} + +int dp_interface_get_notification_type(const int id, int *type) +{ + return __dp_interface_get_int(id, DP_CMD_GET_NOTIFICATION_TYPE, type); +} diff --git a/provider-interface/download-provider-interface.pc.in b/provider-interface/download-provider-interface.pc.in new file mode 100644 index 0000000..b6e579c --- /dev/null +++ b/provider-interface/download-provider-interface.pc.in @@ -0,0 +1,8 @@ +# Package Information + +Name: @PROJECT_NAME@ +Description: @PACKAGE_DESCRIPTION@ +Version: @VERSION@ +Requires: @PC_REQUIRED@ +Libs: -L@LIB_INSTALL_DIR@ -l@PROJECT_NAME@ +Cflags: -I/usr/include/@PKG_NAME@ diff --git a/provider-interface/include/download-provider-interface.h b/provider-interface/include/download-provider-interface.h new file mode 100755 index 0000000..8d1ef06 --- /dev/null +++ b/provider-interface/include/download-provider-interface.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DOWNLOAD_PROVIDER_INTERFACE_H__ +#define __DOWNLOAD_PROVIDER_INTERFACE_H__ + +#ifndef EXPORT_API +#define EXPORT_API __attribute__((visibility("default"))) +#endif + +#include <tizen.h> +#include <bundle.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +// sync with url-download +typedef enum +{ + DOWNLOAD_ADPATOR_STATE_NONE, + DOWNLOAD_ADPATOR_STATE_READY, + DOWNLOAD_ADPATOR_STATE_QUEUED, + DOWNLOAD_ADPATOR_STATE_DOWNLOADING, + DOWNLOAD_ADPATOR_STATE_PAUSED, + DOWNLOAD_ADPATOR_STATE_COMPLETED, + DOWNLOAD_ADPATOR_STATE_FAILED, + DOWNLOAD_ADPATOR_STATE_CANCELED, +} download_adaptor_state_e; + +typedef enum +{ + DOWNLOAD_ADAPTOR_NETWORK_DATA_NETWORK, + DOWNLOAD_ADAPTOR_NETWORK_WIFI, + DOWNLOAD_ADAPTOR_NETWORK_WIFI_DIRECT, + DOWNLOAD_ADAPTOR_NETWORK_ALL +} download_adaptor_network_type_e ; + +typedef enum +{ + DOWNLOAD_ADAPTOR_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ + DOWNLOAD_ADAPTOR_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ + DOWNLOAD_ADAPTOR_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory */ + DOWNLOAD_ADAPTOR_ERROR_NETWORK_UNREACHABLE = TIZEN_ERROR_NETWORK_UNREACHABLE, /**< Network is unreachable */ + DOWNLOAD_ADAPTOR_ERROR_CONNECTION_TIMED_OUT = TIZEN_ERROR_CONNECTION_TIME_OUT, /**< Http session time-out */ + DOWNLOAD_ADAPTOR_ERROR_NO_SPACE = TIZEN_ERROR_FILE_NO_SPACE_ON_DEVICE, /**< No space left on device */ + DOWNLOAD_ADAPTOR_ERROR_FIELD_NOT_FOUND = TIZEN_ERROR_KEY_NOT_AVAILABLE, /**< Specified field not found */ + DOWNLOAD_ADAPTOR_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */ + DOWNLOAD_ADAPTOR_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< Not supported */ + DOWNLOAD_ADAPTOR_ERROR_INVALID_STATE = TIZEN_ERROR_WEB_CLASS | 0x21, /**< Invalid state */ + DOWNLOAD_ADAPTOR_ERROR_CONNECTION_FAILED = TIZEN_ERROR_WEB_CLASS | 0x22, /**< Connection failed */ + DOWNLOAD_ADAPTOR_ERROR_INVALID_URL = TIZEN_ERROR_WEB_CLASS | 0x24, /**< Invalid URL */ + DOWNLOAD_ADAPTOR_ERROR_INVALID_DESTINATION = TIZEN_ERROR_WEB_CLASS | 0x25, /**< Invalid destination */ + DOWNLOAD_ADAPTOR_ERROR_TOO_MANY_DOWNLOADS = TIZEN_ERROR_WEB_CLASS | 0x26, /**< Full of available simultaneous downloads */ + DOWNLOAD_ADAPTOR_ERROR_QUEUE_FULL = TIZEN_ERROR_WEB_CLASS | 0x27, /**< Full of available downloading items from server*/ + DOWNLOAD_ADAPTOR_ERROR_ALREADY_COMPLETED = TIZEN_ERROR_WEB_CLASS | 0x28, /**< The download is already completed */ + DOWNLOAD_ADAPTOR_ERROR_FILE_ALREADY_EXISTS = TIZEN_ERROR_WEB_CLASS | 0x29, /**< It is failed to rename the downloaded file */ + DOWNLOAD_ADAPTOR_ERROR_CANNOT_RESUME = TIZEN_ERROR_WEB_CLASS | 0x2a, /**< It cannot resume */ + DOWNLOAD_ADAPTOR_ERROR_TOO_MANY_REDIRECTS = TIZEN_ERROR_WEB_CLASS | 0x30, /**< In case of too may redirects from http response header*/ + DOWNLOAD_ADAPTOR_ERROR_UNHANDLED_HTTP_CODE = TIZEN_ERROR_WEB_CLASS | 0x31, /**< The download cannot handle the http status value */ + DOWNLOAD_ADAPTOR_ERROR_REQUEST_TIMEOUT = TIZEN_ERROR_WEB_CLASS | 0x32, /**< There are no action after client create a download id*/ + DOWNLOAD_ADAPTOR_ERROR_RESPONSE_TIMEOUT = TIZEN_ERROR_WEB_CLASS | 0x33, /**< It does not call start API in some time although the download is created*/ + DOWNLOAD_ADAPTOR_ERROR_SYSTEM_DOWN = TIZEN_ERROR_WEB_CLASS | 0x34, /**< There are no response from client after rebooting download daemon*/ + DOWNLOAD_ADAPTOR_ERROR_ID_NOT_FOUND = TIZEN_ERROR_WEB_CLASS | 0x35, /**< The download id is not existed in download service module*/ + DOWNLOAD_ADAPTOR_ERROR_INVALID_NETWORK_TYPE = TIZEN_ERROR_WEB_CLASS | 0x36, /**< error when setting network bonding and network all is not set */ + DOWNLOAD_ADAPTOR_ERROR_NO_DATA = TIZEN_ERROR_NO_DATA, /**< No data because the set API is not called */ + DOWNLOAD_ADAPTOR_ERROR_IO_ERROR = TIZEN_ERROR_IO_ERROR , /**< Internal I/O error */ +} download_adaptor_error_e; + +// sync types with url-download.. +typedef void (*dp_interface_state_changed_cb) (int id, int state, void *user_data); +typedef void (*dp_interface_progress_cb) (int id, unsigned long long received, void *user_data); + +EXPORT_API int dp_interface_set_state_changed_cb + (const int id, dp_interface_state_changed_cb callback, void *user_data); +EXPORT_API int dp_interface_unset_state_changed_cb(int id); +EXPORT_API int dp_interface_set_progress_cb + (const int id, dp_interface_progress_cb callback, void *user_data); +EXPORT_API int dp_interface_unset_progress_cb(const int id); + +EXPORT_API int dp_interface_create(int *id); +EXPORT_API int dp_interface_destroy(const int id); + +EXPORT_API int dp_interface_start(const int id); +EXPORT_API int dp_interface_pause(const int id); +EXPORT_API int dp_interface_cancel(const int id); + +EXPORT_API int dp_interface_set_url(const int id, const char *url); +EXPORT_API int dp_interface_get_url(const int id, char **url); +EXPORT_API int dp_interface_set_network_type(const int id, int net_type); +EXPORT_API int dp_interface_get_network_type(const int id, int *net_type); +EXPORT_API int dp_interface_set_destination(const int id, const char *path); +EXPORT_API int dp_interface_get_destination(const int id, char **path); +EXPORT_API int dp_interface_set_file_name(const int id, const char *file_name); +EXPORT_API int dp_interface_get_file_name(const int id, char **file_name); +EXPORT_API int dp_interface_set_notification(const int id, int enable); +EXPORT_API int dp_interface_get_notification(const int id, int *enable); +EXPORT_API int dp_interface_set_notification_extra_param(const int id, char *key, char *value); +EXPORT_API int dp_interface_get_notification_extra_param(const int id, char **key, char **value); +EXPORT_API int dp_interface_get_downloaded_file_path(const int id, char **path); +EXPORT_API int dp_interface_get_mime_type(const int id, char **mime_type); +EXPORT_API int dp_interface_set_auto_download(const int id, int enable); +EXPORT_API int dp_interface_get_auto_download(const int id, int *enable); +EXPORT_API int dp_interface_add_http_header_field(const int id, const char *field, const char *value); +EXPORT_API int dp_interface_get_http_header_field(const int id, const char *field, char **value); +EXPORT_API int dp_interface_get_http_header_field_list(const int id, char ***fields, int *length); +EXPORT_API int dp_interface_remove_http_header_field(const int id, const char *field); +EXPORT_API int dp_interface_get_state(const int id, int *state); +EXPORT_API int dp_interface_get_temp_path(const int id, char **temp_path); +EXPORT_API int dp_interface_get_content_name(const int id, char **content_name); +EXPORT_API int dp_interface_get_content_size(const int id, unsigned long long *content_size); +EXPORT_API int dp_interface_get_error(const int id, int *error); +EXPORT_API int dp_interface_get_http_status(const int id, int *http_status); + +// Notification Extra Param +// N values per a key +EXPORT_API int dp_interface_add_noti_extra(const int id, const char *key, const char **values, const unsigned length); +EXPORT_API int dp_interface_get_noti_extra_values(const int id, const char *key, char ***values, unsigned *length); +EXPORT_API int dp_interface_remove_noti_extra_key(const int id, const char *key); + +EXPORT_API int dp_interface_set_notification_bundle(const int id, int type, bundle *b); +EXPORT_API int dp_interface_get_notification_bundle(const int id, int type, bundle **b); +EXPORT_API int dp_interface_set_notification_title(const int id, const char *title); +EXPORT_API int dp_interface_get_notification_title(const int id, char **title); +EXPORT_API int dp_interface_set_notification_description(const int id, const char *description); +EXPORT_API int dp_interface_get_notification_description(const int id, char **description); +EXPORT_API int dp_interface_set_notification_type(const int id, int type); +EXPORT_API int dp_interface_get_notification_type(const int id, int *type); +#ifdef __cplusplus +} +#endif + +#endif /* __DOWNLOAD_PROVIDER_INTERFACE_H__ */ diff --git a/provider/CMakeLists.txt b/provider/CMakeLists.txt new file mode 100755 index 0000000..fef1222 --- /dev/null +++ b/provider/CMakeLists.txt @@ -0,0 +1,71 @@ + +## PROJECT NAME +PROJECT(${PKG_NAME} C) + +IF("${CMAKE_BUILD_TYPE}" STREQUAL "") + SET(CMAKE_BUILD_TYPE "Debug") +ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "") +MESSAGE("Build type: ${CMAKE_BUILD_TYPE}") + +INCLUDE(FindPkgConfig) + +pkg_check_modules(dp2_pkgs REQUIRED glib-2.0 + gobject-2.0 + sqlite3 + capi-appfw-app-manager + capi-network-connection + wifi-direct + notification + appsvc + bundle + libsmack + libsystemd-daemon + libtzplatform-config + dlog) + +FOREACH(flag ${dp2_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +## INCLUDES +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/agent/include) + +set(DP2_LINK_LIBRARIES ${GLIB-2_LIBRARIES} + ${GOBJECT-2_LIBRARIES} + pthread + capi-appfw-app-manager + ) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") +SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -Wall") + +IF(DEFINED DATABASE_SCHEMA_FILE) + ADD_DEFINITIONS(-DDATABASE_SCHEMA_FILE=\"${DATABASE_SCHEMA_FILE}\") +ENDIF(DEFINED DATABASE_SCHEMA_FILE) + +IF(DEFINED IMAGE_DIR) + ADD_DEFINITIONS(-DIMAGE_DIR=\"${IMAGE_DIR}\") +ENDIF(DEFINED IMAGE_DIR) + +IF(DEFINED LOCALE_DIR) + ADD_DEFINITIONS(-DPKG_NAME=\"${PKG_NAME}\") + ADD_DEFINITIONS(-DLOCALE_DIR=\"${LOCALE_DIR}\") +ENDIF(DEFINED LOCALE_DIR) + + +ADD_EXECUTABLE(${PROJECT_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-pid.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-socket.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-slots.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-network.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-db.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-request.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-da-interface.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-thread-request.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-thread-queue.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-notification.c + ${CMAKE_CURRENT_SOURCE_DIR}/download-provider-main.c ) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${dp2_pkgs_LDFLAGS} ${DP2_LINK_LIBRARIES} -ldl) +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${BIN_INSTALL_DIR}) + +INSTALL(FILES include/download-provider-defs.h DESTINATION ${INCLUDE_INSTALL_DIR}/${PKG_NAME}) diff --git a/provider/download-provider-da-interface.c b/provider/download-provider-da-interface.c new file mode 100755 index 0000000..87eb375 --- /dev/null +++ b/provider/download-provider-da-interface.c @@ -0,0 +1,903 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <time.h> +#include <sys/time.h> +#include <string.h> +#include <dlfcn.h> // dlopen + +#include <sys/smack.h> +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" +#include "download-provider-socket.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-notification.h" +#include "download-provider-request.h" +#include "download-provider-network.h" +#include "download-provider-da-interface.h" + +#include "download-agent-defs.h" +#include "download-agent-interface.h" + +#define DP_SDCARD_MNT_POINT "/opt/storage/sdcard" + +static void *g_da_handle = NULL; +static int (*download_agent_init)(da_client_cb_t *) = NULL; // int da_init(da_client_cb_t *da_client_callback); +static int (*download_agent_deinit)() = NULL; // int da_deinit(); +static int (*download_agent_is_alive)(int) = NULL; // int da_is_valid_download_id(int download_id); +static int (*download_agent_suspend)(int) = NULL; // int da_suspend_download(int download_id); +static int (*download_agent_resume)(int) = NULL; // int da_resume_download(int download_id); +static int (*download_agent_cancel)(int) = NULL; // int da_cancel_download(int download_id); +static int (*download_agent_start)(const char *, extension_data_t *, int *) = NULL; // int da_start_download_with_extension(const char *url, extension_data_t *ext_data, int *download_id); + + +int dp_is_file_exist(const char *file_path) +{ + struct stat file_state; + int stat_ret; + + if (file_path == NULL) { + TRACE_ERROR("[NULL-CHECK] file path is NULL"); + return -1; + } + + stat_ret = stat(file_path, &file_state); + + if (stat_ret == 0) + if (file_state.st_mode & S_IFREG) + return 0; + + return -1; +} + +static int __change_error(int err) +{ + int ret = DP_ERROR_NONE; + switch (err) { + case DA_RESULT_OK: + ret = DP_ERROR_NONE; + break; + case DA_ERR_INVALID_ARGUMENT: + ret = DP_ERROR_INVALID_PARAMETER; + break; + case DA_ERR_FAIL_TO_MEMALLOC: + ret = DP_ERROR_OUT_OF_MEMORY; + break; + case DA_ERR_UNREACHABLE_SERVER: + ret = DP_ERROR_NETWORK_UNREACHABLE; + break; + case DA_ERR_HTTP_TIMEOUT: + ret = DP_ERROR_CONNECTION_TIMED_OUT; + break; + case DA_ERR_DISK_FULL: + ret = DP_ERROR_NO_SPACE; + break; + case DA_ERR_INVALID_STATE: + ret = DP_ERROR_INVALID_STATE; + break; + case DA_ERR_NETWORK_FAIL: + ret = DP_ERROR_CONNECTION_FAILED; + break; + case DA_ERR_INVALID_URL: + ret = DP_ERROR_INVALID_URL; + break; + case DA_ERR_INVALID_INSTALL_PATH: + ret = DP_ERROR_INVALID_DESTINATION; + break; + case DA_ERR_ALREADY_MAX_DOWNLOAD: + ret = DP_ERROR_TOO_MANY_DOWNLOADS; + break; + case DA_ERR_FAIL_TO_CREATE_THREAD: + case DA_ERR_FAIL_TO_OBTAIN_MUTEX: + case DA_ERR_FAIL_TO_ACCESS_FILE: + case DA_ERR_FAIL_TO_GET_CONF_VALUE: + case DA_ERR_FAIL_TO_ACCESS_STORAGE: + default: + ret = DP_ERROR_IO_ERROR; + break; + } + return ret; +} + +static void __download_info_cb(user_download_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + dp_request_slots *request_slot = (dp_request_slots *) user_data; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + return ; + } + CLIENT_MUTEX_LOCK(&request_slot->mutex); + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + dp_request *request = request_slot->request; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + int request_id = request->id; + + // update info before sending event + if (info->tmp_saved_path != NULL) { + + TRACE_SECURE_DEBUG("[STARTED][%d] [%s]", request_id, info->tmp_saved_path); + int conds_count = 6; // id + tmp_saved_path + file_size + content_name + etag + int conds_index = 0; + db_conds_list_fmt conds_p[conds_count]; + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + + conds_p[conds_index].column = DP_DB_COL_TMP_SAVED_PATH; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->tmp_saved_path; + conds_index++; + if (info->file_type != NULL) { + conds_p[conds_index].column = DP_DB_COL_MIMETYPE; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->file_type; + conds_index++; + } + if (info->content_name != NULL) { + conds_p[conds_index].column = DP_DB_COL_CONTENT_NAME; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->content_name; + conds_index++; + } + if (info->etag != NULL) { + conds_p[conds_index].column = DP_DB_COL_ETAG; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->etag; + conds_index++; + } + if (info->file_size > 0) { + request->file_size = info->file_size; + conds_p[conds_index].column = DP_DB_COL_CONTENT_SIZE; + conds_p[conds_index].type = DP_DB_COL_TYPE_INT64; + conds_p[conds_index].value = &info->file_size; + conds_index++; + } + + int check_id = dp_db_get_int_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ID); + if (check_id == request_id) { // update + if (dp_db_update_columns(request_id, DP_DB_TABLE_DOWNLOAD_INFO, conds_index, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + request->error = DP_ERROR_NO_SPACE; + TRACE_ERROR("[SQLITE_FULL][%d]", request_id); + if (dp_cancel_agent_download(request->agent_id) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", request_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + } + } else { // insert + conds_p[conds_index].column = DP_DB_COL_ID; + conds_p[conds_index].type = DP_DB_COL_TYPE_INT; + conds_p[conds_index].value = &request_id; + conds_index++; + if (dp_db_insert_columns(DP_DB_TABLE_DOWNLOAD_INFO, conds_index, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + request->error = DP_ERROR_NO_SPACE; + TRACE_ERROR("[SQLITE_FULL][%d]", request_id); + if (dp_cancel_agent_download(request->agent_id) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", request_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + } + } + + } + + request->ip_changed = 0; + request->state = DP_STATE_DOWNLOADING; + request->error = DP_ERROR_NONE; + dp_request_state_response(request); + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); +} + +static void __progress_cb(user_progress_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + dp_request_slots *request_slot = (dp_request_slots *) user_data; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + return ; + } + CLIENT_MUTEX_LOCK(&request_slot->mutex); + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + dp_request *request = request_slot->request; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + if (request->state == DP_STATE_DOWNLOADING) { + request->received_size = info->received_size; + time_t tt = time(NULL); + struct tm *localTime = localtime(&tt); + // send event every 1 second. + if (request->progress_lasttime != localTime->tm_sec) { + request->progress_lasttime = localTime->tm_sec; + if (request->auto_notification) { + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } else { + int noti_type = dp_db_get_int_column(request->id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if(noti_type == DP_NOTIFICATION_TYPE_ALL) + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } + + if (request->progress_cb && request->group != NULL && + request->group->event_socket >= 0 && + request->received_size > 0) + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, + request->received_size); + } + } + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); +} + +static int __is_transmute_smack(char *path) +{ + char *dir_label = NULL; + int ret = -1; + if (smack_getlabel(path, &dir_label, SMACK_LABEL_TRANSMUTE) == 0 && + dir_label != NULL) { + if (strncmp(dir_label, "TRUE", strlen(dir_label)) == 0) + ret = 0; + } + free(dir_label); + return ret; +} + +static dp_error_type __set_dir_smack_label(char *smack_label, char *dir_path, char *saved_path) +{ + if (smack_label == NULL || dir_path== NULL || saved_path == NULL) + return DP_ERROR_PERMISSION_DENIED; + + int is_setted_dir_label = 0; + dp_error_type errorcode = DP_ERROR_NONE; + + TRACE_SECURE_INFO("[PARSE] dir path [%s]", dir_path); + if (__is_transmute_smack(dir_path) < 0) { + TRACE_DEBUG("[SMACK] no transmute"); + } else { + char *dir_label = NULL; + if (smack_getlabel(dir_path, &dir_label, + SMACK_LABEL_ACCESS) == 0) { + if (smack_have_access(smack_label, dir_label, "t") > 0) { + if (smack_setlabel(saved_path, dir_label, + SMACK_LABEL_ACCESS) != 0) { + TRACE_SECURE_ERROR("[SMACK ERROR] label:%s", + dir_label); + errorcode = DP_ERROR_PERMISSION_DENIED; + } else { + is_setted_dir_label = 1; + } + } else { + TRACE_SECURE_ERROR("[SMACK ERROR] access:%s/%s", + smack_label, dir_label); + errorcode = DP_ERROR_PERMISSION_DENIED; + } + } else { + TRACE_SECURE_ERROR("[SMACK ERROR] no label:", dir_path); + errorcode = DP_ERROR_PERMISSION_DENIED; + } + free(dir_label); + } + if (is_setted_dir_label == 0 && + smack_setlabel(saved_path, smack_label, + SMACK_LABEL_ACCESS) != 0) { + TRACE_SECURE_ERROR("[SMACK ERROR] label:%s", smack_label); + errorcode = DP_ERROR_PERMISSION_DENIED; + // remove file. + if (dp_is_file_exist(saved_path) == 0) + unlink(saved_path); + } + return errorcode; +} + +static dp_error_type __set_file_permission_to_client(dp_request *request, char *saved_path) +{ + dp_error_type errorcode = DP_ERROR_NONE; + char *str = NULL; + char *smack_label = NULL; + str = strrchr(saved_path, '/'); + dp_credential cred; + if (request->group == NULL) { + cred.uid = dp_db_cond_get_int(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_UID, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + cred.gid = dp_db_cond_get_int(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_GID, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + } else { + cred = request->group->credential; + } + TRACE_DEBUG + ("[chown][%d] [%d][%d]", request->id, cred.uid, cred.gid); + if (chown(saved_path, cred.uid, cred.gid) < 0) + TRACE_STRERROR("[ERROR][%d] chown", request->id); + if (chmod(saved_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) + TRACE_STRERROR("[ERROR][%d] chmod", request->id); + if (dp_is_smackfs_mounted() == 1) { + if (request->group == NULL) { + // get smack_label from sql + smack_label = + dp_db_cond_get_text(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_SMACK_LABEL, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + } else { + smack_label = dp_strdup(request->group->smack_label); + } + if (smack_label == NULL) { + TRACE_SECURE_ERROR("[SMACK][%d] no label", request->id); + errorcode = DP_ERROR_PERMISSION_DENIED; + } else { + size_t len = str - (saved_path); + char *dir_path = (char *)calloc(len + 1, sizeof(char)); + if (dir_path != NULL) { + strncpy(dir_path, saved_path, len); + errorcode = + __set_dir_smack_label(smack_label, dir_path, + saved_path); + free(dir_path); + } else { + TRACE_SECURE_ERROR("[ERROR] calloc"); + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + free(smack_label); + } + } + return errorcode; +} + +static void __finished_cb(user_finished_info_t *info, void *user_data) +{ + if (!info) { + TRACE_ERROR("[NULL-CHECK] Agent info"); + return ; + } + dp_request_slots *request_slot = (dp_request_slots *) user_data; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + return ; + } + CLIENT_MUTEX_LOCK(&request_slot->mutex); + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + dp_request *request = request_slot->request; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + int request_id = request->id; + dp_state_type state = DP_STATE_NONE; + dp_error_type errorcode = DP_ERROR_NONE; + + if (info->http_status > 0) + if (dp_db_replace_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_HTTP_STATUS, + DP_DB_COL_TYPE_INT, &info->http_status) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + + if (info->err == DA_RESULT_OK) { + + int conds_count = 5; // id + saved_path + content_name + http_status + file_size + int conds_index = 0; + db_conds_list_fmt conds_p[conds_count]; + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + + char *content_name = NULL; + if (info->saved_path != NULL) { + char *str = NULL; + if(!(strncmp(DP_SDCARD_MNT_POINT, info->saved_path, + strlen(DP_SDCARD_MNT_POINT)) == 0)) { + errorcode = __set_file_permission_to_client(request, + info->saved_path); + } + str = strrchr(info->saved_path, '/'); + if (str != NULL) { + str++; + content_name = dp_strdup(str); + TRACE_SECURE_DEBUG("[PARSE][%d] content_name [%s]", + request_id, content_name); + } + + conds_p[conds_index].column = DP_DB_COL_SAVED_PATH; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = info->saved_path; + conds_index++; + if (content_name != NULL) { + conds_p[conds_index].column = DP_DB_COL_CONTENT_NAME; + conds_p[conds_index].type = DP_DB_COL_TYPE_TEXT; + conds_p[conds_index].value = content_name; + conds_index++; + } + + if (errorcode == DP_ERROR_NONE) { + state = DP_STATE_COMPLETED; + TRACE_SECURE_INFO("[COMPLETED][%d] saved to [%s]", + request_id, info->saved_path); + } else { + state = DP_STATE_FAILED; + TRACE_SECURE_INFO("[FAILED][%d] saved to [%s]", + request_id, info->saved_path); + } + } else { + TRACE_ERROR("Cannot enter here"); + TRACE_ERROR("[ERROR][%d] No SavedPath", request_id); + errorcode = DP_ERROR_INVALID_DESTINATION; + state = DP_STATE_FAILED; + } + if (request->file_size == 0) { + request->file_size = request->received_size; + conds_p[conds_index].column = DP_DB_COL_CONTENT_SIZE; + conds_p[conds_index].type = DP_DB_COL_TYPE_INT64; + conds_p[conds_index].value = &request->file_size; + conds_index++; + } + + int check_id = dp_db_get_int_column(request_id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ID); + if (check_id == request_id) { // update + if (dp_db_update_columns(request_id, DP_DB_TABLE_DOWNLOAD_INFO, conds_index, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + request->error = DP_ERROR_NO_SPACE; + TRACE_ERROR("[SQLITE_FULL][%d]", request_id); + if (dp_cancel_agent_download(request->agent_id) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", request_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + } + } else { // insert + conds_p[conds_index].column = DP_DB_COL_ID; + conds_p[conds_index].type = DP_DB_COL_TYPE_INT; + conds_p[conds_index].value = &request_id; + conds_index++; + if (dp_db_insert_columns(DP_DB_TABLE_DOWNLOAD_INFO, conds_index, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + request->error = DP_ERROR_NO_SPACE; + TRACE_ERROR("[SQLITE_FULL][%d]", request_id); + if (dp_cancel_agent_download(request->agent_id) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", request_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + } + } + free(content_name); + } else { + char *tmp_saved_path = + dp_request_get_tmpsavedpath(request_id, request, &errorcode); + + if (tmp_saved_path != NULL) { + errorcode = __set_file_permission_to_client(request, tmp_saved_path); + free(tmp_saved_path); + } else { + TRACE_ERROR("Cannot enter here"); + TRACE_ERROR("[ERROR][%d] No SavedPath", request_id); + } + + if (info->err == DA_RESULT_USER_CANCELED) { + state = DP_STATE_CANCELED; + errorcode = request->error; + TRACE_INFO("[CANCELED][%d]", request_id); + } else { + state = DP_STATE_FAILED; + errorcode = __change_error(info->err); + TRACE_ERROR("[FAILED][%d][%s]", request_id, + dp_print_errorcode(errorcode)); + } + } + + request->state = state; + request->error = errorcode; + + // auto resume when failed by ip_changed. + if (state == DP_STATE_FAILED && info->err == DA_ERR_NETWORK_FAIL && + request->network_type != DP_NETWORK_TYPE_WIFI_DIRECT) { + if (request->ip_changed == 1 && + dp_get_network_connection_instant_status() != + DP_NETWORK_TYPE_OFF) { + // resume + TRACE_DEBUG("[RESUME][%d] will be queued", request_id); + request->state = DP_STATE_QUEUED; + request->error = DP_ERROR_NONE; + } else { + TRACE_DEBUG("[CHECK][%d] check again in timeout", request_id); + } + } else { + // stay on memory till called destroy by client or timeout + if (request->group != NULL && + request->group->event_socket >= 0) { + /* update the received file size. + * The last received file size cannot update + * because of reducing update algorithm*/ + if (request->received_size > 0) { + dp_ipc_send_event(request->group->event_socket, + request->id, DP_STATE_DOWNLOADING, request->error, + request->received_size); + } + } + dp_request_state_response(request); + } + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + + dp_thread_queue_manager_wake_up(); +} + +static void __paused_cb(user_paused_info_t *info, void *user_data) +{ + dp_request_slots *request_slot = (dp_request_slots *) user_data; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + return ; + } + CLIENT_MUTEX_LOCK(&request_slot->mutex); + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request req_id:%d", info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + dp_request *request = request_slot->request; + if (request->id < 0 || (request->agent_id != info->download_id)) { + TRACE_ERROR("[NULL-CHECK] agent_id : %d req_id %d", + request->agent_id, info->download_id); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + if (request->state != DP_STATE_PAUSE_REQUESTED) { + TRACE_ERROR("[CHECK] now status:%d", request->state); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return ; + } + + int request_id = request->id; + + if (dp_db_update_date + (request_id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request_id); + + request->state = DP_STATE_PAUSED; + request->error = DP_ERROR_NONE; + dp_request_state_response(request); + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + + dp_thread_queue_manager_wake_up(); +} + +int dp_init_agent() +{ + + g_da_handle = dlopen("libdownloadagent2.so", RTLD_LAZY | RTLD_GLOBAL); + if (!g_da_handle) { + TRACE_ERROR("[dlopen] %s", dlerror()); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + dlerror(); /* Clear any existing error */ + + *(void **) (&download_agent_init) = dlsym(g_da_handle, "da_init"); + if (download_agent_init == NULL ) { + TRACE_ERROR("[dlsym] da_init:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_deinit) = dlsym(g_da_handle, "da_deinit"); + if (download_agent_deinit == NULL ) { + TRACE_ERROR("[dlsym] da_deinit:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_is_alive) = dlsym(g_da_handle, "da_is_valid_download_id"); + if (download_agent_is_alive == NULL ) { + TRACE_ERROR("[dlsym] da_is_valid_download_id:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_suspend) = dlsym(g_da_handle, "da_suspend_download"); + if (download_agent_suspend == NULL ) { + TRACE_ERROR("[dlsym] da_suspend_download:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_resume) = dlsym(g_da_handle, "da_resume_download"); + if (download_agent_resume == NULL ) { + TRACE_ERROR("[dlsym] da_resume_download:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_cancel) = dlsym(g_da_handle, "da_cancel_download"); + if (download_agent_cancel == NULL ) { + TRACE_ERROR("[dlsym] da_cancel_download:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + *(void **) (&download_agent_start) = dlsym(g_da_handle, "da_start_download_with_extension"); + if (download_agent_start == NULL ) { + TRACE_ERROR("[dlsym] da_start_download_with_extension:%s", dlerror()); + dlclose(g_da_handle); + g_da_handle = NULL; + return DP_ERROR_OUT_OF_MEMORY; + } + + int da_ret = -1; + da_client_cb_t da_cb = { + __download_info_cb, + __progress_cb, + __finished_cb, + __paused_cb + }; + da_ret = (*download_agent_init)(&da_cb); + if (da_ret != DA_RESULT_OK) { + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +void dp_deinit_agent() +{ + if (g_da_handle != NULL) { + if (download_agent_deinit != NULL) + (*download_agent_deinit)(); + + dlclose(g_da_handle); + g_da_handle = NULL; + } +} + +// 1 : alive +// 0 : not alive +int dp_is_alive_download(int req_id) +{ + int da_ret = 0; + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return 0; + } + if (download_agent_is_alive != NULL) + da_ret = (*download_agent_is_alive)(req_id); + return da_ret; +} + +// 0 : success +// -1 : failed +dp_error_type dp_cancel_agent_download(int req_id) +{ + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return -1; + } + if (dp_is_alive_download(req_id) == 0) { + TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id); + return -1; + } + if (download_agent_cancel != NULL) { + if ((*download_agent_cancel)(req_id) == DA_RESULT_OK) + return 0; + } + return -1; +} + +// 0 : success +// -1 : failed +dp_error_type dp_pause_agent_download(int req_id) +{ + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return -1; + } + if (dp_is_alive_download(req_id) == 0) { + TRACE_ERROR("[CHECK agent-id:%d] dead request", req_id); + return -1; + } + if (download_agent_suspend != NULL) { + if ((*download_agent_suspend)(req_id) == DA_RESULT_OK) + return 0; + } + return -1; +} + + +// 0 : success +// -1 : failed +// -2 : pended +dp_error_type dp_start_agent_download(dp_request_slots *request_slot) +{ + int da_ret = -1; + int req_dl_id = -1; + dp_error_type errorcode = DP_ERROR_NONE; + extension_data_t ext_data = {0,}; + char *etag = NULL; + + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] download_clientinfo_slot"); + return DP_ERROR_INVALID_PARAMETER; + } + + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] download_clientinfo_slot"); + return DP_ERROR_INVALID_PARAMETER; + } + + dp_request *request = request_slot->request; + + char *url = dp_request_get_url(request->id, &errorcode); + if (url == NULL) { + TRACE_ERROR("[ERROR][%d] URL is NULL", request->id); + return DP_ERROR_INVALID_URL; + } + char *destination = + dp_request_get_destination(request->id, request, &errorcode); + if (destination != NULL) + ext_data.install_path = destination; + + char *filename = + dp_request_get_filename(request->id, request, &errorcode); + if (filename != NULL) + ext_data.file_name = filename; + + // call start_download() of download-agent + + char *tmp_saved_path = + dp_request_get_tmpsavedpath(request->id, request, &errorcode); + if (tmp_saved_path) { + etag = dp_request_get_etag(request->id, request, &errorcode); + if (etag) { + TRACE_DEBUG("[RESUME][%d]", request->id); + ext_data.etag = etag; + ext_data.temp_file_path = tmp_saved_path; + } else { + /* FIXME later : It is better to handle the unlink function in download agaent module + * or in upload the request data to memory after the download provider process is restarted */ + TRACE_SECURE_INFO("[RESTART][%d] try to remove tmp file [%s]", + request->id, tmp_saved_path); + if (dp_is_file_exist(tmp_saved_path) == 0) + if (unlink(tmp_saved_path) != 0) + TRACE_STRERROR + ("[ERROR][%d] remove file", request->id); + } + } + char *pkg_name = dp_request_get_pkg_name(request->id, request, &errorcode); + if (pkg_name != NULL) + ext_data.pkg_name = pkg_name; + // get headers list from httpheaders table(DB) + int headers_count = dp_db_get_cond_rows_count + (request->id, DP_DB_TABLE_HTTP_HEADERS, NULL, 0, NULL); + if (headers_count > 0) { + ext_data.request_header = calloc(headers_count, sizeof(char*)); + if (ext_data.request_header != NULL) { + ext_data.request_header_count = dp_db_get_http_headers_list + (request->id, (char**)ext_data.request_header); + } + } + + ext_data.user_data = (void *)request_slot; + + // call start API of agent lib + if (download_agent_start != NULL) + da_ret = (*download_agent_start)(url, &ext_data, &req_dl_id); + if (ext_data.request_header_count > 0) { + int len = 0; + int i = 0; + len = ext_data.request_header_count; + for (i = 0; i < len; i++) { + if (ext_data.request_header[i]) + free((void *)(ext_data.request_header[i])); + } + free(ext_data.request_header); + } + free(url); + if (destination) + free(destination); + if (filename) + free(filename); + if (tmp_saved_path) + free(tmp_saved_path); + if (etag) + free(etag); + if (pkg_name) + free(pkg_name); + + // if start_download() return error cause of maximun download limitation, + // set state to DOWNLOAD_STATE_PENDED. + if (da_ret == DA_ERR_ALREADY_MAX_DOWNLOAD) { + TRACE_DEBUG("[PENDING][%d] DA_ERR_ALREADY_MAX_DOWNLOAD [%d]", + request->id, da_ret); + return DP_ERROR_TOO_MANY_DOWNLOADS; + } else if (da_ret != DA_RESULT_OK) { + TRACE_ERROR("[ERROR][%d] DP_ERROR_CONNECTION_FAILED [%d]", + request->id, da_ret); + return __change_error(da_ret); + } + TRACE_DEBUG("[SUCCESS][%d] agent_id [%d]", request->id, req_dl_id); + request->agent_id = req_dl_id; + return DP_ERROR_NONE; +} + +dp_error_type dp_resume_agent_download(int req_id) +{ + int da_ret = -1; + if (req_id < 0) { + TRACE_ERROR("[NULL-CHECK] req_id"); + return DP_ERROR_INVALID_PARAMETER; + } + if (download_agent_resume != NULL) + da_ret = (*download_agent_resume)(req_id); + if (da_ret == DA_RESULT_OK) + return DP_ERROR_NONE; + else if (da_ret == DA_ERR_INVALID_STATE) + return DP_ERROR_INVALID_STATE; + return __change_error(da_ret); +} + diff --git a/provider/download-provider-db.c b/provider/download-provider-db.c new file mode 100755 index 0000000..6f1d5cf --- /dev/null +++ b/provider/download-provider-db.c @@ -0,0 +1,2352 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <tzplatform_config.h> + +#include <stdlib.h> +#include <sqlite3.h> + +#include "download-provider-config.h" +#include "download-provider-db.h" +#include "download-provider-slots.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" + +#define DATABASE_FILE tzplatform_mkpath(TZ_SYS_DB,".download-provider.db") + +//BASIC +#define DP_DB_BASIC_GET_QUERY_FORMAT "SELECT %s FROM %s WHERE id = ?" +#define DP_DB_BASIC_SET_QUERY_FORMAT "UPDATE %s SET %s = ? WHERE id = ?" +#define DP_DB_BASIC_INSERT_QUERY_FORMAT "INSERT INTO %s (id, %s) VALUES (?, ?)" +#define DP_DB_BASIC_NOW_DATE_QUERY_FORMAT "UPDATE %s SET %s = DATETIME('now') WHERE id = ?" + +// COND +#define DP_DB_COND_GET_QUERY_FORMAT "SELECT %s FROM %s WHERE id = ? AND %s = ?" +#define DP_DB_COND_SET_QUERY_FORMAT "UPDATE %s SET %s = ? WHERE id = ? AND %s = ?" + +typedef enum { + DP_DB_QUERY_TYPE_GET = 10, + DP_DB_QUERY_TYPE_SET = 20, + DP_DB_QUERY_TYPE_INSERT = 30, + DP_DB_QUERY_TYPE_NOW_DATE = 40 +} db_query_type; + +sqlite3 *g_dp_db_handle = 0; +sqlite3_stmt *g_dp_db_logging_new_stmt = NULL; +sqlite3_stmt *g_dp_db_logging_status_stmt = NULL; +sqlite3_stmt *g_dp_db_logging_get_state_stmt = NULL; + +static void __dp_finalize(sqlite3_stmt *stmt) +{ + if (sqlite3_finalize(stmt) != SQLITE_OK) + TRACE_ERROR("failed sqlite3_finalize [%s]", + sqlite3_errmsg(g_dp_db_handle)); +} + +// called when terminating process +void dp_db_close() +{ + if (g_dp_db_handle) { + if (g_dp_db_logging_new_stmt != NULL) + __dp_finalize(g_dp_db_logging_new_stmt); + if (g_dp_db_logging_status_stmt != NULL) + __dp_finalize(g_dp_db_logging_status_stmt); + sqlite3_exec(g_dp_db_handle, "VACUUM;", 0, 0, 0); // remove empty page of db + sqlite3_close(g_dp_db_handle); + } + g_dp_db_handle = 0; +} + +static void __load_sql_schema() +{ + char *rebuild_query = + sqlite3_mprintf("sqlite3 %s '.read %s'", DATABASE_FILE, DATABASE_SCHEMA_FILE); + if (rebuild_query != NULL) { + TRACE_SECURE_INFO("[QUERY] %s", rebuild_query); + system(rebuild_query); + sqlite3_free(rebuild_query); + } +} + +static int __dp_sql_open() +{ + return dp_db_open(); +} + +static int __check_table(char *table) +{ + //"SELECT name FROM sqlite_master WHERE type='table' AND name='" + table +"'"; + sqlite3_stmt *stmt = NULL; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + char *query = sqlite3_mprintf("SELECT name FROM sqlite_master WHERE type='table' AND name='%s'", table); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + int ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_step(stmt) == SQLITE_ROW) { + __dp_finalize(stmt); + return 1; + } + __dp_finalize(stmt); + return 0; +} + +// called when launching process or in every API +int dp_db_open() +{ + if (g_dp_db_handle == 0) { + TRACE_SECURE_INFO("TRY to open [%s]", DATABASE_FILE); + if (sqlite3_open_v2(DATABASE_FILE, &g_dp_db_handle, + SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) { + TRACE_ERROR("[ERROR][%s][%s]", DATABASE_FILE, + sqlite3_errmsg(g_dp_db_handle)); + int errorcode = sqlite3_errcode(g_dp_db_handle); + dp_db_close(); + if (errorcode == SQLITE_CORRUPT) { + TRACE_SECURE_INFO("unlink [%s]", DATABASE_FILE); + unlink(DATABASE_FILE); + errorcode = SQLITE_CANTOPEN; + } + if (errorcode == SQLITE_CANTOPEN) { + __load_sql_schema(); + return dp_db_open(); + } + return -1; + } + sqlite3_exec(g_dp_db_handle, "PRAGMA journal_mode=PERSIST;", 0, 0, 0); + sqlite3_exec(g_dp_db_handle, "PRAGMA foreign_keys=ON;", 0, 0, 0); + + // if not found group table. load again. (FOTA) + // new table(groups) created by smack_label. 2013.07.09 + if (__check_table(DP_DB_TABLE_GROUPS) == 0) + __load_sql_schema(); + } + return g_dp_db_handle ? 0 : -1; +} + +int dp_db_is_full_error() +{ + if (g_dp_db_handle == 0) { + TRACE_ERROR("HANDLE is null"); + return -1; + } + if (sqlite3_errcode(g_dp_db_handle) == SQLITE_FULL) + return 0; + return -1; +} + +int dp_db_get_count_by_limit_time() +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT count(id) FROM logging \ + WHERE createtime < DATETIME('now','-48 hours')", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_ROW) { + int count = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return count; + } + __dp_finalize(stmt); + return 0; +} + +int dp_db_get_list_by_limit_time(dp_request_slots *requests, int limit) +{ + int errorcode = SQLITE_OK; + int i = 0; + sqlite3_stmt *stmt = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT id, state FROM logging WHERE \ + createtime < DATETIME('now','-48 hours') \ + ORDER BY createtime ASC LIMIT ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW && i < limit) { + // allocation & initialization + requests[i].request = dp_request_new(); + // ID + requests[i].request->id = sqlite3_column_int(stmt, 0); + // state + requests[i].request->state = sqlite3_column_int(stmt, 1); + i++; + } + + __dp_finalize(stmt); + return i; +} + +int dp_db_crashed_list(dp_request_slots *requests, int limit) +{ + int errorcode = SQLITE_OK; + int i = 0; + int buffer_length = 0; + sqlite3_stmt *stmt = NULL; + char *buffer = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT id, state, packagename FROM logging WHERE \ + (state = ? OR state = ? OR state = ?) \ + AND createtime > DATETIME('now','-48 hours') LIMIT ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, DP_STATE_QUEUED) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 2, DP_STATE_DOWNLOADING) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 3, DP_STATE_CONNECTING) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_bind_int(stmt, 4, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + // allocation & initialization + requests[i].request = dp_request_new(); + // ID + requests[i].request->id = sqlite3_column_int(stmt, 0); + // state + requests[i].request->state = sqlite3_column_int(stmt, 1); + // packagename + buffer = (char *)(sqlite3_column_text(stmt, 2)); + requests[i].request->packagename = NULL; + if (buffer) { + buffer_length = strlen(buffer); + if (buffer_length > 1) { + requests[i].request->packagename + = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(requests[i].request->packagename, buffer, + buffer_length * sizeof(char)); + requests[i].request->packagename[buffer_length] = '\0'; + } + } + i++; + } + + __dp_finalize(stmt); + return i; +} + +int dp_db_limit_rows(int limit) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (limit <= 0) { + TRACE_ERROR("[CHECK LIMIT] %d", limit); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // apply "ON DELETE CASCADE" + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "DELETE FROM logging WHERE id NOT IN \ + (SELECT id FROM logging ORDER BY createtime ASC LIMIT ?)", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, limit) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +dp_request *dp_db_load_logging_request(int id) +{ + int errorcode = SQLITE_OK; + int buffer_length = 0; + sqlite3_stmt *stmt = NULL; + char *buffer = NULL; + dp_request *request = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT state, errorcode, startcount, packagename \ + FROM logging WHERE id = ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + request = dp_request_new(); + if (request == NULL) { + TRACE_ERROR("dp_request_new failed"); + __dp_finalize(stmt); + return NULL; + } + request->id = id; + request->state = sqlite3_column_int(stmt, 0); + request->error = sqlite3_column_int(stmt, 1); + request->startcount = sqlite3_column_int(stmt, 2); + + buffer = (char *)(sqlite3_column_text(stmt, 3)); + if (buffer) { + buffer_length = strlen(buffer); + if (buffer_length > 1) { + request->packagename + = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(request->packagename, buffer, + buffer_length * sizeof(char)); + request->packagename[buffer_length] = '\0'; + } + } + } else { + TRACE_ERROR("sqlite3_step is failed. [%s] errorcode[%d]", + sqlite3_errmsg(g_dp_db_handle), errorcode); + __dp_finalize(stmt); + return NULL; + } + __dp_finalize(stmt); + return request; +} + +int dp_db_remove_all(int id) +{ + #if 0 + dp_db_remove(id, DP_DB_TABLE_REQUEST_INFO); + dp_db_remove(id, DP_DB_TABLE_DOWNLOAD_INFO); + dp_db_remove(id, DP_DB_TABLE_HTTP_HEADERS); + dp_db_remove(id, DP_DB_TABLE_NOTIFICATION); + #endif + // apply "ON DELETE CASCADE" + dp_db_remove(id, DP_DB_TABLE_LOG); + return -1; +} + +int dp_db_remove(int id, char *table) +{ + int errorcode = SQLITE_OK; + int query_len = 0; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query_format = "DELETE FROM %s WHERE id = ? "; + // 2 means the length of one %s + query_len = strlen(query_format) - 2 + strlen(table); + if (query_len < strlen(query_format)) { + TRACE_ERROR("[CHECK QUERY FORMAT] [%s][%s]", + query_format, table); + return -1; + } + + query = (char *)calloc((query_len + 1), sizeof(char)); + if (query == NULL) { + TRACE_STRERROR("[CALLOC]"); + return -1; + } + query[query_len] = '\0'; + + ret = snprintf(query, query_len + 1, query_format, table); + + if (ret < 0) { + TRACE_STRERROR("[CHECK COMBINE] [%s]", query); + free(query); + return -1; + } + + // check error of sqlite3_prepare_v2 + if (sqlite3_prepare_v2 + (g_dp_db_handle, query, -1, &stmt, NULL) != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + free(query); + return -1; + } + free(query); + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +static sqlite3_stmt *__prepare_query(sqlite3 *handle, + db_query_type type, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + int ret = -1; + + if (type == DP_DB_QUERY_TYPE_GET) { + query_format = DP_DB_BASIC_GET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_SET) { + query_format = DP_DB_BASIC_SET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_INSERT) { + query_format = DP_DB_BASIC_INSERT_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_NOW_DATE) { + query_format = DP_DB_BASIC_NOW_DATE_QUERY_FORMAT; + } else { + TRACE_ERROR("[CHECK QUERY TYPE] [%d]", type); + return NULL; + } + + if (type == DP_DB_QUERY_TYPE_GET) + query = sqlite3_mprintf(query_format, column, table); + else + query = sqlite3_mprintf(query_format, table, column); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return NULL; + } + + ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(handle)); + __dp_finalize(stmt); + return NULL; + } + return stmt; +} + +int dp_db_insert_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_INSERT, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // VALUES ( id ) + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_insert_blob_column(int id, char *table, char *column, + void *value, unsigned length) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_INSERT, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + errorcode = sqlite3_bind_blob(stmt, 2, value, (int)length, NULL); + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // VALUES ( id ) + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_SET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 1, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 1, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 2, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_set_blob_column(int id, char *table, char *column, + void *value, unsigned length) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_SET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + int errorcode = SQLITE_OK; + errorcode = sqlite3_bind_blob(stmt, 1, value, (int)length, NULL); + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 2, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_replace_column(int id, char *table, char *column, + db_column_data_type datatype, void *value) +{ + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + int check_id = dp_db_get_int_column(id, table, DP_DB_COL_ID); + if (check_id != id) // INSERT + return dp_db_insert_column(id, table, column, datatype, value); + // UPDATE + return dp_db_set_column(id, table, column, datatype, value); +} + +int dp_db_replace_blob_column(int id, char *table, char *column1, + void *value, unsigned length) +{ + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (!column1) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + int check_id = dp_db_get_int_column(id, table, DP_DB_COL_ID); + if (check_id != id) // INSERT + { + return dp_db_insert_blob_column(id, table, column1, value, length); + } + // UPDATE + return dp_db_set_blob_column(id, table, column1, value, length); +} + +// success : 0 +// error : -1 +char *dp_db_get_text_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int buffer_length = 0; + char *columntext = NULL; + char *buffer = (char *)(sqlite3_column_text(stmt, 0)); + if (buffer && (buffer_length = strlen(buffer)) > 1) { + columntext = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(columntext, buffer, buffer_length * sizeof(char)); + columntext[buffer_length] = '\0'; + } + __dp_finalize(stmt); + return columntext; + } + __dp_finalize(stmt); + return NULL; +} + +// success : 0 +// error : -1 +void *dp_db_get_blob_column(int id, char *table, char *column, int *length) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int blob_length = 0; + void *blob_data = NULL; + blob_length = sqlite3_column_bytes(stmt, 0); + if(blob_length > 0){ + blob_data = (void*)calloc(blob_length, sizeof(unsigned char)); + if(blob_data != NULL){ + memcpy(blob_data, sqlite3_column_blob(stmt, 0), + sizeof(unsigned char)*blob_length); + } else { + TRACE_ERROR("[MEM] allocating"); + blob_length = -1; + } + } else { + TRACE_ERROR("NO DATA"); + blob_length = -1; + } + __dp_finalize(stmt); + *length = blob_length; + return blob_data; + } + __dp_finalize(stmt); + return NULL; +} + + +int dp_db_get_int_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int columnvalue = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return columnvalue; + } + __dp_finalize(stmt); + return -1; +} + +long long dp_db_get_int64_column(int id, char *table, char *column) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + long long columnvalue = sqlite3_column_int64(stmt, 0); + __dp_finalize(stmt); + return columnvalue; + } + __dp_finalize(stmt); + return -1; +} + +int dp_db_update_date(int id, char *table, char *column) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_NOW_DATE, table, column); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("Failed : [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +static sqlite3_stmt *__prepare_cond_query(sqlite3 *handle, + db_query_type type, char *table, + char *column, char *cond_column) +{ + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + int ret = -1; + + if (type == DP_DB_QUERY_TYPE_GET) { + query_format = DP_DB_COND_GET_QUERY_FORMAT; + } else if (type == DP_DB_QUERY_TYPE_SET) { + query_format = DP_DB_COND_SET_QUERY_FORMAT; + } else { + TRACE_ERROR("[CHECK QUERY TYPE] [%d]", type); + return NULL; + } + + if (type == DP_DB_QUERY_TYPE_GET) + query = sqlite3_mprintf(query_format, column, table, cond_column); + else + query = sqlite3_mprintf(query_format, table, column, cond_column); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return NULL; + } + + ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(handle)); + __dp_finalize(stmt); + return NULL; + } + return stmt; + +} + +int dp_db_cond_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value, + char *condcolumn, db_column_data_type condtype, void *condvalue) +{ + int errorcode = SQLITE_OK; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (!condcolumn) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + stmt = __prepare_cond_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_SET, table, column, condcolumn); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (datatype == DP_DB_COL_TYPE_INT) { + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); + } else if (datatype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, 1, *cast_value); +#else + int *cast_value = value; + errorcode = sqlite3_bind_int(stmt, 1, *cast_value); +#endif + } else if (datatype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 1, (char*)value, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", datatype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 3, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 3, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 3, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 3, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + datatype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 2, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + __dp_finalize(stmt); + return -1; +} + +char *dp_db_cond_get_text_column(int id, char *table, char *column, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return NULL; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (!column) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + stmt = __prepare_cond_query + (g_dp_db_handle, DP_DB_QUERY_TYPE_GET, table, column, condcolumn); + if (stmt == NULL) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + int errorcode = SQLITE_OK; + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return NULL; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + // WHERE id = ? + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (sqlite3_step(stmt) == SQLITE_ROW) { + int buffer_length = 0; + char *columntext = NULL; + char *buffer = (char *)(sqlite3_column_text(stmt, 0)); + if (buffer && (buffer_length = strlen(buffer)) > 1) { + columntext = (char *)calloc(buffer_length + 1, sizeof(char)); + memcpy(columntext, buffer, buffer_length * sizeof(char)); + columntext[buffer_length] = '\0'; + } + __dp_finalize(stmt); + return columntext; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; +} + +int dp_db_cond_remove(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query_format = NULL; + char *query = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (!condcolumn) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query_format = "DELETE FROM %s WHERE id = ? AND %s = ?"; + + query = sqlite3_mprintf(query_format, table, condcolumn); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE] [%s]", query_format); + return -1; + } + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +static int __dp_sql_bind_value(sqlite3_stmt *stmt, + db_column_data_type condtype, void *value, int index) +{ + int errorcode = SQLITE_ERROR; + int *cast_value = 0; + + if (stmt == NULL) + return SQLITE_ERROR; + + switch (condtype) { + case DP_DB_COL_TYPE_INT: + cast_value = value; + errorcode = sqlite3_bind_int(stmt, index, *cast_value); + break; + case DP_DB_COL_TYPE_INT64: +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, index, *cast_value); +#else + cast_value = value; + errorcode = sqlite3_bind_int(stmt, index, *cast_value); +#endif + break; + case DP_DB_COL_TYPE_TEXT: + errorcode = + sqlite3_bind_text(stmt, index, (char *)value, -1, SQLITE_STATIC); + break; + default: + errorcode = SQLITE_ERROR; + break; + } + return errorcode; +} + +char *dp_db_cond_get_text(char *table, char *column, char *condcolumn, + db_column_data_type condtype, void *condvalue) +{ + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int ret = -1; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return NULL; + } + + if (column == NULL) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return NULL; + } + if (condcolumn == NULL) { + TRACE_ERROR("[CHECK Condition]"); + return NULL; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return NULL; + } + + query = sqlite3_mprintf("SELECT %s FROM %s WHERE %s IS ?", + column, table, condcolumn); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return NULL; + } + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + + if (__dp_sql_bind_value(stmt, condtype, condvalue, 1) != SQLITE_OK) { + TRACE_ERROR + ("[BIND][%d][%s]", condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return NULL; + } + if (sqlite3_step(stmt) == SQLITE_ROW && + sqlite3_column_type(stmt, 0) == SQLITE_TEXT) { + int getbytes = sqlite3_column_bytes(stmt, 0); + if (getbytes > 0) { + char *getstr = (char *)calloc(getbytes + 1, sizeof(char)); + if (getstr != NULL) { + memcpy(getstr, sqlite3_column_text(stmt, 0), + getbytes * sizeof(char)); + getstr[getbytes] = '\0'; + } + __dp_finalize(stmt); + return getstr; + } + } + __dp_finalize(stmt); + return NULL; +} + +int dp_db_cond_get_int(char *table, char *column, char *condcolumn, + db_column_data_type condtype, void *condvalue) +{ + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int ret = -1; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (column == NULL) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + if (condcolumn == NULL) { + TRACE_ERROR("[CHECK Condition]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("__dp_sql_open[%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query = sqlite3_mprintf("SELECT %s FROM %s WHERE %s IS ?", + column, table, condcolumn); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (__dp_sql_bind_value(stmt, condtype, condvalue, 1) != SQLITE_OK) { + TRACE_ERROR + ("[BIND][%d][%s]", condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_step(stmt) == SQLITE_ROW && + sqlite3_column_type(stmt, 0) == SQLITE_INTEGER) { + int columnvalue = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return columnvalue; + } + __dp_finalize(stmt); + return -1; +} + +int dp_db_get_cond_rows_count(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (!table) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (condcolumn) + query = + sqlite3_mprintf + ("SELECT count(id) FROM %s WHERE id = ? AND %s = ?", + table, condcolumn); + else + query = + sqlite3_mprintf + ("SELECT count(id) FROM %s WHERE id = ?", table); + + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + if (condcolumn) { + if (condtype == DP_DB_COL_TYPE_INT) { + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); + } else if (condtype == DP_DB_COL_TYPE_INT64) { +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = condvalue; + errorcode = sqlite3_bind_int64(stmt, 2, *cast_value); +#else + int *cast_value = condvalue; + errorcode = sqlite3_bind_int(stmt, 2, *cast_value); +#endif + } else if (condtype == DP_DB_COL_TYPE_TEXT) { + errorcode = sqlite3_bind_text(stmt, 2, (char*)condvalue, -1, NULL); + } else { + TRACE_ERROR("[CHECK TYPE] Not Support [%d]", condtype); + __dp_finalize(stmt); + return -1; + } + if (errorcode != SQLITE_OK) { + TRACE_ERROR("[BIND] [%d] [%s]", + condtype, sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + + if (sqlite3_bind_int(stmt, 1, id) + != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int[%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_ROW) { + int count = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return count; + } + __dp_finalize(stmt); + return 0; +} + +int dp_db_get_http_headers_list(int id, char **headers) +{ + int errorcode = SQLITE_OK; + int i = 0; + int headers_index = 0; + sqlite3_stmt *stmt = NULL; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("db_util_open is failed [%s]", + sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, + "SELECT header_field, header_data FROM httpheaders WHERE id = ?", + -1, &stmt, NULL); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + if (sqlite3_bind_int(stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("sqlite3_bind_int [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + int buffer_length = 0; + char *header_field = (char *)(sqlite3_column_text(stmt, 0)); + char *header_data = (char *)(sqlite3_column_text(stmt, 1)); + i++; + // REF : http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + buffer_length = strlen(header_field) + strlen(header_data) + 1; + char *headers_buffer = calloc(buffer_length + 1, sizeof(char)); + if (headers_buffer == NULL) { + TRACE_ERROR("[CALLOC] headers_buffer"); + continue; + } + int len = snprintf(headers_buffer, buffer_length + 1, + "%s:%s", header_field, header_data); + if (len <= 0) { + if (headers_buffer) + free(headers_buffer); + continue; + } else { + headers_buffer[len] = '\0'; + } + headers[headers_index++] = headers_buffer; + } + + __dp_finalize(stmt); + return headers_index; +} + +static char *__merge_strings(char *dest, const char *src, char sep) +{ + int dest_length = 0; + int src_length = 0; + char *temp_dest = NULL; + + if (dest == NULL || src == NULL) + return NULL; + + dest_length = strlen(dest); + src_length = strlen(src); + + temp_dest = sqlite3_realloc(dest, dest_length + src_length + 2); + if (temp_dest == NULL) { + free(dest); + return NULL; + } + temp_dest = strncat(temp_dest, &sep, 1); + temp_dest = strncat(temp_dest, src, src_length); + return temp_dest; +} + +static char *__get_conds_query(int count, db_conds_list_fmt *conds, char *op) +{ + char *conditions = NULL; + int i = 0; + + if (count > 0 && conds != NULL) { + conditions = sqlite3_mprintf("WHERE"); + for (i = 0; i < count; i++) { + char *token = + sqlite3_mprintf("%s %s ?", conds[i].column, + (conds[i].is_like == 1 ? "LIKE" : "=")); + if (token != NULL) { + conditions = __merge_strings(conditions, token, ' '); + sqlite3_free(token); + token = NULL; + } + if (i < count - 1 && op) + conditions = __merge_strings(conditions, op, ' '); + } + } + return conditions; +} + +static int __bind_value(sqlite3_stmt *stmt, + db_column_data_type condtype, void *value, int index) +{ + int errorcode = SQLITE_ERROR; + int *cast_value = 0; + + if (stmt == NULL) + return SQLITE_ERROR; + + switch (condtype) { + case DP_DB_COL_TYPE_INT: + cast_value = value; + errorcode = sqlite3_bind_int(stmt, index, *cast_value); + break; + case DP_DB_COL_TYPE_INT64: +#ifdef SQLITE_INT64_TYPE + sqlite3_int64 *cast_value = value; + errorcode = sqlite3_bind_int64(stmt, index, *cast_value); +#else + cast_value = value; + errorcode = sqlite3_bind_int(stmt, index, *cast_value); +#endif + break; + case DP_DB_COL_TYPE_TEXT: + errorcode = + sqlite3_bind_text(stmt, index, (char *)value, -1, SQLITE_STATIC); + break; + default: + errorcode = SQLITE_ERROR; + break; + } + return errorcode; +} + +int dp_db_insert_columns(char *table, int column_count, + db_conds_list_fmt *columns) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int i = 0; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (column_count <= 0) { + TRACE_ERROR("[CHECK db_conds_list_fmt count]"); + return -1; + } + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query = + sqlite3_mprintf("INSERT INTO %s ", table); + query = __merge_strings(query, columns[0].column, '('); + for (i = 1; i < column_count; i++) { + char *column_query = NULL; + column_query = sqlite3_mprintf(", %s", columns[i].column); + query = __merge_strings(query, column_query, ' '); + sqlite3_free(column_query); + } + query = __merge_strings(query, " VALUES ", ')'); + query = __merge_strings(query, "?", '('); + for (i = 1; i < column_count; i++) { + query = __merge_strings(query, ", ?", ' '); + } + query = __merge_strings(query, ")", ' '); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + TRACE_DEBUG("query:%s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + for (i = 0; i < column_count; i++) { + if (__bind_value + (stmt, columns[i].type, columns[i].value, (i + 1)) != + SQLITE_OK) { + TRACE_ERROR("[BIND][%d][%s]", columns[i].type, + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_update_columns(int id, char *table, int column_count, + db_conds_list_fmt *columns) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int i = 0; + + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (column_count <= 0) { + TRACE_ERROR("[CHECK db_conds_list_fmt count]"); + return -1; + } + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query = + sqlite3_mprintf("UPDATE %s SET %s = ?", table, columns[0].column); + for (i = 1; i < column_count; i++) { + char *column_query = NULL; + column_query = sqlite3_mprintf("%s = ?", columns[i].column); + query = __merge_strings(query, column_query, ','); + sqlite3_free(column_query); + } + query = __merge_strings(query, "WHERE id = ?", ' '); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + TRACE_DEBUG("query:%s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if ( ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + for (i = 0; i < column_count; i++) { + if (__bind_value + (stmt, columns[i].type, columns[i].value, (i + 1)) != + SQLITE_OK) { + TRACE_ERROR("[BIND][%d][%s]", columns[i].type, + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + if (sqlite3_bind_int(stmt, column_count + 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] ID [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + __dp_finalize(stmt); + return 0; + } + TRACE_ERROR("[SQL] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; +} + +int dp_db_get_conds_rows_count(char *table, + char *getcolumn, char *op, + int conds_count, db_conds_list_fmt *conds) +{ + int errorcode = SQLITE_OK; + int ret = -1; + sqlite3_stmt *stmt = NULL; + char *query = NULL; + int i = 0; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (getcolumn == NULL) { + TRACE_ERROR("[CHECK RESULT COLUMN]"); + return -1; + } + if (op == NULL) { + TRACE_ERROR("[CHECK OPERATOR] AND or OR"); + return -1; + } + if (conds_count <= 0) { + TRACE_ERROR("[CHECK db_conds_list_fmt count]"); + return -1; + } + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + query = + sqlite3_mprintf("SELECT count(%s) FROM %s", getcolumn, table); + + char *conditions = __get_conds_query(conds_count, conds, op); + if (conditions != NULL) { + query = __merge_strings(query, conditions, ' '); + sqlite3_free(conditions); + } + + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + for (i = 0; i < conds_count; i++) { + if (__bind_value + (stmt, conds[i].type, conds[i].value, (i + 1)) != + SQLITE_OK) { + TRACE_ERROR("[BIND][%d][%s]", conds[i].type, + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + errorcode = sqlite3_step(stmt); + if (errorcode == SQLITE_ROW) { + int count = sqlite3_column_int(stmt, 0); + __dp_finalize(stmt); + return count; + } + __dp_finalize(stmt); + return 0; +} + +int dp_db_get_conds_list(char *table, char *getcolumn, + db_column_data_type gettype, void **list, + int rowslimit, int rowsoffset, + char *ordercolumn, char *ordering, + char *op, int conds_count, + db_conds_list_fmt *conds) +{ + int errorcode = SQLITE_OK; + int rows_count = 0; + sqlite3_stmt *stmt = NULL; + int i = 0; + + if (table == NULL) { + TRACE_ERROR("[CHECK TABLE NAME]"); + return -1; + } + if (op == NULL) { + TRACE_ERROR("[CHECK OPERATOR] AND or OR"); + return -1; + } + if (getcolumn == NULL) { + TRACE_ERROR("[CHECK COLUMN NAME]"); + return -1; + } + if (conds_count <= 0) { + TRACE_ERROR("[CHECK db_conds_list_fmt count]"); + return -1; + } + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + char *limit = NULL; + char *order = NULL; + char *query = sqlite3_mprintf("SELECT %s FROM %s", getcolumn, table); + char *conditions = __get_conds_query(conds_count, conds, op); + if (conditions != NULL) { + query = __merge_strings(query, conditions, ' '); + sqlite3_free(conditions); + } + + if (ordercolumn != NULL) { + order = + sqlite3_mprintf + ("ORDER BY %s %s", ordercolumn, + (ordering == NULL ? "ASC" : ordering)); + if (order != NULL) { + query = __merge_strings(query, order, ' '); + sqlite3_free(order); + } + } + if (rowslimit > 0) { // 0 or negative : no limitation + if (rowsoffset >= 0) + limit = + sqlite3_mprintf("LIMIT %d OFFSET %d", rowslimit, + rowsoffset); + else + limit = sqlite3_mprintf("LIMIT %d", rowslimit); + if (limit != NULL) { + query = __merge_strings(query, limit, ' '); + sqlite3_free(limit); + } + } + + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + errorcode = + sqlite3_prepare_v2(g_dp_db_handle, query, -1, &stmt, NULL); + sqlite3_free(query); + if (errorcode != SQLITE_OK) { + TRACE_ERROR("sqlite3_prepare_v2 is failed. [%s]", + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + for (i = 0; i < conds_count; i++) { + if (__bind_value + (stmt, conds[i].type, conds[i].value, (i + 1)) != + SQLITE_OK) { + TRACE_ERROR + ("[BIND][%d][%s]", conds[i].type, + sqlite3_errmsg(g_dp_db_handle)); + __dp_finalize(stmt); + return -1; + } + } + while ((errorcode = sqlite3_step(stmt)) == SQLITE_ROW) { + switch (gettype) { + case DP_DB_COL_TYPE_INT: + { + int **list_int_p = (int **)list; + *list_int_p[rows_count++] = sqlite3_column_int(stmt, 0); + break; + } + case DP_DB_COL_TYPE_INT64: + { +#ifdef SQLITE_INT64_TYPE + long long **list_long_p = (long long **)list; + *list_long_p[rows_count++] = sqlite3_column_int64(stmt, 0); +#else + int **list_int_p = (int **)list; + *list_int_p[rows_count++] = sqlite3_column_int(stmt, 0); +#endif + break; + } + case DP_DB_COL_TYPE_TEXT: + { + int getbytes = sqlite3_column_bytes(stmt, 0); + if (getbytes > 0) { + char *getstr = (char *)calloc((getbytes + 1), sizeof(char)); + if (getstr != NULL) { + memcpy(getstr, sqlite3_column_text(stmt, 0), + getbytes * sizeof(char)); + getstr[getbytes] = '\0'; + list[rows_count++] = getstr; + } + } + break; + } + default: + break; + } + if (rows_count >= rowslimit) + break; + } + __dp_finalize(stmt); + return rows_count; +} + +static void __dp_db_reset(sqlite3_stmt *stmt) +{ + if (stmt != 0) { + sqlite3_clear_bindings(stmt); + if (sqlite3_reset(stmt) != SQLITE_OK) { + sqlite3 *handle = sqlite3_db_handle(stmt); + TRACE_ERROR("failed sqlite3_reset [%s]", + sqlite3_errmsg(handle)); + } + } +} + +int dp_db_request_new_logging(const int id, const int state, const char *pkgname) +{ + int errorcode = SQLITE_OK; + int ret = -1; + char *query = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (g_dp_db_logging_new_stmt == NULL) { + query = + sqlite3_mprintf("INSERT INTO %s (%s, %s, %s, %s) VALUES (?, ?, ?, DATETIME('now'))", + DP_DB_TABLE_LOG, DP_DB_COL_ID, DP_DB_COL_STATE, + DP_DB_COL_PACKAGENAME, DP_DB_COL_CREATE_TIME); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + TRACE_DEBUG("query:%s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &g_dp_db_logging_new_stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + } + if (sqlite3_bind_int(g_dp_db_logging_new_stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_new_stmt); + return -1; + } + if (sqlite3_bind_int(g_dp_db_logging_new_stmt, 2, state) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_new_stmt); + return -1; + } + if (sqlite3_bind_text(g_dp_db_logging_new_stmt, 3, pkgname, -1, SQLITE_STATIC) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_new_stmt); + return -1; + } + + errorcode = sqlite3_step(g_dp_db_logging_new_stmt); + __dp_db_reset(g_dp_db_logging_new_stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + return 0; + } + return -1; +} + +int dp_db_request_update_status(const int id, const int state, const int download_error) +{ + int errorcode = SQLITE_OK; + int ret = -1; + char *query = NULL; + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[SQL HANDLE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (g_dp_db_logging_status_stmt == NULL) { + query = + sqlite3_mprintf("UPDATE %s SET %s = ?, %s = ?, %s = DATETIME('now') WHERE %s = ?", + DP_DB_TABLE_LOG, DP_DB_COL_STATE, DP_DB_COL_ERRORCODE, + DP_DB_COL_ACCESS_TIME, DP_DB_COL_ID); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + TRACE_DEBUG("query:%s", query); + + ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &g_dp_db_logging_status_stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + } + if (sqlite3_bind_int(g_dp_db_logging_status_stmt, 1, state) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_status_stmt); + return -1; + } + if (sqlite3_bind_int(g_dp_db_logging_status_stmt, 2, download_error) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_status_stmt); + return -1; + } + if (sqlite3_bind_int(g_dp_db_logging_status_stmt, 3, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_status_stmt); + return -1; + } + + errorcode = sqlite3_step(g_dp_db_logging_status_stmt); + __dp_db_reset(g_dp_db_logging_status_stmt); + if (errorcode == SQLITE_OK || errorcode == SQLITE_DONE) { + return 0; + } + return -1; +} + +int dp_db_get_state(int id) +{ + if (id <= 0) { + TRACE_ERROR("[CHECK ID]"); + return -1; + } + + if (__dp_sql_open() < 0) { + TRACE_ERROR("[OPEN] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + + if (g_dp_db_logging_get_state_stmt == NULL) { + char *query = sqlite3_mprintf("SELECT %s FROM %s WHERE %s = ?", + DP_DB_COL_STATE, DP_DB_TABLE_LOG, DP_DB_COL_ID); + if (query == NULL) { + TRACE_ERROR("[CHECK COMBINE]"); + return -1; + } + + TRACE_DEBUG("query:%s", query); + + int ret = sqlite3_prepare_v2(g_dp_db_handle, query, -1, &g_dp_db_logging_get_state_stmt, NULL); + sqlite3_free(query); + if (ret != SQLITE_OK) { + TRACE_ERROR("[PREPARE] [%s]", sqlite3_errmsg(g_dp_db_handle)); + return -1; + } + } + + if (sqlite3_bind_int(g_dp_db_logging_get_state_stmt, 1, id) != SQLITE_OK) { + TRACE_ERROR("[BIND] [%s]", sqlite3_errmsg(g_dp_db_handle)); + __dp_db_reset(g_dp_db_logging_get_state_stmt); + return -1; + } + + int state = DP_STATE_NONE; + if (sqlite3_step(g_dp_db_logging_get_state_stmt) == SQLITE_ROW) { + state = sqlite3_column_int(g_dp_db_logging_get_state_stmt, 0); + } + __dp_db_reset(g_dp_db_logging_get_state_stmt); + return state; +} diff --git a/provider/download-provider-main.c b/provider/download-provider-main.c new file mode 100755 index 0000000..6e71cb5 --- /dev/null +++ b/provider/download-provider-main.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <glib.h> +#include <glib-object.h> +#include <pthread.h> +#include <locale.h> +#include <libintl.h> +#include <systemd/sd-daemon.h> + +#include "vconf.h" + +#include "download-provider-config.h" +#include "download-provider-log.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-slots.h" +#include "download-provider-db.h" +#include "download-provider-network.h" +#include "download-provider-queue.h" +#include "download-provider-notification.h" +#include "download-provider-da-interface.h" + +// declare functions +int dp_lock_pid(char *path); +void *dp_thread_requests_manager(void *arg); +static pthread_t g_dp_thread_queue_manager_pid = 0; + +// declare global variables +// need for libsoup, decided the life-time by mainloop. +GMainLoop *g_main_loop_handle = 0; + +void dp_terminate(int signo) +{ + TRACE_DEBUG("Received SIGTERM:%d", signo); + if (g_main_loop_is_running(g_main_loop_handle)) + g_main_loop_quit(g_main_loop_handle); +} + +static gboolean __dp_idle_start_service(void *data) +{ + // declare all resources + pthread_t thread_pid; + pthread_attr_t thread_attr; + + // initialize + if (pthread_attr_init(&thread_attr) != 0) { + TRACE_STRERROR("failed to init pthread attr"); + dp_terminate(SIGTERM); + return FALSE; + } + if (pthread_attr_setdetachstate(&thread_attr, + PTHREAD_CREATE_DETACHED) != 0) { + TRACE_STRERROR("failed to set detach option"); + dp_terminate(SIGTERM); + return FALSE; + } + + // create thread for managing QUEUEs + if (pthread_create + (&g_dp_thread_queue_manager_pid, NULL, dp_thread_queue_manager, + data) != 0) { + TRACE_STRERROR + ("failed to create pthread for run_manage_download_server"); + dp_terminate(SIGTERM); + } + + // start service, accept url-download ( client package ) + if (pthread_create + (&thread_pid, &thread_attr, dp_thread_requests_manager, + data) != 0) { + TRACE_STRERROR + ("failed to create pthread for run_manage_download_server"); + dp_terminate(SIGTERM); + } + return FALSE; +} + +void __set_locale() +{ + char *str = NULL; + str = vconf_get_str(VCONFKEY_LANGSET); + if (str != NULL) { + setlocale(LC_ALL, str); + bindtextdomain(PKG_NAME, LOCALE_DIR); + textdomain(PKG_NAME); + } + free(str); +} +void __lang_changed_cb(keynode_t *key, void* data) +{ + __set_locale(); +} + +int main(int argc, char **argv) +{ + dp_privates *privates = NULL; + int lock_fd = -1; + + if (chdir("/") < 0) { + TRACE_STRERROR("failed to call setsid or chdir"); + exit(EXIT_FAILURE); + } + +#if 0 + // close all console I/O + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +#endif + + if (signal(SIGTERM, dp_terminate) == SIG_ERR) { + TRACE_ERROR("failed to register signal callback"); + exit(EXIT_FAILURE); + } + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + TRACE_ERROR("failed to register signal callback"); + exit(EXIT_FAILURE); + } + if (signal(SIGINT, dp_terminate) == SIG_ERR) { + TRACE_ERROR("failed to register signal callback"); + exit(EXIT_FAILURE); + } + // write IPC_FD_PATH. and lock + if ((lock_fd = dp_lock_pid(DP_LOCK_PID)) < 0) { + TRACE_ERROR + ("It need to check download-provider is already alive"); + TRACE_ERROR("Or fail to create pid file in (%s)", + DP_LOCK_PID); + exit(EXIT_FAILURE); + } + + g_type_init(); + + // locale + __set_locale(); + if (vconf_notify_key_changed(VCONFKEY_LANGSET, __lang_changed_cb, NULL) != 0) + TRACE_ERROR("Fail to set language changed vconf callback"); + + privates = (dp_privates *) calloc(1, sizeof(dp_privates)); + if (!privates) { + TRACE_ERROR("[CRITICAL] failed to alloc for private info"); + goto DOWNLOAD_EXIT; + } + privates->groups = dp_client_group_slots_new(DP_MAX_GROUP); + if (privates->groups == NULL) { + TRACE_ERROR("[CRITICAL] failed to alloc for groups"); + goto DOWNLOAD_EXIT; + } + privates->requests = dp_request_slots_new(DP_MAX_REQUEST); + if (privates->requests == NULL) { + TRACE_ERROR("[CRITICAL] failed to alloc for requests"); + goto DOWNLOAD_EXIT; + } + + // ready socket ( listen ) + privates->listen_fd = dp_accept_socket_new(); + if (privates->listen_fd < 0) { + TRACE_ERROR("[CRITICAL] failed to bind SOCKET"); + goto DOWNLOAD_EXIT; + } + + dp_db_open(); + + // convert to request type, insert all to privates->requests + // timeout of request thread will start these jobs by queue thread + // load all list from (queue table) + if (dp_db_crashed_list(privates->requests, DP_MAX_REQUEST) > 0) { + int i = 0; + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (!privates->requests[i].request) + continue; + dp_request *request = privates->requests[i].request; + TRACE_DEBUG + ("ID [%d] state[%d]", request->id, request->state); + + // load to memory, Can be started automatically. + if (request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_CONNECTING) { + request->state = DP_STATE_QUEUED; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + } + + if (request->state == DP_STATE_QUEUED) { + int auto_download = dp_db_get_int_column(request->id, + DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_AUTO_DOWNLOAD); + if (auto_download == 1) { + // auto retry... defaultly, show notification + request->auto_notification = 1; + request->start_time = (int)time(NULL); + continue; + } + // do not retry this request + request->state = DP_STATE_FAILED; + request->error = DP_ERROR_SYSTEM_DOWN; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &request->state) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, + DP_DB_COL_ERRORCODE, DP_DB_COL_TYPE_INT, + &request->error) < 0) { + TRACE_ERROR("[CHECK SQL]"); + } + } + + // if wanna restart, call continue before this line. + // default. update state/error. move to history. unload memory + // remove from memory + dp_request_free(request); + privates->requests[i].request = NULL; + } + } // query crashed_list + +#if 0 + if (argc != 2 || memcmp(argv[1], "service", 7) != 0) { + // in first launch in booting time, not request. terminate by self + if (dp_get_request_count(privates->requests) <= 0) { + TRACE_DEBUG("First Boot, No Request"); + goto DOWNLOAD_EXIT; + } + } +#endif + + dp_clear_downloadinginfo_notification(); + + if (dp_init_agent() != DP_ERROR_NONE) { + TRACE_ERROR("[CRITICAL] failed to init agent"); + goto DOWNLOAD_EXIT; + } + + privates->connection = 0; + privates->network_status = DP_NETWORK_TYPE_OFF; + if (dp_network_connection_init(privates) < 0) { + TRACE_DEBUG("use instant network check"); + privates->connection = 0; + } + + // libsoup need mainloop. + g_main_loop_handle = g_main_loop_new(NULL, 0); + + g_idle_add(__dp_idle_start_service, privates); + + sd_notify(0, "READY=1"); + + g_main_loop_run(g_main_loop_handle); + +DOWNLOAD_EXIT : + + TRACE_DEBUG("Download-Provider will be terminated."); + if (vconf_ignore_key_changed(VCONFKEY_LANGSET, __lang_changed_cb) != 0) + TRACE_ERROR("Fail to unset language changed vconf callback"); + + dp_deinit_agent(); + + if (privates != NULL) { + + if (privates->connection) + dp_network_connection_destroy(privates->connection); + + if (privates->listen_fd >= 0) + privates->listen_fd = -1; + + if (g_dp_thread_queue_manager_pid > 0 && + pthread_kill(g_dp_thread_queue_manager_pid, 0) != ESRCH) { + //pthread_cancel(g_dp_thread_queue_manager_pid); + //send signal to queue thread + dp_thread_queue_manager_wake_up(); + int status; + pthread_join(g_dp_thread_queue_manager_pid, (void **)&status); + g_dp_thread_queue_manager_pid = 0; + } + dp_request_slots_free(privates->requests, DP_MAX_REQUEST); + privates->requests = NULL; + dp_client_group_slots_free(privates->groups, DP_MAX_GROUP); + privates->groups = NULL; + free(privates); + privates = NULL; + } + dp_db_close(); + + // delete pid file + if (access(DP_LOCK_PID, F_OK) == 0) { + close(lock_fd); + unlink(DP_LOCK_PID); + } + exit(EXIT_SUCCESS); +} diff --git a/provider/download-provider-network.c b/provider/download-provider-network.c new file mode 100755 index 0000000..ac3fc5b --- /dev/null +++ b/provider/download-provider-network.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-pthread.h" +#include "download-provider-network.h" + +#ifdef SUPPORT_WIFI_DIRECT +#include <wifi-direct.h> +#endif + +extern pthread_mutex_t g_dp_queue_mutex; +extern pthread_cond_t g_dp_queue_cond; + +#if 0 +typedef enum +{ + CONNECTION_TYPE_DISCONNECTED = 0, /**< Disconnected */ + CONNECTION_TYPE_WIFI = 1, /**< Wi-Fi type */ + CONNECTION_TYPE_CELLULAR = 2, /**< Cellular type */ + CONNECTION_TYPE_ETHERNET = 3, /**< Ethernet type */ + CONNECTION_TYPE_BT = 4, /**< Bluetooth type */ +} connection_type_e; +typedef enum +{ + CONNECTION_CELLULAR_STATE_OUT_OF_SERVICE = 0, /**< Out of service */ + CONNECTION_CELLULAR_STATE_FLIGHT_MODE = 1, /**< Flight mode */ + CONNECTION_CELLULAR_STATE_ROAMING_OFF = 2, /**< Roaming is turned off */ + CONNECTION_CELLULAR_STATE_CALL_ONLY_AVAILABLE = 3, /**< Call is only available */ + CONNECTION_CELLULAR_STATE_AVAILABLE = 4, /**< Available but not connected yet */ + CONNECTION_CELLULAR_STATE_CONNECTED = 5, /**< Connected */ +} connection_cellular_state_e +typedef enum +{ + CONNECTION_WIFI_STATE_DEACTIVATED = 0, /**< Deactivated state */ + CONNECTION_WIFI_STATE_DISCONNECTED = 1, /**< disconnected state */ + CONNECTION_WIFI_STATE_CONNECTED = 2, /**< Connected state */ +} connection_wifi_state_e; +typedef enum +{ + CONNECTION_ETHERNET_STATE_DEACTIVATED = 0, /**< Deactivated state */ + CONNECTION_ETHERNET_STATE_DISCONNECTED = 1, /**< disconnected state */ + CONNECTION_ETHERNET_STATE_CONNECTED = 2, /**< Connected state */ +} connection_ethernet_state_e; +typedef enum +{ + CONNECTION_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ + CONNECTION_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ + CONNECTION_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory error */ + CONNECTION_ERROR_INVALID_OPERATION = TIZEN_ERROR_INVALID_OPERATION, /**< Invalid Operation */ + CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED = TIZEN_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED, /**< Address family not supported */ + CONNECTION_ERROR_OPERATION_FAILED = TIZEN_ERROR_NETWORK_CLASS|0x0401, /**< Operation failed */ + CONNECTION_ERROR_ITERATOR_END = TIZEN_ERROR_NETWORK_CLASS|0x0402, /**< End of iteration */ + CONNECTION_ERROR_NO_CONNECTION = TIZEN_ERROR_NETWORK_CLASS|0x0403, /**< There is no connection */ + CONNECTION_ERROR_NOW_IN_PROGRESS = TIZEN_ERROR_NOW_IN_PROGRESS, /** Now in progress */ + CONNECTION_ERROR_ALREADY_EXISTS = TIZEN_ERROR_NETWORK_CLASS|0x0404, /**< Already exists */ + CONNECTION_ERROR_OPERATION_ABORTED = TIZEN_ERROR_NETWORK_CLASS|0x0405, /**< Operation is aborted */ + CONNECTION_ERROR_DHCP_FAILED = TIZEN_ERROR_NETWORK_CLASS|0x0406, /**< DHCP failed */ + CONNECTION_ERROR_INVALID_KEY = TIZEN_ERROR_NETWORK_CLASS|0x0407, /**< Invalid key */ + CONNECTION_ERROR_NO_REPLY = TIZEN_ERROR_NETWORK_CLASS|0x0408, /**< No reply */ +} connection_error_e; + + +static void __print_connection_errorcode_to_string(connection_error_e errorcode) +{ + switch(errorcode) + { + case CONNECTION_ERROR_INVALID_PARAMETER : + TRACE_DEBUG("CONNECTION_ERROR_INVALID_PARAMETER"); + break; + case CONNECTION_ERROR_OUT_OF_MEMORY : + TRACE_DEBUG("CONNECTION_ERROR_OUT_OF_MEMORY"); + break; + case CONNECTION_ERROR_INVALID_OPERATION : + TRACE_DEBUG("CONNECTION_ERROR_INVALID_OPERATION"); + break; + case CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED : + TRACE_DEBUG("CONNECTION_ERROR_ADDRESS_FAMILY_NOT_SUPPORTED"); + break; + case CONNECTION_ERROR_OPERATION_FAILED : + TRACE_DEBUG("CONNECTION_ERROR_OPERATION_FAILED"); + break; + case CONNECTION_ERROR_ITERATOR_END : + TRACE_DEBUG("CONNECTION_ERROR_ITERATOR_END"); + break; + case CONNECTION_ERROR_NO_CONNECTION : + TRACE_DEBUG("CONNECTION_ERROR_NO_CONNECTION"); + break; + case CONNECTION_ERROR_NOW_IN_PROGRESS : + TRACE_DEBUG("CONNECTION_ERROR_NOW_IN_PROGRESS"); + break; + case CONNECTION_ERROR_ALREADY_EXISTS : + TRACE_DEBUG("CONNECTION_ERROR_ALREADY_EXISTS"); + break; + case CONNECTION_ERROR_OPERATION_ABORTED : + TRACE_DEBUG("CONNECTION_ERROR_OPERATION_ABORTED"); + break; + case CONNECTION_ERROR_DHCP_FAILED : + TRACE_DEBUG("CONNECTION_ERROR_DHCP_FAILED"); + break; + case CONNECTION_ERROR_INVALID_KEY : + TRACE_DEBUG("CONNECTION_ERROR_INVALID_KEY"); + break; + case CONNECTION_ERROR_NO_REPLY : + TRACE_DEBUG("CONNECTION_ERROR_NO_REPLY"); + break; + default : + TRACE_DEBUG("CONNECTION_ERROR_NONE"); + break; + } +} +#endif + + +#ifdef SUPPORT_WIFI_DIRECT +// support WIFI-Direct +static void __dp_network_wifi_direct_connection_state_changed_cb(wifi_direct_error_e error_code, wifi_direct_connection_state_e connection_state, const char *mac_address, void *data) +{ + dp_privates *privates = (dp_privates*)data; + if (privates == NULL) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return ; + } + + if (connection_state == WIFI_DIRECT_CONNECTION_RSP) { + TRACE_DEBUG("WIFI_DIRECT_CONNECTION_RSP"); + privates->is_connected_wifi_direct = 1; + return ; + } + privates->is_connected_wifi_direct = 0; +} + +//return 0 : connected +int dp_network_wifi_direct_is_connected() +{ + int is_connected = -1; + wifi_direct_state_e wifi_state = WIFI_DIRECT_STATE_DEACTIVATED; + if (wifi_direct_get_state(&wifi_state) == 0) { + switch (wifi_state) + { + case WIFI_DIRECT_STATE_DEACTIVATED : + TRACE_DEBUG("WIFI_DIRECT_STATE_DEACTIVATED"); + break; + case WIFI_DIRECT_STATE_DEACTIVATING : + TRACE_DEBUG("WIFI_DIRECT_STATE_DEACTIVATING"); + break; + case WIFI_DIRECT_STATE_ACTIVATING : + TRACE_DEBUG("WIFI_DIRECT_STATE_ACTIVATING"); + break; + case WIFI_DIRECT_STATE_ACTIVATED : + TRACE_DEBUG("WIFI_DIRECT_STATE_ACTIVATED"); + break; + case WIFI_DIRECT_STATE_DISCOVERING : + TRACE_DEBUG("WIFI_DIRECT_STATE_DISCOVERING"); + break; + case WIFI_DIRECT_STATE_CONNECTING : + TRACE_DEBUG("WIFI_DIRECT_STATE_CONNECTING"); + break; + case WIFI_DIRECT_STATE_DISCONNECTING : + TRACE_DEBUG("WIFI_DIRECT_STATE_DISCONNECTING"); + break; + case WIFI_DIRECT_STATE_CONNECTED : + is_connected = 0; + TRACE_DEBUG("WIFI_DIRECT_STATE_CONNECTED"); + break; + default : + break; + } + } + return is_connected; +} +#endif + + +////////////////////////////////////////////////////////////////////////// +/// @brief check the status in more detail by connection type +/// @return dp_network_type +static dp_network_type __dp_get_network_connection_status(connection_h connection, connection_type_e type) +{ + dp_network_type network_type = DP_NETWORK_TYPE_OFF; + if (type == CONNECTION_TYPE_WIFI) { + connection_wifi_state_e wifi_state; + wifi_state = CONNECTION_WIFI_STATE_DEACTIVATED; + if (connection_get_wifi_state + (connection, &wifi_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_wifi_state"); + if (wifi_state == CONNECTION_WIFI_STATE_CONNECTED) { + TRACE_DEBUG("[CONNECTION_WIFI] CONNECTED"); + network_type = DP_NETWORK_TYPE_WIFI; + } else { + TRACE_DEBUG("[CONNECTION_WIFI] [%d]", wifi_state); + } + } else if (type == CONNECTION_TYPE_CELLULAR) { + connection_cellular_state_e cellular_state; + cellular_state = CONNECTION_CELLULAR_STATE_OUT_OF_SERVICE; + if (connection_get_cellular_state + (connection, &cellular_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_cellular_state"); + if (cellular_state == CONNECTION_CELLULAR_STATE_CONNECTED) { + TRACE_DEBUG("[CONNECTION_CELLULAR] DATA NETWORK CONNECTED"); + network_type = DP_NETWORK_TYPE_DATA_NETWORK; + } else { + TRACE_DEBUG("[CONNECTION_CELLULAR] [%d]", cellular_state); + } + } else if (type == CONNECTION_TYPE_ETHERNET) { + connection_ethernet_state_e ethernet_state; + ethernet_state = CONNECTION_ETHERNET_STATE_DISCONNECTED; + if (connection_get_ethernet_state + (connection, ðernet_state) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_get_ethernet_state"); + if (ethernet_state == CONNECTION_ETHERNET_STATE_CONNECTED) { + TRACE_DEBUG("[CONNECTION_ETHERNET] ETHERNET CONNECTED"); + network_type = DP_NETWORK_TYPE_ETHERNET; + } else { + TRACE_DEBUG("[CONNECTION_ETHERNET] [%d]", ethernet_state); + } + } else { + TRACE_DEBUG("[DISCONNECTED]"); + network_type = DP_NETWORK_TYPE_OFF; + } + return network_type; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief [callback] called whenever changed network status +/// @todo care requests by network status +static void __dp_network_connection_type_changed_cb(connection_type_e type, void *data) +{ + dp_privates *privates = (dp_privates*)data; + if (privates == NULL) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return ; + } + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + #if 1 // this callback guarantee that already connectdd + if (type == CONNECTION_TYPE_WIFI) { + TRACE_DEBUG("[CONNECTION_WIFI] CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_WIFI; + } else if (type == CONNECTION_TYPE_CELLULAR) { + TRACE_DEBUG("[CONNECTION_CELLULAR] DATA NETWORK CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_DATA_NETWORK; + } else if (type == CONNECTION_TYPE_ETHERNET) { + TRACE_DEBUG("[CONNECTION_ETHERNET] ETHERNET CONNECTED"); + privates->network_status = DP_NETWORK_TYPE_ETHERNET; + } else { + TRACE_DEBUG("[DISCONNECTED]"); + privates->network_status = DP_NETWORK_TYPE_OFF; + } + if (privates->network_status != DP_NETWORK_TYPE_OFF) + pthread_cond_signal(&g_dp_queue_cond); + #else + privates->network_status = + __dp_get_network_connection_status(privates->connection, type); + #endif + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); +} + +////////////////////////////////////////////////////////////////////////// +/// @brief [callback] called when changed network ip +/// @todo auto resume feature +static void __dp_network_connection_ip_changed_cb(const char *ip, const char *ipv6, void *data) +{ + dp_privates *privates = (dp_privates*)data; + if (privates == NULL) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return ; + } + if (privates->network_status != DP_NETWORK_TYPE_OFF) { + dp_request_slots *requests = privates->requests; + int i = 0; + for (i = 0; i < DP_MAX_REQUEST; i++) { + int locked = pthread_mutex_trylock(&requests[i].mutex); + // locking failure means it used by other thread. + if (locked == 0) { + if (requests[i].request != NULL) { + if (requests[i].request->state == DP_STATE_DOWNLOADING || + (requests[i].request->state == DP_STATE_FAILED && + requests[i].request->error == DP_ERROR_CONNECTION_FAILED)) { + requests[i].request->ip_changed = 1; + } + } + CLIENT_MUTEX_UNLOCK(&requests[i].mutex); + } + } + } +} + +////////////////////////////////////////////////////////////////////////// +/// @brief create connection handle & regist callback +/// @return 0 : success -1 : failed +int dp_network_connection_init(dp_privates *privates) +{ + int retcode = 0; + + if (!privates) { + TRACE_ERROR("[CRITICAL] Invalid data"); + return -1; + } + +#ifdef SUPPORT_WIFI_DIRECT + if (wifi_direct_initialize() == 0) { + wifi_direct_set_connection_state_changed_cb + (__dp_network_wifi_direct_connection_state_changed_cb, privates); + if (dp_network_wifi_direct_is_connected() == 0) + privates->is_connected_wifi_direct = 1; + } +#endif + + if ((retcode = connection_create(&privates->connection)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_create [%d]", retcode); + return -1; + } + if ((retcode = connection_set_type_changed_cb + (privates->connection, __dp_network_connection_type_changed_cb, + privates)) != CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_set_type_changed_cb [%d]", retcode); + connection_destroy(privates->connection); + return -1; + } + if ((retcode = connection_set_ip_address_changed_cb + (privates->connection, __dp_network_connection_ip_changed_cb, + privates)) != CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed __dp_network_connection_ip_changed_cb [%d]", retcode); + connection_destroy(privates->connection); + return -1; + } + connection_type_e type = CONNECTION_TYPE_DISCONNECTED; + if ((retcode = connection_get_type(privates->connection, &type)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_get_type [%d]", retcode); + connection_destroy(privates->connection); + return -1; + } + privates->network_status = + __dp_get_network_connection_status(privates->connection, type); + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief destroy connection handle +void dp_network_connection_destroy(connection_h connection) +{ +#ifdef SUPPORT_WIFI_DIRECT + wifi_direct_unset_connection_state_changed_cb(); + wifi_direct_deinitialize(); +#endif + + connection_unset_type_changed_cb(connection); + connection_unset_ip_address_changed_cb(connection); + connection_destroy(connection); +} + +////////////////////////////////////////////////////////////////////////// +/// @brief check network status using connection API +/// @todo the standard of enabled networking can be changed later +/// @return Network type +dp_network_type dp_get_network_connection_instant_status() +{ + int retcode = 0; + connection_h network_handle = NULL; + dp_network_type network_type = DP_NETWORK_TYPE_OFF; + if ((retcode = connection_create(&network_handle)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_create [%d]", retcode); + return DP_NETWORK_TYPE_OFF; + } + + connection_type_e type = CONNECTION_TYPE_DISCONNECTED; + if ((retcode = connection_get_type(network_handle, &type)) != + CONNECTION_ERROR_NONE) { + TRACE_ERROR("Failed connection_get_type [%d]", retcode); + connection_destroy(network_handle); + return DP_NETWORK_TYPE_OFF; + } + network_type = + __dp_get_network_connection_status(network_handle, type); + + if (connection_destroy(network_handle) != CONNECTION_ERROR_NONE) + TRACE_ERROR("Failed connection_destroy"); + + return network_type; +} diff --git a/provider/download-provider-notification.c b/provider/download-provider-notification.c new file mode 100755 index 0000000..f6e2f8b --- /dev/null +++ b/provider/download-provider-notification.c @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <time.h> +#include <sys/time.h> +#include <stdlib.h> +#include <stdio.h> + +#include "bundle.h" +#include "notification.h" +#include "appsvc.h" + +#include "download-provider-defs.h" + +#include "download-provider-notification.h" +#include "download-provider-request.h" +#include "download-provider-db.h" +#include "download-provider-log.h" + +#include <libintl.h> +#define S_(s) dgettext("sys_string", s) +#define __(s) dgettext(PKG_NAME, s) + +#define DP_NOTIFICATION_ICON_PATH IMAGE_DIR"/Q02_Notification_download_complete.png" +#define DP_NOTIFICATION_ONGOING_ICON_PATH IMAGE_DIR"/Notification_download_animation.gif" +#define DP_NOTIFICATION_DOWNLOADING_ICON_PATH "reserved://indicator/ani/downloading" +#define DP_NOTIFICATION_FAILED_ICON_PATH IMAGE_DIR"/Q02_Notification_download_failed.png" + +static const char *__noti_error_str( + notification_error_e err) +{ + switch (err) { + case NOTIFICATION_ERROR_INVALID_DATA: + return "NOTIFICATION_ERROR_INVALID_DATA"; + case NOTIFICATION_ERROR_NO_MEMORY: + return "NOTIFICATION_ERROR_NO_MEMORY"; + case NOTIFICATION_ERROR_FROM_DB: + return "NOTIFICATION_ERROR_FROM_DB"; + case NOTIFICATION_ERROR_ALREADY_EXIST_ID: + return "NOTIFICATION_ERROR_ALREADY_EXIST_ID"; + case NOTIFICATION_ERROR_FROM_DBUS: + return "NOTIFICATION_ERROR_FROM_DBUS"; + case NOTIFICATION_ERROR_NOT_EXIST_ID: + return "NOTIFICATION_ERROR_NOT_EXIST_ID"; + default: + break; + } + return "Unknown error"; +} + +static char *__get_string_sender(char *url) +{ + char *temp = NULL; + char *found = NULL; + char *found1 = NULL; + char *sender = NULL; + char *credential_sender = NULL; + + if (url == NULL) + return NULL; + + found = strstr(url, "://"); + if (found) { + temp = found + 3; + } else { + temp = url; + } + found = strchr(temp, '/'); + if (found) { + int len = 0; + len = found - temp; + sender = calloc(len + 1, sizeof(char)); + if (sender == NULL) + return NULL; + snprintf(sender, len + 1, "%s", temp); + } else { + sender = dp_strdup(temp); + } + + // For credential URL + found = strchr(sender, '@'); + found1 = strchr(sender, ':'); + if (found && found1 && found1 < found) { + int len = 0; + found = found + 1; + len = strlen(found); + credential_sender = calloc(len + 1, sizeof(char)); + if (credential_sender == NULL) { + free(sender); + return NULL; + } + snprintf(credential_sender, len + 1, "%s", found); + free(sender); + return credential_sender; + } else { + return sender; + } +} + +static char *__get_string_size(long long file_size) +{ + const char *unitStr[4] = {"B", "KB", "MB", "GB"}; + double doubleTypeBytes = 0.0; + int unit = 0; + long long bytes = file_size; + long long unitBytes = bytes; + char *temp = NULL; + + /* using bit operation to avoid floating point arithmetic */ + for (unit = 0; (unitBytes > 1024 && unit < 4); unit++) { + unitBytes = unitBytes >> 10; + } + unitBytes = 1 << (10 * unit); + + if (unit > 3) + unit = 3; + + char str[64] = {0}; + if (unit == 0) { + snprintf(str, sizeof(str), "%lld %s", bytes, unitStr[unit]); + } else { + doubleTypeBytes = ((double)bytes / (double)unitBytes); + snprintf(str, sizeof(str), "%.2f %s", doubleTypeBytes, unitStr[unit]); + } + + str[63] = '\0'; + temp = dp_strdup(str); + return temp; +} + +static char *__get_string_status(dp_state_type state) +{ + char *message = NULL; + switch (state) { + case DP_STATE_COMPLETED: + message = __("IDS_DM_HEADER_DOWNLOAD_COMPLETE"); + break; + case DP_STATE_CANCELED: + case DP_STATE_FAILED: + message = S_("IDS_COM_POP_DOWNLOAD_FAILED"); + break; + default: + break; + } + return message; +} + +int dp_set_downloadinginfo_notification(int id, char *packagename) +{ + notification_h noti_handle = NULL; + notification_error_e err = NOTIFICATION_ERROR_NONE; + int privId = 0; + bundle *b = NULL; + void *blob_data = NULL; + bundle_raw* b_raw = NULL; + int length; + + noti_handle = notification_create(NOTIFICATION_TYPE_ONGOING); + if (!noti_handle) { + TRACE_ERROR("[FAIL] create notification handle"); + return -1; + } + + char *title = dp_db_get_text_column(id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_TITLE); + if(title != NULL) { + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_TITLE, + title, NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + free(title); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + } else { + char *content_name = + dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + + if (content_name == NULL) + content_name = strdup("No Name"); + + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_TITLE, content_name, NULL, + NOTIFICATION_VARIABLE_TYPE_NONE); + if (content_name) + free(content_name); + } + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_image(noti_handle, + NOTIFICATION_IMAGE_TYPE_ICON, DP_NOTIFICATION_ONGOING_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + blob_data = dp_db_get_blob_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_RAW_BUNDLE_ONGOING, &length); + if (blob_data != NULL) { + b_raw = (bundle_raw*)blob_data; + if (b_raw != NULL) { + b = bundle_decode_raw(b_raw, length); + if (b != NULL) { + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%s]", __noti_error_str(err)); + bundle_free_encoded_rawdata(&b_raw); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free_encoded_rawdata(&b_raw); + bundle_free(b); + } + } + } else { + b = bundle_create(); + if (!b) { + TRACE_ERROR("[FAIL] create bundle"); + notification_free(noti_handle); + return -1; + } + + if (packagename && + appsvc_set_pkgname(b, packagename) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set pkg name"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%s]", __noti_error_str(err)); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free(b); + } + err = notification_set_property(noti_handle, + NOTIFICATION_PROP_DISABLE_TICKERNOTI); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set property [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_image(noti_handle, + NOTIFICATION_IMAGE_TYPE_ICON_FOR_INDICATOR, DP_NOTIFICATION_DOWNLOADING_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon indicator [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_display_applist(noti_handle, NOTIFICATION_DISPLAY_APP_ALL); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set disable icon [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_insert(noti_handle, &privId); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set insert [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + TRACE_DEBUG("m_noti_id [%d]", privId); + notification_free(noti_handle); + return privId; +} + +int dp_set_downloadedinfo_notification(int priv_id, int id, char *packagename, dp_state_type state) +{ + notification_h noti_handle = NULL; + notification_error_e err = NOTIFICATION_ERROR_NONE; + int privId = 0; + bundle *b = NULL; + void *blob_data = NULL; + bundle_raw* b_raw = NULL; + int length = 0; + + if (priv_id >= 0) { + err = notification_delete_by_priv_id(NULL, NOTIFICATION_TYPE_ONGOING, + priv_id); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] delete notification handle [%s]", + __noti_error_str(err)); + } + } + + noti_handle = notification_create(NOTIFICATION_TYPE_NOTI); + + if (!noti_handle) { + TRACE_ERROR("[FAIL] create notification handle"); + return -1; + } + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_TITLE, + __get_string_status(state), NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + char *description = + dp_db_get_text_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_DESCRIPTION); + if(description != NULL) { + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_INFO_2, description, + NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + free(description); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set description [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + } else { + char *url = NULL; + url = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_URL); + if (url) { + char *sender_str = __get_string_sender(url); + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_INFO_2, + sender_str, NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + free(sender_str); + free(url); + notification_free(noti_handle); + return -1; + } + free(sender_str); + free(url); + } + } + + char *title = dp_db_get_text_column(id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_TITLE); + if(title != NULL) { + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_CONTENT, + title, NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + free(title); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + } else { + char *content_name = + dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + if (content_name == NULL) + content_name = strdup("No Name"); + + err = notification_set_text(noti_handle, + NOTIFICATION_TEXT_TYPE_CONTENT, content_name, + NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + free(content_name); + } + + time_t tt = time(NULL); + err = notification_set_time_to_text(noti_handle, + NOTIFICATION_TEXT_TYPE_INFO_SUB_1, tt); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set time [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + if (state == DP_STATE_COMPLETED) { + blob_data = dp_db_get_blob_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_RAW_BUNDLE_COMPLETE, &length); + if (blob_data != NULL) { + b_raw = (bundle_raw*)blob_data; + if (b_raw != NULL) { + b = bundle_decode_raw(b_raw, length); + if (b != NULL) { + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%s]", __noti_error_str(err)); + bundle_free_encoded_rawdata(&b_raw); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free_encoded_rawdata(&b_raw); + } + } + } else { + char *file_path = NULL; + b = bundle_create(); + if (!b) { + TRACE_ERROR("[FAIL] create bundle"); + notification_free(noti_handle); + return -1; + } + if (appsvc_set_operation(b, APPSVC_OPERATION_VIEW) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] appsvc set operation"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + file_path = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_SAVED_PATH); + if (file_path == NULL) { + TRACE_ERROR("[FAIL] get file path"); + bundle_free(b); + notification_free(noti_handle); + return -1; + + } + if (appsvc_set_uri(b, file_path) != APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] appsvc set uri"); + bundle_free(b); + notification_free(noti_handle); + free(file_path); + return -1; + } + free(file_path); + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option[%s]", __noti_error_str(err)); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + } + long long file_size = dp_db_get_int64_column(id, + DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_SIZE); + char *size_str = __get_string_size(file_size); + + err = notification_set_text(noti_handle, NOTIFICATION_TEXT_TYPE_INFO_1, + size_str, NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set title [%s]", __noti_error_str(err)); + free(size_str); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + free(size_str); + + err = notification_set_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON, + DP_NOTIFICATION_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + } else if (state == DP_STATE_CANCELED || state == DP_STATE_FAILED) { + blob_data = dp_db_get_blob_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_RAW_BUNDLE_FAIL, &length); + if (blob_data != NULL) { + b_raw = (bundle_raw *)blob_data; + if (b_raw != NULL) { + b = bundle_decode_raw(b_raw, length); + if (b != NULL) { + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option [%s]", __noti_error_str(err)); + bundle_free_encoded_rawdata(&b_raw); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + bundle_free_encoded_rawdata(&b_raw); + } + } + } else { + b = bundle_create(); + if (!b) { + TRACE_ERROR("[FAIL] create bundle"); + notification_free(noti_handle); + return -1; + } + if (packagename && + appsvc_set_pkgname(b, packagename) != + APPSVC_RET_OK) { + TRACE_ERROR("[FAIL] set pkg name"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + err = notification_set_execute_option(noti_handle, + NOTIFICATION_EXECUTE_TYPE_SINGLE_LAUNCH, "View", NULL, b); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set execute option[%s]", __noti_error_str(err)); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + } + err = notification_set_image(noti_handle, NOTIFICATION_IMAGE_TYPE_ICON, + DP_NOTIFICATION_FAILED_ICON_PATH); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set icon [%s]", __noti_error_str(err)); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + } else { + TRACE_ERROR("[CRITICAL] invalid state"); + bundle_free(b); + notification_free(noti_handle); + return -1; + } + + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set time [%s]", __noti_error_str(err)); + notification_free(noti_handle); + bundle_free(b); + return -1; + } + + bundle_free(b); + + err = notification_set_property(noti_handle, + NOTIFICATION_PROP_DISABLE_TICKERNOTI); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set property [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_set_display_applist(noti_handle, + NOTIFICATION_DISPLAY_APP_ALL ^ NOTIFICATION_DISPLAY_APP_INDICATOR); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set disable icon [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + err = notification_insert(noti_handle, &privId); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] set insert [%s]", __noti_error_str(err)); + notification_free(noti_handle); + return -1; + } + + TRACE_DEBUG("m_noti_id [%d]", privId); + notification_free(noti_handle); + return privId; +} + +void dp_update_downloadinginfo_notification(int priv_id, double received_size, double file_size) +{ + notification_error_e err = NOTIFICATION_ERROR_NONE; + if (priv_id < 0) { + TRACE_ERROR("[FAIL] Invalid priv_id[%d]", priv_id); + return; + } + + if (file_size > 0) { + double progress; + progress = received_size / file_size; + err = notification_update_progress(NULL, priv_id, progress); + if (err != NOTIFICATION_ERROR_NONE) + TRACE_ERROR("[FAIL] update noti progress[%s]", + __noti_error_str(err)); + } else { + err = notification_update_size(NULL, priv_id, received_size); + if (err != NOTIFICATION_ERROR_NONE) + TRACE_ERROR("[FAIL] update noti progress[%s]", + __noti_error_str(err)); + } +} + +void dp_clear_downloadinginfo_notification() +{ + notification_error_e err = NOTIFICATION_ERROR_NONE; + err = notification_delete_all_by_type(NULL, NOTIFICATION_TYPE_ONGOING); + if (err != NOTIFICATION_ERROR_NONE) { + TRACE_ERROR("[FAIL] clear noti [%s]", __noti_error_str(err)); + } + return; +} diff --git a/provider/download-provider-pid.c b/provider/download-provider-pid.c new file mode 100755 index 0000000..bd1a223 --- /dev/null +++ b/provider/download-provider-pid.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <unistd.h> +#include <fcntl.h> + +////////////////////////////////////////////////////////////////////////// +/// @brief check whether daemon is alive +/// @warning lockfd should be managed without close() +/// @param the patch for locking the file +int dp_lock_pid(char *path) +{ + int lockfd = -1; + if ((lockfd = open(path, O_WRONLY | O_CREAT, (0666 & (~000)))) < 0) { + return -1; + } else if (lockf(lockfd, F_TLOCK, 0) < 0) { + close(lockfd); + return -1; + } + return lockfd; +} diff --git a/provider/download-provider-request.c b/provider/download-provider-request.c new file mode 100755 index 0000000..cbd4af6 --- /dev/null +++ b/provider/download-provider-request.c @@ -0,0 +1,930 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <sys/time.h> +#include <sys/statfs.h> +#include <sys/smack.h> +#include "download-provider.h" +#include "download-provider-log.h" + +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-db.h" +#include "download-provider-pthread.h" + +#include "download-provider-notification.h" + +///////// below functions are called by main thread of thread-request.c + +////////////////////////////////////////////////////////////////////////// +/// @brief create unique id as integer type +/// @return unique id combined local time and the special calculation +static int __get_download_request_id(void) +{ + static int last_uniquetime = 0; + int uniquetime = 0; + + do { + struct timeval tval; + int cipher = 1; + int c = 0; + + gettimeofday(&tval, NULL); + + int usec = tval.tv_usec; + for (c = 0; ; c++, cipher++) { + if ((usec /= 10) <= 0) + break; + } + if (tval.tv_usec == 0) + tval.tv_usec = (tval.tv_sec & 0x0fff); + int disit_unit = 10; + for (c = 0; c < cipher - 3; c++) + disit_unit = disit_unit * 10; + uniquetime = tval.tv_sec + ((tval.tv_usec << 2) * 100) + + ((tval.tv_usec >> (cipher - 1)) * disit_unit) + + ((tval.tv_usec + (tval.tv_usec % 10)) & 0x0fff); + } while (last_uniquetime == uniquetime); + last_uniquetime = uniquetime; + return uniquetime; +} + +char *dp_print_state(dp_state_type state) +{ + switch(state) + { + case DP_STATE_NONE : + return "NONE"; + case DP_STATE_READY : + return "READY"; + case DP_STATE_QUEUED : + return "QUEUED"; + case DP_STATE_CONNECTING : + return "CONNECTING"; + case DP_STATE_DOWNLOADING : + return "DOWNLOADING"; + case DP_STATE_PAUSE_REQUESTED : + return "PAUSE_REQUESTED"; + case DP_STATE_PAUSED : + return "PAUSED"; + case DP_STATE_COMPLETED : + return "COMPLETED"; + case DP_STATE_CANCELED : + return "CANCELED"; + case DP_STATE_FAILED : + return "FAILED"; + default : + break; + } + return "UNKNOWN"; +} + +char *dp_print_errorcode(dp_error_type errorcode) +{ + switch(errorcode) + { + case DP_ERROR_NONE : + return "NONE"; + case DP_ERROR_INVALID_PARAMETER : + return "INVALID_PARAMETER"; + case DP_ERROR_OUT_OF_MEMORY : + return "OUT_OF_MEMORY"; + case DP_ERROR_IO_ERROR : + return "IO_ERROR"; + case DP_ERROR_NETWORK_UNREACHABLE : + return "NETWORK_UNREACHABLE"; + case DP_ERROR_CONNECTION_TIMED_OUT : + return "CONNECTION_TIMED_OUT"; + case DP_ERROR_NO_SPACE : + return "NO_SPACE"; + case DP_ERROR_FIELD_NOT_FOUND : + return "FIELD_NOT_FOUND"; + case DP_ERROR_INVALID_STATE : + return "INVALID_STATE"; + case DP_ERROR_CONNECTION_FAILED : + return "CONNECTION_FAILED"; + case DP_ERROR_INVALID_URL : + return "INVALID_URL"; + case DP_ERROR_INVALID_DESTINATION : + return "INVALID_DESTINATION"; + case DP_ERROR_QUEUE_FULL : + return "QUEUE_FULL"; + case DP_ERROR_ALREADY_COMPLETED : + return "ALREADY_COMPLETED"; + case DP_ERROR_FILE_ALREADY_EXISTS : + return "FILE_ALREADY_EXISTS"; + case DP_ERROR_TOO_MANY_DOWNLOADS : + return "TOO_MANY_DOWNLOADS"; + case DP_ERROR_NO_DATA : + return "NO_DATA"; + case DP_ERROR_UNHANDLED_HTTP_CODE : + return "UNHANDLED_HTTP_CODE"; + case DP_ERROR_CANNOT_RESUME : + return "CANNOT_RESUME"; + case DP_ERROR_PERMISSION_DENIED : + return "PERMISSION_DENIED"; + case DP_ERROR_RESPONSE_TIMEOUT : + return "RESPONSE_TIMEOUT"; + case DP_ERROR_REQUEST_TIMEOUT : + return "REQUEST_TIMEOUT"; + case DP_ERROR_SYSTEM_DOWN : + return "SYSTEM_DOWN"; + case DP_ERROR_CLIENT_DOWN : + return "CLIENT_DOWN"; + case DP_ERROR_ID_NOT_FOUND : + return "ID_NOT_FOUND"; + default : + break; + } + return "UNKNOWN"; +} + +int dp_is_smackfs_mounted() +{ + if(smack_smackfs_path() != NULL) + return 1; + TRACE_ERROR("[SMACK DISABLE]"); + return 0; +} + +char *dp_strdup(char *src) +{ + char *dest = NULL; + size_t src_len = 0; + + if (src == NULL) { + TRACE_ERROR("[CHECK PARAM]"); + return NULL; + } + + src_len = strlen(src); + if (src_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", src_len); + return NULL; + } + + dest = (char *)calloc(src_len + 1, sizeof(char)); + if (dest == NULL) { + TRACE_STRERROR("[CHECK] allocation"); + return NULL; + } + memcpy(dest, src, src_len * sizeof(char)); + dest[src_len] = '\0'; + + return dest; +} + +// check param +// create new slot +// fill info to new slot +// make new id +// save info to QUEUE(DB) +dp_error_type dp_request_create(int id, dp_client_group *group, dp_request **empty_slot) +{ + if (id != -1) { + TRACE_ERROR("[CHECK PROTOCOL] ID not -1"); + return DP_ERROR_INVALID_STATE; + } + if (group == NULL || empty_slot == NULL) { + TRACE_ERROR("[CHECK INTERNAL][%d]", id); + return DP_ERROR_INVALID_PARAMETER; + } + // New allocation Slot + dp_request *new_request = dp_request_new(); + if (new_request == NULL) { + TRACE_STRERROR("[CHECK MEMORY][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + + int check_id = -1; + do { + new_request->id = __get_download_request_id(); + check_id = dp_db_get_int_column(new_request->id, + DP_DB_TABLE_LOG, DP_DB_COL_ID); + } while (check_id == new_request->id); // means duplicated id + + new_request->group = group; + if (group->pkgname != NULL && strlen(group->pkgname) > 1) + new_request->packagename = dp_strdup(group->pkgname); + if (new_request->packagename == NULL) { + dp_request_free(new_request); + TRACE_ERROR("[ERROR][%d] OUT_OF_MEMORY [PACKAGENAME]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + new_request->state = DP_STATE_READY; + new_request->error = DP_ERROR_NONE; + new_request->create_time = (int)time(NULL); + + if (dp_db_request_new_logging(new_request->id, new_request->state, + new_request->packagename) < 0) { + dp_request_free(new_request); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL]"); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + *empty_slot = new_request; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_url(int id, dp_request *request, char *url) +{ + int length = 0; + if (url == NULL || (length = strlen(url)) <= 1) + return DP_ERROR_INVALID_URL; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_URL, + DP_DB_COL_TYPE_TEXT, url) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_destination(int id, dp_request *request, char *dest) +{ + int length = 0; + if (!dest || (length = strlen(dest)) <= 1) + return DP_ERROR_INVALID_DESTINATION; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_DESTINATION, + DP_DB_COL_TYPE_TEXT, dest) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_filename(int id, dp_request *request, char *filename) +{ + int length = 0; + if (!filename || (length = strlen(filename)) <= 1) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_FILENAME, + DP_DB_COL_TYPE_TEXT, filename) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + + TRACE_SECURE_DEBUG("ID [%d] Filename[%s]", id, filename); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_title(int id, dp_request *request, char *title) +{ + int length = 0; + if (!title || (length = strlen(title)) <= 1) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_TITLE, + DP_DB_COL_TYPE_TEXT, title) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + + TRACE_SECURE_DEBUG("ID [%d] title[%s]", id, title); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_bundle(int id, dp_request *request, int type, bundle_raw *b, unsigned length) +{ + char *column = NULL; + if (b == NULL || (length < 1)) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + switch(type) { + case DP_NOTIFICATION_BUNDLE_TYPE_ONGOING: + column = DP_DB_COL_RAW_BUNDLE_ONGOING; + break; + case DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE: + column = DP_DB_COL_RAW_BUNDLE_COMPLETE; + break; + case DP_NOTIFICATION_BUNDLE_TYPE_FAILED: + column = DP_DB_COL_RAW_BUNDLE_FAIL; + break; + default: + TRACE_ERROR("[CHECK TYPE][%d]", type); + return DP_ERROR_INVALID_PARAMETER; + } + if (dp_db_replace_blob_column + (id, DP_DB_TABLE_NOTIFICATION, column, b, length) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + + //TRACE_SECURE_DEBUG("ID [%d] title[%s]", id, title); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_description(int id, dp_request *request, char *description) +{ + int length = 0; + if (!description || (length = strlen(description)) <= 1) + return DP_ERROR_INVALID_PARAMETER; + + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_DESCRIPTION, + DP_DB_COL_TYPE_TEXT, description) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + + TRACE_SECURE_DEBUG("ID [%d] description[%s]", id, description); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_noti_type(int id, dp_request *request, unsigned type) +{ + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + if (dp_db_replace_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE, + DP_DB_COL_TYPE_INT, &type) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + return DP_ERROR_NO_SPACE; + } + return DP_ERROR_OUT_OF_MEMORY; + } + if (request) + { + if(!type) + request->auto_notification = 0; + else + request->auto_notification = 1; + } + TRACE_SECURE_DEBUG("ID [%d] enable[%d]", id, type); + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_notification(int id, dp_request *request, unsigned enable) +{ + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NOTIFICATION_ENABLE, DP_DB_COL_TYPE_INT, + &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + // update memory + if (request) + request->auto_notification = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_auto_download(int id, dp_request *request, unsigned enable) +{ + if (request != NULL) { + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_AUTO_DOWNLOAD, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_state_event(int id, dp_request *request, unsigned enable) +{ + if (request == NULL) { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + + if (state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_STATE_EVENT, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + // update memory + if (request != NULL) + request->state_cb = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_progress_event(int id, dp_request *request, unsigned enable) +{ + if (request == NULL) { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + + if (state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_PROGRESS_EVENT, + DP_DB_COL_TYPE_INT, &enable) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + // update memory + if (request) + request->progress_cb = enable; + return DP_ERROR_NONE; +} + +dp_error_type dp_request_set_network_type(int id, dp_request *request, int type) +{ + if (request != NULL) { + if (request->state == DP_STATE_CONNECTING || + request->state == DP_STATE_DOWNLOADING || + request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + return DP_ERROR_INVALID_STATE; + } + } else { + // check id in logging table. + dp_state_type state = dp_db_get_state(id); + // check again from logging table + if (state == DP_STATE_CONNECTING || + state == DP_STATE_DOWNLOADING || + state == DP_STATE_COMPLETED) { + TRACE_ERROR("[ERROR][%d] now[%s]", id, dp_print_state(state)); + return DP_ERROR_INVALID_STATE; + } + } + + // update queue DB + if (dp_db_replace_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_NETWORK_TYPE, + DP_DB_COL_TYPE_INT, &type) < 0) { + TRACE_ERROR("[CHECK SQL][%d]", id); + return DP_ERROR_OUT_OF_MEMORY; + } + // update memory + if (request) + request->network_type = type; + return DP_ERROR_NONE; +} + +char *dp_request_get_url(int id, dp_error_type *errorcode) +{ + char *url = NULL; + url = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_URL); + if (url == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return url; +} + +char *dp_request_get_destination(int id, dp_request *request, dp_error_type *errorcode) +{ + char *dest = NULL; + dest = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_DESTINATION); + if (dest == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return dest; +} + +char *dp_request_get_filename(int id, dp_request *request, dp_error_type *errorcode) +{ + char *filename = NULL; + filename = dp_db_get_text_column + (id, DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_FILENAME); + if (filename == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return filename; +} + +char *dp_request_get_title(int id, dp_request *request, dp_error_type *errorcode) +{ + char *title = NULL; + title = dp_db_get_text_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_TITLE); + if (title == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return title; +} + +bundle_raw *dp_request_get_bundle(int id, dp_request *request, dp_error_type *errorcode, char* column, int *length) +{ + void *blob_data = NULL; + blob_data = dp_db_get_blob_column + (id, DP_DB_TABLE_NOTIFICATION, column, length); + if (blob_data == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return (bundle_raw*)blob_data; +} + + +char *dp_request_get_description(int id, dp_request *request, dp_error_type *errorcode) +{ + char *description = NULL; + description = dp_db_get_text_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_DESCRIPTION); + if (description == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return description; +} + +int dp_request_get_noti_type(int id, dp_request *request, dp_error_type *errorcode) +{ + int type = -1; + type = dp_db_get_int_column + (id, DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if (type == -1) + *errorcode = DP_ERROR_NO_DATA; + return type; +} + + + +char *dp_request_get_contentname(int id, dp_request *request, dp_error_type *errorcode) +{ + char *content = NULL; + content = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_NAME); + if (content == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return content; +} + +char *dp_request_get_etag(int id, dp_request *request, dp_error_type *errorcode) +{ + char *etag = NULL; + etag = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_ETAG); + if (etag == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return etag; +} + +char *dp_request_get_savedpath(int id, dp_request *request, dp_error_type *errorcode) +{ + char *savedpath = NULL; + savedpath = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_SAVED_PATH); + if (savedpath == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return savedpath; +} + +char *dp_request_get_tmpsavedpath(int id, dp_request *request, dp_error_type *errorcode) +{ + char *tmppath = NULL; + tmppath = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_TMP_SAVED_PATH); + if (tmppath == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return tmppath; +} + +char *dp_request_get_mimetype(int id, dp_request *request, dp_error_type *errorcode) +{ + char *mimetype = NULL; + mimetype = dp_db_get_text_column + (id, DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_MIMETYPE); + if (mimetype == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return mimetype; +} + +char *dp_request_get_pkg_name(int id, dp_request *request, dp_error_type *errorcode) +{ + char *pkg_name = NULL; + pkg_name = dp_db_get_text_column + (id, DP_DB_TABLE_LOG, DP_DB_COL_PACKAGENAME); + if (pkg_name == NULL) { + *errorcode = DP_ERROR_NO_DATA; + return NULL; + } + return pkg_name; +} + +dp_request *dp_request_load_from_log(int id, dp_error_type *errorcode) +{ + dp_request *request = NULL; + + request = dp_db_load_logging_request(id); + if (request == NULL) { + *errorcode = DP_ERROR_ID_NOT_FOUND; + return NULL; + } + if (request->state == DP_STATE_COMPLETED) { + TRACE_ERROR + ("[ERROR][%d] now[%s]", id, dp_print_state(request->state)); + *errorcode = DP_ERROR_INVALID_STATE; + dp_request_free(request); + return NULL; + } + return request; +} + + +void dp_request_state_response(dp_request *request) +{ + if (request == NULL) { + return ; + } + + TRACE_INFO("[INFO][%d] state:%s error:%s", request->id, + dp_print_state(request->state), + dp_print_errorcode(request->error)); + + if (dp_db_request_update_status(request->id, request->state, + request->error) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + + if (request->group != NULL && request->group->event_socket >= 0 && + request->state_cb == 1) { + dp_ipc_send_event(request->group->event_socket, request->id, + request->state, request->error, 0); + } + + if (request->state == DP_STATE_DOWNLOADING) { + if (request->auto_notification == 1 && + request->packagename != NULL) { + if (request->noti_priv_id != -1) { + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } else { + request->noti_priv_id = dp_set_downloadinginfo_notification + (request->id, request->packagename); + } + } else { + int noti_type = dp_db_get_int_column(request->id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if (noti_type == DP_NOTIFICATION_TYPE_ALL && + request->packagename != NULL) { + if (request->noti_priv_id != -1) { + dp_update_downloadinginfo_notification + (request->noti_priv_id, + (double)request->received_size, + (double)request->file_size); + } else { + request->noti_priv_id = dp_set_downloadinginfo_notification + (request->id, request->packagename); + } + } + } + request->start_time = (int)time(NULL); + request->pause_time = 0; + request->stop_time = 0; + } else if (request->state == DP_STATE_PAUSED) { + if (request->group != NULL) + request->group->queued_count--; + request->pause_time = (int)time(NULL); + } else { + if (request->group != NULL ) + request->group->queued_count--; + + if (request->auto_notification == 1 && + request->packagename != NULL) { + request->noti_priv_id = dp_set_downloadedinfo_notification + (request->noti_priv_id, request->id, + request->packagename, request->state); + + } else { + int noti_type = dp_db_get_int_column(request->id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if (noti_type > DP_NOTIFICATION_TYPE_NONE && + request->packagename != NULL) + request->noti_priv_id = dp_set_downloadedinfo_notification + (request->noti_priv_id, request->id, + request->packagename, request->state); + } + + request->stop_time = (int)time(NULL); + } +} diff --git a/provider/download-provider-slots.c b/provider/download-provider-slots.c new file mode 100755 index 0000000..ad39ee8 --- /dev/null +++ b/provider/download-provider-slots.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdio.h> +#include <stdlib.h> + +#include <time.h> +#include <sys/time.h> + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-pthread.h" + +#include "download-provider-slots.h" +#include "download-provider-socket.h" + +dp_group_slots *dp_client_group_slots_new(int size) +{ + dp_group_slots *slots = NULL; + if (size <= 0) + return NULL; + slots = (dp_group_slots *) calloc(size, + sizeof(dp_group_slots)); + return slots; +} + +dp_request_slots *dp_request_slots_new(int size) +{ + int i = 0; + dp_request_slots *slots = NULL; + if (size <= 0) + return NULL; + slots = (dp_request_slots *) calloc(size, + sizeof(dp_request_slots)); + for (; i < size; i++) + CLIENT_MUTEX_INIT(&slots[i].mutex, NULL); + return slots; +} + +void dp_request_init(dp_request *request) +{ + if (request == NULL) + return ; + + request->id = -1; + request->agent_id = -1; + request->create_time = 0; + request->start_time = 0; + request->pause_time = 0; + request->stop_time = 0; + request->state = DP_STATE_NONE; + request->error = DP_ERROR_NONE; + request->state_cb = 0; + request->progress_cb = 0; + request->progress_lasttime = 0; + request->received_size = 0; + request->file_size = 0; + request->network_type = DP_NETWORK_TYPE_ALL; + request->startcount = 0; + request->auto_notification = 0; + request->noti_priv_id = -1; + request->packagename = NULL; + request->group = NULL; +} + +dp_request *dp_request_new() +{ + dp_request *request = NULL; + request = (dp_request *) calloc(1, + sizeof(dp_request)); + if (!request) + return NULL; + dp_request_init(request); + return request; +} + +int dp_request_slot_free(dp_request_slots *request_slot) +{ + if (request_slot == NULL) + return -1; + CLIENT_MUTEX_LOCK(&request_slot->mutex); + dp_request_free(request_slot->request); + request_slot->request = NULL; + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + return 0; +} + +int dp_request_free(dp_request *request) +{ + if (request == NULL) + return -1; + free(request->packagename); + dp_request_init(request); + free(request); + return 0; +} + +int dp_client_group_free(dp_client_group *group) +{ + if (group != NULL) { + if (group->cmd_socket > 0) + dp_socket_free(group->cmd_socket); + group->cmd_socket = -1; + if (group->event_socket > 0) + dp_socket_free(group->event_socket); + group->event_socket = -1; + group->queued_count = 0; + free(group->pkgname); + free(group->smack_label); + free(group); + } + return 0; +} + +int dp_client_group_slots_free(dp_group_slots *slots, int size) +{ + int i = 0; + if (slots) { + for (; i < size; i++) { + if (slots->group) + dp_client_group_free(slots->group); + slots->group = NULL; + } + free(slots); + } + slots = NULL; + return 0; +} + +int dp_request_slots_free(dp_request_slots *slots, int size) +{ + int i = 0; + if (slots != NULL) { + for (; i < size; i++) { + dp_request_free(slots[i].request); + slots[i].request = NULL; + CLIENT_MUTEX_DESTROY(&slots[i].mutex); + } + free(slots); + } + slots = NULL; + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return count of requests in slot +int dp_get_request_count(dp_request_slots *slots) +{ + int i = 0; + int count = 0; + + if (!slots) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (slots[i].request != NULL) + count++; + } + return count; +} diff --git a/provider/download-provider-socket.c b/provider/download-provider-socket.c new file mode 100755 index 0000000..7feabf8 --- /dev/null +++ b/provider/download-provider-socket.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <pthread.h> + +#include <time.h> +#include <sys/time.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <systemd/sd-daemon.h> +#include <signal.h> + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-socket.h" + +////////////////////////////////////////////////////////////////////////// +/// @brief write the error to socket +/// @return if success, return 0 +int dp_ipc_send_errorcode(int fd, dp_error_type errorcode) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + + if (fd >= 0 && write(fd, &errorcode, sizeof(dp_error_type)) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief write the progressinfo to socket +/// @return if success, return 0 +int dp_ipc_send_event(int fd, int id, dp_state_type state, + dp_error_type errorcode, unsigned long long received_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR][%d] CHECK FD[%d]", id, fd); + return -1; + } + + dp_event_info eventinfo; + eventinfo.id = id; + eventinfo.state = state; + eventinfo.err = errorcode; + eventinfo.received_size = received_size; + + // write + if (fd >= 0 && write(fd, &eventinfo, sizeof(dp_event_info)) <= 0) { + TRACE_STRERROR("[ERROR][%d] write FD[%d]", id, fd); + return -1; + } + return 0; +} + +// keep the order/ unsigned , str +char *dp_ipc_read_string(int fd) +{ + unsigned length = 0; + size_t recv_size = 0; + unsigned remain_size = 0; + size_t buffer_size = 0; + char *str = NULL; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return NULL; + } + + // read flexible URL from client. + ssize_t recv_bytes = read(fd, &length, sizeof(unsigned)); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return NULL; + } + if (length < 1 || length > DP_MAX_URL_LEN) { + TRACE_ERROR("[STRING LEGNTH] [%d]", length); + return NULL; + } + str = (char *)calloc((length + 1), sizeof(char)); + if (str == NULL) { + TRACE_STRERROR("[ERROR] calloc length:%d FD[%d]", length, fd); + return NULL; + } + remain_size = length; + do { + buffer_size = 0; + if (remain_size > DP_DEFAULT_BUFFER_SIZE) + buffer_size = DP_DEFAULT_BUFFER_SIZE; + else + buffer_size = remain_size; + recv_size = (size_t)read(fd, str + (int)(length - remain_size), + buffer_size * sizeof(char)); + if (recv_size > DP_DEFAULT_BUFFER_SIZE) { + recv_size = -1; + break; + } + if (recv_size > 0) + remain_size = remain_size - (unsigned)recv_size; + } while (recv_size > 0 && remain_size > 0); + + if (recv_size == 0) { + TRACE_STRERROR("[ERROR] closed peer:%d", fd); + free(str); + return NULL; + } + str[length] = '\0'; + return str; +} + + +// 0 : Socket Error +// -1 : Invalid type +unsigned dp_ipc_read_bundle(int fd, int *type, bundle_raw **b) +{ + unsigned length = 0; + size_t recv_size = 0; + unsigned remain_size = 0; + size_t buffer_size = 0; + bundle_raw *b_raw = NULL; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return 0; + } + + // read flexible URL from client. + ssize_t recv_bytes = read(fd, type, sizeof(int)); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] type[%d]", fd, type); + return 0; + } + if ((*type) != DP_NOTIFICATION_BUNDLE_TYPE_ONGOING && + (*type) != DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE && + (*type) != DP_NOTIFICATION_BUNDLE_TYPE_FAILED) { + TRACE_ERROR("[NOTI TYPE] [%d]", *type); + return -1; + } + // read flexible URL from client. + recv_bytes = read(fd, &length, sizeof(unsigned)); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return 0; + } + if (length < 1 || length > DP_MAX_URL_LEN) { + TRACE_ERROR("[STRING LEGNTH] [%d]", length); + return 0; + } + b_raw = (bundle_raw *)calloc(length, 1); + if (b_raw == NULL) { + TRACE_STRERROR("[ERROR] calloc length:%d FD[%d]", length, fd); + return 0; + } + remain_size = length; + do { + buffer_size = 0; + if (remain_size > DP_DEFAULT_BUFFER_SIZE) + buffer_size = DP_DEFAULT_BUFFER_SIZE; + else + buffer_size = remain_size; + recv_size = (size_t)read(fd, b_raw + (int)(length - remain_size), + buffer_size * sizeof(char)); + if (recv_size > DP_DEFAULT_BUFFER_SIZE) { + recv_size = -1; + break; + } + if (recv_size > 0) + remain_size = remain_size - (unsigned)recv_size; + } while (recv_size > 0 && remain_size > 0); + + if (recv_size <= 0) { + TRACE_STRERROR("[ERROR] closed peer:%d", fd); + bundle_free_encoded_rawdata(&b_raw); + return 0; + } + *b = b_raw; + return length; +} + +// keep the order/ unsigned , str +int dp_ipc_send_string(int fd, const char *str) +{ + unsigned length = 0; + + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (str == NULL) { + TRACE_ERROR("[ERROR] CHECK STRING FD[%d]", fd); + return -1; + } + + length = strlen(str); + if (length < 1) { + TRACE_ERROR("[ERROR] CHECK LENGTH FD[%d]", fd); + return -1; + } + if (fd >= 0 && write(fd, &length, sizeof(unsigned)) <= 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return -1; + } + if (fd >= 0 && write(fd, str, length * sizeof(char)) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_ipc_send_bundle(int fd, bundle_raw *b, unsigned length) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (b == NULL) { + TRACE_ERROR("[ERROR] CHECK STRING FD[%d]", fd); + return -1; + } + + if (length < 1) { + TRACE_ERROR("[ERROR] CHECK LENGTH FD[%d]", fd); + return -1; + } + if (fd >= 0 && write(fd, &length, sizeof(unsigned)) <= 0) { + TRACE_STRERROR("[ERROR] read FD[%d] length[%d]", fd, length); + return -1; + } + if (fd >= 0 && write(fd, b, length) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_ipc_send_custom_type(int fd, void *value, size_t type_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (value == NULL) { + TRACE_ERROR("[ERROR] CHECK VALUE FD[%d]", fd); + return -1; + } + if (fd >= 0 && write(fd, value, type_size) <= 0) { + TRACE_STRERROR("[ERROR] write FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_ipc_read_custom_type(int fd, void *value, size_t type_size) +{ + if (fd < 0) { + TRACE_ERROR("[ERROR] CHECK FD[%d]", fd); + return -1; + } + if (value == NULL) { + TRACE_ERROR("[ERROR] CHECK VALUE FD[%d]", fd); + return -1; + } + + ssize_t recv_bytes = read(fd, value, type_size); + if (recv_bytes < 0) { + TRACE_STRERROR("[ERROR] read FD[%d]", fd); + return -1; + } + return 0; +} + +int dp_accept_socket_new() +{ + int sockfd = -1; + struct sockaddr_un listenaddr; + + int n = sd_listen_fds(1); + if (n > 1) { + TRACE_STRERROR("too many file descriptors received"); + return -1; + } else if (n == 1) { + int r; + if ((r = sd_is_socket_unix(SD_LISTEN_FDS_START, SOCK_STREAM, 1, DP_IPC, 0)) <= 0) { + TRACE_STRERROR("passed systemd file descriptor is of wrong type"); + return -1; + } + sockfd = SD_LISTEN_FDS_START + 0; + } else { + if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + TRACE_STRERROR("failed to create socket"); + return -1; + } + + bzero(&listenaddr, sizeof(listenaddr)); + listenaddr.sun_family = AF_UNIX; + strcpy(listenaddr.sun_path, DP_IPC); + + if (bind(sockfd, (struct sockaddr *)&listenaddr, sizeof listenaddr) != + 0) { + TRACE_STRERROR("[CRITICAL] bind"); + close(sockfd); + return -1; + } + + if (chmod(listenaddr.sun_path, 0777) < 0) { + TRACE_STRERROR("[CRITICAL] chmod"); + close(sockfd); + return -1; + } + + // need 3 socket per a group + if (listen(sockfd, DP_MAX_GROUP * 3) != 0) { + TRACE_STRERROR("[CRITICAL] listen"); + close(sockfd); + return -1; + } + } + return sockfd; +} + +int dp_socket_free(int sockfd) +{ + TRACE_DEBUG("[%d]", sockfd); + if (sockfd < 0) + return -1; + shutdown(sockfd, 0); + close(sockfd); + return 0; +} diff --git a/provider/download-provider-thread-queue.c b/provider/download-provider-thread-queue.c new file mode 100755 index 0000000..b82618d --- /dev/null +++ b/provider/download-provider-thread-queue.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <time.h> +#include <sys/time.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#include <signal.h> + +#include <pthread.h> + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-network.h" +#include "download-provider-da-interface.h" + +void dp_terminate(int signo); + +pthread_mutex_t g_dp_queue_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t g_dp_queue_cond = PTHREAD_COND_INITIALIZER; + + +////////////////////////////////////////////////////////////////////////// +/// @brief check network status is matched with the type setted by user +/// @return matched : 0 mispatch : -1 +static int __is_matched_network(dp_network_type now_state, dp_network_type setted_state) +{ + if (now_state == setted_state + || now_state == DP_NETWORK_TYPE_ETHERNET + || setted_state == DP_NETWORK_TYPE_ALL) + return 0; + #if 0 + if (setted_state == DP_NETWORK_TYPE_ALL + || setted_state == DP_NETWORK_TYPE_DATA_NETWORK + || now_state == DP_NETWORK_TYPE_WIFI + || now_state == DP_NETWORK_TYPE_ETHERNET + || (setted_state == DP_NETWORK_TYPE_WIFI + && (now_state == DP_NETWORK_TYPE_WIFI + || now_state == DP_NETWORK_TYPE_ETHERNET)) + ) + return 0; + #endif + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief the count of slot downloading currently +static unsigned __get_active_count(dp_request_slots *requests) +{ + unsigned count = 0; + unsigned i = 0; + + if (requests == NULL) + return 0; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + int locked = pthread_mutex_trylock(&requests[i].mutex); + // locking failure means it used by other thread. + if (locked == 0) { + if (requests[i].request != NULL) { + if (requests[i].request->state == DP_STATE_CONNECTING || + requests[i].request->state == DP_STATE_DOWNLOADING) + count++; + } + CLIENT_MUTEX_UNLOCK(&requests[i].mutex); + } + } + return count; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief index of slot which last time is oldest +static int __get_oldest_request_with_network(dp_request_slots *requests, dp_state_type state, dp_network_type now_state) +{ + int i = 0; + int oldest_time = (int)time(NULL); + oldest_time++; // most last time + int oldest_index = -1; + + if (requests == NULL) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + int locked = pthread_mutex_trylock(&requests[i].mutex); + // locking failure means it used by other thread. + if (locked == 0) { + if (requests[i].request != NULL) { + if (requests[i].request->state == state && + requests[i].request->start_time > 0 && + requests[i].request->start_time < oldest_time && + __is_matched_network(now_state, + requests[i].request->network_type) == 0) { + oldest_time = requests[i].request->start_time; + oldest_index = i; + } + } + CLIENT_MUTEX_UNLOCK(&requests[i].mutex); + } + } + return oldest_index; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief THREAD function for calling da_start_download_with_extension. +/// @warning da_start_download_with_extension can take long time +/// @param the pointer of memory slot allocated for this request. +/// @todo simplify da_start_download_with_extension +static void *__request_download_start_agent(void *args) +{ + dp_error_type errcode = DP_ERROR_NONE; + + dp_request_slots *request_slot = (dp_request_slots *) args; + if (request_slot == NULL) { + TRACE_ERROR("[NULL-CHECK] request_slot"); + pthread_exit(NULL); + return 0; + } + + CLIENT_MUTEX_LOCK(&request_slot->mutex); + + if (request_slot->request == NULL) { + TRACE_ERROR("[NULL-CHECK] request"); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + pthread_exit(NULL); + return 0; + } + + dp_request *request = request_slot->request; + + if (dp_is_alive_download(request->agent_id)) { + errcode = dp_resume_agent_download(request->agent_id); + } else { + // call agent start function + errcode = dp_start_agent_download(request_slot); + } + // send to state callback. + if (errcode == DP_ERROR_NONE) { + // CONNECTING + request->state = DP_STATE_CONNECTING; + request->error = DP_ERROR_NONE; + request->startcount++; + if (dp_db_set_column + (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STARTCOUNT, + DP_DB_COL_TYPE_INT, &request->startcount) < 0) + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + } else if (errcode == DP_ERROR_TOO_MANY_DOWNLOADS) { + // PENDED + request->state = DP_STATE_QUEUED; + request->error = DP_ERROR_TOO_MANY_DOWNLOADS; + } else if (errcode == DP_ERROR_CONNECTION_FAILED) { + // FAILED + request->state = DP_STATE_FAILED; + request->error = DP_ERROR_CONNECTION_FAILED; + if (request->group != NULL && + request->group->event_socket >= 0) { + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + } + } else if (errcode == DP_ERROR_INVALID_STATE) { + // API FAILED + request->error = DP_ERROR_INVALID_STATE; + if (request->group != NULL && + request->group->event_socket >= 0) { + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + } + } else { + request->state = DP_STATE_FAILED; + request->error = errcode; + if (request->group != NULL && + request->group->event_socket >= 0) { + dp_ipc_send_event(request->group->event_socket, + request->id, request->state, request->error, 0); + } + } + if (dp_db_request_update_status(request->id, request->state, request->error) < 0) { + TRACE_ERROR("[ERROR][%d][SQL]", request->id); + } + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + + if (errcode == DP_ERROR_NONE) { + TRACE_DEBUG("try other requests -----------------"); + dp_thread_queue_manager_wake_up(); + } + + pthread_exit(NULL); + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief create thread. +/// @warning if failed to create thread, change to PENED. +/// @param the pointer of memory slot allocated for this request. +static int __request_download_start_thread(dp_request_slots *request_slot) +{ + // declare all resources + pthread_t thread_pid; + pthread_attr_t thread_attr; + + if (request_slot == NULL || request_slot->request == NULL) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + return -1; + } + dp_request *request = request_slot->request; + + // initialize + if (pthread_attr_init(&thread_attr) != 0) { + TRACE_STRERROR("[ERROR][%d] pthread_attr_init", request->id); + return -1; + } + if (pthread_attr_setdetachstate(&thread_attr, + PTHREAD_CREATE_DETACHED) != 0) { + TRACE_STRERROR + ("[ERROR][%d] pthread_attr_setdetachstate", request->id); + return -1; + } + + request->state = DP_STATE_CONNECTING; + if (pthread_create(&thread_pid, &thread_attr, + __request_download_start_agent, request_slot) != 0) { + TRACE_STRERROR("[ERROR][%d] pthread_create", request->id); + pthread_attr_destroy(&thread_attr); + request->state = DP_STATE_QUEUED; + return -1; + } + pthread_attr_destroy(&thread_attr); + return 0; +} + + +void dp_thread_queue_manager_wake_up() +{ + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + pthread_cond_signal(&g_dp_queue_cond); + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); +} + + +// Main role : start download, check status of queue. +// No timeout. Wake up by Signal be sent from other thread +void *dp_thread_queue_manager(void *arg) +{ + int i; + int active_count; + dp_client_group *group = NULL; + dp_request *request = NULL; + + dp_privates *privates = (dp_privates*)arg; + if (!privates) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; + } + + pthread_cond_init(&g_dp_queue_cond, NULL); + while (privates != NULL && privates->listen_fd >= 0) { + + CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex)); + pthread_cond_wait(&g_dp_queue_cond, &g_dp_queue_mutex); + + // request thread response instantly + CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex)); + + if (privates == NULL || privates->requests == NULL || + privates->listen_fd < 0) { + TRACE_DEBUG("Terminate Thread"); + break; + } + + // if failed to initialize the callback for checking connection + if (!privates->connection) + privates->network_status = + dp_get_network_connection_instant_status(); + +#ifdef SUPPORT_WIFI_DIRECT + if (privates->is_connected_wifi_direct == 0) { + if (dp_network_wifi_direct_is_connected() == 0) { + // wifi-direct activated. pass. + privates->is_connected_wifi_direct = 1; + } + } +#endif + + if (privates->network_status == DP_NETWORK_TYPE_OFF && + privates->is_connected_wifi_direct == 0) { + TRACE_DEBUG("[CHECK NETWORK STATE]"); + continue; + } + + active_count = __get_active_count(privates->requests); + + TRACE_DEBUG("Status Queue: now active[%d] max[%d]", + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + + // Start Conditions + // 1. state is QUEUED + // 2. 1 QUEUED per 1 Group : need not to check max limitation.!! + // if no group, it will be started later. + // 3. most old last time : below conditions need max limitation. + // 4. match network connection type + // 5. wifi-direct on + + // search group having 1 queued_count + // guarantee 1 instant download per 1 group + if (active_count >= DP_MAX_DOWNLOAD_AT_ONCE) { + for (i = 0; i < DP_MAX_REQUEST; i++) { + CLIENT_MUTEX_LOCK(&privates->requests[i].mutex); + request = privates->requests[i].request; + if (request != NULL && request->state == DP_STATE_QUEUED) { + group = privates->requests[i].request->group; + if (group && group->queued_count == 1) { + if (__is_matched_network + (privates->network_status, + request->network_type) == 0 || + (privates->is_connected_wifi_direct == 1 && + request->network_type == + DP_NETWORK_TYPE_WIFI_DIRECT)) { + if (__request_download_start_thread(&privates->requests[i]) == 0) { + TRACE_DEBUG + ("[Guarantee Intant Download] Group [%s]", group->pkgname); + active_count++; + } + } + } + } + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + } + } + + if (active_count >= DP_MAX_DOWNLOAD_AT_ONCE) { + TRACE_DEBUG("[BUSY] Active[%d] Max[%d]", + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + continue; + } + + // can start download more. + // search oldest request + while(active_count < DP_MAX_DOWNLOAD_AT_ONCE) { + +#ifdef SUPPORT_WIFI_DIRECT + // WIFI-Direct first + if (privates->is_connected_wifi_direct == 1) { + i = __get_oldest_request_with_network(privates->requests, + DP_STATE_QUEUED, DP_NETWORK_TYPE_WIFI_DIRECT); + if (i >= 0) { + TRACE_DEBUG("Found WIFI-Direct request %d", i); + if (__request_download_start_thread(&privates->requests[i]) == 0) + active_count++; + continue; + } + } +#endif + + i = __get_oldest_request_with_network(privates->requests, + DP_STATE_QUEUED, privates->network_status); + if (i < 0) { + TRACE_DEBUG + ("No Request now active[%d] max[%d]", + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + break; + } + TRACE_DEBUG("QUEUE Status now %d active %d/%d", i, + active_count, DP_MAX_DOWNLOAD_AT_ONCE); + __request_download_start_thread(&privates->requests[i]); + active_count++; + } + } + pthread_cond_destroy(&g_dp_queue_cond); + pthread_exit(NULL); + return 0; +} diff --git a/provider/download-provider-thread-request.c b/provider/download-provider-thread-request.c new file mode 100755 index 0000000..6b03c08 --- /dev/null +++ b/provider/download-provider-thread-request.c @@ -0,0 +1,2158 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <time.h> +#include <sys/time.h> + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <signal.h> + +#include <app_manager.h> +#include <sys/smack.h> +#include <bundle.h> + +#include "download-provider.h" +#include "download-provider-log.h" +#include "download-provider-config.h" +#include "download-provider-slots.h" +#include "download-provider-socket.h" +#include "download-provider-pthread.h" +#include "download-provider-db.h" +#include "download-provider-queue.h" +#include "download-provider-request.h" +#include "download-provider-network.h" +#include "download-provider-da-interface.h" +#include "download-provider-notification.h" + +void dp_terminate(int signo); + +static char *__print_command(dp_command_type cmd) +{ + switch(cmd) + { + case DP_CMD_CREATE : + return "CREATE"; + case DP_CMD_START : + return "START"; + case DP_CMD_PAUSE : + return "PAUSE"; + case DP_CMD_CANCEL : + return "CANCEL"; + case DP_CMD_DESTROY : + return "DESTROY"; + case DP_CMD_FREE : + return "FREE"; + case DP_CMD_ECHO : + return "ECHO"; + case DP_CMD_SET_URL : + return "SET_URL"; + case DP_CMD_SET_DESTINATION : + return "SET_DESTINATION"; + case DP_CMD_SET_FILENAME : + return "SET_FILENAME"; + case DP_CMD_SET_NOTIFICATION : + return "SET_NOTIFICATION"; + case DP_CMD_SET_STATE_CALLBACK : + return "SET_STATE_CALLBACK"; + case DP_CMD_SET_PROGRESS_CALLBACK : + return "SET_PROGRESS_CALLBACK"; + case DP_CMD_SET_AUTO_DOWNLOAD : + return "SET_AUTO_DOWNLOAD"; + case DP_CMD_SET_NETWORK_TYPE : + return "SET_NETWORK_TYPE"; + case DP_CMD_SET_HTTP_HEADER : + return "SET_HTTP_HEADER"; + case DP_CMD_DEL_HTTP_HEADER : + return "DEL_HTTP_HEADER"; + case DP_CMD_GET_HTTP_HEADER : + return "GET_HTTP_HEADER"; + case DP_CMD_GET_URL : + return "GET_URL"; + case DP_CMD_GET_DESTINATION : + return "GET_DESTINATION"; + case DP_CMD_GET_FILENAME : + return "GET_FILENAME"; + case DP_CMD_GET_NOTIFICATION : + return "GET_NOTIFICATION"; + case DP_CMD_GET_STATE_CALLBACK : + return "GET_STATE_CALLBACK"; + case DP_CMD_GET_PROGRESS_CALLBACK : + return "GET_PROGRESS_CALLBACK"; + case DP_CMD_GET_HTTP_HEADERS : + return "GET_HTTP_HEADERS"; + case DP_CMD_GET_HTTP_HEADER_LIST: + return "GET_HTTP_HEADER_LIST"; + case DP_CMD_ADD_EXTRA_PARAM : + return "ADD_EXTRA_PARAM"; + case DP_CMD_GET_EXTRA_PARAM : + return "GET_EXTRA_PARAM"; + case DP_CMD_REMOVE_EXTRA_PARAM : + return "REMOVE_EXTRA_PARAM"; + case DP_CMD_GET_AUTO_DOWNLOAD : + return "GET_AUTO_DOWNLOAD"; + case DP_CMD_GET_NETWORK_TYPE : + return "GET_NETWORK_TYPE"; + case DP_CMD_GET_SAVED_PATH : + return "GET_SAVED_PATH"; + case DP_CMD_GET_TEMP_SAVED_PATH : + return "GET_TEMP_SAVED_PATH"; + case DP_CMD_GET_MIME_TYPE : + return "GET_MIME_TYPE"; + case DP_CMD_GET_RECEIVED_SIZE : + return "GET_RECEIVED_SIZE"; + case DP_CMD_GET_TOTAL_FILE_SIZE : + return "GET_TOTAL_FILE_SIZE"; + case DP_CMD_GET_CONTENT_NAME : + return "GET_CONTENT_NAME"; + case DP_CMD_GET_HTTP_STATUS : + return "GET_HTTP_STATUS"; + case DP_CMD_GET_ETAG : + return "DP_CMD_GET_ETAG"; + case DP_CMD_GET_STATE : + return "GET_STATE"; + case DP_CMD_GET_ERROR : + return "ERROR"; + case DP_CMD_SET_COMMAND_SOCKET : + return "SET_COMMAND_SOCKET"; + case DP_CMD_SET_EVENT_SOCKET : + return "SET_EVENT_SOCKET"; + case DP_CMD_SET_NOTIFICATION_BUNDLE: + return "SET_NOTIFICATION_BUNDLE"; + case DP_CMD_SET_NOTIFICATION_TITLE: + return "SET_NOTIFICATION_TITLE"; + case DP_CMD_SET_NOTIFICATION_DESCRIPTION: + return "SET_NOTIFICATION_DESCRIPTION"; + case DP_CMD_SET_NOTIFICATION_TYPE: + return "SET_NOTIFICATION_TYPE"; + case DP_CMD_GET_NOTIFICATION_BUNDLE: + return "GET_NOTIFICATION_BUNDLE"; + case DP_CMD_GET_NOTIFICATION_TITLE: + return "GET_NOTIFICATION_TITLE"; + case DP_CMD_GET_NOTIFICATION_DESCRIPTION: + return "GET_NOTIFICATION_DESCRIPTION"; + case DP_CMD_GET_NOTIFICATION_TYPE: + return "GET_NOTIFICATION_TYPE"; + default : + break; + } + return "UNKNOWN COMMAND"; +} + +/* compare two string */ +static int __cmp_string(char *s1, char *s2) +{ + size_t s1_len = 0; + size_t s2_len = 0; + + if (s1 == NULL || s2 == NULL) { + TRACE_ERROR("[CHECK PARAM]"); + return -1; + } + + s1_len = strlen(s1); + if (s1_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", s1_len); + return -1; + } + + s2_len = strlen(s2); + if (s2_len <= 0) { + TRACE_ERROR("[CHECK PARAM] len[%d]", s2_len); + return -1; + } + + if (s1_len != s2_len) { + TRACE_ERROR("[DIFF] len[%d:%d]", s1_len, s2_len); + return -1; + } + + if (strncmp(s1, s2, s1_len) != 0) { + TRACE_ERROR("[DIFF] cmp"); + return -1; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return index of empty slot +static int __get_empty_request_index(dp_request_slots *slots) +{ + int i = 0; + + if (slots == NULL) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (slots[i].request == NULL) + return i; + } + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return index of slot having same ID +/// @param ID want to search +static int __get_same_request_index(dp_request_slots *slots, int id) +{ + int i = 0; + + if (!slots || id < 0) + return -1; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + if (slots[i].request != NULL) { + if (slots[i].request->id == id) + return i; + } + } + return -1; +} + +////////////////////////////////////////////////////////////////////////// +/// @brief return custom value via IPC +static void __send_return_custom_type(int fd, dp_error_type errcode, void *value, size_t type_size) +{ + dp_ipc_send_errorcode(fd, errcode); + dp_ipc_send_custom_type(fd, value, type_size); +} + +static int __is_downloading(dp_state_type state) +{ + if (state == DP_STATE_CONNECTING || state == DP_STATE_DOWNLOADING) + return 0; + return -1; +} + +static int __is_started(dp_state_type state) +{ + if (state == DP_STATE_QUEUED || __is_downloading(state) == 0) + return 0; + return -1; +} + +static int __is_stopped(dp_state_type state) +{ + if (state == DP_STATE_COMPLETED || state == DP_STATE_FAILED || + state == DP_STATE_CANCELED) + return 0; + return -1; +} + +// cancel the downloading if no auto-download, then clear group +static void __clear_group(dp_privates *privates, dp_client_group *group) +{ + dp_request *request = NULL; + int i = 0; + + for (i = 0; i < DP_MAX_REQUEST; i++) { + + CLIENT_MUTEX_LOCK(&privates->requests[i].mutex); + + if (privates->requests[i].request == NULL) { + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + continue; + } + + request = privates->requests[i].request; + + if (request->group == NULL || request->id <= 0 || + request->group != group) { + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + continue; + } + + // if stopped, paused or not started yet, clear request + if (__is_started(request->state) < 0) { + TRACE_DEBUG("[FREE][%d] state[%s]", request->id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + dp_request_slot_free(&privates->requests[i]); + continue; + } + + // disconnect the request from group. + TRACE_SECURE_DEBUG("[DISCONNECT][%d] state:%s pkg:%s sock:%d", + request->id, dp_print_state(request->state), + request->group->pkgname, request->group->cmd_socket); + request->group = NULL; + request->state_cb = 0; + request->progress_cb = 0; + + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + + } + // clear this group + dp_client_group_free(group); +} + +static void __dp_remove_tmpfile(int id, dp_request *request) +{ + dp_error_type errorcode = DP_ERROR_NONE; + char *path = + dp_request_get_tmpsavedpath(id, request, &errorcode); + if (errorcode == DP_ERROR_NONE && + (path != NULL && strlen(path) > 0) && + dp_is_file_exist(path) == 0) { + TRACE_SECURE_DEBUG("[REMOVE][%d] TEMP FILE [%s]", id, path); + if (unlink(path) != 0) + TRACE_STRERROR("[ERROR][%d] remove file", id); + } + free(path); +} + +static int __dp_check_valid_directory(dp_request *request, char *dir) +{ + int ret = -1; + int ret_val = 0; + char *dir_label = NULL; + struct stat dir_state; + + ret_val = stat(dir, &dir_state); + if (ret_val == 0) { + dp_credential cred; + if (request->group == NULL) { + cred.uid = dp_db_cond_get_int(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_UID, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + cred.gid = dp_db_cond_get_int(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_GID, + DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + } else { + cred = request->group->credential; + } + if (dir_state.st_uid == cred.uid + && (dir_state.st_mode & (S_IRUSR | S_IWUSR)) + == (S_IRUSR | S_IWUSR)) { + ret = 0; + } else if (dir_state.st_gid == cred.gid + && (dir_state.st_mode & (S_IRGRP | S_IWGRP)) + == (S_IRGRP | S_IWGRP)) { + ret = 0; + } else if ((dir_state.st_mode & (S_IROTH | S_IWOTH)) + == (S_IROTH | S_IWOTH)) { + ret = 0; + } + } + + if (ret != 0) + return ret; + + ret_val = smack_getlabel(dir, &dir_label, SMACK_LABEL_ACCESS); + if (ret_val != 0) { + TRACE_SECURE_ERROR("[ERROR][%d][SMACK ERROR]", request->id); + free(dir_label); + return -1; + } + + char *smack_label = NULL; + if (request->group == NULL) { + // get smack_label from sql + smack_label = dp_db_cond_get_text(DP_DB_TABLE_GROUPS, + DP_DB_GROUPS_COL_SMACK_LABEL, DP_DB_GROUPS_COL_PKG, + DP_DB_COL_TYPE_TEXT, request->packagename); + if (smack_label != NULL) { + ret_val = smack_have_access(smack_label, dir_label, "rw"); + free(smack_label); + } + } else { + if (request->group->smack_label != NULL) { + ret_val = smack_have_access(request->group->smack_label, dir_label, "rw"); + } + } + if (ret_val == 0) { + TRACE_SECURE_ERROR("[ERROR][%d][SMACK NO RULE]", request->id); + ret = -1; + } else if (ret_val < 0){ + TRACE_SECURE_ERROR("[ERROR][%d][SMACK ERROR]", request->id); + ret = -1; + } + free(dir_label); + return ret; +} + +static int __dp_call_cancel_agent(dp_request *request) +{ + int ret = -1; + if (request != NULL) { + if (request->agent_id >= 0) { + TRACE_INFO("[%d]cancel_agent(%d) state:%s", request->id, + request->agent_id, dp_print_state(request->state)); + if (dp_cancel_agent_download(request->agent_id) == 0) + ret = 0; + } else { + TRACE_DEBUG("[CHECK] agent-id"); + } + } + return ret; +} + +static int __dp_add_extra_param(int fd, int id) +{ + dp_error_type ret = DP_ERROR_NONE; + int length = 0; + int i = 0; + unsigned values_length = 0; + char *key = NULL; + char **values = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK] socket"); + return DP_ERROR_IO_ERROR; + } + if (id < 0) { + TRACE_ERROR("[CHECK] ID"); + return DP_ERROR_INVALID_PARAMETER; + } + + if (dp_ipc_read_custom_type(fd, &length, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", id); + ret = DP_ERROR_IO_ERROR; + } else { + TRACE_DEBUG("[RECV] length %d", length); + if (length <= 0) { + ret = DP_ERROR_INVALID_PARAMETER; + } else { + key = dp_ipc_read_string(fd); + if (key == NULL) { + TRACE_ERROR("[ERROR][%d][IO_ERROR] key", id); + ret = DP_ERROR_IO_ERROR; + } else { + if (length > 1) { + TRACE_SECURE_DEBUG("[RECV] key : %s", key); + // get values + values = (char **)calloc((length - 1), sizeof(char *)); + if (values == NULL) { + ret = DP_ERROR_OUT_OF_MEMORY; + } else { + for (i = 0; i < length - 1; i++) { + values[i] = dp_ipc_read_string(fd); + if (values[i] == NULL) { + ret = DP_ERROR_IO_ERROR; + break; + } + values_length++; + } + } + } else { + TRACE_ERROR("[ERROR][%d] length [%d]", id, length); + ret = DP_ERROR_INVALID_PARAMETER; + } + } + } + } + if (ret == DP_ERROR_NONE) { + // store to DB + for (i = 0; i < length - 1; i++) { + int conds_count = 3; + db_conds_list_fmt conds_p[conds_count]; // id + key + value + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + conds_p[0].column = DP_DB_COL_ID; + conds_p[0].type = DP_DB_COL_TYPE_INT; + conds_p[0].value = &id; + conds_p[1].column = DP_DB_COL_EXTRA_KEY; + conds_p[1].type = DP_DB_COL_TYPE_TEXT; + conds_p[1].value = key; + conds_p[2].column = DP_DB_COL_EXTRA_VALUE; + conds_p[2].type = DP_DB_COL_TYPE_TEXT; + conds_p[2].value = values[i]; + int check_key = + dp_db_get_conds_rows_count(DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_ID, "AND", conds_count, conds_p); + if (check_key <= 0) { // create newly + // insert + if (dp_db_insert_columns(DP_DB_TABLE_NOTIFICATION, + conds_count, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", id); + ret = DP_ERROR_NO_SPACE; + } else { + ret = DP_ERROR_OUT_OF_MEMORY; + } + break; + } + } // else skip. already exist + } + } + free(key); + for (i = 0; i < values_length; i++) { + free(values[i]); + } + free(values); + return ret; +} + +static int __dp_get_extra_param_values(int fd, int id, char ***values, + unsigned *count) +{ + dp_error_type ret = DP_ERROR_NONE; + int length = 0; + char *key = NULL; + char **rows_array = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK] socket"); + return DP_ERROR_IO_ERROR; + } + if (id < 0) { + TRACE_ERROR("[CHECK] ID"); + return DP_ERROR_INVALID_PARAMETER; + } + + if (dp_ipc_read_custom_type(fd, &length, sizeof(int)) < 0) { + TRACE_ERROR("[ERROR][%d] [DP_ERROR_IO_ERROR]", id); + ret = DP_ERROR_IO_ERROR; + } else { + TRACE_DEBUG("[RECV] length %d", length); + if (length != 1) { // only a key + ret = DP_ERROR_INVALID_PARAMETER; + } else { + if ((key = dp_ipc_read_string(fd)) == NULL) { + TRACE_ERROR("[ERROR][%d][IO_ERROR] key", id); + ret = DP_ERROR_IO_ERROR; + } + } + } + if (ret == DP_ERROR_NONE) { + int conds_count = 2; + db_conds_list_fmt conds_p[conds_count]; // id + key + value + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + conds_p[0].column = DP_DB_COL_ID; + conds_p[0].type = DP_DB_COL_TYPE_INT; + conds_p[0].value = &id; + conds_p[1].column = DP_DB_COL_EXTRA_KEY; + conds_p[1].type = DP_DB_COL_TYPE_TEXT; + conds_p[1].value = key; + int check_rows = dp_db_get_conds_rows_count + (DP_DB_TABLE_NOTIFICATION, DP_DB_COL_EXTRA_VALUE, "AND", + conds_count, conds_p); + if (check_rows <= 0) { + // NO_DATA + ret = DP_ERROR_NO_DATA; + } else { + rows_array = (char **)calloc(check_rows, sizeof(char *)); + if (rows_array == NULL) { + ret = DP_ERROR_OUT_OF_MEMORY; + } else { + // getting the array from DB with key condition + int rows_count = + dp_db_get_conds_list(DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_VALUE, DP_DB_COL_TYPE_TEXT, + (void **)rows_array, check_rows, -1, NULL, NULL, + "AND", conds_count, conds_p); + if (rows_count <= 0) { + // NO_DATA + ret = DP_ERROR_NO_DATA; + free(rows_array); + } else { + *count = rows_count; + *values = rows_array; + } + } + free(key); + } + } + return ret; +} + +static int __dp_remove_extra_param(int fd, int id) +{ + dp_error_type ret = DP_ERROR_NONE; + char *key = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK] socket"); + return DP_ERROR_IO_ERROR; + } + if (id < 0) { + TRACE_ERROR("[CHECK] ID"); + return DP_ERROR_INVALID_PARAMETER; + } + + if ((key = dp_ipc_read_string(fd)) == NULL) { + TRACE_ERROR("[ERROR][%d] INVALID_PARAMETER", id); + ret = DP_ERROR_IO_ERROR; + } + if (ret == DP_ERROR_NONE) { + if (dp_db_cond_remove(id, DP_DB_TABLE_NOTIFICATION, + DP_DB_COL_EXTRA_KEY, DP_DB_COL_TYPE_TEXT, key) < 0) { + TRACE_ERROR("[fail][%d][sql]", id); + TRACE_SECURE_ERROR("[fail]key:%s", key); + ret = DP_ERROR_OUT_OF_MEMORY; + } + } + TRACE_DEBUG("[ERROR][%d][%s]", id, dp_print_errorcode(ret)); + TRACE_SECURE_DEBUG("key:%s", key); + free(key); + return ret; +} + +static int __dp_get_http_header_fields(int fd, int id, char ***values, + unsigned *count) +{ + dp_error_type ret = DP_ERROR_NONE; + char **rows_array = NULL; + + if (fd < 0) { + TRACE_ERROR("[CHECK] socket"); + return DP_ERROR_IO_ERROR; + } + if (id < 0) { + TRACE_ERROR("[CHECK] ID"); + return DP_ERROR_INVALID_PARAMETER; + } + + db_conds_list_fmt conds_p; + conds_p.column = DP_DB_COL_ID; + conds_p.type = DP_DB_COL_TYPE_INT; + conds_p.value = &id; + conds_p.is_like = 0; + int check_rows = dp_db_get_conds_rows_count + (DP_DB_TABLE_HTTP_HEADERS, DP_DB_COL_HEADER_FIELD, "AND", + 1, &conds_p); + if (check_rows <= 0) { + // NO_DATA + ret = DP_ERROR_NO_DATA; + } else { + rows_array = (char **)calloc(check_rows, sizeof(char *)); + if (rows_array == NULL) { + ret = DP_ERROR_OUT_OF_MEMORY; + } else { + int rows_count = + dp_db_get_conds_list(DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, + (void **)rows_array, check_rows, -1, NULL, NULL, + "AND", 1, &conds_p); + if (rows_count <= 0) { + // NO_DATA + ret = DP_ERROR_NO_DATA; + free(rows_array); + } else { + *count = rows_count; + *values = rows_array; + } + } + } + return ret; +} + +static int __dp_set_group_new(int clientfd, dp_group_slots *groups, + dp_credential credential, fd_set *listen_fdset) +{ + // search in groups. + // if same group. update it. + // search same pkg or pid in groups + int pkg_len = 0; + int i = 0; + struct timeval tv_timeo; // 2.5 sec + char *pkgname = NULL; + char *smack_label = NULL; + int ret = 0; + + tv_timeo.tv_sec = 2; + tv_timeo.tv_usec = 500000; + if (setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv_timeo, + sizeof(tv_timeo)) < 0) { + TRACE_STRERROR("[CRITICAL] setsockopt SO_RCVTIMEO"); + return -1; + } + + // getting the package name via pid + if (app_manager_get_package(credential.pid, &pkgname) == + APP_MANAGER_ERROR_NONE) { + TRACE_SECURE_DEBUG("package : %s", pkgname); + } else + TRACE_ERROR("[CRITICAL] app_manager_get_package"); + + //// TEST CODE ... to allow sample client ( no package name ). + if (pkgname == NULL) { + pkgname = dp_strdup("unknown_app"); + TRACE_DEBUG("default package naming : %s", pkgname); + } + + if (pkgname == NULL) { + TRACE_ERROR("[CRITICAL] app_manager_get_package"); + return -1; + } + if ((pkg_len = strlen(pkgname)) <= 0) { + TRACE_ERROR("[CRITICAL] pkgname:%s", pkgname); + free(pkgname); + return -1; + } + + for (i = 0; i < DP_MAX_GROUP; i++) { + if (groups[i].group != NULL) { + // clean garbage slot + if (groups[i].group->cmd_socket <= 0 || + groups[i].group->pkgname == NULL) { + dp_client_group_free(groups[i].group); + groups[i].group = NULL; + continue; + } + if (strlen(groups[i].group->pkgname) == pkg_len && + strncmp(groups[i].group->pkgname, pkgname, + pkg_len) == 0 ) { + // Found Same Group + TRACE_SECURE_INFO("UPDATE Group: slot:%d pid:%d sock:%d [%s]", + i, credential.pid, clientfd, pkgname); + if (groups[i].group->cmd_socket > 0 && + groups[i].group->cmd_socket != clientfd) { + FD_CLR(groups[i].group->cmd_socket, listen_fdset); + dp_socket_free(groups[i].group->cmd_socket); + } + groups[i].group->cmd_socket = clientfd; + free(pkgname); + return 0; + } + } + } + + // new client + // search emtpy slot in groups + for (i = 0; i < DP_MAX_GROUP; i++) + if (groups[i].group == NULL) + break; + if (i >= DP_MAX_GROUP) { + TRACE_ERROR("[CRITICAL] No space in groups"); + free(pkgname); + return -1; + } + // allocation + groups[i].group = + (dp_client_group *)calloc(1, sizeof(dp_client_group)); + if (groups[i].group == NULL) { + TRACE_ERROR("[CRITICAL] calloc, ignore this client"); + free(pkgname); + return -1; + } + + // fill info + groups[i].group->cmd_socket = clientfd; + groups[i].group->event_socket = -1; + groups[i].group->queued_count = 0; + groups[i].group->pkgname = dp_strdup(pkgname); + groups[i].group->credential.pid = credential.pid; + groups[i].group->credential.uid = credential.uid; + groups[i].group->credential.gid = credential.gid; + + int conds_count = 4; + db_conds_list_fmt conds_p[conds_count]; + memset(&conds_p, 0x00, conds_count * sizeof(db_conds_list_fmt)); + conds_p[0].column = DP_DB_GROUPS_COL_UID; + conds_p[0].type = DP_DB_COL_TYPE_INT; + conds_p[0].value = &credential.uid; + conds_p[1].column = DP_DB_GROUPS_COL_GID; + conds_p[1].type = DP_DB_COL_TYPE_INT; + conds_p[1].value = &credential.gid; + conds_p[2].column = DP_DB_GROUPS_COL_PKG; + conds_p[2].type = DP_DB_COL_TYPE_TEXT; + conds_p[2].value = pkgname; + + if (dp_is_smackfs_mounted() == 1) { + ret = smack_new_label_from_socket(clientfd, &smack_label); + if (ret < 0) { + TRACE_ERROR("[CRITICAL] cannot get smack label"); + free(pkgname); + free(smack_label); + return -1; + } + TRACE_SECURE_INFO("credential label:[%s]", smack_label); + groups[i].group->smack_label = smack_label; + + conds_p[3].column = DP_DB_GROUPS_COL_SMACK_LABEL; + conds_p[3].type = DP_DB_COL_TYPE_TEXT; + conds_p[3].value = smack_label; + } else { + conds_count = 3; // ignore smack label + groups[i].group->smack_label = NULL; + } + + if (dp_db_insert_columns(DP_DB_TABLE_GROUPS, conds_count, conds_p) < 0) { + free(pkgname); + free(smack_label); + if (dp_db_is_full_error() == 0) + TRACE_ERROR("[SQLITE_FULL]"); + return -1; + } + + TRACE_SECURE_INFO("New Group: slot:%d pid:%d sock:%d [%s]", i, + credential.pid, clientfd, pkgname); + free(pkgname); + return 0; +} + + +static int __dp_set_group_event_sock(int clientfd, + dp_group_slots *groups, dp_credential credential) +{ + int i = 0; + + TRACE_DEBUG("Check event pid:%d sock:%d", credential.pid, clientfd); + // search same pid in groups + for (i = 0; i < DP_MAX_GROUP; i++) { + if (groups[i].group != NULL && + groups[i].group->credential.pid == credential.pid) { + if (groups[i].group->event_socket > 0 && + groups[i].group->event_socket != clientfd) + dp_socket_free(groups[i].group->event_socket); + groups[i].group->event_socket = clientfd; + TRACE_SECURE_INFO + ("Found Group : slot:%d pid:%d csock:%d esock:%d [%s]", + i, credential.pid, groups[i].group->cmd_socket, + clientfd, groups[i].group->pkgname); + break; + } + } + if (i >= DP_MAX_GROUP) { + TRACE_ERROR + ("[CRITICAL] Not found group for PID [%d]", credential.pid); + return -1; + } + return 0; +} + +static dp_error_type __dp_do_get_command(int sock, dp_command* cmd, dp_request *request) +{ + unsigned is_checked = 1; + dp_error_type errorcode = DP_ERROR_NONE; + + char *read_str = NULL; + // No read(), write a string + switch(cmd->cmd) { + case DP_CMD_GET_URL: + read_str = dp_request_get_url(cmd->id, &errorcode); + break; + case DP_CMD_GET_DESTINATION: + read_str = dp_request_get_destination(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_FILENAME: + read_str = dp_request_get_filename(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_SAVED_PATH: + read_str = dp_request_get_savedpath(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_TEMP_SAVED_PATH: + read_str = dp_request_get_tmpsavedpath(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_MIME_TYPE: + read_str = dp_request_get_mimetype(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_CONTENT_NAME: + read_str = dp_request_get_contentname(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_ETAG: + read_str = dp_request_get_etag(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_NOTIFICATION_TITLE: + read_str = dp_request_get_title(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_NOTIFICATION_DESCRIPTION: + read_str = dp_request_get_description(cmd->id, request, &errorcode); + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (read_str == NULL || strlen(read_str) < 1) + errorcode = DP_ERROR_NO_DATA; + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_string(sock, read_str); + } else { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + free(read_str); + return errorcode; + } + + // No read(), write a integer variable + int read_int = 0; + errorcode = DP_ERROR_NONE; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_GET_NOTIFICATION: + read_int = dp_db_get_int_column(cmd->id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NOTIFICATION_ENABLE); + break; + case DP_CMD_GET_AUTO_DOWNLOAD: + read_int = dp_db_get_int_column(cmd->id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_AUTO_DOWNLOAD); + break; + case DP_CMD_GET_NETWORK_TYPE: + read_int = dp_db_get_int_column(cmd->id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NETWORK_TYPE); + break; + case DP_CMD_GET_HTTP_STATUS: + read_int = dp_db_get_int_column(cmd->id, DP_DB_TABLE_DOWNLOAD_INFO, + DP_DB_COL_HTTP_STATUS); + break; + case DP_CMD_GET_STATE: + if (request == NULL) { + read_int = dp_db_get_state(cmd->id); + } else { + read_int = request->state; + } + break; + case DP_CMD_GET_NOTIFICATION_TYPE: + TRACE_DEBUG("DP_CMD_GET_NOTIFICATION_TYPE"); + read_int = dp_request_get_noti_type(cmd->id, request, &errorcode); + break; + case DP_CMD_GET_ERROR: + if (request == NULL) { + read_int = dp_db_get_int_column(cmd->id, + DP_DB_TABLE_LOG, DP_DB_COL_ERRORCODE); + } else { + read_int = request->error; + } + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (read_int < 0) + errorcode = DP_ERROR_NO_DATA; + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_custom_type(sock, &read_int, sizeof(int)); + } else { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + return errorcode; + } + + // No read(), write a long long variable + unsigned long long recv_long = 0; + errorcode = DP_ERROR_NONE; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_GET_RECEIVED_SIZE: + if (request == NULL) + errorcode = DP_ERROR_NO_DATA; + else + recv_long = request->received_size; + break; + case DP_CMD_GET_TOTAL_FILE_SIZE: + if (request != NULL) { + recv_long = request->file_size; + } else { + long long file_size = + dp_db_get_int64_column(cmd->id, + DP_DB_TABLE_DOWNLOAD_INFO, DP_DB_COL_CONTENT_SIZE); + if (file_size < 0) + errorcode = DP_ERROR_NO_DATA; + else // casting + recv_long = file_size; + } + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_custom_type(sock, &recv_long, + sizeof(unsigned long long)); + } else { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + return errorcode; + } + + // No read(), write a bundle variable + bundle_raw *b_raw = NULL; + int length = -1; + char *column = NULL; + errorcode = DP_ERROR_NONE; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_GET_NOTIFICATION_BUNDLE: + TRACE_DEBUG("DP_CMD_GET_NOTIFICATION_BUNDLE"); + dp_ipc_send_errorcode(sock, DP_ERROR_NONE); + if ((dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0)) { + TRACE_ERROR("DP_CMD_GET_NOTIFICATION_BUNDLE read fail"); + errorcode = DP_ERROR_IO_ERROR; + break; + } + switch(read_int) { + case DP_NOTIFICATION_BUNDLE_TYPE_ONGOING: + column = DP_DB_COL_RAW_BUNDLE_ONGOING; + break; + case DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE: + column = DP_DB_COL_RAW_BUNDLE_COMPLETE; + break; + case DP_NOTIFICATION_BUNDLE_TYPE_FAILED: + column = DP_DB_COL_RAW_BUNDLE_FAIL; + break; + default: + TRACE_ERROR("[CHECK TYPE][%d]", read_int); + errorcode = DP_ERROR_INVALID_PARAMETER; + break; + } + b_raw = dp_request_get_bundle(cmd->id, request, + &errorcode, column, &length); + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_bundle(sock, b_raw, length); + } else { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + bundle_free_encoded_rawdata(&b_raw); + return errorcode; + } + + dp_ipc_send_errorcode(sock, DP_ERROR_NONE); + // complex read() and write(). + char *read_str2 = NULL; + errorcode = DP_ERROR_NONE; + read_str = NULL; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_GET_HTTP_HEADER: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + read_str2 = dp_db_cond_get_text_column(cmd->id, + DP_DB_TABLE_HTTP_HEADERS, DP_DB_COL_HEADER_DATA, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, read_str); + if (read_str2 == NULL) + errorcode = DP_ERROR_NO_DATA; + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) + dp_ipc_send_string(sock, read_str2); + break; + case DP_CMD_GET_HTTP_HEADER_LIST: + { + char **values = NULL; + unsigned rows_count = 0; + errorcode = __dp_get_http_header_fields(sock, + cmd->id, &values, &rows_count); + if (errorcode == DP_ERROR_NONE) { + __send_return_custom_type(sock, DP_ERROR_NONE, + &rows_count, sizeof(int)); + // sending strings + int i = 0; + for (i = 0; i < rows_count; i++) { + if (dp_ipc_send_string(sock, values[i]) < 0) + break; + } + for (i = 0; i < rows_count; i++) + free(values[i]); + } else { + if (errorcode != DP_ERROR_IO_ERROR) + dp_ipc_send_errorcode(sock, errorcode); + } + free(values); + break; + } + case DP_CMD_GET_EXTRA_PARAM: + { + char **values = NULL; + unsigned rows_count = 0; + errorcode = __dp_get_extra_param_values(sock, + cmd->id, &values, &rows_count); + if (errorcode == DP_ERROR_NONE) { + __send_return_custom_type(sock, DP_ERROR_NONE, + &rows_count, sizeof(int)); + // sending strings + int i = 0; + for (i = 0; i < rows_count; i++) { + if (dp_ipc_send_string(sock, values[i]) < 0) + break; + } + for (i = 0; i < rows_count; i++) + free(values[i]); + } else { + if (errorcode != DP_ERROR_IO_ERROR) + dp_ipc_send_errorcode(sock, errorcode); + } + free(values); + break; + } + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (errorcode != DP_ERROR_NONE) { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + free(read_str); + free(read_str2); + return errorcode; + } + return DP_ERROR_UNKNOWN; +} + +static dp_error_type __dp_do_set_command(int sock, dp_command *cmd, dp_request *request) +{ + unsigned is_checked = 1; + int read_int = 0; + dp_error_type errorcode = DP_ERROR_NONE; + char *read_str = NULL; + bundle_raw *b_raw = NULL; + unsigned bundle_length = 0; + int noti_bundle_type = 0; + + dp_ipc_send_errorcode(sock, DP_ERROR_NONE); + // read a interger or a string, return errorcode. + errorcode = DP_ERROR_NONE; + switch(cmd->cmd) { + case DP_CMD_SET_STATE_CALLBACK: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_state_event(cmd->id, request, read_int); + break; + case DP_CMD_SET_PROGRESS_CALLBACK: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_progress_event(cmd->id, request, read_int); + break; + case DP_CMD_SET_NETWORK_TYPE: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if (read_int == DP_NETWORK_TYPE_ALL || + read_int == DP_NETWORK_TYPE_WIFI || + read_int == DP_NETWORK_TYPE_DATA_NETWORK || + read_int == DP_NETWORK_TYPE_ETHERNET || + read_int == DP_NETWORK_TYPE_WIFI_DIRECT) + errorcode = dp_request_set_network_type(cmd->id, request, read_int); + else + errorcode = DP_ERROR_INVALID_PARAMETER; + break; + case DP_CMD_SET_AUTO_DOWNLOAD: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_auto_download(cmd->id, request, read_int); + break; + case DP_CMD_SET_NOTIFICATION: + if (dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_notification(cmd->id, request, read_int); + break; + case DP_CMD_SET_URL: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_url(cmd->id, request, read_str); + break; + case DP_CMD_SET_DESTINATION: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if (dp_is_smackfs_mounted() == 1 && + __dp_check_valid_directory(request, read_str) != 0){ + errorcode = DP_ERROR_PERMISSION_DENIED; + break; + } + errorcode = dp_request_set_destination(cmd->id, request, read_str); + break; + case DP_CMD_SET_FILENAME: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_filename(cmd->id, request, read_str); + break; + case DP_CMD_SET_NOTIFICATION_BUNDLE: + bundle_length = dp_ipc_read_bundle(sock, ¬i_bundle_type, &b_raw); + if (bundle_length == 0) { + errorcode = DP_ERROR_IO_ERROR; + break; + } else if (bundle_length == -1) { + errorcode = DP_ERROR_INVALID_PARAMETER; + break; + } + errorcode = dp_request_set_bundle(cmd->id, request, + noti_bundle_type, b_raw, bundle_length); + break; + case DP_CMD_SET_NOTIFICATION_TITLE: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_title(cmd->id, request, read_str); + break; + case DP_CMD_SET_NOTIFICATION_DESCRIPTION: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + errorcode = dp_request_set_description(cmd->id, request, read_str); + break; + case DP_CMD_SET_NOTIFICATION_TYPE: + if ((dp_ipc_read_custom_type(sock, &read_int, sizeof(int)) < 0)) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if (read_int == DP_NOTIFICATION_TYPE_NONE || + read_int == DP_NOTIFICATION_TYPE_COMPLETE_ONLY || + read_int == DP_NOTIFICATION_TYPE_ALL) + errorcode = dp_request_set_noti_type(cmd->id, request, read_int); + else + errorcode = DP_ERROR_INVALID_PARAMETER; + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + free(read_str); + bundle_free_encoded_rawdata(&b_raw); + if (errorcode != DP_ERROR_NONE) { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + if (errorcode == DP_ERROR_IO_ERROR) + return errorcode; + dp_ipc_send_errorcode(sock, errorcode); + return errorcode; + } + + // complex read() and write(). + char *read_str2 = NULL; + errorcode = DP_ERROR_NONE; + read_str = NULL; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_SET_HTTP_HEADER: + { + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if ((read_str2 = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + int conds_count = 3; // id + field + data + db_conds_list_fmt conds_p[conds_count]; + memset(&conds_p, 0x00, + conds_count * sizeof(db_conds_list_fmt)); + conds_p[0].column = DP_DB_COL_ID; + conds_p[0].type = DP_DB_COL_TYPE_INT; + conds_p[0].value = &cmd->id; + conds_p[1].column = DP_DB_COL_HEADER_FIELD; + conds_p[1].type = DP_DB_COL_TYPE_TEXT; + conds_p[1].value = read_str; + conds_p[2].column = DP_DB_COL_HEADER_DATA; + conds_p[2].type = DP_DB_COL_TYPE_TEXT; + conds_p[2].value = read_str2; + if (dp_db_get_conds_rows_count(DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_ID, "AND", 2, conds_p) <= 0) { // insert + if (dp_db_insert_columns(DP_DB_TABLE_HTTP_HEADERS, + conds_count, conds_p) < 0) { + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", cmd->id); + errorcode = DP_ERROR_NO_SPACE; + } else { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + } + } else { // update data by field + if (dp_db_cond_set_column(cmd->id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_DATA, DP_DB_COL_TYPE_TEXT, read_str2, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, read_str) < 0) { + if (dp_db_is_full_error() == 0) { + TRACE_ERROR("[SQLITE_FULL][%d]", cmd->id); + errorcode = DP_ERROR_NO_SPACE; + } else { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + } + } + dp_ipc_send_errorcode(sock, errorcode); + break; + } + case DP_CMD_DEL_HTTP_HEADER: + if ((read_str = dp_ipc_read_string(sock)) == NULL) { + errorcode = DP_ERROR_IO_ERROR; + break; + } + if (dp_db_get_cond_rows_count(cmd->id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, + read_str) <= 0) { + errorcode = DP_ERROR_NO_DATA; + } else { + if (dp_db_cond_remove(cmd->id, DP_DB_TABLE_HTTP_HEADERS, + DP_DB_COL_HEADER_FIELD, DP_DB_COL_TYPE_TEXT, read_str) < 0) + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + dp_ipc_send_errorcode(sock, errorcode); + break; + case DP_CMD_ADD_EXTRA_PARAM: + errorcode = __dp_add_extra_param(sock, cmd->id); + if (errorcode != DP_ERROR_IO_ERROR) + dp_ipc_send_errorcode(sock, errorcode); + break; + case DP_CMD_REMOVE_EXTRA_PARAM: + errorcode = __dp_remove_extra_param(sock, cmd->id); + if (errorcode != DP_ERROR_IO_ERROR) + dp_ipc_send_errorcode(sock, errorcode); + break; + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (errorcode != DP_ERROR_NONE) { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + free(read_str); + free(read_str2); + return errorcode; + } + return DP_ERROR_UNKNOWN; +} + +static dp_error_type __dp_do_action_command(int sock, dp_command* cmd, dp_request *request) +{ + unsigned is_checked = 1; + int read_int = 0; + dp_error_type errorcode = DP_ERROR_NONE; + + read_int = 0; + errorcode = DP_ERROR_NONE; + is_checked = 1; + switch(cmd->cmd) { + case DP_CMD_DESTROY: + if (request != NULL) {// just update the state + if (__is_started(request->state) == 0) { + read_int = DP_STATE_CANCELED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &read_int) < 0) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + if (__dp_call_cancel_agent(request) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", cmd->id); + request->state = DP_STATE_CANCELED; + if (request->auto_notification == 1 && + request->packagename != NULL) { + request->noti_priv_id = dp_set_downloadedinfo_notification + (request->noti_priv_id, request->id, + request->packagename, request->state); + } else { + int noti_type = dp_db_get_int_column(request->id, + DP_DB_TABLE_NOTIFICATION, DP_DB_COL_NOTI_TYPE); + if (noti_type > DP_NOTIFICATION_TYPE_NONE && + request->packagename != NULL) + request->noti_priv_id = dp_set_downloadedinfo_notification + (request->noti_priv_id, request->id, + request->packagename, request->state); + } + } + } + } + break; + case DP_CMD_PAUSE: + { + // to check fastly, divide the case by request value + if (request == NULL) { + dp_state_type state = dp_db_get_state(cmd->id); + // already paused or stopped + if (state > DP_STATE_DOWNLOADING) { + errorcode = DP_ERROR_INVALID_STATE; + } else { + // change state to paused. + state = DP_STATE_PAUSED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, &state) < 0) + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + break; + } + + if (request->state > DP_STATE_DOWNLOADING) { + errorcode = DP_ERROR_INVALID_STATE; + break; + } + + // before downloading including QUEUED + if (__is_downloading(request->state) < 0) { + dp_state_type state = DP_STATE_PAUSED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &state) < 0) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + request->state = state; + } + break; + } + // only downloading. + dp_state_type state = DP_STATE_PAUSE_REQUESTED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, &state) < 0) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + TRACE_INFO("[%s][%d]pause_agent(%d) state:%s", + __print_command(cmd->cmd), cmd->id, + request->agent_id, + dp_print_state(request->state)); + if (dp_pause_agent_download + (request->agent_id) < 0) { + TRACE_ERROR("[fail][%d]pause_agent(%d)", + cmd->id, request->agent_id); + } + request->state = state; + request->error = DP_ERROR_NONE; + request->pause_time = (int)time(NULL); + dp_db_update_date(cmd->id, DP_DB_TABLE_LOG, + DP_DB_COL_ACCESS_TIME); + } + break; + } + case DP_CMD_CANCEL: + { + // to check fastly, divide the case by request value + if (request == NULL) { + dp_state_type state = dp_db_get_state(cmd->id); + // already paused or stopped + if (__is_stopped(state) == 0) { + errorcode = DP_ERROR_INVALID_STATE; + } else { + // change state to canceled. + state = DP_STATE_CANCELED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, &state) < 0) + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + break; + } + + if (__is_stopped(request->state) == 0) { + TRACE_ERROR("[%s][%d]__is_stopped agentid:%d state:%s", + __print_command(cmd->cmd), cmd->id, + request->agent_id, dp_print_state(request->state)); + errorcode = DP_ERROR_INVALID_STATE; + } else { + // change state to canceled. + dp_state_type state = DP_STATE_CANCELED; + if (dp_db_set_column(cmd->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE, + DP_DB_COL_TYPE_INT, &state) < 0) + errorcode = DP_ERROR_OUT_OF_MEMORY; + } + if (errorcode == DP_ERROR_NONE) { + if (__dp_call_cancel_agent(request) < 0) + TRACE_ERROR("[fail][%d]cancel_agent", cmd->id); + request->state = DP_STATE_CANCELED; + } + dp_db_update_date(cmd->id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME); + break; + } + default: + is_checked = 0; + break; + } + if (is_checked == 1) { + if (errorcode != DP_ERROR_NONE) { + TRACE_ERROR("[ERROR][%d][%s][%s]", cmd->id, + __print_command(cmd->cmd), dp_print_errorcode(errorcode)); + } + dp_ipc_send_errorcode(sock, errorcode); + return errorcode; + } + return DP_ERROR_UNKNOWN; +} + +static dp_error_type __do_dp_start_command(int sock, int id, dp_privates *privates, + dp_client_group *group, dp_request *request) +{ + dp_error_type errorcode = DP_ERROR_NONE; + + // need URL at least + char *url = dp_request_get_url(id, &errorcode); + free(url); + if (errorcode == DP_ERROR_NO_DATA) { + errorcode = DP_ERROR_INVALID_URL; + dp_ipc_send_errorcode(sock, errorcode); + return errorcode; + } + + if (request == NULL) { // Support Re-download + // Support Re-download + int index = __get_empty_request_index(privates->requests); + if (index < 0) { // Busy, No Space in slot + errorcode = DP_ERROR_QUEUE_FULL; + TRACE_SECURE_ERROR("[ERROR][%d][RESTORE][%s]", id, + dp_print_errorcode(errorcode)); + } else { + CLIENT_MUTEX_LOCK(&privates->requests[index].mutex); + dp_request *tmp_request = + dp_request_load_from_log (id, &errorcode); + if (tmp_request != NULL) { + // restore callback info + tmp_request->state_cb = dp_db_get_int_column(id, + DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_STATE_EVENT); + tmp_request->progress_cb = dp_db_get_int_column(id, + DP_DB_TABLE_REQUEST_INFO, DP_DB_COL_PROGRESS_EVENT); + tmp_request->auto_notification = + dp_db_get_int_column(id, DP_DB_TABLE_REQUEST_INFO, + DP_DB_COL_NOTIFICATION_ENABLE); + if (group != NULL) // for send event to client + tmp_request->group = group; + privates->requests[index].request = tmp_request; + request = privates->requests[index].request; + } else { + TRACE_SECURE_ERROR("[ERROR][%d][RESTORE][%s]", id, + dp_print_errorcode(errorcode)); + dp_ipc_send_errorcode(sock, errorcode); + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + return errorcode; + } + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + } + } + + // check status + if (errorcode == DP_ERROR_NONE && + (__is_started(request->state) == 0 || + request->state == DP_STATE_COMPLETED)) + errorcode = DP_ERROR_INVALID_STATE; + + if (errorcode == DP_ERROR_NONE) { + + dp_state_type state = DP_STATE_QUEUED; + if (dp_db_set_column(id, DP_DB_TABLE_LOG, + DP_DB_COL_STATE, DP_DB_COL_TYPE_INT, &state) < 0) { + errorcode = DP_ERROR_OUT_OF_MEMORY; + } else { + if (group != NULL) + group->queued_count++; + request->start_time = (int)time(NULL); + request->pause_time = 0; + request->stop_time = 0; + request->state = state; + request->error = DP_ERROR_NONE; + dp_db_update_date(id, DP_DB_TABLE_LOG, DP_DB_COL_ACCESS_TIME); + } + + } + + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode != DP_ERROR_NONE) { + TRACE_SECURE_ERROR("[ERROR][%d][START][%s]", id, + dp_print_errorcode(errorcode)); + } + return errorcode; +} + +// in url-download, make 3 connection before send CREATE command. +// after accepting, fill info to pacakgelist. +// 3 socket per 1 package ( info/request/progress ) +void *dp_thread_requests_manager(void *arg) +{ + fd_set rset, eset, listen_fdset, except_fdset; + struct timeval timeout; // for timeout of select + long flexible_timeout = DP_CARE_CLIENT_MAX_INTERVAL; + int listenfd, clientfd, maxfd; + socklen_t clientlen; + struct sockaddr_un clientaddr; + dp_credential credential; + unsigned i, is_timeout; + int prev_timeout = 0; + dp_error_type errorcode = DP_ERROR_NONE; + + dp_privates *privates = (dp_privates*)arg; + if (privates == NULL || privates->groups == NULL) { + TRACE_ERROR("[CRITICAL] Invalid Address"); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; + } + + listenfd = privates->listen_fd; + maxfd = listenfd; + + TRACE_DEBUG("Ready to listen [%d][%s]", listenfd, DP_IPC); + + FD_ZERO(&listen_fdset); + FD_ZERO(&except_fdset); + FD_SET(listenfd, &listen_fdset); + FD_SET(listenfd, &except_fdset); + + while (privates != NULL && privates->listen_fd >= 0) { + + // select with timeout + // initialize timeout structure for calling timeout exactly + memset(&timeout, 0x00, sizeof(struct timeval)); + timeout.tv_sec = flexible_timeout; + clientfd = -1; + credential.pid = -1; + credential.uid = -1; + credential.gid = -1; + is_timeout = 1; + + rset = listen_fdset; + eset = except_fdset; + + if (select((maxfd + 1), &rset, 0, &eset, &timeout) < 0) { + TRACE_STRERROR("[CRITICAL] select"); + break; + } + + if (privates == NULL || privates->listen_fd < 0) { + TRACE_DEBUG("Terminate Thread"); + break; + } + + if (FD_ISSET(listenfd, &eset) > 0) { + TRACE_ERROR("[CRITICAL] listenfd Exception of socket"); + break; + } else if (FD_ISSET(listenfd, &rset) > 0) { + // new client(package) called url_download_create API. + // update g_dp_request_max_fd & g_dp_info_max_fd + // add to socket to g_dp_request_rset & g_dp_info_rset + + is_timeout = 0; + + // Anyway accept client. + clientlen = sizeof(clientaddr); + clientfd = accept(listenfd, + (struct sockaddr*)&clientaddr, + &clientlen); + if (clientfd < 0) { + TRACE_ERROR("[CRITICAL] accept provider was crashed ?"); + // provider need the time of refresh. + break; + } + + dp_command_type connect_cmd = DP_CMD_NONE; + if (dp_ipc_read_custom_type(clientfd, + &connect_cmd, sizeof(dp_command_type)) < 0) { + TRACE_ERROR("[CRITICAL] CAPI not support CONNECT CMD"); + close(clientfd); + continue; + } + if (connect_cmd <= 0) { + TRACE_ERROR("[CRITICAL] peer terminate ?"); + close(clientfd); + continue; + } + +#ifdef SO_PEERCRED + // getting the info of client + socklen_t cr_len = sizeof(credential); + if (getsockopt(clientfd, SOL_SOCKET, SO_PEERCRED, + &credential, &cr_len) == 0) { + TRACE_DEBUG + ("credential : pid=%d, uid=%d, gid=%d", + credential.pid, credential.uid, credential.gid); + } +#else // In case of not supported SO_PEERCRED + int client_pid = 0; + if (dp_ipc_read_custom_type(clientfd, + &client_pid, sizeof(int)) < 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } + if (client_pid <= 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } + credential.pid = client_pid; + if (dp_ipc_read_custom_type(clientfd, + &credential.uid, sizeof(int)) < 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } + if (dp_ipc_read_custom_type(clientfd, + &credential.gid, sizeof(int)) < 0) { + TRACE_ERROR("[CRITICAL] not support SO_PEERCRED"); + close(clientfd); + continue; + } +#endif + + switch(connect_cmd) { + case DP_CMD_SET_COMMAND_SOCKET: + if (__dp_set_group_new(clientfd, privates->groups, + credential, &listen_fdset) == 0) { + FD_SET(clientfd, &listen_fdset); + if (clientfd > maxfd) + maxfd = clientfd; + } else { + close(clientfd); + } + break; + case DP_CMD_SET_EVENT_SOCKET: + if (__dp_set_group_event_sock(clientfd, + privates->groups, credential) < 0) + close(clientfd); + break; + default: + TRACE_ERROR("[CRITICAL] Bad access,ignore this client"); + close(clientfd); + break; + } + } // New Connection + + // listen cmd_socket of all group + for (i = 0; i < DP_MAX_GROUP; i++) { + dp_client_group *group = privates->groups[i].group; + if (group == NULL || group->cmd_socket < 0) + continue; + const int sock = group->cmd_socket; + int time_of_job = (int)time(NULL); + if (FD_ISSET(sock, &rset) > 0) { + int index = -1; + dp_command command; + + is_timeout = 0; + command.cmd = DP_CMD_NONE; + command.id = -1; + + if (dp_ipc_read_custom_type(sock, &command, + sizeof(dp_command)) < 0) { + TRACE_STRERROR("failed to read cmd"); + //Resource temporarily unavailable + continue; + } + + if (command.cmd == 0 || command.cmd >= DP_CMD_LAST_SECT) { // Client meet some Error. + TRACE_SECURE_INFO("[Closed Peer] pkg:%s sock:%d slot:%d", + group->pkgname, sock, i); + // check all request included to this group + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + continue; + } + + // Echo .client can check whether provider is busy + if (command.cmd == DP_CMD_ECHO) { + // provider can clear read buffer here + TRACE_DEBUG("[ECHO] sock:%d", sock); + if (dp_ipc_send_errorcode(sock, + DP_ERROR_NONE) < 0) { + // disconnect this group, bad client + TRACE_ERROR("[ECHO] IO ERROR CLEAN sock:%d", sock); + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + } + continue; + } + + if (command.cmd == DP_CMD_CREATE) { + TRACE_SECURE_INFO("[CREATE] id:%d sock:%d pid:%d gindex:%d pkg:%s", + command.id, sock, + group->credential.pid, i, group->pkgname); + // search empty slot in privates->requests + index = + __get_empty_request_index(privates->requests); + if (index < 0) { + TRACE_ERROR("[CHECK] [DP_ERROR_QUEUE_FULL]"); + // Busy, No Space in slot + dp_ipc_send_errorcode(sock, DP_ERROR_QUEUE_FULL); + } else { + errorcode = DP_ERROR_NONE; + CLIENT_MUTEX_LOCK(&privates->requests[index].mutex); + errorcode = dp_request_create(command.id, group, + &privates->requests[index].request); + dp_ipc_send_errorcode(sock, errorcode); + if (errorcode == DP_ERROR_NONE) { + dp_ipc_send_custom_type(sock, + &privates->requests[index].request->id, + sizeof(int)); + TRACE_DEBUG("[CREATE] GOOD id:%d slot:%d time:%d", + privates->requests[index].request->id, + index, ((int)time(NULL) - time_of_job)); + } else { + TRACE_ERROR("[ERROR][%s]", + dp_print_errorcode(errorcode)); + } + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + } + continue; + } + + // below commands must need ID + // check exception before searching slots. + if (command.id < 0) { + TRACE_ERROR("[CHECK PROTOCOL] ID should not be -1"); + dp_ipc_send_errorcode(sock, + DP_ERROR_INVALID_PARAMETER); + // disconnect this group, bad client + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + continue; + } + + // search id in requests slot + index = __get_same_request_index(privates->requests, + command.id); + + dp_request_slots *request_slot = NULL; + unsigned is_loaded = 0; + errorcode = DP_ERROR_NONE; + + if (index >= 0) { + CLIENT_MUTEX_LOCK(&privates->requests[index].mutex); + if (privates->requests[index].request != NULL) + is_loaded = 1; + else + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + } + + errorcode = DP_ERROR_UNKNOWN; // check matched command + + // divide on memory or from DB + if (is_loaded == 1 && index >= 0) { // already locked + + TRACE_SECURE_INFO("[%s] id:%d sock:%d pid:%d gindex:%d slot:%d pkg:%s", + __print_command(command.cmd), command.id, sock, + group->credential.pid, i, index, group->pkgname); + + request_slot = &privates->requests[index]; + + // auth by pkgname + if (__cmp_string(group->pkgname, request_slot->request->packagename) < 0) { + TRACE_SECURE_ERROR("[ERROR][%d] Auth [%s]/[%s]", + command.id, group->pkgname, request_slot->request->packagename); + TRACE_ERROR("[ERROR][%d][INVALID_PARAMETER]", command.id); + dp_ipc_send_errorcode(sock, DP_ERROR_INVALID_PARAMETER); + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + continue; + } + + // if no group, update group. + if (request_slot->request->group == NULL) + request_slot->request->group = group; + + if (command.cmd == DP_CMD_START) + errorcode = __do_dp_start_command(sock, command.id, privates, group, request_slot->request); + else if (command.cmd > DP_CMD_SET_SECT && command.cmd < DP_CMD_LAST_SECT) + errorcode = __dp_do_set_command(sock, &command, request_slot->request); + else if (command.cmd > DP_CMD_GET_SECT && command.cmd < DP_CMD_SET_SECT) + errorcode = __dp_do_get_command(sock, &command, request_slot->request); + else if (command.cmd > DP_CMD_ACTION_SECT && command.cmd < DP_CMD_GET_SECT) + errorcode = __dp_do_action_command(sock, &command, request_slot->request); + + CLIENT_MUTEX_UNLOCK(&request_slot->mutex); + + if (command.cmd == DP_CMD_START && errorcode == DP_ERROR_NONE) { + //send signal to queue thread + dp_thread_queue_manager_wake_up(); + } + if (command.cmd == DP_CMD_FREE) {// enter after unlock + dp_request_slot_free(request_slot); + errorcode = DP_ERROR_NONE; + } + + } else { // not found on the slots + + TRACE_SECURE_INFO("[%s] id:%d sock:%d pid:%d gindex:%d slot:no pkg:%s", + __print_command(command.cmd), command.id, sock, + group->credential.pid, i, group->pkgname); + + int check_id = dp_db_get_int_column(command.id, + DP_DB_TABLE_LOG, DP_DB_COL_ID); + if (check_id < 0 || check_id != command.id) { + errorcode = DP_ERROR_ID_NOT_FOUND; + TRACE_ERROR("[ERROR][%d][ID_NOT_FOUND]", command.id); + dp_ipc_send_errorcode(sock, DP_ERROR_ID_NOT_FOUND); + continue; + } + // auth by pkgname + char *auth_pkgname = dp_db_get_text_column(command.id, + DP_DB_TABLE_LOG, DP_DB_COL_PACKAGENAME); + if (auth_pkgname == NULL) { + TRACE_ERROR("[ERROR][%d][ID_NOT_FOUND]", command.id); + dp_ipc_send_errorcode(sock, DP_ERROR_ID_NOT_FOUND); + continue; + } + // auth by pkgname + if (__cmp_string(group->pkgname, auth_pkgname) < 0) { + TRACE_SECURE_ERROR("[ERROR][%d] Auth [%s]/[%s]", + command.id, group->pkgname, auth_pkgname); + TRACE_ERROR("[ERROR][%d][INVALID_PARAMETER]", command.id); + dp_ipc_send_errorcode(sock, DP_ERROR_INVALID_PARAMETER); + free(auth_pkgname); + continue; + } + free(auth_pkgname); + + if (command.cmd == DP_CMD_START) + errorcode = __do_dp_start_command(sock, command.id, privates, group, NULL); + else if (command.cmd == DP_CMD_FREE) + errorcode = DP_ERROR_NONE; + else if (command.cmd > DP_CMD_SET_SECT && command.cmd < DP_CMD_LAST_SECT) + errorcode = __dp_do_set_command(sock, &command, NULL); + else if (command.cmd > DP_CMD_GET_SECT && command.cmd < DP_CMD_SET_SECT) + errorcode = __dp_do_get_command(sock, &command, NULL); + else if (command.cmd > DP_CMD_ACTION_SECT && command.cmd < DP_CMD_GET_SECT) + errorcode = __dp_do_action_command(sock, &command, NULL); + + if (command.cmd == DP_CMD_START && errorcode == DP_ERROR_NONE) { + //send signal to queue thread + dp_thread_queue_manager_wake_up(); + } + } + + if (errorcode == DP_ERROR_IO_ERROR) { + TRACE_ERROR("[IO_ERROR][%d] slot:%d pid:%d", + command.id, i, + privates->groups[i].group->credential.pid); + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + continue; + } + if (errorcode == DP_ERROR_UNKNOWN) { // UnKnown Command + TRACE_INFO("[UNKNOWN][%d] slot:%d pid:%d", + command.id, i, + privates->groups[i].group->credential.pid); + dp_ipc_send_errorcode(sock, DP_ERROR_IO_ERROR); + // disconnect this group, bad client + FD_CLR(sock, &listen_fdset); + __clear_group(privates, group); + privates->groups[i].group = NULL; + } + + } // FD_ISSET + } // DP_MAX_GROUP + + // timeout + if (is_timeout == 1) { + int now_timeout = (int)time(NULL); + TRACE_DEBUG("[TIMEOUT] prev %ld, now %ld, setted %ld sec", + prev_timeout, now_timeout, flexible_timeout); + if (prev_timeout == 0) { + prev_timeout = now_timeout; + } else if (now_timeout < prev_timeout || + (now_timeout - prev_timeout) > flexible_timeout) { + TRACE_ERROR("[WARN] check system date prev[%ld]now[%ld]", + prev_timeout, now_timeout); + } else { + if ((now_timeout - prev_timeout) < + DP_CARE_CLIENT_MIN_INTERVAL) { + // this is error. + // terminate Process + TRACE_STRERROR + ("[CRITICAL] Sock exception prev[%ld]now[%ld][%ld]", + prev_timeout, now_timeout, flexible_timeout); + break; + } + } + + // get 48hour old request from log DB + // clear old request + dp_db_limit_rows(DP_LOG_DB_LIMIT_ROWS); + int old_request_count = dp_db_get_count_by_limit_time(); + if (old_request_count > 0) { + if (old_request_count > DP_LOG_DB_CLEAR_LIMIT_ONE_TIME) + old_request_count = DP_LOG_DB_CLEAR_LIMIT_ONE_TIME; + + TRACE_DEBUG + ("[CLEAR] [%d] old reqeusts", old_request_count); + + dp_request_slots *old_requests = + dp_request_slots_new(old_request_count); + if (old_requests != NULL) { + int list_count = dp_db_get_list_by_limit_time + (old_requests, old_request_count); + for (i = 0; i < list_count; i++) { + // search on slots by ID. + int index = __get_same_request_index + (privates->requests, + old_requests[i].request->id); + if (index >= 0) { + CLIENT_MUTEX_LOCK(&privates->requests[index].mutex); + dp_request *request = + privates->requests[index].request; + // if downloading..remain it. + if (request == NULL || __is_downloading(request->state) == 0) { + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + continue; + } + CLIENT_MUTEX_UNLOCK(&privates->requests[index].mutex); + TRACE_DEBUG("[CLEAR][%d] 48 hour state:%s", + request->id, + dp_print_state(request->state)); + // unload from slots ( memory ) + dp_request_slot_free(&privates->requests[index]); + } + // remove tmp file regardless + __dp_remove_tmpfile + (old_requests[i].request->id, + old_requests[i].request); + // remove from DB + dp_db_remove_all(old_requests[i].request->id); + } + dp_request_slots_free(old_requests, old_request_count); + } + } + + // clean slots + int ready_requests = 0; + for (i = 0; i < DP_MAX_REQUEST; i++) { + CLIENT_MUTEX_LOCK(&privates->requests[i].mutex); + if (privates->requests[i].request == NULL) { + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + continue; + } + dp_request *request = privates->requests[i].request; + + // If downloading is too slow ? how to deal this request? + // can limit too slot download using starttime.(48 hours) + if (__is_downloading(request->state) == 0) { + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + continue; + } + + // paused & agent_id not exist.... unload from memory. + if (request->state == DP_STATE_PAUSED && + dp_is_alive_download(request->agent_id) == 0) { + TRACE_DEBUG("[FREE][%d] dead agent-id(%d) state:%s", + request->id, request->agent_id, + dp_print_state(request->state)); + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + dp_request_slot_free(&privates->requests[i]); + continue; + } + + // client should call START command in 60 sec + // unload from memory + if (request->start_time <= 0 && + (now_timeout - request->create_time) > + DP_CARE_CLIENT_MAX_INTERVAL) { + int download_id = request->id; + dp_state_type state = DP_STATE_FAILED; + errorcode = DP_ERROR_RESPONSE_TIMEOUT; + TRACE_DEBUG + ("[FREE][%d] start in %d sec state:%s last:%ld", + download_id, DP_CARE_CLIENT_MAX_INTERVAL, + dp_print_state(request->state), + request->start_time); + if (request->group != NULL && + request->state_cb == 1 && + request->group->event_socket >= 0) + dp_ipc_send_event(request->group->event_socket, + download_id, state, errorcode, 0); + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + dp_request_slot_free(&privates->requests[i]); + // no problem although updating is failed. + if (dp_db_request_update_status(download_id, state, errorcode) < 0) { + TRACE_ERROR("[fail][%d][sql] set state:%s error:%s", + download_id, dp_print_state(state), dp_print_errorcode(errorcode)); + } + continue; + } + + // stopped by ipchanged. decide auto resume + if (request->stop_time <= 0 && + request->state == DP_STATE_FAILED && + request->error == DP_ERROR_CONNECTION_FAILED) { + if (dp_get_network_connection_instant_status() != + DP_NETWORK_TYPE_OFF && + request->ip_changed == 1) { + TRACE_DEBUG("[RESUME][%d] will be queued", + request->id); + request->state = DP_STATE_QUEUED; + request->error = DP_ERROR_NONE; + ready_requests++; + } else { + dp_request_state_response(request); + } + } + + // client should call DESTROY command in MAX_INTERVAL sec after finished + if (request->stop_time > 0 && + (now_timeout - request->stop_time) > + DP_CARE_CLIENT_MAX_INTERVAL) { + // check state again. stop_time means it's stopped + if (__is_stopped(request->state) == 0) { + TRACE_DEBUG + ("[FREE][%d] by timeout state:%s stop:%ld", + request->id, + dp_print_state(request->state), + request->stop_time); + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + dp_request_slot_free(&privates->requests[i]); + continue; + } + } + + // check after clear + if (request->state == DP_STATE_QUEUED) + ready_requests++; + + CLIENT_MUTEX_UNLOCK(&privates->requests[i].mutex); + } + + if (ready_requests > 0) { + //send signal to queue thread will check queue. + dp_thread_queue_manager_wake_up(); + } else { + // if no request & timeout is bigger than 60 sec + // terminate by self. + if ((now_timeout - prev_timeout) >= flexible_timeout && + dp_get_request_count(privates->requests) <= 0) { + TRACE_DEBUG("No Request. Terminate Daemon"); + break; + } + } + prev_timeout = now_timeout; + } else { + prev_timeout = 0; + } + } + TRACE_DEBUG("terminate main thread ..."); + dp_terminate(SIGTERM); + pthread_exit(NULL); + return 0; +} diff --git a/provider/include/download-provider-config.h b/provider/include/download-provider-config.h new file mode 100755 index 0000000..96862ec --- /dev/null +++ b/provider/include/download-provider-config.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_CONFIG_H +#define DOWNLOAD_PROVIDER2_CONFIG_H + +#include <download-provider.h> +#include <download-provider-slots.h> + +#include <net_connection.h> + +#define DP_LOCK_PID "/tmp/download-provider.lock" + +#define DP_CARE_CLIENT_MIN_INTERVAL 5 +#define DP_CARE_CLIENT_MAX_INTERVAL 120 + +// check this value should be lower than DP_MAX_REQUEST +#define DP_MAX_DOWNLOAD_AT_ONCE 50 + +#define DP_LOG_DB_LIMIT_ROWS 1000 +#define DP_LOG_DB_CLEAR_LIMIT_ONE_TIME 100 + +// Share the structure for all threads +typedef struct { + int listen_fd; + int is_connected_wifi_direct; + connection_h connection; + dp_network_type network_status; + dp_group_slots *groups; + dp_request_slots *requests; +} dp_privates; + +#endif diff --git a/provider/include/download-provider-da-interface.h b/provider/include/download-provider-da-interface.h new file mode 100755 index 0000000..ab6e716 --- /dev/null +++ b/provider/include/download-provider-da-interface.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_DA_INTERFACE_H +#define DOWNLOAD_PROVIDER2_DA_INTERFACE_H + +int dp_is_file_exist(const char *file_path); +int dp_init_agent(); +void dp_deinit_agent(); +dp_error_type dp_start_agent_download(dp_request_slots *request); +dp_error_type dp_resume_agent_download(int req_id); +dp_error_type dp_pause_agent_download(int req_id); +dp_error_type dp_cancel_agent_download(int req_id); +int dp_is_alive_download(int req_id); + +#endif diff --git a/provider/include/download-provider-db.h b/provider/include/download-provider-db.h new file mode 100755 index 0000000..24a7d27 --- /dev/null +++ b/provider/include/download-provider-db.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_DB_H +#define DOWNLOAD_PROVIDER2_DB_H + +#include "download-provider-config.h" +#include "download-provider-slots.h" + +/* + * Memory ( sync with logging ) : id, state, errorcode, startcount, packagename + * DB TABLES + * logging : id, state, errorcode, startcount, createtime, accesstime, packagename + * requestinfo : id, auto_download, network_type, filename, destination, url + * downloadinfo : id, http_status, content_size, mimetype, contentname, saved_path, tmp_saved_path, etag + * httpheaders : id, header_field, header_data + * notification : id, noti_enable, extra_key, extra_data + */ +/* +CREATE TABLE IF NOT EXISTS groups +( + id INTEGER UNIQUE PRIMARY KEY, + uid INTEGER DEFAULT 0, + gid INTEGER DEFAULT 0, + extra_int INTEGER DEFAULT 0, + packagename TEXT DEFAULT NULL, + smack_label TEXT DEFAULT NULL, + extra TEXT DEFAULT NULL, + date_first_connected DATE, + date_last_connected DATE +); + +CREATE TABLE logging +( + id INTEGER UNIQUE PRIMARY KEY, + state INTEGER DEFAULT 0, + errorcode INTEGER DEFAULT 0, + startcount INTEGER DEFAULT 0, + packagename TEXT DEFAULT NULL, + createtime DATE, + accesstime DATE +); + +CREATE TABLE requestinfo +( + id INTEGER UNIQUE PRIMARY KEY, + auto_download BOOLEAN DEFAULT 0, + state_event BOOLEAN DEFAULT 0, + progress_event BOOLEAN DEFAULT 0, + network_type TINYINT DEFAULT 0, + filename TEXT DEFAULT NULL, + destination TEXT DEFAULT NULL, + url TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE downloadinfo +( + id INTEGER UNIQUE PRIMARY KEY, + http_status INTEGER DEFAULT 0, + content_size UNSIGNED BIG INT DEFAULT 0, + mimetype VARCHAR(64) DEFAULT NULL, + content_name TEXT DEFAULT NULL, + saved_path TEXT DEFAULT NULL, + tmp_saved_path TEXT DEFAULT NULL, + etag TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE httpheaders +( + id INTEGER NOT NULL, + header_field TEXT DEFAULT NULL, + header_data TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE TABLE notification +( + id INTEGER UNIQUE PRIMARY KEY, + noti_enable BOOLEAN DEFAULT 0, + extra_key TEXT DEFAULT NULL, + extra_data TEXT DEFAULT NULL, + FOREIGN KEY(id) REFERENCES logging(id) ON DELETE CASCADE +); + +CREATE UNIQUE INDEX requests_index ON logging (id, state, errorcode, packagename, createtime, accesstime); +*/ + +#define DP_DB_TABLE_GROUPS "groups" +#define DP_DB_TABLE_LOG "logging" +#define DP_DB_TABLE_REQUEST_INFO "requestinfo" +#define DP_DB_TABLE_DOWNLOAD_INFO "downloadinfo" +#define DP_DB_TABLE_HTTP_HEADERS "httpheaders" +#define DP_DB_TABLE_NOTIFICATION "notification" + +#define DP_DB_COL_ID "id" +#define DP_DB_COL_STATE "state" +#define DP_DB_COL_ERRORCODE "errorcode" +#define DP_DB_COL_NETWORK_TYPE "network_type" +#define DP_DB_COL_HTTP_STATUS "http_status" +#define DP_DB_COL_AUTO_DOWNLOAD "auto_download" +#define DP_DB_COL_STATE_EVENT "state_event" +#define DP_DB_COL_PROGRESS_EVENT "progress_event" +#define DP_DB_COL_CONTENT_SIZE "content_size" +#define DP_DB_COL_CREATE_TIME "createtime" +#define DP_DB_COL_ACCESS_TIME "accesstime" +#define DP_DB_COL_STARTCOUNT "startcount" +#define DP_DB_COL_PACKAGENAME "packagename" +#define DP_DB_COL_DESTINATION "destination" +#define DP_DB_COL_FILENAME "filename" +#define DP_DB_COL_CONTENT_NAME "content_name" +#define DP_DB_COL_MIMETYPE "mimetype" +#define DP_DB_COL_ETAG "etag" +#define DP_DB_COL_SAVED_PATH "saved_path" +#define DP_DB_COL_TMP_SAVED_PATH "tmp_saved_path" +#define DP_DB_COL_URL "url" +#define DP_DB_COL_HEADER_FIELD "header_field" +#define DP_DB_COL_HEADER_DATA "header_data" +#define DP_DB_COL_NOTIFICATION_ENABLE "noti_enable" +#define DP_DB_COL_EXTRA_KEY "extra_key" +#define DP_DB_COL_DISTINCT_EXTRA_KEY "DISTINCT extra_key" +#define DP_DB_COL_EXTRA_VALUE "extra_data" +#define DP_DB_COL_RAW_BUNDLE_ONGOING "raw_bundle_data_ongoing_state" +#define DP_DB_COL_RAW_BUNDLE_COMPLETE "raw_bundle_data_complete_state" +#define DP_DB_COL_RAW_BUNDLE_FAIL "raw_bundle_data_fail_state" +#define DP_DB_COL_TITLE "title" +#define DP_DB_COL_DESCRIPTION "description" +#define DP_DB_COL_NOTI_TYPE "noti_type" + +#define DP_DB_GROUPS_COL_UID "uid" +#define DP_DB_GROUPS_COL_GID "gid" +#define DP_DB_GROUPS_COL_PKG "packagename" +#define DP_DB_GROUPS_COL_SMACK_LABEL "smack_label" + + +typedef enum { + DP_DB_COL_TYPE_NONE = 0, + DP_DB_COL_TYPE_INT = 10, + DP_DB_COL_TYPE_INT64 = 20, + DP_DB_COL_TYPE_TEXT = 30 +} db_column_data_type; + +typedef struct { + char *column; + db_column_data_type type; + int is_like; + void *value; +} db_conds_list_fmt; + +int dp_db_open(); +void dp_db_close(); + +int dp_db_is_full_error(); + +int dp_db_remove_all(int id); +int dp_db_remove(int id, char *table); +int dp_db_insert_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +int dp_db_insert_blob_column(int id, char *table, char *column, + void *value, unsigned length); +int dp_db_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +int dp_db_set_blob_column(int id, char *table, char *column, + void *value, unsigned length); +int dp_db_replace_column(int id, char *table, char *column, + db_column_data_type datatype, void *value); +int dp_db_replace_blob_column(int id, char *table, char *column, + void *value, unsigned length); +char *dp_db_get_text_column(int id, char *table, char *column); +void *dp_db_get_blob_column(int id, char *table, char *column, int *length); +int dp_db_get_int_column(int id, char *table, char *column); +long long dp_db_get_int64_column(int id, char *table, char *column); +int dp_db_update_date(int id, char *table, char *column); + +// cond : id & cond +int dp_db_cond_set_column(int id, char *table, char *column, + db_column_data_type datatype, void *value, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +char *dp_db_cond_get_text_column(int id, char *table, char *column, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +int dp_db_cond_remove(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue); +int dp_db_get_cond_rows_count(int id, char *table, + char *condcolumn, db_column_data_type condtype, + void *condvalue); + +char *dp_db_cond_get_text(char *table, char *column, char *condcolumn, + db_column_data_type condtype, void *condvalue); +int dp_db_cond_get_int(char *table, char *column, char *condcolumn, + db_column_data_type condtype, void *condvalue); + +// Special API for http headers +int dp_db_get_http_headers_list(int id, char **headers); + +// For auto-download in booting time +int dp_db_crashed_list(dp_request_slots *requests, int limit); + +// For loading to memory when no in memory +dp_request *dp_db_load_logging_request(int id); + +// For limitation by 48 hours & 1000 rows +int dp_db_limit_rows(int limit); +int dp_db_get_count_by_limit_time(); +int dp_db_get_list_by_limit_time(dp_request_slots *requests, int limit); + +int dp_db_insert_columns(char *table, int column_count, + db_conds_list_fmt *columns); +int dp_db_update_columns(int id, char *table, int column_count, + db_conds_list_fmt *columns); + +int dp_db_get_conds_rows_count(char *table, char *getcolumn, char *op, + int conds_count, db_conds_list_fmt *conds); + +int dp_db_get_conds_list(char *table, char *getcolumn, + db_column_data_type gettype, void **list, + int rowslimit, int rowsoffset, + char *ordercolumn, char *ordering, + char *op, int conds_count, + db_conds_list_fmt *conds); + +int dp_db_request_new_logging(const int id, const int state, const char *pkgname); +int dp_db_request_update_status(const int id, const int state, const int download_error); +int dp_db_get_state(int id); +int dp_db_request_new_logging(const int id, + const int state, const char *pkgname); +#endif diff --git a/provider/include/download-provider-defs.h b/provider/include/download-provider-defs.h new file mode 100755 index 0000000..4387e49 --- /dev/null +++ b/provider/include/download-provider-defs.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER_DEFS_H +#define DOWNLOAD_PROVIDER_DEFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + DP_STATE_NONE = 0, + DP_STATE_READY = DP_STATE_NONE + 5, // created id, set some info. + DP_STATE_QUEUED = DP_STATE_NONE + 10, // request to start + DP_STATE_CONNECTING = DP_STATE_NONE + 15, // try to connect to url + DP_STATE_DOWNLOADING = DP_STATE_NONE + 20, // started + DP_STATE_PAUSE_REQUESTED = DP_STATE_NONE + 25, + DP_STATE_PAUSED = DP_STATE_NONE + 30, // paused actually + DP_STATE_COMPLETED = DP_STATE_NONE + 40, + DP_STATE_CANCELED = DP_STATE_NONE + 45, // stopped with error + DP_STATE_FAILED = DP_STATE_NONE + 50 // failed with error +} dp_state_type; + +typedef enum { + DP_ERROR_NONE = 10, + DP_ERROR_INVALID_PARAMETER = DP_ERROR_NONE + 1, + DP_ERROR_OUT_OF_MEMORY = DP_ERROR_NONE + 2, + DP_ERROR_IO_ERROR = DP_ERROR_NONE + 3, + DP_ERROR_NETWORK_UNREACHABLE = DP_ERROR_NONE + 4, + DP_ERROR_CONNECTION_TIMED_OUT = DP_ERROR_NONE + 5, + DP_ERROR_NO_SPACE = DP_ERROR_NONE + 6, + DP_ERROR_FIELD_NOT_FOUND = DP_ERROR_NONE + 7, + DP_ERROR_INVALID_STATE = DP_ERROR_NONE + 8, + DP_ERROR_CONNECTION_FAILED = DP_ERROR_NONE + 9, + DP_ERROR_INVALID_URL = DP_ERROR_NONE + 10, + DP_ERROR_INVALID_DESTINATION = DP_ERROR_NONE + 11, + DP_ERROR_QUEUE_FULL = DP_ERROR_NONE + 12, + DP_ERROR_ALREADY_COMPLETED = DP_ERROR_NONE + 13, + DP_ERROR_FILE_ALREADY_EXISTS = DP_ERROR_NONE + 14, + DP_ERROR_TOO_MANY_DOWNLOADS = DP_ERROR_NONE + 15, + DP_ERROR_NO_DATA = DP_ERROR_NONE + 17, + DP_ERROR_UNHANDLED_HTTP_CODE = DP_ERROR_NONE + 18, + DP_ERROR_CANNOT_RESUME = DP_ERROR_NONE + 19, + DP_ERROR_PERMISSION_DENIED = DP_ERROR_NONE + 20, + DP_ERROR_INVALID_NETWORK_TYPE = DP_ERROR_NONE + 21, + DP_ERROR_RESPONSE_TIMEOUT = DP_ERROR_NONE + 50, + DP_ERROR_REQUEST_TIMEOUT = DP_ERROR_NONE + 55, + DP_ERROR_SYSTEM_DOWN = DP_ERROR_NONE + 60, + DP_ERROR_CLIENT_DOWN = DP_ERROR_NONE + 65, + DP_ERROR_ID_NOT_FOUND = DP_ERROR_NONE + 90, + DP_ERROR_IO_EAGAIN = DP_ERROR_NONE + 97, + DP_ERROR_IO_EINTR = DP_ERROR_NONE + 98, + DP_ERROR_IO_TIMEOUT = DP_ERROR_NONE + 99, + DP_ERROR_UNKNOWN = DP_ERROR_NONE + 100 +} dp_error_type; + +typedef enum { + DP_NETWORK_TYPE_OFF = -1, + DP_NETWORK_TYPE_ALL = 0, + DP_NETWORK_TYPE_WIFI = 1, + DP_NETWORK_TYPE_DATA_NETWORK = 2, + DP_NETWORK_TYPE_ETHERNET = 3, + DP_NETWORK_TYPE_WIFI_DIRECT = 4 +} dp_network_type; + +typedef enum { + DP_NOTIFICATION_BUNDLE_TYPE_ONGOING = 0, // Ongoing, Failed + DP_NOTIFICATION_BUNDLE_TYPE_COMPLETE, // Completed + DP_NOTIFICATION_BUNDLE_TYPE_FAILED // Failed +} dp_notification_bundle_type; + +typedef enum { + DP_NOTIFICATION_TYPE_NONE = 0, // Not register Noti. + DP_NOTIFICATION_TYPE_COMPLETE_ONLY, // Success, Failed + DP_NOTIFICATION_TYPE_ALL // Ongoing, Success, Failed +} dp_notification_type; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/provider/include/download-provider-log.h b/provider/include/download-provider-log.h new file mode 100755 index 0000000..e828905 --- /dev/null +++ b/provider/include/download-provider-log.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_LOG_H +#define DOWNLOAD_PROVIDER2_LOG_H + +#ifdef SUPPORT_LOG_MESSAGE +#include <errno.h> +#include <dlog.h> +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "DOWNLOAD_PROVIDER" +#define TRACE_DEBUG(format, ARG...) LOGD(format, ##ARG) +#define TRACE_ERROR(format, ARG...) LOGE(format, ##ARG) +#define TRACE_STRERROR(format, ARG...) LOGE(format" [%s]", ##ARG, strerror(errno)) +#define TRACE_INFO(format, ARG...) LOGI(format, ##ARG) + +#ifdef SECURE_LOGD +#define TRACE_SECURE_DEBUG(format, ARG...) SECURE_LOGD(format, ##ARG) +#else +#define TRACE_SECURE_DEBUG(...) do { } while(0) +#endif +#ifdef SECURE_LOGI +#define TRACE_SECURE_INFO(format, ARG...) SECURE_LOGI(format, ##ARG) +#else +#define TRACE_SECURE_INFO(...) do { } while(0) +#endif +#ifdef SECURE_LOGE +#define TRACE_SECURE_ERROR(format, ARG...) SECURE_LOGE(format, ##ARG) +#else +#define TRACE_SECURE_ERROR(...) do { } while(0) +#endif + +#else +#define TRACE_DEBUG(...) do { } while(0) +#define TRACE_ERROR(...) do { } while(0) +#define TRACE_STRERROR(...) do { } while(0) +#define TRACE_INFO(...) do { } while(0) +#define TRACE_SECURE_DEBUG(...) do { } while(0) +#define TRACE_SECURE_INFO(...) do { } while(0) +#define TRACE_SECURE_ERROR(...) do { } while(0) +#endif + +#endif diff --git a/provider/include/download-provider-network.h b/provider/include/download-provider-network.h new file mode 100755 index 0000000..98a9b43 --- /dev/null +++ b/provider/include/download-provider-network.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_NETWORK_H +#define DOWNLOAD_PROVIDER2_NETWORK_H + +#include <net_connection.h> + +#include "download-provider.h" + +dp_network_type dp_get_network_connection_status(connection_h connection, connection_type_e type); +void dp_network_connection_type_changed_cb(connection_type_e type, void *data); +int dp_network_connection_init(dp_privates *privates); +void dp_network_connection_destroy(connection_h connection); +dp_network_type dp_get_network_connection_instant_status(); + +#ifdef SUPPORT_WIFI_DIRECT +int dp_network_wifi_direct_is_connected(); +#endif + +#endif diff --git a/provider/include/download-provider-notification.h b/provider/include/download-provider-notification.h new file mode 100755 index 0000000..5a9f5b9 --- /dev/null +++ b/provider/include/download-provider-notification.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_NOTIFICATION_H +#define DOWNLOAD_PROVIDER2_NOTIFICATION_H + +#include "download-provider-config.h" + +int dp_set_downloadinginfo_notification(int id, char *packagename); +int dp_set_downloadedinfo_notification(int priv_id, int id, char *packagename, dp_state_type state); +void dp_update_downloadinginfo_notification(int priv_id, double received_size, double file_size); +void dp_clear_downloadinginfo_notification(void); + +#endif diff --git a/provider/include/download-provider-pthread.h b/provider/include/download-provider-pthread.h new file mode 100755 index 0000000..8c02c59 --- /dev/null +++ b/provider/include/download-provider-pthread.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_PTHREAD_H +#define DOWNLOAD_PROVIDER2_PTHREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <unistd.h> +#include <pthread.h> +#include <errno.h> + +// download-provider use default style mutex. + +#define CLIENT_MUTEX_LOCK(mutex_add) {\ + int ret = 0;\ + ret = pthread_mutex_lock(mutex_add);\ + if (EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with EINVAL.");\ + } else if (EDEADLK == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with EDEADLK.");\ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_lock FAIL with %d.", ret);\ + } \ +} + +#define CLIENT_MUTEX_UNLOCK(mutex_add) {\ + int ret = 0;\ + ret = pthread_mutex_unlock(mutex_add);\ + if (EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with EINVAL.");\ + } else if (EDEADLK == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with EDEADLK.");\ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_unlock FAIL with %d.", ret);\ + } \ +} + +#define CLIENT_MUTEX_DESTROY(mutex_add) { \ + int ret = 0; \ + ret = pthread_mutex_destroy(mutex_add); \ + if(EINVAL == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EINVAL."); \ + } else if(ENOMEM == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with ENOMEM."); \ + } else if(EBUSY == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EBUSY."); \ + if (pthread_mutex_unlock(mutex_add) == 0) \ + pthread_mutex_destroy(mutex_add); \ + } else if (ret != 0) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with %d.", ret); \ + } \ +} + +#define CLIENT_MUTEX_INIT(mutex_add, attr) { \ + int ret = 0; \ + unsigned retry = 3; \ + while (retry > 0) { \ + ret = pthread_mutex_init(mutex_add, attr); \ + if (0 == ret) { \ + break; \ + } else if(EINVAL == ret) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with EINVAL."); \ + } else if(ENOMEM == ret) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with ENOMEM."); \ + usleep(1000); \ + } else if(EBUSY == ret) {\ + TRACE_STRERROR("ERR:pthread_mutex_destroy FAIL with EBUSY."); \ + if (pthread_mutex_unlock(mutex_add) == 0) \ + pthread_mutex_destroy(mutex_add); \ + } else if (ret != 0) { \ + TRACE_STRERROR("ERR:pthread_mutex_init FAIL with %d.", ret); \ + } \ + retry--; \ + } \ +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/provider/include/download-provider-queue.h b/provider/include/download-provider-queue.h new file mode 100755 index 0000000..71bb62b --- /dev/null +++ b/provider/include/download-provider-queue.h @@ -0,0 +1,23 @@ +/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DOWNLOAD_PROVIDER2_QUEUE_H
+#define DOWNLOAD_PROVIDER2_QUEUE_H
+
+void dp_thread_queue_manager_wake_up();
+void *dp_thread_queue_manager(void *arg);
+
+#endif
diff --git a/provider/include/download-provider-request.h b/provider/include/download-provider-request.h new file mode 100755 index 0000000..bc26953 --- /dev/null +++ b/provider/include/download-provider-request.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_REQUEST_H +#define DOWNLOAD_PROVIDER2_REQUEST_H + +#include "download-provider.h" +#include <bundle.h> + +// for Debugging +char *dp_print_state(dp_state_type state); +char *dp_print_errorcode(dp_error_type errorcode); + +int dp_is_smackfs_mounted(void); +char *dp_strdup(char *src); + +dp_error_type dp_request_create(int id, dp_client_group *group, dp_request** empty_slot); +dp_error_type dp_request_set_url(int id, dp_request *request, char *url); +dp_error_type dp_request_set_destination(int id, dp_request *request, char *dest); +dp_error_type dp_request_set_filename(int id, dp_request *request, char *filename); +dp_error_type dp_request_set_title(int id, dp_request *request, char *filename); +dp_error_type dp_request_set_bundle(int id, dp_request *request, int type, bundle_raw *b, unsigned length); +dp_error_type dp_request_set_description(int id, dp_request *request, char *description); +dp_error_type dp_request_set_noti_type(int id, dp_request *request, unsigned type); +dp_error_type dp_request_set_notification(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_auto_download(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_state_event(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_progress_event(int id, dp_request *request, unsigned enable); +dp_error_type dp_request_set_network_type(int id, dp_request *request, int type); +char *dp_request_get_url(int id, dp_error_type *errorcode); +char *dp_request_get_destination(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_filename(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_title(int id, dp_request *request, dp_error_type *errorcode); +bundle_raw *dp_request_get_bundle(int id, dp_request *request, dp_error_type *errorcode, char* column, int* length); +char *dp_request_get_description(int id, dp_request *request, dp_error_type *errorcode); +int dp_request_get_noti_type(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_contentname(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_etag(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_savedpath(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_tmpsavedpath(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_mimetype(int id, dp_request *request, dp_error_type *errorcode); +char *dp_request_get_pkg_name(int id, dp_request *request, dp_error_type *errorcode); +dp_request *dp_request_load_from_log(int id, dp_error_type *errorcode); +void dp_request_state_response(dp_request *request); +#endif diff --git a/provider/include/download-provider-slots.h b/provider/include/download-provider-slots.h new file mode 100755 index 0000000..53859f8 --- /dev/null +++ b/provider/include/download-provider-slots.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_SLOTS_H +#define DOWNLOAD_PROVIDER2_SLOTS_H + +#include "download-provider.h" +#include "download-provider-pthread.h" + +// Backgound Daemon should has the limitation of resource. +#define DP_MAX_GROUP 15 +#define DP_MAX_REQUEST 64 + +typedef struct { + pid_t pid; + uid_t uid; + gid_t gid; +} dp_credential; + +typedef struct { + // send command * get return value. + int cmd_socket; + // send event to client + int event_socket; + unsigned queued_count; // start ++, finish or failed -- ( queue & active ) + // fill by app-manager + char *pkgname; + dp_credential credential; + char *smack_label; +} dp_client_group; + +typedef struct { + int id; // ID created in create request in requests thread. + int agent_id; + int create_time; + int start_time; // setted by START command + int pause_time; // setted by PAUSE command + int stop_time; // time setted by finished_cb + int noti_priv_id; + unsigned state_cb; // set : 1 unset : 0 + unsigned progress_cb; // set : 1 unset : 0 + unsigned startcount; + unsigned auto_notification; + unsigned ip_changed; + dp_state_type state; // downloading state + dp_error_type error; + dp_network_type network_type; + size_t progress_lasttime; + unsigned long long received_size; // progress + unsigned long long file_size; + char *packagename; + dp_client_group *group; // indicate dp_client_group included this request +} dp_request; + +typedef struct { + dp_client_group *group; +} dp_group_slots; + +typedef struct { + pthread_mutex_t mutex; + dp_request *request; +} dp_request_slots; + + +// functions +dp_group_slots *dp_client_group_slots_new(int size); +dp_request_slots *dp_request_slots_new(int size); +int dp_client_group_free(dp_client_group *group); +int dp_client_group_slots_free(dp_group_slots *slots, int size); +dp_request *dp_request_new(); +void dp_request_init(dp_request *request); +int dp_request_free(dp_request *request); +int dp_request_slot_free(dp_request_slots *request_slot); +int dp_request_slots_free(dp_request_slots *slots, int size); +int dp_get_request_count(dp_request_slots *slots); + +#endif diff --git a/provider/include/download-provider-socket.h b/provider/include/download-provider-socket.h new file mode 100755 index 0000000..b5a37cb --- /dev/null +++ b/provider/include/download-provider-socket.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_SOCKET_H +#define DOWNLOAD_PROVIDER2_SOCKET_H + +#include <bundle.h> +#include "download-provider.h" +#include "download-provider-slots.h" + +int dp_ipc_send_errorcode(int fd, dp_error_type errorcode); +int dp_ipc_send_event(int fd, int id, dp_state_type state, + dp_error_type errorcode, unsigned long long received_size); +#if 0 +int dp_ipc_send_progressinfo(int fd, int id, + unsigned long long received_size, unsigned long long file_size, + unsigned int chunk_size); +int dp_ipc_send_stateinfo(int fd, int id, dp_state_type state, + dp_error_type errorcode); +#endif +char *dp_ipc_read_string(int fd); +unsigned dp_ipc_read_bundle(int fd, int *type, bundle_raw **b); +int dp_ipc_send_string(int fd, const char *str); +int dp_ipc_send_bundle(int fd, bundle_raw *b, unsigned length); +int dp_ipc_send_custom_type(int fd, void *value, size_t type_size); +int dp_ipc_read_custom_type(int fd, void *value, size_t type_size); +int dp_accept_socket_new(); +int dp_socket_free(int sockfd); + +#endif diff --git a/provider/include/download-provider.h b/provider/include/download-provider.h new file mode 100755 index 0000000..a1f22f3 --- /dev/null +++ b/provider/include/download-provider.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DOWNLOAD_PROVIDER2_H +#define DOWNLOAD_PROVIDER2_H + +#include "download-provider-defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DP_IPC "/tmp/download-provider" + +#define DP_MAX_STR_LEN_64 64 +#define DP_MAX_STR_LEN 256 +#define DP_MAX_PATH_LEN DP_MAX_STR_LEN +#define DP_MAX_URL_LEN 2048 +#define DP_DEFAULT_BUFFER_SIZE 1024 + +typedef enum { + DP_CMD_NONE = 0, + DP_CMD_ECHO, + DP_CMD_CREATE, + DP_CMD_START, + DP_CMD_SET_COMMAND_SOCKET, + DP_CMD_SET_EVENT_SOCKET, + DP_CMD_FREE, + + DP_CMD_ACTION_SECT = 100, + DP_CMD_PAUSE, + DP_CMD_CANCEL, + DP_CMD_DESTROY, + + DP_CMD_GET_SECT = 200, + DP_CMD_GET_URL, + DP_CMD_GET_DESTINATION, + DP_CMD_GET_FILENAME, + DP_CMD_GET_NOTIFICATION, + DP_CMD_GET_STATE_CALLBACK, + DP_CMD_GET_PROGRESS_CALLBACK, + DP_CMD_GET_HTTP_HEADERS, + DP_CMD_GET_AUTO_DOWNLOAD, + DP_CMD_GET_NETWORK_TYPE, + DP_CMD_GET_SAVED_PATH, + DP_CMD_GET_TEMP_SAVED_PATH, + DP_CMD_GET_MIME_TYPE, + DP_CMD_GET_HTTP_HEADER, + DP_CMD_GET_HTTP_HEADER_LIST, + DP_CMD_GET_EXTRA_PARAM, + DP_CMD_GET_RECEIVED_SIZE, + DP_CMD_GET_TOTAL_FILE_SIZE, + DP_CMD_GET_CONTENT_NAME, + DP_CMD_GET_HTTP_STATUS, + DP_CMD_GET_ETAG, + DP_CMD_GET_STATE, + DP_CMD_GET_ERROR, + DP_CMD_GET_NOTIFICATION_BUNDLE, + DP_CMD_GET_NOTIFICATION_TITLE, + DP_CMD_GET_NOTIFICATION_DESCRIPTION, + DP_CMD_GET_NOTIFICATION_TYPE, + + DP_CMD_SET_SECT = 300, + DP_CMD_SET_URL, + DP_CMD_SET_DESTINATION, + DP_CMD_SET_FILENAME, + DP_CMD_SET_NOTIFICATION, + DP_CMD_SET_STATE_CALLBACK, + DP_CMD_SET_PROGRESS_CALLBACK, + DP_CMD_SET_AUTO_DOWNLOAD, + DP_CMD_SET_NETWORK_TYPE, + DP_CMD_SET_HTTP_HEADER, + DP_CMD_SET_EXTRA_PARAM, // prevent build error + DP_CMD_DEL_HTTP_HEADER, + DP_CMD_ADD_EXTRA_PARAM, + DP_CMD_REMOVE_EXTRA_PARAM, + DP_CMD_SET_NOTIFICATION_BUNDLE, + DP_CMD_SET_NOTIFICATION_TITLE, + DP_CMD_SET_NOTIFICATION_DESCRIPTION, + DP_CMD_SET_NOTIFICATION_TYPE, + + DP_CMD_LAST_SECT = 400 +} dp_command_type; + +typedef struct { + unsigned int length; + char *str; +} dp_string; + +typedef struct { + int id; + dp_state_type state; + dp_error_type err; + unsigned long long received_size; +} dp_event_info; + +typedef struct { + dp_command_type cmd; + int id; +} dp_command; + + // Usage IPC : send(dp_command);send(dp_string); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/res/images/Notification_download_animation.gif b/res/images/Notification_download_animation.gif Binary files differnew file mode 100644 index 0000000..17dd2bb --- /dev/null +++ b/res/images/Notification_download_animation.gif diff --git a/res/images/Q02_Notification_download_complete.png b/res/images/Q02_Notification_download_complete.png Binary files differnew file mode 100644 index 0000000..7a11f3e --- /dev/null +++ b/res/images/Q02_Notification_download_complete.png diff --git a/res/images/Q02_Notification_download_failed.png b/res/images/Q02_Notification_download_failed.png Binary files differnew file mode 100644 index 0000000..d59dd6f --- /dev/null +++ b/res/images/Q02_Notification_download_failed.png diff --git a/script/commit-template b/script/commit-template new file mode 100644 index 0000000..e398cc3 --- /dev/null +++ b/script/commit-template @@ -0,0 +1,10 @@ +[Title] + +[Issue#] N/A +[Problem] N/A +[Cause] N/A +[Solution] N/A + +# please fill the each items. +# If this is the commit to fix issue, please replace N/A to the number of issue. + diff --git a/systemd/download-provider.service b/systemd/download-provider.service new file mode 100644 index 0000000..d6f7f34 --- /dev/null +++ b/systemd/download-provider.service @@ -0,0 +1,11 @@ +[Unit] +Description=Download provider service +After=check-mount.service + +[Service] +Type=simple +ExecStart=/usr/bin/download-provider + +[Install] +WantedBy=default.target + diff --git a/systemd/download-provider.socket b/systemd/download-provider.socket new file mode 100644 index 0000000..944b28d --- /dev/null +++ b/systemd/download-provider.socket @@ -0,0 +1,6 @@ +[Socket] +ListenStream=/tmp/download-provider +SocketMode=0777 + +[Install] +WantedBy=sockets.target |