From: Jungki Kwak Date: Tue, 21 Aug 2012 10:10:00 +0000 (+0900) Subject: The base model is changed X-Git-Tag: 2.0_alpha~24 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fcda1e456337e7525cb0e54dc1ca01a83d34c596;p=platform%2Fcore%2Fapi%2Furl-download.git The base model is changed The base model change the libdownload-agent package to the download-provider daemon package modified: CMakeLists.txt modified: debian/changelog modified: include/url_download.h modified: include/url_download_private.h modified: packaging/capi-web-url-download.spec new file: sample/ipc_test.c modified: src/url_download.c new file: src/url_download_provider.c --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 58b5f46..8a35d49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,14 +4,21 @@ SET(fw_name "capi-web-url-download") PROJECT(${fw_name}) +OPTION(ENABLE_PROVIDER "Support download through download-provider daemon" ON) + SET(CMAKE_INSTALL_PREFIX /usr) SET(PREFIX ${CMAKE_INSTALL_PREFIX}) SET(INC_DIR include) INCLUDE_DIRECTORIES(${INC_DIR}) -SET(requires "dlog capi-base-common libdownload-agent bundle") -SET(pc_requires "capi-base-common") +IF (ENABLE_PROVIDER) + SET(requires "dlog capi-base-common bundle capi-appfw-app-manager capi-appfw-application download-provider") +ELSE (ENABLE_PROVIDER) + SET(requires "dlog capi-base-common bundle libdownload-agent") +ENDIF (ENABLE_PROVIDER) +MESSAGE(STATUS "PACKAGES : ${requires}") +SET(pc_requires "capi-base-common capi-appfw-application") INCLUDE(FindPkgConfig) pkg_check_modules(${fw_name} REQUIRED ${requires}) @@ -31,7 +38,13 @@ ADD_DEFINITIONS("-DSLP_DEBUG") SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=/usr/lib") -aux_source_directory(src SOURCES) +IF (ENABLE_PROVIDER) + ADD_DEFINITIONS("-DENABLE_DOWNLOAD_PROVIDER") + SET(SOURCES src/url_download_provider.c) +ELSE (ENABLE_PROVIDER) + SET(SOURCES src/url_download.c) +ENDIF (ENABLE_PROVIDER) +MESSAGE(STATUS "SOURCES : ${SOURCES}") ADD_LIBRARY(${fw_name} SHARED ${SOURCES}) TARGET_LINK_LIBRARIES(${fw_name} ${${fw_name}_LDFLAGS}) diff --git a/debian/changelog b/debian/changelog index 1fb8f4a..8867bc6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,26 @@ +capi-web-url-download (0.0.5-0) unstable; urgency=low + + * Git: slp/api/url-download + * Tag: capi-web-url-download_0.0.5-0 + + * Change to request cancel according to state + * Add exception handling code at start function + * Add initialize the callback data when destroying + * Add error code about max downloading item. + * Remove unused variable and modify wrong API name. + * Modify a make file to define the list of test case's source code. + + -- Jungki Kwak Thu, 10 May 2012 10:46:37 +0900 + +capi-web-url-download (0.0.4-0) unstable; urgency=low + + * Git: slp/api/url-download + * Tag: capi-web-url-download_0.0.4-0 + + * Add a service operation define for the download manager application. + + -- Jungki Kwak Fri, 23 Mar 2012 18:02:24 +0900 + capi-web-url-download (0.0.3-0) unstable; urgency=low * Git: slp/api/url-download diff --git a/include/url_download.h b/include/url_download.h index cc5d4b7..764e4fd 100644 --- a/include/url_download.h +++ b/include/url_download.h @@ -18,6 +18,7 @@ #define __TIZEN_WEB_URL_DOWNLOAD_H__ #include +#include #ifdef __cplusplus extern "C" @@ -59,6 +60,8 @@ typedef enum URL_DOWNLOAD_ERROR_SSL_FAILED = TIZEN_ERROR_WEB_CLASS | 0x23, /**< SSL negotiation failed */ URL_DOWNLOAD_ERROR_INVALID_URL = TIZEN_ERROR_WEB_CLASS | 0x24, /**< Invalid URL */ URL_DOWNLOAD_ERROR_INVALID_DESTINATION = TIZEN_ERROR_WEB_CLASS | 0x25, /**< Invalid destination */ + URL_DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS = TIZEN_ERROR_WEB_CLASS | 0x26, /**< Full of available downloading items */ + URL_DOWNLOAD_ERROR_ALREADY_COMPLETED = TIZEN_ERROR_WEB_CLASS | 0x27, /**< The download is already completed */ } url_download_error_e; @@ -71,6 +74,7 @@ typedef enum URL_DOWNLOAD_STATE_DOWNLOADING, /**< The download is currently running */ URL_DOWNLOAD_STATE_PAUSED, /**< The download is waiting to resume or stop */ URL_DOWNLOAD_STATE_COMPLETED, /**< The download is completed. */ + URL_DOWNLOAD_STATE_FAILED, /**< The download failed. */ } url_download_state_e; @@ -78,13 +82,16 @@ typedef enum * @brief Called when the download is started. * * @param [in] download The download handle + * @param [in] content_name The content name to display at UI layer + * @param [in] mime_type The MIME type string * @param [in] user_data The user data passed from url_download_set_started_cb() * @pre url_download_start() will cause this callback if you register this callback using url_download_set_started_cb() * @see url_download_start() * @see url_download_set_started_cb() * @see url_download_unset_started_cb() */ -typedef void (*url_download_started_cb) (url_download_h download, void *user_data); +typedef void (*url_download_started_cb) (url_download_h download, + const char *content_name, const char *mime_type, void *user_data); /** @@ -104,13 +111,13 @@ typedef void (*url_download_paused_cb) (url_download_h download, void *user_data * @brief Called when the download is completed. * * @param [in] download The download handle - * @param [in] path The absolute path to the downloaded file + * @param [in] installed_path The absolute path to the downloaded file * @param [in] user_data The user data passed from url_download_set_completed_cb() * @pre This callback function will be invoked when the download is completed if you register this callback using url_download_set_paused_cb() * @see url_download_set_completed_cb() * @see url_download_unset_completed_cb() */ -typedef void (*url_download_completed_cb) (url_download_h download, const char * path, void *user_data); +typedef void (*url_download_completed_cb) (url_download_h download, const char *installed_path, void *user_data); /** @@ -178,6 +185,26 @@ typedef bool (*url_download_http_header_field_cb)(url_download_h download, const int url_download_create(url_download_h *download); +/** + * @brief Creates a download handle with the given identifier + * + * @remarks The @a download must be released with url_download_destroy() by you.\n + * The g_type_init() should be called when creating a main loop by user side. \n + * Because the libsoup, which is http stack library of download module, use gobject internally. + * @param [in] id The identifier for the download unique within the application. + * @param [out] download A download handle to be newly created on success + * @return 0 on success, otherwise a negative error value. + * @retval #URL_DOWNLOAD_ERROR_NONE Successful + * @retval #URL_DOWNLOAD_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #URL_DOWNLOAD_ERROR_OUT_OF_MEMORY Out of memory + * @retval #URL_DOWNLOAD_ERROR_IO_ERROR Internal I/O error + * @post The download state will be #URL_DOWNLOAD_STATE_READY + * @see url_download_create() + * @see url_download_destroy() + */ +int url_download_create_by_id(int id, url_download_h *download); + + /** * @brief Destroys the URL download handle. * @@ -260,6 +287,109 @@ int url_download_set_destination(url_download_h download, const char *path); int url_download_get_destination(url_download_h download, char **path); +/** + * @brief Sets the name for the downloaded file. + * + * @details The file will be downloaded to the specified destination as the given file name. + * If the file name is not specified, the downloaded file is saved to an auto-generated file name in the destination. + * + * @remarks This function should be called before downloading (see url_download_start()) + * @param [in] download The download handle + * @param [in] file_name The file name for the downloaded file + * If the @a name is NULL, it clears the previous value. + * @return 0 on success, otherwise a negative error value. + * @retval #URL_DOWNLOAD_ERROR_NONE Successful + * @retval #URL_DOWNLOAD_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #URL_DOWNLOAD_ERROR_OUT_OF_MEMORY Out of memory + * @retval #URL_DOWNLOAD_ERROR_INVALID_STATE Invalid state + * @pre The download state must be #URL_DOWNLOAD_STATE_READY or #URL_DOWNLOAD_STATE_COMPLETED. + * @see url_download_get_file_name() + */ +int url_download_set_file_name(url_download_h download, const char *file_name); + + +/** + * @brief Gets the name for the downloaded file. + * + * @remarks The @a file_name must be released with free() by you. + * @param [in] download The download handle + * @param [out] file_name The file name for the downloaded file + * @return 0 on success, otherwise a negative error value. + * @retval #URL_DOWNLOAD_ERROR_NONE Successful + * @retval #URL_DOWNLOAD_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #URL_DOWNLOAD_ERROR_OUT_OF_MEMORY Out of memory + * @see url_download_set_file_name() + */ +int url_download_get_file_name(url_download_h download, char **file_name); + + +/** + * @brief Sets the service to launch when the notification for the download is selected from the notification tray. + * @details When the notification for the download is selected from the notification tray, the application which is described by the specified service is launched. \n + * If you want to launch the current application, use the explicit launch of the @ref CAPI_SERVICE_MODULE API + * @remarks If the service is not set, the selected notification will be cleared from both the notification tray and the status bar without any action. + * @param[in] download The download handle + * @param[in] service The service handle to launch when the notification for the download is selected \n + * If the @a service is NULL, it clears the previous value. + * @return 0 on success, otherwise a negative error value. + * @retval #URL_DOWNLOAD_ERROR_NONE Successful + * @retval #URL_DOWNLOAD_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #URL_DOWNLOAD_ERROR_OUT_OF_MEMORY Out of memory + * @see url_download_get_notification() + * @see service_create() + */ +int url_download_set_notification(url_download_h download, service_h service); + +/** + * @brief Gets the service to launch when the notification for the download is selected from the notification tray + * @remarks The @a service must be released with service_destroy() by you. + * @param[in] download The download handle + * @param[out] service The service handle to launch when the notification is selected + * @return 0 on success, otherwise a negative error value. + * @retval #URL_DOWNLOAD_ERROR_NONE Successful + * @retval #URL_DOWNLOAD_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #URL_DOWNLOAD_ERROR_OUT_OF_MEMORY Out of memory + * @see url_download_set_notification() + */ +int url_download_get_notification(url_download_h download, service_h *service); + + +/** + * @brief Gets the absolute path to the downloaded file + * + * @remarks This function returns #URL_DOWNLOAD_ERROR_INVALID_STATE if the download is not completed. \n + * The @a path must be released with free() by you. + * @param [in] download The download handle + * @param [out] path The absolute path to the downloaded file + * @return 0 on success, otherwise a negative error value. + * @retval #URL_DOWNLOAD_ERROR_NONE Successful + * @retval #URL_DOWNLOAD_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #URL_DOWNLOAD_ERROR_OUT_OF_MEMORY Out of memory + * @pre The download state must be #URL_DOWNLOAD_STATE_COMPLETED. + * @see url_download_set_file_name() + * @see url_download_set_destination() + */ +int url_download_get_downloaded_file(url_download_h download, char **path); + + +/** + * @brief Gets the MIME type of the downloaded file + * + * @remarks This function returns #URL_DOWNLOAD_ERROR_INVALID_STATE if the download has not been started. \n + * The @a mime_type must be released with free() by you. + * @param [in] download The download handle + * @param [out] mime_type The MIME type of the downloaded file + * @return 0 on success, otherwise a negative error value. + * @retval #URL_DOWNLOAD_ERROR_NONE Successful + * @retval #URL_DOWNLOAD_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #URL_DOWNLOAD_ERROR_OUT_OF_MEMORY Out of memory + * @see url_download_set_file_name() + * @see url_download_set_destination() + * @see url_download_get_downloaded_file() + */ +int url_download_get_mime(url_download_h download, char **mime_type); + + /** * @brief Adds an HTTP header field to the download request * @@ -506,6 +636,7 @@ int url_download_unset_progress_cb(url_download_h download); * * @remarks The URL is the mandatory information to start the download. * @param [in] download The download handle + * @param [out] id The identifier for the download unique within the application. * @return 0 on success, otherwise a negative error value. * @retval #URL_DOWNLOAD_ERROR_NONE Successful * @retval #URL_DOWNLOAD_ERROR_INVALID_PARAMETER Invalid parameter @@ -523,7 +654,7 @@ int url_download_unset_progress_cb(url_download_h download); * @see url_download_unset_started_cb() * @see url_download_started_cb() */ -int url_download_start(url_download_h download); +int url_download_start(url_download_h download, int *id); /** @@ -547,7 +678,6 @@ int url_download_start(url_download_h download); */ int url_download_pause(url_download_h download); - /** * @brief Stops the download, asynchronously. * @@ -583,7 +713,6 @@ int url_download_stop(url_download_h download); */ int url_download_get_state(url_download_h download, url_download_state_e *state); - /** * @brief Retrieves all HTTP header fields to be included with the download * @details This function calls url_download_http_header_field_cb() once for each HTTP header field added.\n diff --git a/include/url_download_private.h b/include/url_download_private.h old mode 100755 new mode 100644 index 0116c92..f9481a0 --- a/include/url_download_private.h +++ b/include/url_download_private.h @@ -19,14 +19,18 @@ #define __TIZEN_WEB_URL_DOWNLOAD_PRIVATE_H__ #include +#ifndef ENABLE_DOWNLOAD_PROVIDER #include +#endif #ifdef __cplusplus extern "C" { #endif +#ifndef ENABLE_DOWNLOAD_PROVIDER typedef da_client_cb_t *url_download_agent_h; +#endif /** * url_download_cb_s @@ -49,14 +53,25 @@ struct url_download_cb_s { }; struct url_download_s { +#ifndef ENABLE_DOWNLOAD_PROVIDER url_download_agent_h agent; da_handle_t id; +#else + uint id; + uint enable_notification; + int requestid; +#endif struct url_download_cb_s callback; url_download_state_e state; char *url; char *destination; bundle *http_header; char *completed_path; + char *content_name; + char *mime_type; + uint file_size; + int sockfd; + pthread_t callback_thread_pid; }; #ifdef __cplusplus diff --git a/packaging/capi-web-url-download.spec b/packaging/capi-web-url-download.spec index 5219ddd..b1a85fd 100644 --- a/packaging/capi-web-url-download.spec +++ b/packaging/capi-web-url-download.spec @@ -1,16 +1,23 @@ +%define ENABLE_DOWNLOAD_PROVIDER 1 Name: capi-web-url-download Summary: CAPI for content download with web url -Version: 0.0.3 -Release: 1 +Version: 0.0.7 +Release: 2 Group: TO_BE_FILLED_IN License: TO_BE_FILLED_IN URL: N/A Source0: %{name}-%{version}.tar.gz BuildRequires: pkgconfig(capi-base-common) -BuildRequires: pkgconfig(libdownload-agent) BuildRequires: pkgconfig(bundle) BuildRequires: pkgconfig(dlog) +%if %ENABLE_DOWNLOAD_PROVIDER +BuildRequires: pkgconfig(capi-appfw-app-manager) +BuildRequires: pkgconfig(capi-appfw-application) +BuildRequires: pkgconfig(download-provider) +%else +BuildRequires: pkgconfig(libdownload-agent) +%endif BuildRequires: cmake BuildRequires: expat-devel @@ -29,7 +36,7 @@ CAPI for content downloading with web url (developement files) %setup -q %build -cmake . -DCMAKE_INSTALL_PREFIX="/usr/lib" +cmake . -DCMAKE_INSTALL_PREFIX="/" make %{?jobs:-j%jobs} @@ -51,3 +58,17 @@ rm -rf %{buildroot} /usr/lib/pkgconfig/capi-web-url-download.pc /usr/include/web/url_download.h +%changelog +* Mon Aug 16 2012 Jungki Kwak +- Add new APIs for notification function +- The TC is changed due to change of url_download_start +- When unseting the callback function, the callback should be initialized although the error is happened. +- It remove the stop function when is called twice when destroying handle +- Add pc requries for app.h + +* Mon Aug 08 2012 Jungki Kwak +- Change requestid to INTEGER from String + +* Mon Aug 06 2012 Jungki Kwak +- The base model is changed to download provider daemon + diff --git a/sample/ipc_test.c b/sample/ipc_test.c new file mode 100644 index 0000000..33d00b4 --- /dev/null +++ b/sample/ipc_test.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include + +#include "web/url_download.h" +#include "log.h" + +bool exit_process_flag = false; + +void download_manager_started_cb (url_download_h download, void *user_data) +{ + TRACE_DEBUG_MSG("started"); +} +void download_manager_completed_cb (url_download_h download, const char * path, void *user_data) +{ + TRACE_DEBUG_MSG("download_manager_completed_cb (%s)",path); + exit_process_flag = true; +} +void download_manager_progress_cb (url_download_h download, unsigned long long received, unsigned long long total, void *user_data) +{ + TRACE_DEBUG_MSG("progress (%d/%d)",received,total); +} + +int main(int argc, char** argv) +{ + url_download_h download; + exit_process_flag = false; + // create download. + url_download_create(&download); + url_download_set_url(download, "abcdefghigk"); + url_download_set_destination(download, "1234567890everywhere"); + + url_download_set_started_cb(download, download_manager_started_cb, NULL); + url_download_set_completed_cb(download, download_manager_completed_cb, NULL); + url_download_set_progress_cb(download, download_manager_progress_cb, NULL); + + // start.... + url_download_start(download); + while(!exit_process_flag) + { + sleep(5); + } + // pasuse + //url_download_pause(&download); + // resume + //url_download_stop(&download); + + url_download_destroy(download); + TRACE_DEBUG_MSG("exit..........."); + exit(EXIT_SUCCESS); +} diff --git a/src/url_download.c b/src/url_download.c index 4c67c59..2c57b47 100644 --- a/src/url_download.c +++ b/src/url_download.c @@ -74,11 +74,9 @@ static const char* url_download_error_to_string(int error_code) case URL_DOWNLOAD_ERROR_CONNECTION_TIMED_OUT: error_name = "CONNECTION_TIMED_OUT"; break; - case URL_DOWNLOAD_ERROR_FIELD_NOT_FOUND: error_name = "FIELD_NOT_FOUND"; break; - case URL_DOWNLOAD_ERROR_NO_SPACE: error_name = "NO_SPACE"; break; @@ -91,15 +89,14 @@ static const char* url_download_error_to_string(int error_code) case URL_DOWNLOAD_ERROR_SSL_FAILED: error_name = "SSL_FAILED"; break; - case URL_DOWNLOAD_ERROR_INVALID_URL: error_name = "INVALID_URL"; break; - case URL_DOWNLOAD_ERROR_INVALID_DESTINATION: error_name = "INVALID_DESTINATION"; break; - + case URL_DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS: + error_name = "FULL_OF_MAX_DOWNLOAD_ITEMS"; default: error_name = "UNKNOWN"; break; @@ -261,7 +258,8 @@ static url_download_error_e url_download_agent_error(int error) case DA_ERR_INVALID_INSTALL_PATH: return URL_DOWNLOAD_ERROR_INVALID_DESTINATION; - + case DA_ERR_ALREADY_MAX_DOWNLOAD: + return URL_DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS; default: return URL_DOWNLOAD_ERROR_IO_ERROR; } @@ -321,7 +319,7 @@ static bool is_available_download_data(url_download_h download) return ret; } -static void url_download_agent_state_cb(user_notify_info_t *notify_info, void* user_data) +static void url_download_agent_state_cb(user_notify_info_t *notify_info, void *user_data) { url_download_h download = NULL; url_download_state_e state = -1; @@ -531,6 +529,9 @@ int url_download_destroy(url_download_h download) download->completed_path = NULL; } + memset(&(download->callback), 0x00, sizeof(struct url_download_cb_s)); + download->id = -1; + url_download_agent_destroy(download->agent); head = _download_list; @@ -1153,6 +1154,7 @@ static int url_download_start_download(url_download_h download) } else { + download->state = URL_DOWNLOAD_STATE_DOWNLOADING; return URL_DOWNLOAD_ERROR_NONE; } } @@ -1181,6 +1183,11 @@ int url_download_start(url_download_h download) return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); } + if (!is_available_download_data(download)) + { + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, "download item is already destroyed!!!!!!!!"); + } + switch (download->state) { case URL_DOWNLOAD_STATE_COMPLETED: @@ -1225,12 +1232,12 @@ int url_download_stop(url_download_h download) { return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); } -/* + if (download->state != URL_DOWNLOAD_STATE_DOWNLOADING) { return url_download_error_invalid_state(__FUNCTION__, download); } -*/ + if (da_cancel_download(download->id)) { return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, "failed to stop the download"); diff --git a/src/url_download_provider.c b/src/url_download_provider.c new file mode 100644 index 0000000..cbf2fd8 --- /dev/null +++ b/src/url_download_provider.c @@ -0,0 +1,1247 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "TIZEN_N_URL_DOWNLOAD" + +#define STATE_IS_RUNNING(_download_) \ + (_download_->state == URL_DOWNLOAD_STATE_DOWNLOADING \ + || _download_->state == URL_DOWNLOAD_STATE_PAUSED) + + +#define STRING_IS_INVALID(_string_) \ + (_string_ == NULL || _string_[0] == '\0') + +static int url_download_resume(url_download_h download); +static int url_download_get_all_http_header_fields( + url_download_h download, char ***fields, int *fields_length); + +url_download_error_e url_download_provider_error(int error) +{ + switch (error) { + case DOWNLOAD_ERROR_NONE: + return URL_DOWNLOAD_ERROR_NONE; + + case DOWNLOAD_ERROR_CONNECTION_FAILED: + return URL_DOWNLOAD_ERROR_CONNECTION_FAILED; + + case DOWNLOAD_ERROR_NETWORK_UNREACHABLE: + return URL_DOWNLOAD_ERROR_NETWORK_UNREACHABLE; + + case DOWNLOAD_ERROR_CONNECTION_TIMED_OUT: + return URL_DOWNLOAD_ERROR_CONNECTION_TIMED_OUT; + + case DOWNLOAD_ERROR_INVALID_DESTINATION: + return URL_DOWNLOAD_ERROR_INVALID_DESTINATION; + + case DOWNLOAD_ERROR_NO_SPACE: + return URL_DOWNLOAD_ERROR_NO_SPACE; + + case DOWNLOAD_ERROR_INVALID_URL: + return URL_DOWNLOAD_ERROR_INVALID_URL; + + case DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS: + return URL_DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS; + + case DOWNLOAD_ERROR_ALREADY_COMPLETED: + return URL_DOWNLOAD_ERROR_ALREADY_COMPLETED; + + default: + return URL_DOWNLOAD_ERROR_IO_ERROR; + } +} + +const char* url_download_error_to_string(int error_code) +{ + char *error_name = NULL; + + switch (error_code) { + case URL_DOWNLOAD_ERROR_NONE: + error_name = "ERROR_NONE"; + break; + case URL_DOWNLOAD_ERROR_INVALID_PARAMETER: + error_name = "INVALID_PARAMETER"; + break; + case URL_DOWNLOAD_ERROR_OUT_OF_MEMORY: + error_name = "OUT_OF_MEMORY"; + break; + case URL_DOWNLOAD_ERROR_IO_ERROR: + error_name = "IO_ERROR"; + break; + case URL_DOWNLOAD_ERROR_NETWORK_UNREACHABLE: + error_name = "NETWORK_UNREACHABLE"; + break; + case URL_DOWNLOAD_ERROR_CONNECTION_TIMED_OUT: + error_name = "CONNECTION_TIMED_OUT"; + break; + case URL_DOWNLOAD_ERROR_FIELD_NOT_FOUND: + error_name = "FIELD_NOT_FOUND"; + break; + case URL_DOWNLOAD_ERROR_NO_SPACE: + error_name = "NO_SPACE"; + break; + case URL_DOWNLOAD_ERROR_INVALID_STATE: + error_name = "INVALID_STATE"; + break; + case URL_DOWNLOAD_ERROR_CONNECTION_FAILED: + error_name = "CONNECTION_FAILED"; + break; + case URL_DOWNLOAD_ERROR_SSL_FAILED: + error_name = "SSL_FAILED"; + break; + case URL_DOWNLOAD_ERROR_INVALID_URL: + error_name = "INVALID_URL"; + break; + case URL_DOWNLOAD_ERROR_INVALID_DESTINATION: + error_name = "INVALID_DESTINATION"; + break; + case URL_DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS: + error_name = "FULL_OF_MAX_DOWNLOAD_ITEMS"; + break; + case URL_DOWNLOAD_ERROR_ALREADY_COMPLETED: + error_name = "ALREADY_COMPLETED"; + break; + default: + error_name = "UNKNOWN"; + break; + } + return error_name; +} + +int url_download_error(const char *function, int error_code, const char *description) +{ + const char *error_name = NULL; + + error_name = url_download_error_to_string(error_code); + if (description) + LOGE("[%s] %s(0x%08x) : %s", function, error_name, error_code, description); + else + LOGE("[%s] %s(0x%08x)", function, error_name, error_code); + + return error_code; +} + +const char* url_download_state_to_string(url_download_state_e state) +{ + switch (state) + { + case URL_DOWNLOAD_STATE_READY: + return "READY"; + + case URL_DOWNLOAD_STATE_DOWNLOADING: + return "DOWNLOADING"; + + case URL_DOWNLOAD_STATE_PAUSED: + return "PAUSED"; + + case URL_DOWNLOAD_STATE_COMPLETED: + return "COMPLETED"; + + default: + return "INVALID"; + } +} + +int url_download_error_invalid_state(const char *function, url_download_h download) +{ + LOGE("[%s] INVALID_STATE(0x%08x) : state(%s)", + function, URL_DOWNLOAD_ERROR_INVALID_STATE, url_download_state_to_string(download->state)); + + return URL_DOWNLOAD_ERROR_INVALID_STATE; +} + +int ipc_receive_header(int fd) +{ + if(fd <= 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + + download_controls msgheader = 0; + if (read(fd, &msgheader, sizeof(download_controls)) < 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + + return msgheader; +} + +int ipc_send_download_control(url_download_h download, download_controls type) +{ + if (download == NULL || download->sockfd <= 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + + // send control + if (send(download->sockfd, &type, sizeof(download_controls), 0) < 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + + return type; +} + +// 1 thread / 1 download +void *run_receive_event_server(void *args) +{ + fd_set rset, exceptset; + struct timeval timeout; + download_state_info stateinfo; + download_content_info downloadinfo; + downloading_state_info downloadinginfo; + download_request_state_info requeststateinfo; + + url_download_h download = (url_download_h)args; + + while(download && download->sockfd > 0) { + FD_ZERO(&rset); + FD_ZERO(&exceptset); + FD_SET(download->sockfd, &rset); + FD_SET(download->sockfd, &exceptset); + timeout.tv_sec = 3; + + if (select((download->sockfd+1), &rset, 0, &exceptset, &timeout) < 0) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + if (download->callback.stopped) { + download->callback.stopped(download, + URL_DOWNLOAD_ERROR_IO_ERROR, + download->callback.stopped_user_data); + } + break; + } + + if (FD_ISSET(download->sockfd, &rset) > 0) { + // read some message from socket. + switch(ipc_receive_header(download->sockfd)) { + case DOWNLOAD_CONTROL_GET_REQUEST_STATE_INFO : + LOGI("[%s] DOWNLOAD_CONTROL_GET_REQUEST_STATE_INFO (started pended request)",__FUNCTION__); + if (read(download->sockfd, &requeststateinfo, sizeof(download_request_state_info)) < 0) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + } + if (requeststateinfo.requestid > 0) { + if (requeststateinfo.requestid != download->requestid) + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + download->requestid = requeststateinfo.requestid; + if (requeststateinfo.stateinfo.state == DOWNLOAD_STATE_FAILED) { + download->state = URL_DOWNLOAD_STATE_READY; + if (download->callback.stopped) { + download->callback.stopped(download, + url_download_provider_error(stateinfo.err), + download->callback.stopped_user_data); + } + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + } else + download->state = URL_DOWNLOAD_STATE_DOWNLOADING; + } else { + LOGE("[%s]Not Found request id (Wrong message)", __FUNCTION__); + download->state = URL_DOWNLOAD_STATE_READY; + if (download->callback.stopped) { + download->callback.stopped(download, + url_download_provider_error(stateinfo.err), + download->callback.stopped_user_data); + } + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + } + break; + case DOWNLOAD_CONTROL_GET_DOWNLOAD_INFO : + if (read(download->sockfd, &downloadinfo, sizeof(download_content_info)) < 0) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + } + LOGI("[%s] DOWNLOAD_CONTROL_GET_DOWNLOAD_INFO [%d]%",__FUNCTION__, downloadinfo.file_size); + download->state = URL_DOWNLOAD_STATE_DOWNLOADING; + download->file_size = downloadinfo.file_size; + if (downloadinfo.mime_type && strlen(downloadinfo.mime_type) > 0) + download->mime_type = strdup(downloadinfo.mime_type); + if (downloadinfo.content_name && strlen(downloadinfo.content_name) > 0) { + download->content_name = strdup(downloadinfo.content_name); + LOGI("content_name[%s] %", downloadinfo.content_name); + } + if (download->callback.started) { + download->callback.started( + download, download->content_name, download->mime_type, + download->callback.started_user_data); + } + break; + case DOWNLOAD_CONTROL_GET_DOWNLOADING_INFO : + if (read(download->sockfd, &downloadinginfo, sizeof(downloading_state_info)) < 0) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + } + // call the function by download-callbacks table. + LOGI("[%s] DOWNLOAD_CONTROL_GET_DOWNLOADING_INFO [%d]%",__FUNCTION__, downloadinginfo.received_size); + if (download->callback.progress) { + download->callback.progress( + download, + downloadinginfo.received_size, download->file_size, + download->callback.progress_user_data); + } + if (downloadinginfo.saved_path && + strlen(downloadinginfo.saved_path) > 0) { + LOGI("[%s] saved path [%s]",__FUNCTION__, downloadinginfo.saved_path); + download->completed_path = strdup(downloadinginfo.saved_path); + } + break; + case DOWNLOAD_CONTROL_GET_STATE_INFO : + if (read(download->sockfd, &stateinfo, sizeof(download_state_info)) < 0) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + } + // call the function by download-callbacks table. + LOGI("[%s] DOWNLOAD_CONTROL_GET_STATE_INFO state[%d]",__FUNCTION__, stateinfo.state); + switch (stateinfo.state) { + case DOWNLOAD_STATE_STOPPED: + LOGI("DOWNLOAD_STATE_STOPPED"); + download->state = URL_DOWNLOAD_STATE_READY; + if (download->callback.stopped) { + download->callback.stopped(download, + url_download_provider_error(stateinfo.err), + download->callback.stopped_user_data); + } + // terminate this thread. + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + break; + + case DOWNLOAD_STATE_DOWNLOADING: + download->state = URL_DOWNLOAD_STATE_DOWNLOADING; + LOGI("DOWNLOAD_STATE_DOWNLOADING"); + break; + case DOWNLOAD_STATE_PAUSE_REQUESTED: + LOGI("DOWNLOAD_STATE_PAUSE_REQUESTED"); + break; + case DOWNLOAD_STATE_PAUSED: + LOGI("DOWNLOAD_STATE_PAUSED"); + download->state = URL_DOWNLOAD_STATE_PAUSED; + if (download->callback.paused) + download->callback.paused(download, download->callback.paused_user_data); + break; + + case DOWNLOAD_STATE_FINISHED: + LOGI("DOWNLOAD_STATE_FINISHED"); + download->state = URL_DOWNLOAD_STATE_COMPLETED; + if (download->callback.completed) + download->callback.completed(download, download->completed_path, download->callback.completed_user_data); + // terminate this thread. + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + break; + case DOWNLOAD_STATE_READY: + LOGI("DOWNLOAD_STATE_READY"); + break; + case DOWNLOAD_STATE_INSTALLING: + LOGI("DOWNLOAD_STATE_INSTALLING"); + break; + case DOWNLOAD_STATE_FAILED: + LOGI("DOWNLOAD_STATE_FAILED"); + download->state = URL_DOWNLOAD_STATE_READY; + if (download->callback.stopped) { + download->callback.stopped(download, + url_download_provider_error(stateinfo.err), + download->callback.stopped_user_data); + } + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + break; + default: + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, "invalid state change event"); + if (download->callback.stopped) { + download->state = URL_DOWNLOAD_STATE_READY; + download->callback.stopped(download, + URL_DOWNLOAD_ERROR_IO_ERROR, + download->callback.stopped_user_data); + } + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + break; + } + + break; + + default : + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, "Invalid message"); + if (download->callback.stopped) { + download->state = URL_DOWNLOAD_STATE_READY; + download->callback.stopped(download, + URL_DOWNLOAD_ERROR_IO_ERROR, + download->callback.stopped_user_data); + } + goto URL_DOWNLOAD_EVENT_THREAD_EXIT; + break; + } + } else if (FD_ISSET(download->sockfd, &exceptset) > 0) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, "IO Exception"); + if (download->callback.stopped) { + download->state = URL_DOWNLOAD_STATE_READY; + download->callback.stopped(download, + URL_DOWNLOAD_ERROR_IO_ERROR, + download->callback.stopped_user_data); + } + + break; + } else { + // wake up by timeout. run extra job. + } + } +URL_DOWNLOAD_EVENT_THREAD_EXIT : + if (download && download->sockfd) { + FD_CLR(download->sockfd, &rset); + FD_CLR(download->sockfd, &exceptset); + if (download->sockfd) + close(download->sockfd); + download->sockfd = 0; + } + return NULL; +} + +// fill the reqeust info. +int url_download_create(url_download_h *download) +{ + url_download_h download_new; + + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + download_new = (url_download_h)calloc(1, sizeof(struct url_download_s)); + if (download_new == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + + download_new->http_header = bundle_create(); + + if (!download_new->http_header) { + url_download_destroy(download_new); + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, "failed to initialize a bundle"); + } + + download_new->state = URL_DOWNLOAD_STATE_READY; + download_new->sockfd = 0; + *download = download_new; + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_create_by_id(int id, url_download_h *download) +{ + int errorcode = URL_DOWNLOAD_ERROR_NONE; + if (id <= 0) + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + errorcode = url_download_create(download); + if (errorcode == URL_DOWNLOAD_ERROR_NONE) + (*download)->requestid = id; + return errorcode; +} + +// disconnect from download-provider +int url_download_destroy(url_download_h download) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + url_download_stop(download); + + if (download->sockfd) + close(download->sockfd); + download->sockfd = 0; +// url_download_stop(download); + if (download->url) + free(download->url); + if (download->destination) + free(download->destination); + if (download->http_header) + free(download->http_header); + if (download->mime_type) + free(download->mime_type); + if (download->content_name) + free(download->content_name); + if (download->completed_path) + free(download->completed_path); + memset(&(download->callback), 0x00, sizeof(struct url_download_cb_s)); + download->id = -1; + free(download); + + download = NULL; + return URL_DOWNLOAD_ERROR_NONE; +} + +// connect to download-provider. then send request info. +int url_download_start(url_download_h download, int *id) +{ + struct sockaddr_un clientaddr; + + char **headers = NULL; + int header_length = 0; + + if (!download || !download->url) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->state == URL_DOWNLOAD_STATE_DOWNLOADING) + return url_download_error_invalid_state(__FUNCTION__, download); + + if (download->state == URL_DOWNLOAD_STATE_PAUSED) + return url_download_resume(download); + + if (download->sockfd) + close(download->sockfd); + if ((download->sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + LOGE("[%s]socket system error : %s",__FUNCTION__,strerror(errno)); + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + + bzero(&clientaddr, sizeof clientaddr); + clientaddr.sun_family = AF_UNIX; + strcpy(clientaddr.sun_path, DOWNLOAD_PROVIDER_IPC); + if (connect(download->sockfd, (struct sockaddr*)&clientaddr, sizeof(clientaddr)) < 0) { + LOGE("[%s]connect system error : %s",__FUNCTION__,strerror(errno)); + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + + download_request_info requestMsg; + memset(&requestMsg, 0x00, sizeof(download_request_info)); + requestMsg.callbackinfo.started = (download->callback.started ? 1 : 0); + requestMsg.callbackinfo.paused = (download->callback.paused ? 1 : 0); + requestMsg.callbackinfo.completed = (download->callback.completed ? 1 : 0); + requestMsg.callbackinfo.stopped = (download->callback.stopped ? 1 : 0); + requestMsg.callbackinfo.progress = (download->callback.progress ? 1 : 0); + requestMsg.notification = download->enable_notification; + + if (download->requestid > 0) + requestMsg.requestid = download->requestid; + + if (download->url) + requestMsg.url.length = strlen(download->url); + + if (download->destination) + requestMsg.install_path.length = strlen(download->destination); + + if (download->content_name) + requestMsg.filename.length = strlen(download->content_name); + + // headers test + if (url_download_get_all_http_header_fields(download, &headers, &header_length) != + URL_DOWNLOAD_ERROR_NONE) { + return url_download_error(__FUNCTION__, + URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + if (header_length > 0) { + int i=0; + requestMsg.headers.rows = header_length; + requestMsg.headers.str = (download_flexible_string*)calloc(requestMsg.headers.rows, + sizeof(download_flexible_string)); + for(i=0; i < requestMsg.headers.rows; i++) + requestMsg.headers.str[i].length = strlen(headers[i]); + } + + char *app_pkgname = NULL; + pid_t client_pid = getpid(); + int errcode = app_manager_get_package(client_pid, &app_pkgname); + if (errcode == APP_MANAGER_ERROR_NONE) + requestMsg.client_packagename.length = strlen(app_pkgname); + else + LOGE("[%s] Failed to get app_pkgname app_manager_get_package",__FUNCTION__); + + ipc_send_download_control(download, DOWNLOAD_CONTROL_START); + + if (send(download->sockfd, &requestMsg, sizeof(download_request_info), 0) < 0) { + if (app_pkgname) + free(app_pkgname); + LOGE("[%s]request send system error : %s", + __FUNCTION__, strerror(errno)); + return url_download_error(__FUNCTION__, + URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + if (requestMsg.client_packagename.length) { + if (send(download->sockfd, app_pkgname, + requestMsg.client_packagename.length * sizeof(char), 0) < 0) { + if (app_pkgname) + free(app_pkgname); + LOGE("[%s]request send system error : %s", + __FUNCTION__, strerror(errno)); + return url_download_error(__FUNCTION__, + URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + } + if (app_pkgname) + free(app_pkgname); + + if (requestMsg.url.length) { + if (send(download->sockfd, download->url, + requestMsg.url.length * sizeof(char), 0) < 0) { + LOGE("[%s]request send system error : %s", + __FUNCTION__, strerror(errno)); + return url_download_error(__FUNCTION__, + URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + } + if (requestMsg.install_path.length) { + if (send(download->sockfd, download->destination, + requestMsg.install_path.length * sizeof(char), 0) < 0) { + LOGE("[%s]request send system error : %s", + __FUNCTION__, strerror(errno)); + return url_download_error(__FUNCTION__, + URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + } + if (requestMsg.filename.length) { + if (send(download->sockfd, download->content_name, + requestMsg.filename.length * sizeof(char), 0) < 0) { + LOGE("[%s]request send system error : %s", + __FUNCTION__, strerror(errno)); + return url_download_error(__FUNCTION__, + URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + } + + if (requestMsg.headers.rows) { + int i=0; + for(i=0; i < requestMsg.headers.rows; i++) { + if (send(download->sockfd, &requestMsg.headers.str[i], + sizeof(download_flexible_string), 0) < 0) { + LOGE("[%s]request send system error : %s", + __FUNCTION__,strerror(errno)); + return url_download_error(__FUNCTION__, + URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + if (send(download->sockfd, headers[i], + requestMsg.headers.str[i].length * sizeof(char), 0) < 0) { + LOGE("[%s]request send system error : %s", + __FUNCTION__,strerror(errno)); + return url_download_error(__FUNCTION__, + URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + } + free(requestMsg.headers.str); + } + + // Sync style + if (ipc_receive_header(download->sockfd) == DOWNLOAD_CONTROL_GET_REQUEST_STATE_INFO) { + download_request_state_info requeststateinfo; + if (read(download->sockfd, &requeststateinfo, sizeof(download_request_state_info)) < 0) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + return -1; + } + if (requeststateinfo.requestid > 0) { + download->requestid = requeststateinfo.requestid; + (*id) = requeststateinfo.requestid; + } + if (requeststateinfo.stateinfo.state == DOWNLOAD_STATE_DOWNLOADING) { + // started download normally. + download->state = URL_DOWNLOAD_STATE_DOWNLOADING; + } + } else { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + url_download_stop(download); + return URL_DOWNLOAD_ERROR_IO_ERROR; + } + + // capi need the thread for listening message from download-provider; this will deal the callbacks. + if (download->callback.completed + || download->callback.stopped + || download->callback.progress + || download->callback.paused) { + // check whether event thread is alive. + if (!download->callback_thread_pid + || pthread_kill(download->callback_thread_pid, 0) != 0) { + // if want to receive the events from download-provider. + // create thread. it will listen the message through select(). + pthread_attr_t thread_attr; + if (pthread_attr_init(&thread_attr) != 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0) { + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + // create thread for receiving the client request. + if (pthread_create(&download->callback_thread_pid, &thread_attr, run_receive_event_server, download) != 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + } + return URL_DOWNLOAD_ERROR_NONE; +} + +// send pause message +int url_download_pause(url_download_h download) +{ + if (download == NULL || download->sockfd <= 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->state != URL_DOWNLOAD_STATE_DOWNLOADING) + return url_download_error_invalid_state(__FUNCTION__, download); + + ipc_send_download_control(download, DOWNLOAD_CONTROL_PAUSE); + + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_resume(url_download_h download) +{ + if (download == NULL || download->sockfd <= 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->state != URL_DOWNLOAD_STATE_PAUSED) + return url_download_error_invalid_state(__FUNCTION__, download); + + ipc_send_download_control(download, DOWNLOAD_CONTROL_RESUME); + + return URL_DOWNLOAD_ERROR_NONE; +} + + +// send stop message +int url_download_stop(url_download_h download) +{ + if (download == NULL || download->sockfd <= 0) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->state != URL_DOWNLOAD_STATE_DOWNLOADING) + return url_download_error_invalid_state(__FUNCTION__, download); + + ipc_send_download_control(download, DOWNLOAD_CONTROL_STOP); + + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_get_state(url_download_h download, url_download_state_e *state) +{ + if (download == NULL || state == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->sockfd + && download->callback_thread_pid <= 0) { // only when does not use the callback. + + ipc_send_download_control(download, DOWNLOAD_CONTROL_GET_STATE_INFO); + + // Sync style + if (ipc_receive_header(download->sockfd) == DOWNLOAD_CONTROL_GET_STATE_INFO) { + download_state_info stateinfo; + if (read(download->sockfd, &stateinfo, sizeof(download_state_info)) < 0) { + *state = download->state; + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + download->state = url_download_provider_error(stateinfo.state); + } else { + *state = download->state; + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + } + } + *state = download->state; + + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_set_url(url_download_h download, const char *url) +{ + char *url_dup = NULL; + + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + if (url != NULL) { + url_dup = strndup(url, strlen(url)); + + if (url_dup == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + + if (download->url != NULL) + free(download->url); + + download->url = url_dup; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_get_url(url_download_h download, char **url) +{ + char *url_dup = NULL; + + if (download == NULL || url == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->url != NULL) { + url_dup = strdup(download->url); + + if (url_dup == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + + *url = url_dup; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_set_destination(url_download_h download, const char *path) +{ + char *path_dup = NULL; + + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + if (path != NULL) { + path_dup = strdup(path); + + if (path_dup == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + + if (download->destination != NULL) + free(download->destination); + + download->destination = path_dup; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_get_destination(url_download_h download, char **path) +{ + char *path_dup = NULL; + + if (download == NULL || path == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->destination != NULL) { + path_dup = strdup(download->destination); + + if (path_dup == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + + *path = path_dup; + + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_set_file_name(url_download_h download, const char *file_name) +{ + if (download == NULL || file_name == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->content_name) + free(download->content_name); + download->content_name = strdup(file_name); + if (download->content_name == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_get_file_name(url_download_h download, char **file_name) +{ + char *filename_dup = NULL; + + if (download == NULL || file_name == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->content_name != NULL) { + filename_dup = strdup(download->content_name); + + if (filename_dup == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + + *file_name = filename_dup; + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_set_notification(url_download_h download, service_h service) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + if (service == NULL) + download->enable_notification = 0; + else + download->enable_notification = 1; + // service_h will be used later. it need to discuss more. + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_get_notification(url_download_h download, service_h *service) +{ + int errorcode = URL_DOWNLOAD_ERROR_NONE; + return errorcode; +} + +int url_download_get_downloaded_file(url_download_h download, char **path) +{ + char *path_dup = NULL; + + if (download == NULL || path == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->completed_path != NULL) { + path_dup = strdup(download->completed_path); + + if (path_dup == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + + *path = path_dup; + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_get_mime(url_download_h download, char **mime_type) +{ + char *mime_dup = NULL; + + if (download == NULL || mime_type == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (download->mime_type != NULL) { + mime_dup = strdup(download->mime_type); + + if (mime_dup == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + } + + *mime_type = mime_dup; + return URL_DOWNLOAD_ERROR_NONE; +} + +int url_download_add_http_header_field(url_download_h download, const char *field, const char *value) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STRING_IS_INVALID(field) || STRING_IS_INVALID(value)) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + if (bundle_get_val(download->http_header, field)) + bundle_del(download->http_header, field); + + if (bundle_add(download->http_header, field, value)) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_get_http_header_field(url_download_h download, const char *field, char **value) +{ + const char *bundle_value; + char *field_value_dup; + + if (download == NULL || value == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STRING_IS_INVALID(field)) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + bundle_value = bundle_get_val(download->http_header, field); + + if (bundle_value == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_FIELD_NOT_FOUND, NULL); + + field_value_dup = strdup(bundle_value); + + if (field_value_dup == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + + *value = field_value_dup; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_remove_http_header_field(url_download_h download, const char *field) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STRING_IS_INVALID(field)) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, "invalid field"); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + if (!bundle_get_val(download->http_header, field)) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_FIELD_NOT_FOUND, NULL); + + if (bundle_del(download->http_header, field)) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_IO_ERROR, NULL); + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_set_started_cb(url_download_h download, url_download_started_cb callback, void* user_data) +{ + if (download == NULL || callback == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.started = callback; + download->callback.started_user_data = user_data; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_unset_started_cb(url_download_h download) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) +// return url_download_error_invalid_state(__FUNCTION__, download); + url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.started = NULL; + download->callback.started_user_data = NULL; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_set_paused_cb(url_download_h download, url_download_paused_cb callback, void* user_data) +{ + if (download == NULL || callback == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.paused = callback; + download->callback.paused_user_data = user_data; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_unset_paused_cb(url_download_h download) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) +// return url_download_error_invalid_state(__FUNCTION__, download); + url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.paused = NULL; + download->callback.paused_user_data = NULL; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_set_completed_cb(url_download_h download, url_download_completed_cb callback, void* user_data) +{ + if (download == NULL || callback == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.completed = callback; + download->callback.completed_user_data = user_data; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_unset_completed_cb(url_download_h download) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) +// return url_download_error_invalid_state(__FUNCTION__, download); + url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.completed = NULL; + download->callback.completed_user_data = NULL; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_set_stopped_cb(url_download_h download, url_download_stopped_cb callback, void* user_data) +{ + if (download == NULL || callback == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.stopped = callback; + download->callback.stopped_user_data = user_data; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_unset_stopped_cb(url_download_h download) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) +// return url_download_error_invalid_state(__FUNCTION__, download); + url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.stopped = NULL; + download->callback.stopped_user_data = NULL; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_set_progress_cb(url_download_h download, url_download_progress_cb callback, void *user_data) +{ + if (download == NULL || callback == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) + return url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.progress = callback; + download->callback.progress_user_data = user_data; + + return URL_DOWNLOAD_ERROR_NONE; +} + + +int url_download_unset_progress_cb(url_download_h download) +{ + if (download == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + if (STATE_IS_RUNNING(download)) +// return url_download_error_invalid_state(__FUNCTION__, download); + url_download_error_invalid_state(__FUNCTION__, download); + + download->callback.progress = NULL; + download->callback.progress_user_data = NULL; + + return URL_DOWNLOAD_ERROR_NONE; +} + +typedef struct http_field_array_s{ + char **array; + int array_length; + int position; +} http_field_array_t; + +void url_download_get_all_http_header_fields_iterator(const char *field_name, const char *field_value, void *user_data) +{ + http_field_array_t *http_field_array; + char *field_buffer; + int field_buffer_length; + const char *field_delimiters = ": "; + + http_field_array = user_data; + + if (http_field_array == NULL) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + return; + } + + // REF : http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + field_buffer_length = strlen(field_name) + strlen(field_delimiters) + strlen(field_value) + 1; + + field_buffer = calloc(field_buffer_length, sizeof(char)); + + if (field_buffer == NULL) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + return; + } + + int len = snprintf(field_buffer, field_buffer_length, "%s%s%s", field_name, field_delimiters, field_value); + if (len == -1) { + if (field_buffer) + free(field_buffer); + return; + } else if ( len > 0 ) + field_buffer[len] = '\0'; + + http_field_array->array[http_field_array->position] = field_buffer; + http_field_array->position++; + +} + +int url_download_get_all_http_header_fields(url_download_h download, char ***fields, int *fields_length) +{ + http_field_array_t http_field_array; + + if (download == NULL || fields == NULL || fields_length == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + http_field_array.position = 0; + http_field_array.array_length = bundle_get_count(download->http_header); + http_field_array.array = calloc(http_field_array.array_length, sizeof(char*)); + + if (http_field_array.array == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_OUT_OF_MEMORY, NULL); + + bundle_iterate(download->http_header, url_download_get_all_http_header_fields_iterator, &http_field_array); + + *fields = http_field_array.array; + *fields_length = http_field_array.array_length; + + return URL_DOWNLOAD_ERROR_NONE; +} + +typedef struct { + url_download_h download; + url_download_http_header_field_cb callback; + void* user_data; + bool foreach_break; +} foreach_context_http_header_field_t; + +static void url_download_foreach_http_header_field_iterator(const char *field_name, const char *field_value, void *user_data) +{ + foreach_context_http_header_field_t *foreach_context; + + foreach_context = user_data; + + if (foreach_context == NULL) { + url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + return; + } + + + if (foreach_context->foreach_break == true) + return; + + if (foreach_context->callback != NULL) + foreach_context->foreach_break = !foreach_context->callback(foreach_context->download, + field_name, foreach_context->user_data); +} + +int url_download_foreach_http_header_field(url_download_h download, url_download_http_header_field_cb callback, void *user_data) +{ + foreach_context_http_header_field_t foreach_context = { + .download = download, + .callback = callback, + .user_data = user_data, + .foreach_break = false + }; + + if (download == NULL || callback == NULL) + return url_download_error(__FUNCTION__, URL_DOWNLOAD_ERROR_INVALID_PARAMETER, NULL); + + bundle_iterate(download->http_header, url_download_foreach_http_header_field_iterator, &foreach_context); + + return URL_DOWNLOAD_ERROR_NONE; +} +