/* * 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. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include // dlopen #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xdgmime.h" #include "content/mime_type.h" #define DP_SDCARD_MNT_POINT tzplatform_mkpath(TZ_SYS_STORAGE, "sdcard") #define DP_EXTERNAL_STORAGE "/opt/media/" #define DP_MAX_FILE_PATH_LEN 256 #define DP_MAX_MIME_TABLE_NUM 15 typedef struct { const char *mime; int content_type; } mime_table_type; static const char *ambiguous_mime_type_list[] = { "text/plain", "application/octet-stream" }; static mime_table_type mime_table[] = { // PDF {"application/pdf", DP_CONTENT_PDF}, // word {"application/msword", DP_CONTENT_WORD}, {"application/vnd.openxmlformats-officedocument.wordprocessingml.document", DP_CONTENT_WORD}, // ppt {"application/vnd.ms-powerpoint", DP_CONTENT_PPT}, {"application/vnd.openxmlformats-officedocument.presentationml.presentation", DP_CONTENT_PPT}, // excel {"application/vnd.ms-excel", DP_CONTENT_EXCEL}, {"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", DP_CONTENT_EXCEL}, // html {"text/html", DP_CONTENT_HTML}, // txt {"text/txt", DP_CONTENT_TEXT}, {"text/plain", DP_CONTENT_TEXT}, // DRM {"application/vnd.oma.drm.content", DP_CONTENT_SD_DRM}, {"application/vnd.oma.drm.message", DP_CONTENT_DRM}, {"application/x-shockwave-flash", DP_CONTENT_FLASH}, {"application/vnd.tizen.package", DP_CONTENT_TPK}, {"text/calendar", DP_CONTENT_VCAL}, }; extern void *g_db_handle; static void *g_da_handle = NULL; static int (*download_agent_init)(void) = NULL; // int da_init(da_client_cb_t *da_client_callback); static int (*download_agent_deinit)(void) = NULL; // int da_deinit(); 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_suspend_without_update)(int) = NULL; // int da_suspend_download_without_update(int download_id); static int (*download_agent_cancel_without_update)(int) = NULL; // int da_cancel_download_without_update(int download_id); static int (*download_agent_start)(const char *url, req_data_t *ext_data, da_cb_t *da_cb_data, int *download_id) = NULL; // int da_start_download_with_extension(const char *url, extension_data_t *ext_data, int *download_id); static dp_content_type __dp_get_content_type(const char *mime, const char *file_path); 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_NETWORK_ERROR; break; case DA_ERR_CONNECTION_FAIL: case DA_ERR_NETWORK_UNAUTHORIZED: 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_ACCESS_FILE: default: ret = DP_ERROR_IO_ERROR; break; } return ret; } static int __dp_da_state_feedback(dp_client_slots_fmt *slot, dp_request_fmt *request) { if (slot == NULL || request == NULL) { TRACE_ERROR("check address"); return -1; // try cancel } TRACE_INFO("[INFO][%d] state:%s error:%s", request->id, dp_print_state(request->state), dp_print_errorcode(request->error)); int errorcode = DP_ERROR_NONE; if (dp_db_update_logging(slot->client.dbhandle, request->id, request->state, request->error, &errorcode) < 0) { TRACE_ERROR("logging failure id:%d error:%d", request->id, errorcode); return -1; // try cancel } request->access_time = (int)time(NULL); if (request->state_cb == 1) { if (slot->client.notify < 0 || dp_notify_feedback(slot->client.notify, slot, request->id, request->state, request->error, 0) < 0) { TRACE_ERROR("id:%d disable state callback by IO_ERROR", request->id); request->state_cb = 0; } } return 0; } static int __precheck_request(dp_client_slots_fmt* slot, dp_request_fmt *request, int agentid) { if (request == NULL) { TRACE_ERROR("null-check req_id:%d", agentid); return -1; } dp_client_fmt *client = &slot->client; for (dp_request_fmt *req = client->requests; req; req = req->next) { if (req == request) { if (request->id < 0 || (request->agent_id != agentid)) { TRACE_ERROR("id-check request_id:%d agent_id:%d req_id:%d", request->id, request->agent_id, agentid); return -1; } return 0; } } return -1; } static int __set_file_permission_to_client(dp_client_slots_fmt *slot, dp_request_fmt *request, char *saved_path) { if (slot == NULL || request == NULL || saved_path == NULL) { TRACE_ERROR("slot:%p request:%p id:%d agentid:%d saved_path:%s", slot, request, (request == NULL ? 0 : request->id), (request == NULL ? 0 : request->agent_id), saved_path ? saved_path : ""); return DP_ERROR_INVALID_PARAMETER; } if (strncmp(saved_path, DP_EXTERNAL_STORAGE, strlen(DP_EXTERNAL_STORAGE)) == 0 || strncmp(DP_SDCARD_MNT_POINT, saved_path, strlen(DP_SDCARD_MNT_POINT)) == 0) { TRACE_INFO("Do not change permission for %s", saved_path); return DP_ERROR_NONE; } struct stat lstat_info; int fd; dp_credential cred = slot->credential; if (lstat(saved_path, &lstat_info) == -1) { TRACE_ERROR("Cannot access to %s", saved_path); return DP_ERROR_PERMISSION_DENIED; } if ((lstat_info.st_mode & S_IFMT) == S_IFLNK) { TRACE_ERROR("%s is a symbolic link.", saved_path); return DP_ERROR_PERMISSION_DENIED; } fd = open(saved_path, O_RDONLY); if (fd == -1) { TRACE_SECURE_ERROR("open failed for file : %s", saved_path); return DP_ERROR_IO_ERROR; } // Change the owner to client's uid. if (fchown(fd, cred.uid, lstat_info.st_gid) != 0) { TRACE_ERROR("[ERROR][%d] permission user:%d group:%d", request->id, cred.uid, lstat_info.st_gid); close(fd); return DP_ERROR_PERMISSION_DENIED; } TRACE_INFO("file owner has been changed to %d:%d", cred.uid, lstat_info.st_gid); close(fd); return DP_ERROR_NONE; } static char *__dp_get_cache_directive_string(const char *cache_control, const char *directive) { if (!cache_control || !directive) return NULL; char *str = strstr(cache_control, directive); return str; } static long __calculate_heuristic_freshness(const char *last_modified) { if (!last_modified) return 0; struct tm tm = {0}; strptime(last_modified, "%a, %d %b %Y %H:%M:%S %Z", &tm); time_t now_time = time(NULL); time_t last_time = timegm(&tm); long freshness_time = (long)difftime(now_time, last_time); // calculate the heuristic freshness as 10% of the freshness time. long heuristic_time = freshness_time / 10.0; return heuristic_time; } static long __dp_determine_cache_max_age(const char *cache_control, const char *last_modified) { if (cache_control && !__dp_get_cache_directive_string(cache_control, CACHE_CONTROL_NO_CACHE)) { char *str = __dp_get_cache_directive_string(cache_control, CACHE_CONTROL_MAX_AGE); if (str) return strtol(str + strlen(CACHE_CONTROL_MAX_AGE), NULL, 10); } return __calculate_heuristic_freshness(last_modified); } static int __dp_store_cache_to_db(const char *package, const char *url, const char *ori_file_name, const char *file_name, const char *etag, const long max_age, const unsigned long long file_size, int *error) { *error = DP_ERROR_NONE; if (dp_db_new_app_cache(g_db_handle, package, url, file_name, true, error) < 0) { TRACE_ERROR("failed to add new app cache"); return -1; } if (dp_db_new_file_cache(g_db_handle, file_name, ori_file_name, etag, max_age, file_size, (const long)time(NULL), error) < 0) { TRACE_ERROR("failed to add new file cache"); } if (*error != DP_ERROR_NONE) return -1; return 0; } static int __dp_update_cache_from_db(const char *url, const char *file_name, const char *new_file_name, const char *etag, const long max_age, const unsigned long long file_size, int *error) { *error = DP_ERROR_NONE; if (strcmp(file_name, new_file_name) != 0) { if (dp_db_update_app_cache(g_db_handle, url, new_file_name, error) < 0) { TRACE_ERROR("failed to update app cache"); return -1; } } int id = dp_db_get_cache_file_id(g_db_handle, file_name, error); if (id < 0) { TRACE_ERROR("failed to get cache file id"); return -1; } if (dp_db_update_file_cache(g_db_handle, id, new_file_name, etag, max_age, file_size, (const long)time(NULL), error) < 0) { TRACE_ERROR("failed to update file cache"); } if (*error != DP_ERROR_NONE) return -1; return 0; } static int __dp_remove_old_file(char *file_name) { char **file_ptr = (char **)malloc(sizeof(char *)); if (!file_ptr) { TRACE_ERROR("failed to allocate the file_array"); return -1; } *file_ptr = file_name; if (dp_remove_files(1, (const char **)file_ptr) < 0) { TRACE_ERROR("failed to remove cache file"); free(file_ptr); return -1; } free(file_ptr); return 0; } static int __dp_store_to_cache(const char *package, const char *url, const char *ori_file, const char *cache_control, const char *etag, const char *last_modified, const char *saved_path, const unsigned long long file_size, int *error) { *error = DP_ERROR_INVALID_PARAMETER; if (!package || !url || !ori_file || !saved_path) { TRACE_ERROR("NULL CHECK!: package, url, ori_file or saved_path"); return -1; } *error = DP_ERROR_NONE; char *file_name = NULL; unsigned length = 0; int pkg_exist = 0; int found = dp_db_get_cache_file_name(g_db_handle, package, url, &file_name, &length, &pkg_exist, error); if (found < 0) { TRACE_ERROR("failed to get cache_file_name"); return -1; } if (found > 0) { if (__dp_remove_old_file(file_name) < 0) { TRACE_ERROR("failed to remove old files"); free(file_name); return -1; } } char *new_file_name = dp_store_file(saved_path); if (!new_file_name) { TRACE_ERROR("failed to store file"); *error = DP_ERROR_DISK_BUSY; free(file_name); return -1; } long max_age = __dp_determine_cache_max_age(cache_control, last_modified); if (found == 0) { if (__dp_store_cache_to_db(package, url, ori_file, new_file_name, etag, (const long)max_age, file_size, error) < 0) TRACE_ERROR("failed to store new cache data to db"); } else { if (__dp_update_cache_from_db(url, file_name, new_file_name, etag, (const long)max_age, file_size, error) < 0) TRACE_ERROR("failed to update cache data from db"); } free(file_name); free(new_file_name); if (*error != DP_ERROR_NONE) return -1; return 0; } // return url only when cache feature is enabled. static char *__dp_get_cache_key_url(void *client_db_handle, const int req_id, int *error) { *error = DP_ERROR_INVALID_PARAMETER; if (!client_db_handle) { TRACE_ERROR("NULL CHECK!: client_db_handle"); return NULL; } if (req_id < 0) { TRACE_ERROR("check req_id"); return NULL; } int cache = 0; unsigned length = 0; char *url = NULL; *error = DP_ERROR_NONE; if (dp_db_get_property_int(client_db_handle, req_id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&cache, error) < 0 || cache <= 0) { TRACE_ERROR("failed to get cache value or cache is not enabled for id:%d", req_id); return NULL; } if (dp_db_get_property_string(client_db_handle, req_id, DP_TABLE_REQUEST, DP_DB_COL_URL, (unsigned char **)&url, &length, error) < 0 || !url) { TRACE_ERROR("failed to get url. id:%d", req_id); return NULL; } return url; } static char *__get_ori_file_name(const char *file_name) { int errorcode = DP_ERROR_INVALID_PARAMETER; if (!file_name) { TRACE_ERROR("NULL CHECK!: file_name"); return NULL; } char *ori_file = NULL; unsigned len = 0; if (dp_db_get_file_cache_string(g_db_handle, file_name, DP_DB_COL_FILENAME, (unsigned char **)&ori_file, &len, &errorcode) < 0 || !ori_file) { return NULL; } return ori_file; } static int __dp_try_cache_download(void *slot, void *request) { int errorcode = DP_ERROR_INVALID_PARAMETER; if (!slot || !request) { TRACE_ERROR("NULL CHECK!: slot or request"); return errorcode; } dp_client_slots_fmt *req_slot = slot; dp_request_fmt *req = request; char *url = __dp_get_cache_key_url(req_slot->client.dbhandle, req->id, &errorcode); if (!url) { TRACE_ERROR("failed to get cache key url"); return errorcode; } char *file_name = NULL; unsigned length = 0; int pkg_exist = 0; int ret = dp_db_get_cache_file_name(g_db_handle, req_slot->pkgname, url, &file_name, &length, &pkg_exist, &errorcode); if (ret <= 0 || !file_name) { TRACE_ERROR("failed to get cache_file_name"); free(url); return errorcode; } char *ori_file = __get_ori_file_name(file_name); if (!ori_file) { TRACE_ERROR("failed to get original file name"); errorcode = DP_ERROR_NO_DATA; return errorcode; } req->file_name = ori_file; errorcode = dp_start_cache_download(file_name, req_slot, req); free(url); free(file_name); return errorcode; } static int __dp_update_cache_validator(void *slot, void *request, const char *cache_control, const char *etag) { int errorcode = DP_ERROR_INVALID_PARAMETER; if (!slot || !request) { TRACE_ERROR("NULL CHECK!: slot or request"); return errorcode; } dp_client_slots_fmt *req_slot = slot; dp_request_fmt *req = request; char *url = __dp_get_cache_key_url(req_slot->client.dbhandle, req->id, &errorcode); if (!url) { TRACE_ERROR("failed to get cache key url for id:%d", req->id); return errorcode; } char *file_name = NULL; unsigned length = 0; int pkg_exist = 0; if (dp_db_get_cache_file_name(g_db_handle, req_slot->pkgname, url, &file_name, &length, &pkg_exist, &errorcode) < 0 || !file_name) { TRACE_ERROR("failed to get cache file name"); free(url); return errorcode; } int id = dp_db_get_cache_file_id(g_db_handle, file_name, &errorcode); if (id < 0) { TRACE_ERROR("failed to get cache file id"); goto done; } long max_age = 0; if (cache_control) { char *str = __dp_get_cache_directive_string(cache_control, CACHE_CONTROL_MAX_AGE); if (str) max_age = strtol(str + strlen(CACHE_CONTROL_MAX_AGE), NULL, 10); } if (max_age > 0) { if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_CACHE_MAX_AGE, 1, (void *)&max_age, &errorcode) < 0) { TRACE_ERROR("failed to update max_age"); goto done; } } if (etag) { if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_ETAG, 2, (void *)etag, &errorcode) < 0) { TRACE_ERROR("failed to update etag"); goto done; } } long current_time = (long)time(NULL); if (dp_db_update_file_cache_validator(g_db_handle, id, DP_DB_COL_CACHE_LAST_UPDATE, 1, (void *)¤t_time, &errorcode) < 0) { TRACE_ERROR("failed to update last_update"); goto done; } done: free(url); free(file_name); return errorcode; } static void __finished_cb(finished_info_t *info, void *user_req_data, void *user_client_data) { if (info == NULL) { TRACE_ERROR("check download info address"); return ; } dp_client_slots_fmt *slot = user_client_data; dp_request_fmt *request = user_req_data; if (slot == NULL || request == NULL) { TRACE_ERROR("check address slot:%p request:%p id:%d agentid:%d", slot, request, (request == NULL ? 0 : request->id), info->download_id); free(info->etag); free(info->cache_control); free(info->last_modified); free(info->saved_path); free(info->ori_file); free(info); return ; } CLIENT_MUTEX_LOCK(&slot->mutex); if (__precheck_request(slot, request, info->download_id) < 0) { TRACE_ERROR("error request agent_id:%d", info->download_id); if (dp_cancel_agent_download(info->download_id) < 0) TRACE_ERROR("failed to call cancel_download(%d)", info->download_id); free(info->etag); free(info->cache_control); free(info->last_modified); free(info->saved_path); free(info->ori_file); free(info); CLIENT_MUTEX_UNLOCK(&slot->mutex); return ; } int state = DP_STATE_NONE; int errorcode = DP_ERROR_NONE; if (info->http_status > 0) { if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_HTTP_STATUS, (void *)&info->http_status, 0, 0, &errorcode) < 0) TRACE_ERROR("id:%d failed to set http_status(%d)", request->id, info->http_status); } TRACE_DEBUG("cache_control: %s, last_modified: %s", info->cache_control, info->last_modified); TRACE_SECURE_DEBUG("[FINISH][%d][%s]", request->id, info->saved_path); if (info->err == DA_RESULT_OK) { if (info->http_status == 304) { errorcode = __dp_update_cache_validator(slot, request, info->cache_control, info->etag); if (errorcode != DP_ERROR_NONE) { state = DP_STATE_FAILED; } else { errorcode = __dp_try_cache_download(slot, request); state = (errorcode == DP_ERROR_NONE) ? DP_STATE_COMPLETED : DP_STATE_FAILED; free(info->etag); free(info->cache_control); free(info->last_modified); free(info->saved_path); free(info->ori_file); free(info); CLIENT_MUTEX_UNLOCK(&slot->mutex); return; } } if (info->saved_path != NULL) { errorcode = __set_file_permission_to_client(slot, request, info->saved_path); } else { if (state != DP_STATE_COMPLETED) { TRACE_ERROR("[ERROR][%d] No SavedPath", request->id); errorcode = DP_ERROR_INVALID_DESTINATION; } } if (errorcode == DP_ERROR_NONE) state = DP_STATE_COMPLETED; else state = DP_STATE_FAILED; } else { if (info->err == DA_RESULT_USER_CANCELED) { TRACE_INFO("[CANCELED][%d]", request->id); // if state is canceled and error is DP_ERROR_IO_EAGAIN mean ip_changed if (request->error == DP_ERROR_IO_EAGAIN) { request->error = DP_ERROR_NONE; } else { state = DP_STATE_CANCELED; errorcode = request->error; } } else { state = DP_STATE_FAILED; errorcode = __change_error(info->err); TRACE_ERROR("[FAILED][%d][%s] agent error:%d", request->id, dp_print_errorcode(errorcode), info->err); } } if (errorcode == DP_ERROR_NONE && info->saved_path != NULL) { char *content_name = NULL; char *str = NULL; str = strrchr(info->saved_path, '/'); if (str != NULL) { str++; content_name = dp_strdup(str); } if (request->file_size == 0) {// missed in download_cb request->file_size = request->received_size; if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_SIZE, (void *)&request->file_size, 0, 1, &errorcode) < 0) TRACE_ERROR("id:%d failed to set content_size(%llu)", request->id, request->file_size); } // update contentname, savedpath if (content_name != NULL) { if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_NAME, (void *)content_name, 0, 2, &errorcode) < 0) TRACE_ERROR("id:%d failed to set content_name", request->id); } if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_SAVED_PATH, (void *)info->saved_path, 0, 2, &errorcode) < 0) TRACE_ERROR("id:%d failed to set saved_path", request->id); free(content_name); // if cache is enabled, store to cache. int cache = 0; if (dp_db_get_property_int(slot->client.dbhandle, request->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&cache, &errorcode) < 0) TRACE_DEBUG("unable to get cache value for id:%d", request->id); if (cache && !__dp_get_cache_directive_string(info->cache_control, CACHE_CONTROL_NO_STORE)) { char *url = __dp_get_cache_key_url(slot->client.dbhandle, request->id, &errorcode); if (url) { if (__dp_store_to_cache(slot->pkgname, url, info->ori_file, info->cache_control, info->etag, info->last_modified, info->saved_path, request->file_size, &errorcode) < 0) TRACE_ERROR("failed to store cache for id:%d", request->id); free(url); } } /* update the received file size. * The last received file size cannot update * because of reducing update algorithm*/ if (request->progress_cb == 1) { if (slot->client.notify < 0 || dp_notify_feedback(slot->client.notify, slot, request->id, DP_STATE_DOWNLOADING, DP_ERROR_NONE, request->received_size) < 0) { TRACE_ERROR("id:%d disable progress callback by IO_ERROR", request->id); request->progress_cb = 0; } } } request->state = state; request->error = errorcode; if (__dp_da_state_feedback(slot, request) < 0) { TRACE_ERROR("id:%d check notify channel", request->id); if (dp_cancel_agent_download_without_update(request->agent_id) < 0) TRACE_ERROR("[fail][%d]cancel_agent", request->id); } if (request->noti_type == DP_NOTIFICATION_TYPE_COMPLETE_ONLY || request->noti_type == DP_NOTIFICATION_TYPE_ALL) { if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION) < 0) TRACE_ERROR("failed to register notification for id:%d", request->id); } free(info->etag); free(info->cache_control); free(info->last_modified); free(info->saved_path); free(info->ori_file); free(info); CLIENT_MUTEX_UNLOCK(&slot->mutex); } static void __paused_cb(int download_id, void *user_req_data, void *user_client_data) { dp_client_slots_fmt *slot = user_client_data; dp_request_fmt *request = user_req_data; if (slot == NULL || request == NULL) { TRACE_ERROR("check address slot:%p request:%p id:%d agentid:%d", slot, request, (request == NULL ? 0 : request->id), download_id); return ; } dp_queue_manager_wake_up(); } static void __download_info_cb(download_info_t *info, void *user_req_data, void *user_client_data) { if (info == NULL) { TRACE_ERROR("check download info address"); return ; } dp_client_slots_fmt *slot = user_client_data; dp_request_fmt *request = user_req_data; if (slot == NULL || request == NULL) { TRACE_ERROR("check address slot:%p request:%p id:%d agentid:%d", slot, request, (request == NULL ? 0 : request->id), info->download_id); free(info->content_name); free(info->etag); free(info->file_type); free(info->tmp_saved_path); free(info); return ; } CLIENT_MUTEX_LOCK(&slot->mutex); if (__precheck_request(slot, request, info->download_id) < 0) { TRACE_ERROR("error request agent_id:%d", info->download_id); if (dp_cancel_agent_download(info->download_id) < 0) TRACE_ERROR("failed to call cancel_download(%d)", info->download_id); free(info->content_name); free(info->etag); free(info->file_type); free(info->tmp_saved_path); free(info); CLIENT_MUTEX_UNLOCK(&slot->mutex); return ; } // update info before sending event TRACE_SECURE_DEBUG("[DOWNLOAD][%d][%s]", request->id, info->tmp_saved_path); if (info->tmp_saved_path != NULL) { int errorcode = DP_ERROR_NONE; if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_MIMETYPE, (void *)info->file_type, 0, 2, &errorcode) < 0) TRACE_ERROR("id:%d failed to set mimetype", request->id); if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_NAME, (void *)info->content_name, 0, 2, &errorcode) < 0) TRACE_ERROR("id:%d failed to set contentname", request->id); if (dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_TMP_SAVED_PATH, (void *)info->tmp_saved_path, 0, 2, &errorcode) < 0) TRACE_ERROR("id:%d failed to set tmp_saved_path", request->id); if (info->file_size > 0 && dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_SIZE, (void *)&(info->file_size), 0, 1, &errorcode) < 0) TRACE_ERROR("id:%d failed to set file size", request->id); if (info->etag && dp_db_replace_property(slot->client.dbhandle, request->id, DP_TABLE_DOWNLOAD, DP_DB_COL_ETAG, (void *)info->etag, 0, 2, &errorcode) < 0) TRACE_ERROR("id:%d failed to set etag", request->id); if (strncmp(DP_SDCARD_MNT_POINT, info->tmp_saved_path, strlen(DP_SDCARD_MNT_POINT)) != 0) errorcode = __set_file_permission_to_client(slot, request, info->tmp_saved_path); request->error = errorcode; } else { request->error = DP_ERROR_IO_ERROR; } if (request->error != DP_ERROR_NONE) { request->state = DP_STATE_FAILED; TRACE_ERROR("id:%d try to cancel(%d)", request->id, info->download_id); if (dp_cancel_agent_download(request->agent_id) < 0) { TRACE_ERROR("[fail][%d] cancel_agent:%d", request->id, info->download_id); } } else { request->state = DP_STATE_DOWNLOADING; request->file_size = info->file_size; // unsigned TRACE_DEBUG("[STARTED] id:%d agentid:%d", request->id, info->download_id); } if (__dp_da_state_feedback(slot, request) < 0) { TRACE_ERROR("id:%d check notify channel", request->id); if (dp_cancel_agent_download(request->agent_id) < 0) TRACE_ERROR("[fail][%d]cancel_agent", request->id); } // notification if (request->noti_type == DP_NOTIFICATION_TYPE_ALL) { if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION_ONGOING_UPDATE) < 0) TRACE_ERROR("failed to register notification for id:%d", request->id); } //get the mime type for dp notification if (request->noti_type > DP_NOTIFICATION_TYPE_NONE) request->content_type = __dp_get_content_type(info->file_type, info->tmp_saved_path); free(info->content_name); free(info->etag); free(info->file_type); free(info->tmp_saved_path); free(info); CLIENT_MUTEX_UNLOCK(&slot->mutex); } static void __progress_cb(int download_id, unsigned long long received_size, void *user_req_data, void *user_client_data) { dp_client_slots_fmt *slot = user_client_data; dp_request_fmt *request = user_req_data; if (slot == NULL || request == NULL) { TRACE_ERROR("check address slot:%p request:%p id:%d agentid:%d", slot, request, (request == NULL ? 0 : request->id), download_id); return ; } CLIENT_MUTEX_LOCK(&slot->mutex); /* if (CLIENT_MUTEX_TRYLOCK(&slot->mutex) != 0) { TRACE_ERROR("slot busy agent_id:%d", download_id); return ; } */ if (__precheck_request(slot, request, download_id) < 0) { TRACE_ERROR("error request agent_id:%d", download_id); if (dp_cancel_agent_download(download_id) < 0) TRACE_ERROR("failed to call cancel_download(%d)", download_id); CLIENT_MUTEX_UNLOCK(&slot->mutex); return ; } TRACE_DEBUG("state: %d, progress %llu", request->state, received_size); // For resume case after pause, it change state from connecting to downloading if (request->state == DP_STATE_CONNECTING) { request->state = DP_STATE_DOWNLOADING; if (__dp_da_state_feedback(slot, request) < 0) { TRACE_ERROR("id:%d check notify channel", request->id); if (dp_cancel_agent_download(request->agent_id) < 0) TRACE_ERROR("[fail][%d]cancel_agent", request->id); } } if (request->state == DP_STATE_DOWNLOADING) { request->received_size = received_size; time_t tt = time(NULL); struct tm localTime ; if (localtime_r(&tt, &localTime) == NULL) { TRACE_ERROR("Error localtime_r"); CLIENT_MUTEX_UNLOCK(&slot->mutex); return; } // send event every 1 second. if (request->progress_lasttime != localTime.tm_sec) { request->progress_lasttime = localTime.tm_sec; if (request->progress_cb == 1) { if (slot->client.notify < 0 || dp_notify_feedback(slot->client.notify, slot, request->id, DP_STATE_DOWNLOADING, DP_ERROR_NONE, received_size) < 0) { // failed to read from socket // ignore this status TRACE_ERROR("id:%d disable progress callback by IO_ERROR", request->id); request->progress_cb = 0; } } if (request->noti_type == DP_NOTIFICATION_TYPE_ALL) { if (dp_notification_manager_push_notification(slot, request, DP_NOTIFICATION_ONGOING_PROGRESS) < 0) TRACE_ERROR("failed to register notification for id:%d", request->id); } } } CLIENT_MUTEX_UNLOCK(&slot->mutex); } static int __dp_is_ambiguous_mime_type(const char *mime_type) { if (mime_type == NULL) return -1; int index = 0; int listSize = sizeof(ambiguous_mime_type_list) / sizeof(const char *); for (index = 0; index < listSize; index++) { if (0 == strcmp(mime_type, ambiguous_mime_type_list[index])) { TRACE_DEBUG("It is ambiguous"); return 0; } } return -1; } static dp_content_type __dp_get_content_type(const char *mime, const char *file_path) { int i = 0; int type = DP_CONTENT_UNKNOWN; char *temp_mime = NULL; if (mime == NULL || strlen(mime) < 1) return DP_CONTENT_UNKNOWN; if ((file_path != NULL) && (strlen(file_path) > 0) && (__dp_is_ambiguous_mime_type(mime) == 0)) { const char *ext = strrchr(file_path, '.'); if (ext == NULL) { TRACE_ERROR("File Extension is NULL"); return type; } mime_type_get_mime_type(ext + 1, &temp_mime); } if (temp_mime == NULL) { temp_mime = (char *)calloc(1, DP_MAX_FILE_PATH_LEN); if (temp_mime == NULL) { TRACE_ERROR("Fail to call calloc"); return type; } strncpy(temp_mime, mime, DP_MAX_FILE_PATH_LEN - 1); } TRACE_SECURE_DEBUG("mime type [%s]", temp_mime); /* Search a content type from mime table. */ for (i = 0; i < DP_MAX_MIME_TABLE_NUM; i++) { if (strcmp(mime_table[i].mime, temp_mime) == 0) { type = mime_table[i].content_type; break; } } if (type == DP_CONTENT_UNKNOWN) { const char *unaliased_mime = NULL; /* unaliased_mimetype means representative mime among similar types */ unaliased_mime = xdg_mime_unalias_mime_type(temp_mime); if (unaliased_mime != NULL) { TRACE_SECURE_DEBUG("unaliased mime type[%s]", unaliased_mime); if (strstr(unaliased_mime, "video/") != NULL) type = DP_CONTENT_VIDEO; else if (strstr(unaliased_mime, "audio/") != NULL) type = DP_CONTENT_MUSIC; else if (strstr(unaliased_mime, "image/") != NULL) type = DP_CONTENT_IMAGE; } } free(temp_mime); TRACE_DEBUG("type[%d]", type); return type; } int dp_init_agent() { g_da_handle = dlopen(LIB_AGENT_PATH, 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_without_update"); *(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"); if (download_agent_start == NULL) { TRACE_ERROR("[dlsym] da_start_download:%s", dlerror()); dlclose(g_da_handle); g_da_handle = NULL; return DP_ERROR_OUT_OF_MEMORY; } *(void **) (&download_agent_cancel_without_update) = dlsym(g_da_handle, "da_cancel_download_without_update"); if (download_agent_cancel_without_update == NULL) { TRACE_ERROR("[dlsym] da_cancel_download_without_update:%s", dlerror()); dlclose(g_da_handle); g_da_handle = NULL; return DP_ERROR_OUT_OF_MEMORY; } *(void **) (&download_agent_suspend_without_update) = dlsym(g_da_handle, "da_suspend_download_without_update"); if (download_agent_suspend_without_update == NULL) { TRACE_ERROR("[dlsym] da_suspend_download_without_update:%s", dlerror()); dlclose(g_da_handle); g_da_handle = NULL; return DP_ERROR_OUT_OF_MEMORY; } int da_ret = -1; da_ret = (*download_agent_init)(); 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)(); // Do not unload a symbol file here. //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) return 0; if (download_agent_is_alive != NULL) da_ret = (*download_agent_is_alive)(req_id); return da_ret; } // 0 : success // -1 : failed int 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 int 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 int dp_cancel_agent_download_without_update(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_without_update != NULL) { if ((*download_agent_cancel_without_update)(req_id) == DA_RESULT_OK) return 0; } return -1; } // 0 : success // -1 : failed int dp_pause_agent_download_without_update(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_without_update != NULL) { if ((*download_agent_suspend_without_update)(req_id) == DA_RESULT_OK) return 0; } return -1; } // 0 : success // -1 : failed // -2 : pended int dp_start_agent_download(void *slot, void *request) { int da_ret = -1; int req_dl_id = -1; req_data_t *req_data = NULL; dp_client_slots_fmt *base_slot = slot; dp_request_fmt *base_req = request; if (slot == NULL || request == NULL) { TRACE_ERROR("check slot or request address"); return DP_ERROR_INVALID_PARAMETER; } da_cb_t da_cb = { __download_info_cb, __progress_cb, __finished_cb, __paused_cb }; req_data = (req_data_t *)calloc(1, sizeof(req_data_t)); if (req_data == NULL) { TRACE_ERROR("[ERROR] calloc"); return DP_ERROR_OUT_OF_MEMORY; } int errorcode = DP_ERROR_NONE; unsigned length = 0; char *url = NULL; char *proxy = NULL; char *destination = NULL; char *tmp_saved_path = NULL; char *user_tmp_file_path = NULL; char *filename = NULL; char *etag = NULL; int user_network_bonding = 0; int user_cache = 0; if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_URL, (unsigned char **)&url, &length, &errorcode) < 0 || url == NULL) { free(req_data); if (errorcode == DP_ERROR_NO_DATA) { TRACE_ERROR("url id:%d NO_DATA", base_req->id); return DP_ERROR_INVALID_URL; } else { TRACE_ERROR("url id:%d DISK_BUSY", base_req->id); return DP_ERROR_DISK_BUSY; } } if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_PROXY, (unsigned char **)&proxy, &length, &errorcode) < 0 || proxy == NULL) { TRACE_DEBUG("proxy id:%d NO_DATA", base_req->id); req_data->proxy = NULL; } else { req_data->proxy = proxy; } if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_TEMP_FILE_PATH, (unsigned char **)&user_tmp_file_path, &length, &errorcode) < 0 || user_tmp_file_path == NULL) { TRACE_DEBUG("user_tmp_file_path id:%d NO_DATA", base_req->id); } if (user_tmp_file_path != NULL) req_data->temp_file_path = user_tmp_file_path; else { if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_FILENAME, (unsigned char **)&filename, &length, &errorcode) < 0 || filename == NULL) { TRACE_DEBUG("filename id:%d NO_DATA", base_req->id); } else req_data->file_name = filename; if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_DESTINATION, (unsigned char **)&destination, &length, &errorcode) < 0 || destination == NULL) { TRACE_DEBUG("destination id:%d NO_DATA", base_req->id); } else req_data->install_path = destination; if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_DOWNLOAD, DP_DB_COL_TMP_SAVED_PATH, (unsigned char **)&tmp_saved_path, &length, &errorcode) < 0 || tmp_saved_path == NULL) { TRACE_DEBUG("tmp_saved_path id:%d NO_DATA", base_req->id); } } if (tmp_saved_path != NULL) { if (dp_db_get_property_string(base_slot->client.dbhandle, base_req->id, DP_TABLE_DOWNLOAD, DP_DB_COL_ETAG, (unsigned char **)&etag, &length, &errorcode) < 0 || etag == NULL) { TRACE_DEBUG("etag id:%d NO_DATA", base_req->id); } if (etag != NULL) { TRACE_DEBUG("try to resume id:%d", base_req->id); req_data->etag = etag; req_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("try to restart id:%d remove tmp file:%s", base_req->id, tmp_saved_path); if (dp_is_file_exist(tmp_saved_path) == 0) { if (unlink(tmp_saved_path) != 0) TRACE_ERROR("failed to remove file id:%d", base_req->id); } } } if (dp_db_get_property_int(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_NETWORK_BONDING, (int *)&user_network_bonding, &errorcode) < 0 || user_network_bonding < 0) { TRACE_DEBUG("unable to get network bonding value for id:%d", base_req->id); } else req_data->network_bonding = user_network_bonding; if (dp_db_get_property_int(base_slot->client.dbhandle, base_req->id, DP_TABLE_REQUEST, DP_DB_COL_CACHE, (int *)&user_cache, &errorcode) < 0 || user_cache < 0) { TRACE_DEBUG("unable to get cache value for id:%d", base_req->id); } else { req_data->cache = user_cache; } req_data->disable_verify_host = base_req->disable_verify_host; req_data->pkg_name = base_slot->pkgname; // get headers list from header table(DB) int headers_count = dp_db_check_duplicated_int(base_slot->client.dbhandle, DP_TABLE_HEADERS, DP_DB_COL_ID, base_req->id, &errorcode); if (headers_count > 0) { req_data->request_header = calloc(headers_count, sizeof(char *)); if (req_data->request_header != NULL) { headers_count = dp_db_get_http_headers_list(base_slot->client.dbhandle, base_req->id, (char **)req_data->request_header, &errorcode); if (headers_count > 0) req_data->request_header_count = headers_count; } } req_data->user_client_data = (void *)slot; req_data->user_req_data = (void *)request; // call start API of agent lib if (download_agent_start != NULL) da_ret = (*download_agent_start)(url, req_data, &da_cb, &req_dl_id); if (req_data->request_header_count > 0) { int len = 0; int i = 0; len = req_data->request_header_count; if (req_data->request_header != NULL) { for (i = 0; i < len; i++) free((void *)(req_data->request_header[i])); } free(req_data->request_header); } free(url); free(destination); free(tmp_saved_path); free(user_tmp_file_path); free(filename); free(etag); free(req_data); if (da_ret == DA_RESULT_OK) { TRACE_DEBUG("request id :%d SUCCESS agent_id:%d", base_req->id, req_dl_id); base_req->agent_id = req_dl_id; } return __change_error(da_ret); } int 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); }