📄 downloader.c
字号:
/* * GPAC Multimedia Framework * * Authors: Jean Le Feuvre * Copyright (c) 2005-2005 ENST * All rights reserved * * This file is part of GPAC / common tools sub-project * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <gpac/download.h>#include <gpac/network.h>#include <gpac/token.h>#include <gpac/thread.h>#include <gpac/list.h>#include <gpac/base_coding.h>#ifdef WIN32//#define GPAC_HAS_SSL#endif#ifdef GPAC_HAS_SSL#include <openssl/ssl.h>#include <openssl/x509.h>#include <openssl/err.h>#include <openssl/rand.h>#endifstatic void gf_dm_connect(GF_DownloadSession *sess);#define GF_DOWNLOAD_AGENT_NAME "GPAC/" GPAC_VERSION#define GF_DOWNLOAD_BUFFER_SIZE 8192/*internal flags*/enum{ GF_DOWNLOAD_SESSION_USE_SSL = 1<<10, GF_DOWNLOAD_SESSION_THREAD_DEAD = 1<<11, GF_DOWNLOAD_IS_ICY = 1<<12,};struct __gf_download_session{ /*this is always 0 and helps differenciating downloads from other interfaces (interfaceType != 0)*/ u32 reserved; struct __gf_download_manager *dm; GF_Thread *th; GF_Mutex *mx; Bool in_callback, destroy; char *server_name; u16 port; char *remote_path; char *user; char *passwd; char cookie[GF_MAX_PATH]; FILE *cache; char *cache_name; /*cache size if existing*/ u32 cache_start_size; GF_Socket *sock; u32 num_retry, status; char *mime_type; u32 flags; u32 total_size, bytes_done, start_time, icy_metaint, icy_count, icy_bytes; u32 bytes_per_sec, window_start, bytes_in_wnd; u32 limit_data_rate; /*0: GET 1: HEAD 2: all the rest */ u32 http_read_type; GF_Err last_error; char *init_data; u32 init_data_size;#ifdef GPAC_HAS_SSL SSL *ssl;#endif void (*do_requests)(struct __gf_download_session *); /*callback for data reception - may not be NULL*/ gf_dm_user_io user_proc; void *usr_cbk; /*private extension*/ void *ext;};struct __gf_download_manager{ char *cache_directory; char szCookieDir[GF_MAX_PATH]; Bool (*GetUserPassword)(void *usr_cbk, const char *site_url, char *usr_name, char *password); void *usr_cbk; GF_Config *cfg; GF_List *sessions;#ifdef GPAC_HAS_SSL SSL_CTX *ssl_ctx;#endif};#ifdef GPAC_HAS_SSLstatic void init_prng (void){ char namebuf[256]; const char *random_file; if (RAND_status ()) return; namebuf[0] = '\0'; random_file = RAND_file_name (namebuf, sizeof (namebuf)); if (random_file && *random_file) RAND_load_file(random_file, 16384); if (RAND_status ()) return;#ifdef WIN32 RAND_screen (); if (RAND_status ()) return;#endif}static int ssl_init(GF_DownloadManager *dm, u32 mode){ SSL_METHOD *meth; if (!dm) return 0; /* The SSL has already been initialized. */ if (dm->ssl_ctx) return 1; /* Init the PRNG. If that fails, bail out. */ init_prng(); if (RAND_status() != 1) goto error; SSL_library_init(); SSL_load_error_strings(); SSLeay_add_all_algorithms(); SSLeay_add_ssl_algorithms(); switch (mode) { case 0: meth = SSLv23_client_method(); break; case 1: meth = SSLv2_client_method(); break; case 2: meth = SSLv3_client_method(); break; case 3: meth = TLSv1_client_method(); break; default: goto error; } dm->ssl_ctx = SSL_CTX_new(meth); if (!dm->ssl_ctx) goto error; SSL_CTX_set_default_verify_paths(dm->ssl_ctx); SSL_CTX_load_verify_locations (dm->ssl_ctx, NULL, NULL); /* SSL_VERIFY_NONE instructs OpenSSL not to abort SSL_connect if the certificate is invalid. We verify the certificate separately in ssl_check_certificate, which provides much better diagnostics than examining the error stack after a failed SSL_connect. */ SSL_CTX_set_verify(dm->ssl_ctx, SSL_VERIFY_NONE, NULL); /* Since fd_write unconditionally assumes partial writes (and handles them correctly), allow them in OpenSSL. */ SSL_CTX_set_mode(dm->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); return 1;error: if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx); dm->ssl_ctx = NULL; return 0;}#endifstatic Bool gf_dm_is_local(GF_DownloadManager *dm, const char *url){ if (!strnicmp(url, "file://", 7)) return 1; if (!strnicmp(url, "file:///", 8)) return 1; if (!strstr(url, "://")) return 1; return 0;}static Bool gf_dm_can_handle_url(GF_DownloadManager *dm, const char *url){ if (!strnicmp(url, "http://", 7)) return 1;#ifdef GPAC_HAS_SSL if (!strnicmp(url, "https://", 8)) return 1;#endif return 0;}void gf_dm_configure_cache(GF_DownloadSession *sess){ u32 i, last_sep; char cache_name[GF_MAX_PATH], tmp[GF_MAX_PATH]; const char *opt; char *doubledots; if (!sess->dm->cache_directory) return; if (sess->flags & GF_NETIO_SESSION_NOT_CACHED) return; strcpy(cache_name, sess->dm->cache_directory); strcpy(tmp, sess->server_name); while ( (doubledots = strchr(tmp, ':')) ) *doubledots = '_'; strcat(tmp, sess->remote_path); last_sep = 0; for (i=0; i<strlen(tmp); i++) { if (tmp[i] == '/') tmp[i] = '_'; else if (tmp[i] == '.') { tmp[i] = '_'; last_sep = i; } } if (last_sep) tmp[last_sep] = '.'; strcat(cache_name, tmp); /*first try, check cached file*/ if (!sess->cache_start_size) { /*if file present figure out how much of the file is downloaded - we assume 2^31 byte file max*/ FILE *the_cache = fopen(cache_name, "rb"); if (the_cache) { fseek(the_cache, 0, SEEK_END); sess->cache_start_size = ftell(the_cache); fclose(the_cache); } } /*second try, disable cached file*/ else { sess->cache_start_size = 0; } sess->cache_name = strdup(cache_name); /*are we using existing cached files ?*/ opt = gf_cfg_get_key(sess->dm->cfg, "Downloader", "RestartFiles"); if (opt && !stricmp(opt, "yes")) sess->cache_start_size = 0;}static void gf_dm_disconnect(GF_DownloadSession *sess){#ifdef GPAC_HAS_SSL if (sess->ssl) { SSL_shutdown(sess->ssl); SSL_free(sess->ssl); sess->ssl = NULL; }#endif if (sess->sock) { gf_sk_del(sess->sock); sess->sock = NULL; } if (sess->cache) fclose(sess->cache); sess->cache = NULL; sess->status = GF_NETIO_DISCONNECTED; if (sess->num_retry) sess->num_retry--;}void gf_dm_sess_del(GF_DownloadSession *sess){ const char *opt; /*self-destruction, let the download manager destroy us*/ if (sess->th && sess->in_callback) { sess->destroy = 1; return; } gf_dm_disconnect(sess); /*if threaded wait for thread exit*/ if (sess->th) { while (!(sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD)) gf_sleep(1); gf_th_del(sess->th); gf_mx_del(sess->mx); } gf_list_del_item(sess->dm->sessions, sess); if (sess->cache_name) { opt = gf_cfg_get_key(sess->dm->cfg, "Downloader", "CleanCache"); if (opt && !stricmp(opt, "yes")) gf_delete_file(sess->cache_name); free(sess->cache_name); } if (sess->server_name) free(sess->server_name); if (sess->remote_path) free(sess->remote_path); if (sess->user) free(sess->user); if (sess->passwd) free(sess->passwd); if (sess->mime_type) free(sess->mime_type); if (sess->cache) fclose(sess->cache); if (sess->init_data) free(sess->init_data); free(sess);}void http_do_requests(GF_DownloadSession *sess);static void gf_dm_sess_notify_state(GF_DownloadSession *sess, u32 dnload_status, GF_Err error){ GF_NETIO_Parameter par; sess->in_callback = 1; memset(&par, 0, sizeof(GF_NETIO_Parameter)); par.msg_type = dnload_status; par.error = error; sess->user_proc(sess->usr_cbk, &par); sess->in_callback = 0;}static void gf_dm_sess_user_io(GF_DownloadSession *sess, GF_NETIO_Parameter *par){ sess->in_callback = 1; sess->user_proc(sess->usr_cbk, par); sess->in_callback = 0;}GF_Err gf_dm_sess_last_error(GF_DownloadSession *sess){ if (!sess) return GF_BAD_PARAM; return sess->last_error;}static GF_Err gf_dm_setup_from_url(GF_DownloadSession *sess, char *url){ char *tmp, tmp_url[GF_MAX_PATH]; const char *opt; if (!strnicmp(url, "http://", 7)) { url += 7; sess->port = 80; sess->do_requests = http_do_requests; } else if (!strnicmp(url, "https://", 8)) { url += 8; sess->port = 443;#ifndef GPAC_HAS_SSL return GF_NOT_SUPPORTED;#endif sess->flags |= GF_DOWNLOAD_SESSION_USE_SSL; sess->do_requests = http_do_requests; } else if (!strnicmp(url, "ftp://", 6)) { url += 6; sess->port = 21; sess->do_requests = NULL; return GF_NOT_SUPPORTED; } /*relative URL*/ else if (!strstr(url, "://")) { u32 i; if (!sess->remote_path) return GF_BAD_PARAM; tmp = gf_url_concatenate(sess->remote_path, url); free(sess->remote_path); sess->remote_path = tmp; for (i=0; i<strlen(sess->remote_path); i++) if (sess->remote_path[i]=='\\') sess->remote_path[i]='/'; } else { return GF_BAD_PARAM; } tmp = strchr(url, '/'); sess->remote_path = strdup(tmp ? tmp : "/"); if (tmp) { tmp[0] = 0; strcpy(tmp_url, url); tmp[0] = '/'; } else { strcpy(tmp_url, url); } tmp = strrchr(tmp_url, ':'); if (tmp) { sess->port = atoi(tmp+1); tmp[0] = 0; } tmp = strrchr(tmp_url, '@'); if (tmp) { sess->server_name = strdup(tmp+1); tmp[0] = 0; tmp = strchr(tmp_url, ':'); if (sess->user) free(sess->user); sess->user = NULL; if (sess->passwd) free(sess->passwd); sess->passwd = NULL; if (tmp) { sess->passwd = strdup(tmp+1); tmp[0] = 0; } sess->user = strdup(tmp_url); } else { sess->server_name = strdup(tmp_url); } /*setup BW limiter*/ sess->limit_data_rate = 0; opt = gf_cfg_get_key(sess->dm->cfg, "Downloader", "MaxRate"); if (opt) { /*use it in in BYTES per second*/ sess->limit_data_rate = 1024 * atoi(opt) / 8; } return GF_OK;}#define GF_WAIT_REPLY_SLEEP 20static u32 gf_dm_session_thread(void *par){ GF_DownloadSession *sess = (GF_DownloadSession *)par; GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] Entering thread ID %d\n", gf_th_id() )); sess->flags &= ~GF_DOWNLOAD_SESSION_THREAD_DEAD; while (!sess->destroy) { gf_mx_p(sess->mx); if (sess->status >= GF_NETIO_DISCONNECTED) { gf_mx_v(sess->mx); break; } if (sess->status < GF_NETIO_CONNECTED) { gf_dm_connect(sess); } else { if (sess->status == GF_NETIO_WAIT_FOR_REPLY) gf_sleep(GF_WAIT_REPLY_SLEEP); sess->do_requests(sess); } gf_mx_v(sess->mx); gf_sleep(2); } /*destroy all sessions*/ gf_dm_disconnect(sess); sess->status = GF_NETIO_STATE_ERROR; sess->last_error = 0; sess->flags |= GF_DOWNLOAD_SESSION_THREAD_DEAD; return 1;}#define SESSION_RETRY_COUNT 20GF_DownloadSession *gf_dm_sess_new(GF_DownloadManager *dm, char *url, u32 dl_flags, gf_dm_user_io user_io,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -